Merge mozilla-central to b2g-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 12 Mar 2014 12:34:49 +0100
changeset 173241 b8368b68d2a2de6e2bf2bc6de893ff3bca6d27f7
parent 173240 9cafc9d40f7859204887955019df06558fee4ae3 (current diff)
parent 173170 a1639dd9dd3dd7b77090039b55675086ddc3cbe6 (diff)
child 173242 7d5b385c54221f5490739ff30887488955addd16
push id26397
push userkwierso@gmail.com
push dateThu, 13 Mar 2014 02:51:22 +0000
treeherdermozilla-central@c12c92db5588 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone30.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to b2g-inbound
netwerk/sctp/src/netinet/sctp_dtrace_declare.h
netwerk/sctp/src/netinet/sctp_dtrace_define.h
netwerk/sctp/src/netinet/sctp_hashdriver.c
netwerk/sctp/src/netinet/sctp_hashdriver.h
--- a/b2g/chrome/content/devtools.js
+++ b/b2g/chrome/content/devtools.js
@@ -35,16 +35,17 @@ XPCOMUtils.defineLazyGetter(this, 'Memor
  */
 let developerHUD = {
 
   _targets: new Map(),
   _frames: new Map(),
   _client: null,
   _webappsActor: null,
   _watchers: [],
+  _logging: true,
 
   /**
    * This method registers a metric watcher that will watch one or more metrics
    * of apps that are being tracked. A watcher must implement the trackApp(app)
    * and untrackApp(app) methods, add entries to the app.metrics map, keep them
    * up-to-date, and call app.display() when values were changed.
    */
   registerWatcher: function dwp_registerWatcher(watcher) {
@@ -80,16 +81,20 @@ let developerHUD = {
         this.trackFrame(systemapp);
 
         let frames = systemapp.contentWindow.document.querySelectorAll('iframe[mozapp]');
         for (let frame of frames) {
           this.trackFrame(frame);
         }
       });
     });
+
+    SettingsListener.observe('hud.logging', enabled => {
+      this._logging = enabled;
+    });
   },
 
   uninit: function dwp_uninit() {
     if (!this._client)
       return;
 
     for (let frame of this._targets.keys()) {
       this.untrackFrame(frame);
@@ -132,20 +137,17 @@ let developerHUD = {
 
   untrackFrame: function dwp_untrackFrame(frame) {
     let target = this._targets.get(frame);
     if (target) {
       for (let w of this._watchers) {
         w.untrackTarget(target);
       }
 
-      // Delete the metrics and call display() to clean up the front-end.
-      delete target.metrics;
-      target.display();
-
+      target.destroy();
       this._targets.delete(frame);
     }
   },
 
   observe: function dwp_observe(subject, topic, data) {
     if (!this._client)
       return;
 
@@ -178,17 +180,19 @@ let developerHUD = {
           return;
         this.untrackFrame(frame);
         this._frames.delete(mm);
         break;
     }
   },
 
   log: function dwp_log(message) {
-    dump(DEVELOPER_HUD_LOG_PREFIX + ': ' + message + '\n');
+    if (this._logging) {
+      dump(DEVELOPER_HUD_LOG_PREFIX + ': ' + message + '\n');
+    }
   }
 
 };
 
 
 /**
  * An App object represents all there is to know about a Firefox OS app that is
  * being tracked, e.g. its manifest information, current values of watched
@@ -196,32 +200,80 @@ let developerHUD = {
  */
 function Target(frame, actor) {
   this.frame = frame;
   this.actor = actor;
   this.metrics = new Map();
 }
 
 Target.prototype = {
-  display: function target_display() {
+
+  /**
+   * Register a metric that can later be updated. Does not update the front-end.
+   */
+  register: function target_register(metric) {
+    this.metrics.set(metric, 0);
+  },
+
+  /**
+   * Modify one of a target's metrics, and send out an event to notify relevant
+   * parties (e.g. the developer HUD, automated tests, etc).
+   */
+  update: function target_update(metric, value = 0, message) {
+    let metrics = this.metrics;
+    metrics.set(metric, value);
+
     let data = {
-      metrics: []
+      metrics: [], // FIXME(Bug 982066) Remove this field.
+      manifest: this.frame.appManifestURL,
+      metric: metric,
+      value: value,
+      message: message
     };
 
-    let metrics = this.metrics;
+    // FIXME(Bug 982066) Remove this loop.
     if (metrics && metrics.size > 0) {
       for (let name of metrics.keys()) {
         data.metrics.push({name: name, value: metrics.get(name)});
       }
     }
 
-    shell.sendEvent(this.frame, 'developer-hud-update', Cu.cloneInto(data, this.frame));
+    if (message) {
+      developerHUD.log('[' + data.manifest + '] ' + data.message);
+    }
+    this._send(data);
+  },
+
+  /**
+   * Nicer way to call update() when the metric value is a number that needs
+   * to be incremented.
+   */
+  bump: function target_bump(metric, message) {
+    this.update(metric, this.metrics.get(metric) + 1, message);
+  },
 
-    // FIXME(after bug 963239 lands) return event.isDefaultPrevented();
-    return false;
+  /**
+   * Void a metric value and make sure it isn't displayed on the front-end
+   * anymore.
+   */
+  clear: function target_clear(metric) {
+    this.update(metric, 0);
+  },
+
+  /**
+   * Tear everything down, including the front-end by sending a message without
+   * widgets.
+   */
+  destroy: function target_destroy() {
+    delete this.metrics;
+    this._send({});
+  },
+
+  _send: function target_send(data) {
+    shell.sendEvent(this.frame, 'developer-hud-update', Cu.cloneInto(data, this.frame));
   }
 
 };
 
 
 /**
  * The Console Watcher tracks the following metrics in apps: reflows, warnings,
  * and errors.
@@ -247,32 +299,31 @@ let consoleWatcher = {
       SettingsListener.observe('hud.' + metric, false, watch => {
         // Watch or unwatch the metric.
         if (watching[metric] = watch) {
           return;
         }
 
         // If unwatched, remove any existing widgets for that metric.
         for (let target of this._targets.values()) {
-          target.metrics.set(metric, 0);
-          target.display();
+          target.clear(metric);
         }
       });
     }
 
     client.addListener('logMessage', this.consoleListener);
     client.addListener('pageError', this.consoleListener);
     client.addListener('consoleAPICall', this.consoleListener);
     client.addListener('reflowActivity', this.consoleListener);
   },
 
   trackTarget: function cw_trackTarget(target) {
-    target.metrics.set('reflows', 0);
-    target.metrics.set('warnings', 0);
-    target.metrics.set('errors', 0);
+    target.register('reflows');
+    target.register('warnings');
+    target.register('errors');
 
     this._client.request({
       to: target.actor.consoleActor,
       type: 'startListeners',
       listeners: ['LogMessage', 'PageError', 'ConsoleAPI', 'ReflowActivity']
     }, (res) => {
       this._targets.set(target.actor.consoleActor, target);
     });
@@ -283,92 +334,74 @@ let consoleWatcher = {
       to: target.actor.consoleActor,
       type: 'stopListeners',
       listeners: ['LogMessage', 'PageError', 'ConsoleAPI', 'ReflowActivity']
     }, (res) => { });
 
     this._targets.delete(target.actor.consoleActor);
   },
 
-  bump: function cw_bump(target, metric) {
-    if (!this._watching[metric]) {
-      return false;
-    }
-
-    let metrics = target.metrics;
-    metrics.set(metric, metrics.get(metric) + 1);
-    return true;
-  },
-
   consoleListener: function cw_consoleListener(type, packet) {
     let target = this._targets.get(packet.from);
+    let metric;
     let output = '';
 
     switch (packet.type) {
 
       case 'pageError':
         let pageError = packet.pageError;
 
         if (pageError.warning || pageError.strict) {
-          if (!this.bump(target, 'warnings')) {
-            return;
-          }
-          output = 'warning (';
+          metric = 'warnings';
+          output += 'warning (';
         } else {
-          if (!this.bump(target, 'errors')) {
-            return;
-          }
+          metric = 'errors';
           output += 'error (';
         }
 
         let {errorMessage, sourceName, category, lineNumber, columnNumber} = pageError;
         output += category + '): "' + (errorMessage.initial || errorMessage) +
           '" in ' + sourceName + ':' + lineNumber + ':' + columnNumber;
         break;
 
       case 'consoleAPICall':
-        switch (packet.message.level) {
+        switch (packet.output.level) {
 
           case 'error':
-            if (!this.bump(target, 'errors')) {
-              return;
-            }
-            output = 'error (console)';
+            metric = 'errors';
+            output += 'error (console)';
             break;
 
           case 'warn':
-            if (!this.bump(target, 'warnings')) {
-              return;
-            }
-            output = 'warning (console)';
+            metric = 'warnings';
+            output += 'warning (console)';
             break;
 
           default:
             return;
         }
         break;
 
       case 'reflowActivity':
-        if (!this.bump(target, 'reflows')) {
-          return;
-        }
+        metric = 'reflows';
 
         let {start, end, sourceURL} = packet;
         let duration = Math.round((end - start) * 100) / 100;
-        output = 'reflow: ' + duration + 'ms';
+        output += 'reflow: ' + duration + 'ms';
         if (sourceURL) {
           output += ' ' + this.formatSourceURL(packet);
         }
         break;
     }
 
-    if (!target.display()) {
-      // If the information was not displayed, log it.
-      developerHUD.log(output);
+    if (!this._watching[metric]) {
+      return;
     }
+
+    target.bump(metric, output);
   },
 
   formatSourceURL: function cw_formatSourceURL(packet) {
     // Abbreviate source URL
     let source = WebConsoleUtils.abbreviateSourceURL(packet.sourceURL);
 
     // Add function name and line number
     let {functionName, sourceLine} = packet;
@@ -400,34 +433,29 @@ let eventLoopLagWatcher = {
 
     // Toggle the state of existing fronts.
     let fronts = this._fronts;
     for (let target of fronts.keys()) {
       if (value) {
         fronts.get(target).start();
       } else {
         fronts.get(target).stop();
-        target.metrics.set('jank', 0);
-        target.display();
+        target.clear('jank');
       }
     }
   },
 
   trackTarget: function(target) {
-    target.metrics.set('jank', 0);
+    target.register('jank');
 
     let front = new EventLoopLagFront(this._client, target.actor);
     this._fronts.set(target, front);
 
     front.on('event-loop-lag', time => {
-      target.metrics.set('jank', time);
-
-      if (!target.display()) {
-        developerHUD.log('jank: ' + time + 'ms');
-      }
+      target.update('jank', time, 'jank: ' + time + 'ms');
     });
 
     if (this._active) {
       front.start();
     }
   },
 
   untrackTarget: function(target) {
@@ -473,18 +501,17 @@ let memoryWatcher = {
     SettingsListener.observe('hud.appmemory', false, enabled => {
       if (this._active = enabled) {
         for (let target of this._fronts.keys()) {
           this.measure(target);
         }
       } else {
         for (let target of this._fronts.keys()) {
           clearTimeout(this._timers.get(target));
-          target.metrics.set('memory', 0);
-          target.display();
+          target.clear('memory');
         }
       }
     });
   },
 
   measure: function mw_measure(target) {
 
     // TODO Also track USS (bug #976024).
@@ -510,29 +537,28 @@ let memoryWatcher = {
       if (watch.style) {
         total += parseInt(data.styleSize);
       }
       if (watch.other) {
         total += parseInt(data.otherSize);
       }
       // TODO Also count images size (bug #976007).
 
-      target.metrics.set('memory', total);
-      target.display();
+      target.update('memory', total);
       let duration = parseInt(data.jsMilliseconds) + parseInt(data.nonJSMilliseconds);
       let timer = setTimeout(() => this.measure(target), 100 * duration);
       this._timers.set(target, timer);
     }, (err) => {
       console.error(err);
     });
   },
 
   trackTarget: function mw_trackTarget(target) {
-    target.metrics.set('uss', 0);
-    target.metrics.set('memory', 0);
+    target.register('uss');
+    target.register('memory');
     this._fronts.set(target, MemoryFront(this._client, target.actor));
     if (this._active) {
       this.measure(target);
     }
   },
 
   untrackTarget: function mw_untrackTarget(target) {
     let front = this._fronts.get(target);
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1348,17 +1348,16 @@ pref("shumway.disabled", true);
 // The maximum amount of decoded image data we'll willingly keep around (we
 // might keep around more than this, but we'll try to get down to this value).
 // (This is intentionally on the high side; see bug 746055.)
 pref("image.mem.max_decoded_image_kb", 256000);
 
 // Default social providers
 pref("social.manifest.facebook", "{\"origin\":\"https://www.facebook.com\",\"name\":\"Facebook Share\",\"shareURL\":\"https://www.facebook.com/sharer/sharer.php?u=%{url}\",\"iconURL\":\"data:image/x-icon;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8%2F9hAAAAX0lEQVQ4jWP4%2F%2F8%2FAyUYTFhHzjgDxP9JxGeQDSBVMxgTbUBCxer%2Fr999%2BQ8DJBuArJksA9A10s8AXIBoA0B%2BR%2FY%2FjD%2BEwoBoA1yT5v3PbdmCE8MAshhID%2FUMoDgzUYIBj0Cgi7ar4coAAAAASUVORK5CYII%3D\",\"icon32URL\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAAsTAAALEwEAmpwYAAADbklEQVRYCc1Xv08UQRj99tctexAuCEFjRE0kGBEtLDSGqIWNxkYKbTAxNlY2JhaGWltNtNFeKgsKKxITK43/gCYW+IsoRhA4D47bH7fn9+bcvdm5JR7sefolC3Ozu9978+bNN7PayUv3HN3umdY0Y6IWBtSJ0HSTarXqTOiuTep6Lj+tdxAcA8RAgSmwdd2aCDs0clldYALb/FvgYVhjmfliVA2XpjEgWo0Attn42Z6WH1RFor5ehwo9XQIUZMoVn4qlCoVMSo62EvD8Kh0b3U2Xz43R2PBO6mUCGDlAf65V6MadZzT/rUimoccc2kYA4BfPHqJb105RzjJigKhRq9kEJUBIjgYVuXeL7SAI6eD+Abp5dTwVHOmEHxT50d8WBYJqSOdPj5BjW8gZR8UNqFR2xagx/65XFYaMH+BGWwiYpi4UkBPPLxTp9v1Z+lHc4DWvCQXWmIy6EjITgKowVd5Jjv7N3Hd6y5esigoOwpkJIAmMpZpLJGdiaaC4F0UmAj6bD84GCEwmB/qxMmRilmnwb/mpjAocHh4UEoNAt5NLZB7oy9OJo0PxqkAtePdhiSqunyC1LQUwWMPQaOr6GRre258Ajn4cP7KHcEXhsxpXbj+lT19X2TMNGTLVAcjcalS8gDwsQ2UOMhH4k8FkcrEn5E5ub2sKohxLK2VR77Hl9RUcsrgeRIEiVOT6z+tDbIeLy+vk+kGTCbXxycet6xhl//3f6bJEkdHYhA+mLtDIvoH4ieev5+juoxdk5+pjhALYEdXIpEB5w+NlSKSzqVQ/+H7IO6BLtl3fngGMiqhGJgIwlM6qpyUGFjySdk8m0Zg0ubeD7X9OIDEFajltRQgUJaUKx69tdgaQa0FMADuahZPMFtcEwNPm2hA7ZI5sK4aoE2NvYI+o8hkCIe7CwTv68zS0q9Dk5vpbm/8FXxitSzmMFHpsGj0wyLUheTwD2Y9fVgh1Ae0EPUgD9241ZEnld+v5kgnVZ/8fE0brVh5BK+1oCqKKF72Dk7HwBsssB/pklU1dfChy3S659H5+uelgIb+8WRv1/uGTV9Sdb5wJFlfW6fPCalMhwhSU1j2xKwKbP838GcOwJja4TqO0bjdmXxYTy1EYjFdCWoCEYZhseH/GDL3yJPHnuW6YmT7P1SlIA4768Hke4vOcsX8BE346lLHhDUQAAAAASUVORK5CYII=\", \"icon64URL\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAACNNJREFUeNrtm3tw1NUVxz/399hHHkgCaCBGEFEEREVFYFQcSoOKdkZay4z+4dDpYIsjHWx1WoTMhFi1gzBSpVgVGbU4U1sHfPESKODwEEnRYDFAICEIeZIQshs2u/v73ds/drMsyW7YLEkl2Z6Z32yy+9v7u+fc7znne8+5KzgvAjDunzlv0M13PjDZ6c4cARj0WhEoaZ1tOn3yq9XLf/tNU0O1D5Ad7wq/OpxpaXOL1j5uZAwuaGlVgwNBhULRm0XXBG6HZrlNa9uRrzfM+3DlgjIgGMsA7rl/XDdHOnNf9vosTfVuvTsaQhdkZ4iykh2rHtqydvkxwI58BhjTfv7MmP55E9/1nLNdfU15ACkVvoAaMCRvRPa+re9+DgTaPjMAx+DrJv3M67Mz+6LybWLb4NfTHhxzx31DDhZvOtqGAgNwWbjGICV9XQJB0e/KobcOP1i8qTzaAEYgaDtNU/V5A9hSaUFLuQEt2gVQSgml+j4CUAIppYgK/m0GkCjZ9xGAUNAu0LUhgJRAAAIVzwBSqVRQH4hlAClRKZAFhOgEASoFECBR8QwgUyQGdJT/B8HzCEiBNKhUJzEgBYIgQsTJAkohe9oFZHgHKvQoHtZ9K3tewfiixXABLdoFeuSSEmkF+PH4QTz7+M3o+ENptzvGtS36uSwmjMpAYF10XCllHCYoe84FlLS555Zs5jx6J6ahY+iCl98pJiDNS1hwSZop+cm91zJmxEBefGsPlu1AxKC67V3gf5oGlZSMuz6Dp2fdhWnoAEwaN5T5hsYLb+4hKB1dcgelFDpB8ifk8thDt3DO5+fZxRvxBV0IjQR0EB3KfD1GhJS0GZnnYuGcKTgdF9ZWx4/No/BJjUUrdtJqm4iL+K5SCmSAiWMHMevhcQzNzaa6ron5SzfQ7HeiaSKx+au4m6HupcJKSYZdZVI4dypuV2yo3zoql0VP3cOiFV/Q4jdiGkGhQFqMGpbJL346kbE3DEYIQWNTC39Ysp4Gr4HQtZDyiRhA0NlmSHZbRM7pr1H0m6lckeHqdGXG3jCYoqfupeC17bT49fNRXIFSNrkDTGbNGM9dtw1D10M1DI/Xx3NLP6OqETRdDy1eglPT4rqA7K56gCIrXfHCvHwGZqUnBMtR113FS/N+xHPLtuJpDa1mVobg0emjmX7vqEjsUErhaw1Q8Mo6yk4F0A1HeOW7kIlFx/u7jworRabLpmhuPjmD+iG7YNDrrhnIS09P5cW/buOeO67lkftvJt3tDE06PE7Qsnh++QYOHPOim86wcVUS0+whJug0ghTMmcK1V2eH8m2UHP++nrwhAyIwjiXDcrN5vXAGhqGhFBeMYUvJ0re2sPfgGTTDGUZrEogVopMgmGQWUCgMEWTBE5MZPSLngnGqas/w9j92s31fJfmThvPM7HyMMKRj+qgmOiBHSsnr73/B5r1V6A53KD4k3bFS8dNgckFQoWPxu1kTGDc6N7JqzR4ff/+smE+2H8FSLkx3FluLawhaG3n2iXwcppGoV/Hemt18tK0c3UwLIfUS2nVafBeQJNUXkAF+/dht3H37cKSUBC2bTTv+w98++YazPg1dT0NoIUhruoMd+2sJrtjI7381rQM3iCVrN33N++tL0c30xFNdp0GQeFRYQhcRIO0gv5xxE/fdfSO2bfNVSTmrPtxLZW0A3XSh6VporaLG1XQHu0pOU/TaOhY8+QAuZ3w6vHnnQd74536EkZ50wOsSE0zcugolbWZOG8GM/LGUVdSw6sM97D/UgGa60QxXzMJDZAq6yb7SJgr//CkLn5pOmsvR4Z5dxUdZ9t6XoKfFjNyXkqZjuoBUCpGgCyhp8eDdQ5k++UaWrdrMlr2VSFxoZlpE8YtNWGgGXx9ppnDZpxTMfTCS8gAOlJ5g8ds7kCItTIi6j6FqMVxAC2sV2RB1ekmLCTcNpH+myeyFH7BxT1Voopoe4RKJXQqhmxw45mXh0o/xeH0opThcXs2iv2wmoFyhAnbC4yX+3PgISMDShrA5XHGa3d9UITRniIeTfHASmsGhEz7mL/mI2TPv4sU3t+KzHAnu7JKpCosOkcSIICAB5hZE0OiRCM0Iwb0b6LPQdI5W+Zn/yucoYYayRk+16eK1xqRMDAHtA0r3lep0lNAjO8kfpCpMqpTF4xZEUqA7rIlOCiKpgADVWXc4FQwgEfHPB5AiByTixIDUCYJx+wJoqdIcJV5VOAWygEZcF7BToT2upFDKtuz2BrAtf8v3mju972cBJX2exso6ok6N64BhOtM11xXXPBz6v6340PcuO+DZfaJkzWqgqY3L64Bqaaz0ZV45Mkc308dG2kd97FLSaq4v317gazr5HeCLRoACFTxbfeBw+oDhWYYj4/rw+30H+rb/VMPxXQsbKnbuABqJOi4vogyRiRB5/XNvvz3zytFTDEf61eF9b0dCKTS36c4afymTsgLeQ9Ly13X/aYnzE1Uy6PV7679trNy1xe+tKwPqAH/0Vla0qw65gH7AFeG/Y3Uy9P45o0bm3PTIaplM6lTK9jWf/OBUyQcrpdXaTIyfsXQb9QcLaAn7vJd2vxY5XxBpo8pwDmgFGsLKx1oeh8OVmUUSLXUlrWZPzbdLag9v+BjUqfDzepKAyDDcZbznGHG+1NmqSKHpVlfbadJqLW+o2LHobNX+PUB1WPkfnHwYyTmX6lI7Lehr3F576NM/+T3V3wH17f2w1xkg2ggXuSvga6p8p+bgmpVKWpXAmVh+2AsNEKogdYYAJa0GT03J4obyf60HTgKe6PTTqw0QOpcQ3wXs4LlDZyq2FXrrS4uBmjDxuCw3G5eIgA46yeC5ho11pWsWW35PWTibBC4Xf+9eBLRPg0q2+s5UvHG6bMNqJYPHw7nXutxZYvIIiMoCSgbrPVX/fv7syS+3AKfC5MOmF4iRpP6RjrId8O5vrNhS1NpUWQLUholUr6muXEoatP3emrWNR9e/avk9R8P+HuxNypPkrk93pGdnK0VtXemaN6UdOHo55vdE5b/0NKx+K4AxtAAAAABJRU5ErkJggg==\", \"description\":\"Easily share the web to your Facebook friends.\",\"author\":\"Facebook\",\"homepageURL\":\"https://www.facebook.com\",\"builtin\":\"true\",\"version\":1}");
 
-pref("social.sidebar.open", true);
 pref("social.sidebar.unload_timeout_ms", 10000);
 
 pref("dom.identity.enabled", false);
 
 // Turn on the CSP 1.0 parser for Content Security Policy headers
 pref("security.csp.speccompliant", true);
 
 // Block insecure active content on https pages
@@ -1379,30 +1378,29 @@ pref("geo.wifi.uri", "https://www.google
 pref("network.disable.ipc.security", true);
 
 // CustomizableUI debug logging.
 pref("browser.uiCustomization.debug", false);
 
 // CustomizableUI state of the browser's user interface
 pref("browser.uiCustomization.state", "");
 
-// The URL where remote content that composes the UI for Firefox Accounts should
-// be fetched. Must use HTTPS.
-pref("identity.fxaccounts.remote.uri", "https://accounts.firefox.com/?service=sync&context=fx_desktop_v1");
+// The remote content URL shown for FxA signup. Must use HTTPS.
+pref("identity.fxaccounts.remote.signup.uri", "https://accounts.firefox.com/signup?service=sync&context=fx_desktop_v1");
 
 // The URL where remote content that forces re-authentication for Firefox Accounts
 // should be fetched.  Must use HTTPS.
 pref("identity.fxaccounts.remote.force_auth.uri", "https://accounts.firefox.com/force_auth?service=sync&context=fx_desktop_v1");
 
 // The remote content URL shown for signin in. Must use HTTPS.
 pref("identity.fxaccounts.remote.signin.uri", "https://accounts.firefox.com/signin?service=sync&context=fx_desktop_v1");
 
 // The URL we take the user to when they opt to "manage" their Firefox Account.
 // Note that this will always need to be in the same TLD as the
-// "identity.fxaccounts.remote.uri" pref.
+// "identity.fxaccounts.remote.signup.uri" pref.
 pref("identity.fxaccounts.settings.uri", "https://accounts.firefox.com/settings");
 
 // On GTK, we now default to showing the menubar only when alt is pressed:
 #ifdef MOZ_WIDGET_GTK
 pref("ui.key.menuAccessKeyFocuses", true);
 #endif
 
 
--- a/browser/base/content/aboutSocialError.xhtml
+++ b/browser/base/content/aboutSocialError.xhtml
@@ -74,20 +74,17 @@
       }
     }
 
     function setUpStrings() {
       let brandBundle = Services.strings.createBundle("chrome://branding/locale/brand.properties");
       let browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
 
       let productName = brandBundle.GetStringFromName("brandShortName");
-      let provider = Social && Social.provider;
-      if (config.origin) {
-        provider = Social && Social._getProviderFromOrigin(config.origin);
-      }
+      let provider = Social._getProviderFromOrigin(config.origin);
       let providerName = provider && provider.name;
 
       // Sets up the error message
       let msg = browserBundle.formatStringFromName("social.error.message", [productName, providerName], 2);
       document.getElementById("main-error-msg").textContent = msg;
 
       // Sets up the buttons' labels and accesskeys
       let btnTryAgain = document.getElementById("btnTryAgain");
@@ -95,31 +92,32 @@
       btnTryAgain.accessKey = browserBundle.GetStringFromName("social.error.tryAgain.accesskey");
 
       let btnCloseSidebar = document.getElementById("btnCloseSidebar");
       btnCloseSidebar.textContent = browserBundle.GetStringFromName("social.error.closeSidebar.label");
       btnCloseSidebar.accessKey = browserBundle.GetStringFromName("social.error.closeSidebar.accesskey");
     }
 
     function closeSidebarButton() {
-      Social.toggleSidebar();
+      SocialSidebar.toggleSidebar();
     }
 
     function tryAgainButton() {
       config.tryAgainCallback();
     }
 
     function loadQueryURL() {
       window.location.href = config.queryURL;
     }
 
     function reloadProvider() {
       // Just incase the current provider *isn't* in a frameworker-error
       // state, reload the current one.
-      Social.provider.reload();
+      let provider = Social._getProviderFromOrigin(config.origin);
+      provider.reload();
       // If the problem is a frameworker-error, it may be that the child
       // process crashed - and if that happened, then *all* providers in that
       // process will have crashed.  However, only the current provider is
       // likely to have the error surfaced in the UI - so we reload *all*
       // providers that are in a frameworker-error state.
       for (let provider of Social.providers) {
         if (provider.enabled && provider.errorState == "frameworker-error") {
           provider.reload();
--- a/browser/base/content/aboutaccounts/aboutaccounts.js
+++ b/browser/base/content/aboutaccounts/aboutaccounts.js
@@ -95,17 +95,17 @@ let wrapper = {
       return;
     }
 
     let iframe = document.getElementById("remote");
     this.iframe = iframe;
     iframe.addEventListener("load", this);
 
     try {
-      iframe.src = url || fxAccounts.getAccountsURI();
+      iframe.src = url || fxAccounts.getAccountsSignUpURI();
     } catch (e) {
       error("Couldn't init Firefox Account wrapper: " + e.message);
     }
   },
 
   handleEvent: function (evt) {
     switch (evt.type) {
       case "load":
@@ -218,17 +218,17 @@ let wrapper = {
         log("Unexpected remote command received: " + evt.detail.command + ". Ignoring command.");
         break;
     }
   },
 
   injectData: function (type, content) {
     let authUrl;
     try {
-      authUrl = fxAccounts.getAccountsURI();
+      authUrl = fxAccounts.getAccountsSignUpURI();
     } catch (e) {
       error("Couldn't inject data: " + e.message);
       return;
     }
     let data = {
       type: type,
       content: content
     };
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -1319,16 +1319,32 @@ let BookmarkingUI = {
   },
 
   onMainMenuPopupShowing: function BUI_onMainMenuPopupShowing(event) {
     this._updateBookmarkPageMenuItem();
     PlacesCommandHook.updateBookmarkAllTabsCommand();
   },
 
   _showBookmarkedNotification: function BUI_showBookmarkedNotification() {
+    /*
+     * We're dynamically setting pointer-events to none here for the duration
+     * of the bookmark menu button's dropmarker animation in order to avoid
+     * having it end up in the overflow menu. This happens because it gaining
+     * focus triggers a style change which triggers an overflow event, even
+     * though this does not happen if no focus change occurs. The core issue
+     * is tracked in https://bugzilla.mozilla.org/show_bug.cgi?id=981637
+     */
+    let onDropmarkerAnimationEnd = () => {
+      this.button.removeEventListener("animationend", onDropmarkerAnimationEnd);
+      this.button.style.removeProperty("pointer-events");
+    };
+    let onDropmarkerAnimationStart = () => {
+      this.button.removeEventListener("animationstart", onDropmarkerAnimationStart);
+      this.button.style.pointerEvents = 'none';
+    };
 
     if (this._notificationTimeout) {
       clearTimeout(this._notificationTimeout);
     }
 
     if (this.notifier.style.transform == '') {
       let isRTL = getComputedStyle(this.button).direction == "rtl";
       let buttonRect = this.button.getBoundingClientRect();
@@ -1349,23 +1365,26 @@ let BookmarkingUI = {
     let isInBookmarksToolbar = this.button.classList.contains("bookmark-item");
     if (isInBookmarksToolbar)
       this.notifier.setAttribute("in-bookmarks-toolbar", true);
 
     let isInOverflowPanel = this.button.getAttribute("overflowedItem") == "true";
     if (!isInOverflowPanel) {
       this.notifier.setAttribute("notification", "finish");
       this.button.setAttribute("notification", "finish");
+      this.button.addEventListener('animationstart', onDropmarkerAnimationStart);
+      this.button.addEventListener("animationend", onDropmarkerAnimationEnd);
     }
 
     this._notificationTimeout = setTimeout( () => {
       this.notifier.removeAttribute("notification");
       this.notifier.removeAttribute("in-bookmarks-toolbar");
       this.button.removeAttribute("notification");
       this.notifier.style.transform = '';
+      this.button.style.removeProperty("pointer-events");
     }, 1000);
   },
 
   _showSubview: function() {
     let view = document.getElementById("PanelUI-bookmarks");
     view.addEventListener("ViewShowing", this);
     view.addEventListener("ViewHiding", this);
     let anchor = document.getElementById(this.BOOKMARK_BUTTON_ID);
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -111,20 +111,19 @@
       oncommand="OpenBrowserWindow({private: true});"/>
     <command id="Tools:RemoteWindow"
       oncommand="OpenBrowserWindow({remote: true});"/>
     <command id="Tools:NonRemoteWindow"
       oncommand="OpenBrowserWindow({remote: false});"/>
     <command id="History:UndoCloseTab" oncommand="undoCloseTab();"/>
     <command id="History:UndoCloseWindow" oncommand="undoCloseWindow();"/>
     <command id="Social:SharePage" oncommand="SocialShare.sharePage();" disabled="true"/>
-    <command id="Social:ToggleSidebar" oncommand="Social.toggleSidebar();" hidden="true"/>
+    <command id="Social:ToggleSidebar" oncommand="SocialSidebar.toggleSidebar();" hidden="true"/>
     <command id="Social:ToggleNotifications" oncommand="Social.toggleNotifications();" hidden="true"/>
     <command id="Social:FocusChat" oncommand="SocialChatBar.focus();" hidden="true" disabled="true"/>
-    <command id="Social:Toggle" oncommand="Social.toggle();" hidden="true"/>
     <command id="Social:Addons" oncommand="BrowserOpenAddonsMgr('addons://list/service');"/>
   </commandset>
 
   <commandset id="placesCommands">
     <command id="Browser:ShowAllBookmarks"
              oncommand="PlacesCommandHook.showPlacesOrganizer('AllBookmarks');"/>
     <command id="Browser:ShowAllHistory"
              oncommand="PlacesCommandHook.showPlacesOrganizer('History');"/>
--- a/browser/base/content/browser-social.js
+++ b/browser/base/content/browser-social.js
@@ -46,177 +46,142 @@ XPCOMUtils.defineLazyGetter(this, "Creat
 
 XPCOMUtils.defineLazyGetter(this, "CreateSocialMarkWidget", function() {
   let tmp = {};
   Cu.import("resource:///modules/Social.jsm", tmp);
   return tmp.CreateSocialMarkWidget;
 });
 
 SocialUI = {
+  _initialized: false,
+
   // Called on delayed startup to initialize the UI
   init: function SocialUI_init() {
+    if (this._initialized) {
+      return;
+    }
+
     Services.obs.addObserver(this, "social:ambient-notification-changed", false);
     Services.obs.addObserver(this, "social:profile-changed", false);
     Services.obs.addObserver(this, "social:frameworker-error", false);
-    Services.obs.addObserver(this, "social:provider-set", false);
     Services.obs.addObserver(this, "social:providers-changed", false);
     Services.obs.addObserver(this, "social:provider-reload", false);
     Services.obs.addObserver(this, "social:provider-enabled", false);
     Services.obs.addObserver(this, "social:provider-disabled", false);
 
-    Services.prefs.addObserver("social.sidebar.open", this, false);
     Services.prefs.addObserver("social.toast-notifications.enabled", this, false);
 
     gBrowser.addEventListener("ActivateSocialFeature", this._activationEventHandler.bind(this), true, true);
     document.getElementById("PanelUI-popup").addEventListener("popupshown", SocialMarks.updatePanelButtons, true);
 
     // menupopups that list social providers. we only populate them when shown,
     // and if it has not been done already.
     document.getElementById("viewSidebarMenu").addEventListener("popupshowing", SocialSidebar.populateSidebarMenu, true);
     document.getElementById("social-statusarea-popup").addEventListener("popupshowing", SocialSidebar.populateSidebarMenu, true);
 
-    if (!Social.initialized) {
-      Social.init();
-    } else if (Social.providers.length > 0) {
-      // Social was initialized during startup in a previous window. If we have
-      // providers enabled initialize the UI for this window.
-      this.observe(null, "social:providers-changed", null);
-      this.observe(null, "social:provider-set", Social.provider ? Social.provider.origin : null);
-    }
+    Social.init().then((update) => {
+      if (update)
+        this._providersChanged();
+      // handle SessionStore for the sidebar state
+      SocialSidebar.restoreWindowState();
+    });
+
+    this._initialized = true;
   },
 
   // Called on window unload
   uninit: function SocialUI_uninit() {
+    if (!this._initialized) {
+      return;
+    }
+
     Services.obs.removeObserver(this, "social:ambient-notification-changed");
     Services.obs.removeObserver(this, "social:profile-changed");
     Services.obs.removeObserver(this, "social:frameworker-error");
-    Services.obs.removeObserver(this, "social:provider-set");
     Services.obs.removeObserver(this, "social:providers-changed");
     Services.obs.removeObserver(this, "social:provider-reload");
     Services.obs.removeObserver(this, "social:provider-enabled");
     Services.obs.removeObserver(this, "social:provider-disabled");
 
-    Services.prefs.removeObserver("social.sidebar.open", this);
     Services.prefs.removeObserver("social.toast-notifications.enabled", this);
 
     document.getElementById("PanelUI-popup").removeEventListener("popupshown", SocialMarks.updatePanelButtons, true);
     document.getElementById("viewSidebarMenu").removeEventListener("popupshowing", SocialSidebar.populateSidebarMenu, true);
     document.getElementById("social-statusarea-popup").removeEventListener("popupshowing", SocialSidebar.populateSidebarMenu, true);
-  },
 
-  _matchesCurrentProvider: function (origin) {
-    return Social.provider && Social.provider.origin == origin;
+    this._initialized = false;
   },
 
   observe: function SocialUI_observe(subject, topic, data) {
     // Exceptions here sometimes don't get reported properly, report them
     // manually :(
     try {
       switch (topic) {
         case "social:provider-enabled":
           SocialMarks.populateToolbarPalette();
           SocialStatus.populateToolbarPalette();
           break;
         case "social:provider-disabled":
           SocialMarks.removeProvider(data);
           SocialStatus.removeProvider(data);
+          SocialSidebar.disableProvider(data);
           break;
         case "social:provider-reload":
           SocialStatus.reloadProvider(data);
           // if the reloaded provider is our current provider, fall through
-          // to social:provider-set so the ui will be reset
-          if (!Social.provider || Social.provider.origin != data)
+          // to social:providers-changed so the ui will be reset
+          if (!SocialSidebar.provider || SocialSidebar.provider.origin != data)
             return;
-          // be sure to unload the sidebar as it will not reload if the origin
-          // has not changed, it will be loaded in provider-set below. Other
-          // panels will be unloaded or handle reload.
+          // currently only the sidebar and flyout have a selected provider.
+          // sidebar provider has changed (possibly to null), ensure the content
+          // is unloaded and the frames are reset, they will be loaded in
+          // providers-changed below if necessary.
           SocialSidebar.unloadSidebar();
-          // fall through to social:provider-set
-        case "social:provider-set":
-          // Social.provider has changed (possibly to null), update any state
-          // which depends on it.
-          this._updateActiveUI();
-
           SocialFlyout.unload();
-          SocialChatBar.update();
-          SocialShare.update();
-          SocialSidebar.update();
-          SocialStatus.populateToolbarPalette();
-          SocialMarks.populateToolbarPalette();
-          break;
+          // fall through to providers-changed to ensure the reloaded provider
+          // is correctly reflected in any UI and the multi-provider menu
         case "social:providers-changed":
-          // the list of providers changed - this may impact the "active" UI.
-          this._updateActiveUI();
-          // and the multi-provider menu
-          SocialSidebar.clearProviderMenus();
-          SocialShare.populateProviderMenu();
-          SocialStatus.populateToolbarPalette();
-          SocialMarks.populateToolbarPalette();
+          this._providersChanged();
           break;
 
         // Provider-specific notifications
         case "social:ambient-notification-changed":
           SocialStatus.updateButton(data);
           break;
         case "social:profile-changed":
           // make sure anything that happens here only affects the provider for
           // which the profile is changing, and that anything we call actually
           // needs to change based on profile data.
           SocialStatus.updateButton(data);
           break;
         case "social:frameworker-error":
-          if (this.enabled && Social.provider.origin == data) {
+          if (this.enabled && SocialSidebar.provider && SocialSidebar.provider.origin == data) {
             SocialSidebar.setSidebarErrorMessage();
           }
           break;
 
         case "nsPref:changed":
-          if (data == "social.sidebar.open") {
-            SocialSidebar.update();
-          } else if (data == "social.toast-notifications.enabled") {
+          if (data == "social.toast-notifications.enabled") {
             SocialSidebar.updateToggleNotifications();
           }
           break;
       }
     } catch (e) {
       Components.utils.reportError(e + "\n" + e.stack);
       throw e;
     }
   },
 
-  _updateActiveUI: function SocialUI_updateActiveUI() {
-    // The "active" UI isn't dependent on there being a provider, just on
-    // social being "active" (but also chromeless/PB)
-    let enabled = Social.providers.length > 0 && !this._chromeless &&
-                  !PrivateBrowsingUtils.isWindowPrivate(window);
-
-    let toggleCommand = document.getElementById("Social:Toggle");
-    toggleCommand.setAttribute("hidden", enabled ? "false" : "true");
-
-    if (enabled) {
-      // enabled == true means we at least have a defaultProvider
-      let provider = Social.provider || Social.defaultProvider;
-      // We only need to update the command itself - all our menu items use it.
-      let label;
-      if (Social.providers.length == 1) {
-        label = gNavigatorBundle.getFormattedString(Social.provider
-                                                    ? "social.turnOff.label"
-                                                    : "social.turnOn.label",
-                                                    [provider.name]);
-      } else {
-        label = gNavigatorBundle.getString(Social.provider
-                                           ? "social.turnOffAll.label"
-                                           : "social.turnOnAll.label");
-      }
-      let accesskey = gNavigatorBundle.getString(Social.provider
-                                                 ? "social.turnOff.accesskey"
-                                                 : "social.turnOn.accesskey");
-      toggleCommand.setAttribute("label", label);
-      toggleCommand.setAttribute("accesskey", accesskey);
-    }
+  _providersChanged: function() {
+    SocialSidebar.clearProviderMenus();
+    SocialSidebar.update();
+    SocialChatBar.update();
+    SocialShare.populateProviderMenu();
+    SocialStatus.populateToolbarPalette();
+    SocialMarks.populateToolbarPalette();
   },
 
   // This handles "ActivateSocialFeature" events fired against content documents
   // in this window.
   _activationEventHandler: function SocialUI_activationHandler(e) {
     let targetDoc;
     let node;
     if (e.target instanceof HTMLDocument) {
@@ -258,17 +223,21 @@ SocialUI = {
       try {
         data = JSON.parse(data);
       } catch(e) {
         Cu.reportError("Social Service manifest parse error: "+e);
         return;
       }
     }
     Social.installProvider(targetDoc, data, function(manifest) {
-      Social.activateFromOrigin(manifest.origin);
+      Social.activateFromOrigin(manifest.origin, function(provider) {
+        if (provider.sidebarURL) {
+          SocialSidebar.show(provider.origin);
+        }
+      });
     });
   },
 
   showLearnMore: function() {
     let url = Services.urlFormatter.formatURLPref("app.support.baseURL") + "social-api";
     openUILinkIn(url, "tab");
   },
 
@@ -306,17 +275,17 @@ SocialUI = {
     this._chromeless = chromeless;
     return chromeless;
   },
 
   get enabled() {
     // Returns whether social is enabled *for this window*.
     if (this._chromeless || PrivateBrowsingUtils.isWindowPrivate(window))
       return false;
-    return !!Social.provider;
+    return Social.providers.length > 0;
   },
 
   // called on tab/urlbar/location changes and after customization. Update
   // anything that is tab specific.
   updateState: function() {
     if (!this.enabled)
       return;
     SocialMarks.update();
@@ -333,16 +302,17 @@ SocialChatBar = {
   get isAvailable() {
     return SocialUI.enabled;
   },
   // Does this chatbar have any chats (whether minimized, collapsed or normal)
   get hasChats() {
     return !!this.chatbar.firstElementChild;
   },
   openChat: function(aProvider, aURL, aCallback, aMode) {
+    this.update();
     if (!this.isAvailable)
       return false;
     this.chatbar.openChat(aProvider, aURL, aCallback, aMode);
     // We only want to focus the chat if it is as a result of user input.
     let dwu = window.QueryInterface(Ci.nsIInterfaceRequestor)
                     .getInterface(Ci.nsIDOMWindowUtils);
     if (dwu.isHandlingUserInput)
       this.chatbar.focus();
@@ -385,23 +355,25 @@ SocialFlyout = {
     if (!SocialUI.enabled || panel.firstChild)
       return;
     // create and initialize the panel for this window
     let iframe = document.createElement("iframe");
     iframe.setAttribute("type", "content");
     iframe.setAttribute("class", "social-panel-frame");
     iframe.setAttribute("flex", "1");
     iframe.setAttribute("tooltip", "aHTMLTooltip");
-    iframe.setAttribute("origin", Social.provider.origin);
+    iframe.setAttribute("origin", SocialSidebar.provider.origin);
     panel.appendChild(iframe);
   },
 
   setFlyoutErrorMessage: function SF_setFlyoutErrorMessage() {
     this.iframe.removeAttribute("src");
-    this.iframe.webNavigation.loadURI("about:socialerror?mode=compactInfo", null, null, null, null);
+    this.iframe.webNavigation.loadURI("about:socialerror?mode=compactInfo&origin=" +
+                                 encodeURIComponent(this.iframe.getAttribute("origin")),
+                                 null, null, null, null);
     sizeSocialPanelToContent(this.panel, this.iframe);
   },
 
   unload: function() {
     let panel = this.panel;
     panel.hidePopup();
     if (!panel.firstChild)
       return
@@ -437,17 +409,17 @@ SocialFlyout = {
   onHidden: function(aEvent) {
     this._dynamicResizer.stop();
     this._dynamicResizer = null;
     this.iframe.docShell.isActive = false;
     this.dispatchPanelEvent("socialFrameHide");
   },
 
   load: function(aURL, cb) {
-    if (!Social.provider)
+    if (!SocialSidebar.provider)
       return;
 
     this.panel.hidden = false;
     let iframe = this.iframe;
     // same url with only ref difference does not cause a new load, so we
     // want to go right to the callback
     let src = iframe.contentDocument && iframe.contentDocument.documentURIObject;
     if (!src || !src.equalsExceptRef(Services.io.newURI(aURL, null, null))) {
@@ -532,18 +504,20 @@ SocialShare = {
   },
 
   getSelectedProvider: function() {
     let provider;
     let lastProviderOrigin = this.iframe && this.iframe.getAttribute("origin");
     if (lastProviderOrigin) {
       provider = Social._getProviderFromOrigin(lastProviderOrigin);
     }
+    // if they have a provider selected in the sidebar use that for the initial
+    // default in share
     if (!provider)
-      provider = Social.provider || Social.defaultProvider;
+      provider = SocialSidebar.provider;
     // if our provider has no shareURL, select the first one that does
     if (provider && !provider.shareURL) {
       let providers = [p for (p of Social.providers) if (p.shareURL)];
       provider = providers.length > 0  && providers[0];
     }
     return provider;
   },
 
@@ -717,24 +691,73 @@ SocialShare = {
     this.panel.openPopup(anchor, "bottomcenter topright", 0, 0, false, false);
     Social.setErrorListener(iframe, this.setErrorMessage.bind(this));
   }
 };
 
 SocialSidebar = {
   // Whether the sidebar can be shown for this window.
   get canShow() {
-    if (PrivateBrowsingUtils.isWindowPrivate(window))
+    if (!SocialUI.enabled || document.mozFullScreen)
       return false;
-    return [p for (p of Social.providers) if (p.enabled && p.sidebarURL)].length > 0;
+    return Social.providers.some(p => p.sidebarURL);
   },
 
   // Whether the user has toggled the sidebar on (for windows where it can appear)
   get opened() {
-    return Services.prefs.getBoolPref("social.sidebar.open") && !document.mozFullScreen;
+    let broadcaster = document.getElementById("socialSidebarBroadcaster");
+    return !broadcaster.hidden;
+  },
+
+  restoreWindowState: function() {
+    this._initialized = true;
+    if (!this.canShow)
+      return;
+
+    if (Services.prefs.prefHasUserValue("social.provider.current")) {
+      // "upgrade" when the first window opens if we have old prefs.  We get the
+      // values from prefs this one time, window state will be saved when this
+      // window is closed.
+      let origin = Services.prefs.getCharPref("social.provider.current");
+      Services.prefs.clearUserPref("social.provider.current");
+      // social.sidebar.open default was true, but we only opened if there was
+      // a current provider
+      let opened = origin && true;
+      if (Services.prefs.prefHasUserValue("social.sidebar.open")) {
+        opened = origin && Services.prefs.getBoolPref("social.sidebar.open");
+        Services.prefs.clearUserPref("social.sidebar.open");
+      }
+      let data = {
+        "hidden": !opened,
+        "origin": origin
+      };
+      SessionStore.setWindowValue(window, "socialSidebar", JSON.stringify(data));
+    }
+
+    let data = SessionStore.getWindowValue(window, "socialSidebar");
+    // if this window doesn't have it's own state, use the state from the opener
+    if (!data && window.opener && !window.opener.closed) {
+      data = SessionStore.getWindowValue(window.opener, "socialSidebar");
+    }
+    if (data) {
+      data = JSON.parse(data);
+      document.getElementById("social-sidebar-browser").setAttribute("origin", data.origin);
+      if (!data.hidden)
+        this.show(data.origin);
+    }
+  },
+
+  saveWindowState: function() {
+    let broadcaster = document.getElementById("socialSidebarBroadcaster");
+    let sidebarOrigin = document.getElementById("social-sidebar-browser").getAttribute("origin");
+    let data = {
+      "hidden": broadcaster.hidden,
+      "origin": sidebarOrigin
+    };
+    SessionStore.setWindowValue(window, "socialSidebar", JSON.stringify(data));
   },
 
   setSidebarVisibilityState: function(aEnabled) {
     let sbrowser = document.getElementById("social-sidebar-browser");
     // it's possible we'll be called twice with aEnabled=false so let's
     // just assume we may often be called with the same state.
     if (aEnabled == sbrowser.docShellIsActive)
       return;
@@ -746,16 +769,20 @@ SocialSidebar = {
 
   updateToggleNotifications: function() {
     let command = document.getElementById("Social:ToggleNotifications");
     command.setAttribute("checked", Services.prefs.getBoolPref("social.toast-notifications.enabled"));
     command.setAttribute("hidden", !SocialUI.enabled);
   },
 
   update: function SocialSidebar_update() {
+    // ensure we never update before restoreWindowState
+    if (!this._initialized)
+      return;
+    this.ensureProvider();
     this.updateToggleNotifications();
     this._updateHeader();
     clearTimeout(this._unloadTimeoutId);
     // Hide the toggle menu item if the sidebar cannot appear
     let command = document.getElementById("Social:ToggleSidebar");
     command.setAttribute("hidden", this.canShow ? "false" : "true");
 
     // Hide the sidebar if it cannot appear, or has been toggled off.
@@ -777,31 +804,31 @@ SocialSidebar = {
         this.unloadSidebar();
       } else {
         this._unloadTimeoutId = setTimeout(
           this.unloadSidebar,
           Services.prefs.getIntPref("social.sidebar.unload_timeout_ms")
         );
       }
     } else {
-      sbrowser.setAttribute("origin", Social.provider.origin);
-      if (Social.provider.errorState == "frameworker-error") {
+      sbrowser.setAttribute("origin", this.provider.origin);
+      if (this.provider.errorState == "frameworker-error") {
         SocialSidebar.setSidebarErrorMessage();
         return;
       }
 
       // Make sure the right sidebar URL is loaded
-      if (sbrowser.getAttribute("src") != Social.provider.sidebarURL) {
+      if (sbrowser.getAttribute("src") != this.provider.sidebarURL) {
         // we check readyState right after setting src, we need a new content
         // viewer to ensure we are checking against the correct document.
         sbrowser.docShell.createAboutBlankContentViewer(null);
         Social.setErrorListener(sbrowser, this.setSidebarErrorMessage.bind(this));
         // setting isAppTab causes clicks on untargeted links to open new tabs
         sbrowser.docShell.isAppTab = true;
-        sbrowser.setAttribute("src", Social.provider.sidebarURL);
+        sbrowser.setAttribute("src", this.provider.sidebarURL);
         PopupNotifications.locationChange(sbrowser);
       }
 
       // if the document has not loaded, delay until it is
       if (sbrowser.contentDocument.readyState != "complete") {
         document.getElementById("social-sidebar-button").setAttribute("loading", "true");
         sbrowser.addEventListener("load", SocialSidebar._loadListener, true);
       } else {
@@ -833,33 +860,65 @@ SocialSidebar = {
     SocialFlyout.unload();
   },
 
   _unloadTimeoutId: 0,
 
   setSidebarErrorMessage: function() {
     let sbrowser = document.getElementById("social-sidebar-browser");
     // a frameworker error "trumps" a sidebar error.
-    if (Social.provider.errorState == "frameworker-error") {
-      sbrowser.setAttribute("src", "about:socialerror?mode=workerFailure");
+    let origin = sbrowser.getAttribute("origin");
+    if (origin) {
+      origin = "&origin=" + encodeURIComponent(origin);
+    }
+    if (this.provider.errorState == "frameworker-error") {
+      sbrowser.setAttribute("src", "about:socialerror?mode=workerFailure" + origin);
     } else {
-      let url = encodeURIComponent(Social.provider.sidebarURL);
-      sbrowser.loadURI("about:socialerror?mode=tryAgain&url=" + url, null, null);
+      let url = encodeURIComponent(this.provider.sidebarURL);
+      sbrowser.loadURI("about:socialerror?mode=tryAgain&url=" + url + origin, null, null);
     }
   },
 
-  // provider will move to a sidebar specific member in bug 894806
-  get provider() {
-    return Social.provider;
+  _provider: null,
+  ensureProvider: function() {
+    if (this._provider)
+      return;
+    // origin for sidebar is persisted, so get the previously selected sidebar
+    // first, otherwise fallback to the first provider in the list
+    let sbrowser = document.getElementById("social-sidebar-browser");
+    let origin = sbrowser.getAttribute("origin");
+    let providers = [p for (p of Social.providers) if (p.sidebarURL)];
+    let provider;
+    if (origin)
+      provider = Social._getProviderFromOrigin(origin);
+    if (!provider && providers.length > 0)
+      provider = providers[0];
+    if (provider)
+      this.provider = provider;
   },
 
-  setProvider: function(origin) {
-    Social.setProviderByOrigin(origin);
-    this._updateHeader();
-    this._updateCheckedMenuItems(origin);
+  get provider() {
+    return this._provider;
+  },
+
+  set provider(provider) {
+    if (!provider || provider.sidebarURL) {
+      this._provider = provider;
+      this._updateHeader();
+      this._updateCheckedMenuItems(provider && provider.origin);
+      this.update();
+    }
+  },
+
+  disableProvider: function(origin) {
+    if (this._provider && this._provider.origin == origin) {
+      this._provider = null;
+      // force a selection of the next provider if there is one
+      this.ensureProvider();
+    }
   },
 
   _updateHeader: function() {
     let provider = this.provider;
     let image, title;
     if (provider) {
       image = "url(" + (provider.icon32URL || provider.iconURL) + ")";
       title = provider.name;
@@ -879,23 +938,40 @@ SocialSidebar = {
         mi.removeAttribute("checked");
         mi.setAttribute("oncommand", "SocialSidebar.show(this.getAttribute('origin'));");
       }
     }
   },
 
   show: function(origin) {
     // always show the sidebar, and set the provider
-    this.setProvider(origin);
-    Services.prefs.setBoolPref("social.sidebar.open", true);
+    let broadcaster = document.getElementById("socialSidebarBroadcaster");
+    broadcaster.hidden = false;
+    if (origin)
+      this.provider = Social._getProviderFromOrigin(origin);
+    else
+      SocialSidebar.update();
+    this.saveWindowState();
   },
 
   hide: function() {
-    Services.prefs.setBoolPref("social.sidebar.open", false);
+    let broadcaster = document.getElementById("socialSidebarBroadcaster");
+    broadcaster.hidden = true;
     this._updateCheckedMenuItems();
+    this.clearProviderMenus();
+    SocialSidebar.update();
+    this.saveWindowState();
+  },
+
+  toggleSidebar: function SocialSidebar_toggle() {
+    let broadcaster = document.getElementById("socialSidebarBroadcaster");
+    if (broadcaster.hidden)
+      this.show();
+    else
+      this.hide();
   },
 
   populateSidebarMenu: function(event) {
     // Providers are removed from the view->sidebar menu when there is a change
     // in providers, so we only have to populate onshowing if there are no
     // provider menus. We populate this menu so long as there are enabled
     // providers with sidebars.
     let popup = event.target;
@@ -1256,19 +1332,21 @@ SocialStatus = {
   },
 
   setPanelErrorMessage: function(aNotificationFrame) {
     if (!aNotificationFrame)
       return;
 
     let src = aNotificationFrame.getAttribute("src");
     aNotificationFrame.removeAttribute("src");
+    let origin = aNotificationFrame.getAttribute("origin");
     aNotificationFrame.webNavigation.loadURI("about:socialerror?mode=tryAgainOnly&url=" +
-                                             encodeURIComponent(src),
-                                             null, null, null, null);
+                                            encodeURIComponent(src) + "&origin=" +
+                                            encodeURIComponent(origin),
+                                            null, null, null, null);
     let panel = aNotificationFrame.parentNode;
     sizeSocialPanelToContent(panel, aNotificationFrame);
   },
 
 };
 
 
 /**
--- a/browser/base/content/browser-syncui.js
+++ b/browser/base/content/browser-syncui.js
@@ -83,42 +83,74 @@ let gSyncUI = {
 
     // notificationbox will listen to observers from now on.
     Services.obs.removeObserver(this, "weave:notification:added");
   },
 
   _wasDelayed: false,
 
   _needsSetup: function SUI__needsSetup() {
+    // We want to treat "account needs verification" as "needs setup". We don't
+    // know what the user's verified state is until Sync is initialized, though,
+    // and we need to get an answer here synchronously (can't wait for
+    // getSignedInUser). So "reach in" to Weave.Service.identity to get the
+    // answer here, and we'll just have to deal with this not having an answer
+    // before Sync is initialized.
+
+    // Referencing Weave.Service will implicitly initialize sync, and we don't
+    // want to force that - so first check if it is ready.
+    let service = Cc["@mozilla.org/weave/service;1"]
+                  .getService(Components.interfaces.nsISupports)
+                  .wrappedJSObject;
+    if (service.ready && Weave.Service.identity._signedInUser) {
+      // If we have a signed in user already, and that user is not verified,
+      // revert to the "needs setup" state.
+      if (!Weave.Service.identity._signedInUser.verified) {
+        return true;
+      }
+    }
+
     let firstSync = "";
     try {
       firstSync = Services.prefs.getCharPref("services.sync.firstSync");
     } catch (e) { }
+
     return Weave.Status.checkSetup() == Weave.CLIENT_NOT_CONFIGURED ||
            firstSync == "notReady";
   },
 
   _loginFailed: function () {
-    // Referencing Weave.Service will implicitly initialize sync, and we don't
+    // Referencing Weave.Status will import a bunch of modules, and we don't
     // want to force that - so first check if it is ready.
     let service = Cc["@mozilla.org/weave/service;1"]
                   .getService(Components.interfaces.nsISupports)
                   .wrappedJSObject;
     if (!service.ready) {
       return false;
     }
+
     return Weave.Status.login == Weave.LOGIN_FAILED_LOGIN_REJECTED;
   },
 
   updateUI: function SUI_updateUI() {
     let needsSetup = this._needsSetup();
     let loginFailed = this._loginFailed();
-    document.getElementById("sync-setup-state").hidden = loginFailed || !needsSetup;
-    document.getElementById("sync-syncnow-state").hidden = loginFailed || needsSetup;
-    document.getElementById("sync-reauth-state").hidden = !loginFailed;
+
+    // Start off with a clean slate
+    document.getElementById("sync-reauth-state").hidden = true;
+    document.getElementById("sync-setup-state").hidden = true;
+    document.getElementById("sync-syncnow-state").hidden = true;
+
+    if (loginFailed) {
+      document.getElementById("sync-reauth-state").hidden = false;
+    } else if (needsSetup) {
+      document.getElementById("sync-setup-state").hidden = false;
+    } else {
+      document.getElementById("sync-syncnow-state").hidden = false;
+    }
 
     if (!gBrowser)
       return;
 
     let syncButton = document.getElementById("sync-button");
     let panelHorizontalButton = document.getElementById("PanelUI-fxa-status");
     [syncButton, panelHorizontalButton].forEach(function(button) {
       if (!button)
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1029,18 +1029,18 @@ var gBrowserInit = {
     Services.obs.addObserver(gXPInstallObserver, "addon-install-failed", false);
     Services.obs.addObserver(gXPInstallObserver, "addon-install-complete", false);
     Services.obs.addObserver(gFormSubmitObserver, "invalidformsubmit", false);
 
     BrowserOffline.init();
     OfflineApps.init();
     IndexedDBPromptHelper.init();
     gFormSubmitObserver.init();
-    SocialUI.init();
     gRemoteTabsUI.init();
+    gPageStyleMenu.init();
 
     // Initialize the full zoom setting.
     // We do this before the session restore service gets initialized so we can
     // apply full zoom settings to tabs restored by the session restore service.
     FullZoom.init();
     PanelUI.init();
     LightweightThemeListener.init();
     WebrtcIndicator.init();
@@ -1087,22 +1087,19 @@ var gBrowserInit = {
       document.getElementById("textfieldDirection-swap").hidden = false;
     }
 
     // Setup click-and-hold gestures access to the session history
     // menus if global click-and-hold isn't turned on
     if (!getBoolPref("ui.click_hold_context_menus", false))
       SetClickAndHoldHandlers();
 
-    // Bug 666804 - NetworkPrioritizer support for e10s
-    if (!gMultiProcessBrowser) {
-      let NP = {};
-      Cu.import("resource:///modules/NetworkPrioritizer.jsm", NP);
-      NP.trackBrowserWindow(window);
-    }
+    let NP = {};
+    Cu.import("resource:///modules/NetworkPrioritizer.jsm", NP);
+    NP.trackBrowserWindow(window);
 
     PlacesToolbarHelper.init();
 
     ctrlTab.readPref();
     gPrefService.addObserver(ctrlTab.prefName, ctrlTab, false);
 
     // Initialize the download manager some time after the app starts so that
     // auto-resume downloads begin (such as after crashing or quitting with
@@ -1190,19 +1187,25 @@ var gBrowserInit = {
     if (gMultiProcessBrowser) {
       // Bug 862519 - Backspace doesn't work in electrolysis builds.
       // We bypass the problem by disabling the backspace-to-go-back command.
       document.getElementById("cmd_handleBackspace").setAttribute("disabled", true);
       document.getElementById("key_delete").setAttribute("disabled", true);
     }
 
     SessionStore.promiseInitialized.then(() => {
+      // Bail out if the window has been closed in the meantime.
+      if (window.closed) {
+        return;
+      }
+
       // Enable the Restore Last Session command if needed
       RestoreLastSessionObserver.init();
 
+      SocialUI.init();
       TabView.init();
 
       setTimeout(function () { BrowserChromeTest.markAsReady(); }, 0);
     });
     this.delayedStartupFinished = true;
 
     Services.obs.notifyObservers(window, "browser-delayed-startup-finished", "");
     TelemetryTimestamps.add("delayedStartupFinished");
@@ -1290,16 +1293,17 @@ var gBrowserInit = {
       this._cancelDelayedStartup();
     } else {
       if (Win7Features)
         Win7Features.onCloseWindow();
 
       gPrefService.removeObserver(ctrlTab.prefName, ctrlTab);
       ctrlTab.uninit();
       TabView.uninit();
+      SocialUI.uninit();
       gBrowserThumbnails.uninit();
       FullZoom.destroy();
 
       Services.obs.removeObserver(gSessionHistoryObserver, "browser:purge-session-history");
       Services.obs.removeObserver(gXPInstallObserver, "addon-install-disabled");
       Services.obs.removeObserver(gXPInstallObserver, "addon-install-started");
       Services.obs.removeObserver(gXPInstallObserver, "addon-install-blocked");
       Services.obs.removeObserver(gXPInstallObserver, "addon-install-failed");
@@ -1314,17 +1318,16 @@ var gBrowserInit = {
 
       if (typeof WindowsPrefSync !== 'undefined') {
         WindowsPrefSync.uninit();
       }
 
       BrowserOffline.uninit();
       OfflineApps.uninit();
       IndexedDBPromptHelper.uninit();
-      SocialUI.uninit();
       LightweightThemeListener.uninit();
       PanelUI.uninit();
     }
 
     // Final window teardown, do this last.
     window.XULBrowserWindow = null;
     window.QueryInterface(Ci.nsIInterfaceRequestor)
           .getInterface(Ci.nsIWebNavigation)
@@ -3492,25 +3495,27 @@ var XULBrowserWindow = {
       aMaxTotalProgress);
   },
 
   // This function fires only for the currently selected tab.
   onStateChange: function (aWebProgress, aRequest, aStateFlags, aStatus) {
     const nsIWebProgressListener = Ci.nsIWebProgressListener;
     const nsIChannel = Ci.nsIChannel;
 
+    let browser = gBrowser.selectedBrowser;
+
     if (aStateFlags & nsIWebProgressListener.STATE_START &&
         aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {
 
       if (aRequest && aWebProgress.isTopLevel) {
         // clear out feed data
-        gBrowser.selectedBrowser.feeds = null;
+        browser.feeds = null;
 
         // clear out search-engine data
-        gBrowser.selectedBrowser.engines = null;
+        browser.engines = null;
       }
 
       this.isBusy = true;
 
       if (!(aStateFlags & nsIWebProgressListener.STATE_RESTORING)) {
         this._busyUI = true;
 
         // XXX: This needs to be based on window activity...
@@ -3541,17 +3546,17 @@ var XULBrowserWindow = {
             }
           }
         }
 
         this.status = "";
         this.setDefaultStatus(msg);
 
         // Disable menu entries for images, enable otherwise
-        if (!gMultiProcessBrowser && content.document && mimeTypeIsTextBased(content.document.contentType))
+        if (browser.documentContentType && mimeTypeIsTextBased(browser.documentContentType))
           this.isImage.removeAttribute('disabled');
         else
           this.isImage.setAttribute('disabled', 'true');
       }
 
       this.isBusy = false;
 
       if (this._busyUI) {
@@ -3585,32 +3590,33 @@ var XULBrowserWindow = {
           if (tooltipWindow == aWebProgress.DOMWindow) {
             pageTooltip.hidePopup();
             break;
           }
         }
       }
     }
 
+    let browser = gBrowser.selectedBrowser;
+
     // Disable menu entries for images, enable otherwise
-    if (!gMultiProcessBrowser && content.document && mimeTypeIsTextBased(content.document.contentType))
+    if (browser.documentContentType && mimeTypeIsTextBased(browser.documentContentType))
       this.isImage.removeAttribute('disabled');
     else
       this.isImage.setAttribute('disabled', 'true');
 
     this.hideOverLinkImmediately = true;
     this.setOverLink("", null);
     this.hideOverLinkImmediately = false;
 
     // We should probably not do this if the value has changed since the user
     // searched
     // Update urlbar only if a new page was loaded on the primary content area
     // Do not update urlbar if there was a subframe navigation
 
-    var browser = gBrowser.selectedBrowser;
     if (aWebProgress.isTopLevel) {
       if ((location == "about:blank" && (gMultiProcessBrowser || !content.opener)) ||
           location == "") {  // Second condition is for new tabs, otherwise
                              // reload function is enabled until tab is refreshed.
         this.reloadCommand.setAttribute("disabled", "true");
       } else {
         this.reloadCommand.removeAttribute("disabled");
       }
@@ -5194,49 +5200,64 @@ function charsetLoadListener() {
   if (charset.length > 0 && (charset != gLastBrowserCharset)) {
     gPrevCharset = gLastBrowserCharset;
     gLastBrowserCharset = charset;
   }
 }
 
 var gPageStyleMenu = {
 
-  _getAllStyleSheets: function (frameset) {
-    var styleSheetsArray = Array.slice(frameset.document.styleSheets);
-    for (let i = 0; i < frameset.frames.length; i++) {
-      let frameSheets = this._getAllStyleSheets(frameset.frames[i]);
-      styleSheetsArray = styleSheetsArray.concat(frameSheets);
-    }
-    return styleSheetsArray;
+  // This maps from a <browser> element (or, more specifically, a
+  // browser's permanentKey) to a CPOW that gives synchronous access
+  // to the list of style sheets in a content window. The use of the
+  // permanentKey is to avoid issues with docshell swapping.
+  _pageStyleSyncHandlers: new WeakMap(),
+
+  init: function() {
+    let mm = window.messageManager;
+    mm.addMessageListener("PageStyle:SetSyncHandler", (msg) => {
+      this._pageStyleSyncHandlers.set(msg.target.permanentKey, msg.objects.syncHandler);
+    });
+  },
+
+  getAllStyleSheets: function () {
+    let handler = this._pageStyleSyncHandlers.get(gBrowser.selectedBrowser.permanentKey);
+    try {
+      return handler.getAllStyleSheets();
+    } catch (ex) {
+      // In case the child died or timed out.
+      return [];
+    }
+  },
+
+  _getStyleSheetInfo: function (browser) {
+    let handler = this._pageStyleSyncHandlers.get(gBrowser.selectedBrowser.permanentKey);
+    try {
+      return handler.getStyleSheetInfo();
+    } catch (ex) {
+      // In case the child died or timed out.
+      return {styleSheets: [], authorStyleDisabled: false, preferredStyleSheetSet: true};
+    }
   },
 
   fillPopup: function (menuPopup) {
+    let styleSheetInfo = this._getStyleSheetInfo(gBrowser.selectedBrowser);
     var noStyle = menuPopup.firstChild;
     var persistentOnly = noStyle.nextSibling;
     var sep = persistentOnly.nextSibling;
     while (sep.nextSibling)
       menuPopup.removeChild(sep.nextSibling);
 
-    var styleSheets = this._getAllStyleSheets(window.content);
+    let styleSheets = styleSheetInfo.styleSheets;
     var currentStyleSheets = {};
-    var styleDisabled = getMarkupDocumentViewer().authorStyleDisabled;
+    var styleDisabled = styleSheetInfo.authorStyleDisabled;
     var haveAltSheets = false;
     var altStyleSelected = false;
 
     for (let currentStyleSheet of styleSheets) {
-      if (!currentStyleSheet.title)
-        continue;
-
-      // Skip any stylesheets whose media attribute doesn't match.
-      if (currentStyleSheet.media.length > 0) {
-        let mediaQueryList = currentStyleSheet.media.mediaText;
-        if (!window.content.matchMedia(mediaQueryList).matches)
-          continue;
-      }
-
       if (!currentStyleSheet.disabled)
         altStyleSelected = true;
 
       haveAltSheets = true;
 
       let lastWithSameTitle = null;
       if (currentStyleSheet.title in currentStyleSheets)
         lastWithSameTitle = currentStyleSheets[currentStyleSheet.title];
@@ -5252,61 +5273,39 @@ var gPageStyleMenu = {
         currentStyleSheets[currentStyleSheet.title] = menuItem;
       } else if (currentStyleSheet.disabled) {
         lastWithSameTitle.removeAttribute("checked");
       }
     }
 
     noStyle.setAttribute("checked", styleDisabled);
     persistentOnly.setAttribute("checked", !altStyleSelected && !styleDisabled);
-    persistentOnly.hidden = (window.content.document.preferredStyleSheetSet) ? haveAltSheets : false;
+    persistentOnly.hidden = styleSheetInfo.preferredStyleSheetSet ? haveAltSheets : false;
     sep.hidden = (noStyle.hidden && persistentOnly.hidden) || !haveAltSheets;
   },
 
-  _stylesheetInFrame: function (frame, title) {
-    return Array.some(frame.document.styleSheets,
-                      function (stylesheet) stylesheet.title == title);
-  },
-
-  _stylesheetSwitchFrame: function (frame, title) {
-    var docStyleSheets = frame.document.styleSheets;
-
-    for (let i = 0; i < docStyleSheets.length; ++i) {
-      let docStyleSheet = docStyleSheets[i];
-
-      if (docStyleSheet.title)
-        docStyleSheet.disabled = (docStyleSheet.title != title);
-      else if (docStyleSheet.disabled)
-        docStyleSheet.disabled = false;
-    }
-  },
-
-  _stylesheetSwitchAll: function (frameset, title) {
-    if (!title || this._stylesheetInFrame(frameset, title))
-      this._stylesheetSwitchFrame(frameset, title);
-
-    for (let i = 0; i < frameset.frames.length; i++)
-      this._stylesheetSwitchAll(frameset.frames[i], title);
-  },
-
-  switchStyleSheet: function (title, contentWindow) {
-    getMarkupDocumentViewer().authorStyleDisabled = false;
-    this._stylesheetSwitchAll(contentWindow || content, title);
+  switchStyleSheet: function (title) {
+    let mm = gBrowser.selectedBrowser.messageManager;
+    mm.sendAsyncMessage("PageStyle:Switch", {title: title});
   },
 
   disableStyle: function () {
-    getMarkupDocumentViewer().authorStyleDisabled = true;
+    let mm = gBrowser.selectedBrowser.messageManager;
+    mm.sendAsyncMessage("PageStyle:Disable");
   },
 };
 
 /* Legacy global page-style functions */
-var getAllStyleSheets   = gPageStyleMenu._getAllStyleSheets.bind(gPageStyleMenu);
+var getAllStyleSheets   = gPageStyleMenu.getAllStyleSheets.bind(gPageStyleMenu);
 var stylesheetFillPopup = gPageStyleMenu.fillPopup.bind(gPageStyleMenu);
 function stylesheetSwitchAll(contentWindow, title) {
-  gPageStyleMenu.switchStyleSheet(title, contentWindow);
+  // We ignore the contentWindow param. Add-ons don't appear to use
+  // it, and it's difficult to support in e10s (where it will be a
+  // CPOW).
+  gPageStyleMenu.switchStyleSheet(title);
 }
 function setStyleDisabled(disabled) {
   if (disabled)
     gPageStyleMenu.disableStyle();
 }
 
 
 var BrowserOffline = {
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -626,16 +626,17 @@
              aria-label="&navbarCmd.label;"
              fullscreentoolbar="true" mode="icons" customizable="true"
              iconsize="small"
              defaultset="urlbar-container,search-container,webrtc-status-button,bookmarks-menu-button,downloads-button,home-button,social-share-button,social-toolbar-item"
              customizationtarget="nav-bar-customization-target"
              overflowable="true"
              overflowbutton="nav-bar-overflow-button"
              overflowtarget="widget-overflow-list"
+             overflowpanel="widget-overflow"
              context="toolbar-context-menu">
 
       <hbox id="nav-bar-customization-target" flex="1">
         <toolbaritem id="urlbar-container" flex="400" persist="width"
                      forwarddisabled="true" title="&locationItem.title;" removable="false"
                      cui-areatype="toolbar"
                      class="chromeclass-location" overflows="false">
           <toolbarbutton id="back-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
@@ -1074,17 +1075,16 @@
                         label="&social.toggleSidebar.label;"
                         accesskey="&social.toggleSidebar.accesskey;"/>
               <menuitem class="social-toggle-notifications-menuitem"
                         type="checkbox"
                         autocheck="false"
                         command="Social:ToggleNotifications"
                         label="&social.toggleNotifications.label;"
                         accesskey="&social.toggleNotifications.accesskey;"/>
-              <menuitem class="social-toggle-menuitem" command="Social:Toggle"/>
               <menuseparator/>
               <menuseparator class="social-provider-menu" hidden="true"/>
               <menuitem class="social-addons-menuitem" command="Social:Addons"
                         label="&social.addons.label;"/>
               <menuitem label="&social.learnMore.label;"
                         accesskey="&social.learnMore.accesskey;"
                         oncommand="SocialUI.showLearnMore();"/>
             </menupopup>
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -285,8 +285,104 @@ let ClickEventHandler = {
 };
 ClickEventHandler.init();
 
 ContentLinkHandler.init(this);
 
 addEventListener("DOMWebNotificationClicked", function(event) {
   sendAsyncMessage("DOMWebNotificationClicked", {});
 }, false);
+
+let PageStyleHandler = {
+  init: function() {
+    addMessageListener("PageStyle:Switch", this);
+    addMessageListener("PageStyle:Disable", this);
+
+    // Send a CPOW to the parent so that it can synchronously request
+    // the list of style sheets.
+    sendSyncMessage("PageStyle:SetSyncHandler", {}, {syncHandler: this});
+  },
+
+  get markupDocumentViewer() {
+    return docShell.contentViewer.QueryInterface(Ci.nsIMarkupDocumentViewer);
+  },
+
+  // Called synchronously via CPOW from the parent.
+  getStyleSheetInfo: function() {
+    let styleSheets = this._filterStyleSheets(this.getAllStyleSheets());
+    return {
+      styleSheets: styleSheets,
+      authorStyleDisabled: this.markupDocumentViewer.authorStyleDisabled,
+      preferredStyleSheetSet: content.document.preferredStyleSheetSet
+    };
+  },
+
+  // Called synchronously via CPOW from the parent.
+  getAllStyleSheets: function(frameset = content) {
+    let selfSheets = Array.slice(frameset.document.styleSheets);
+    let subSheets = Array.map(frameset.frames, frame => this.getAllStyleSheets(frame));
+    return selfSheets.concat(...subSheets);
+  },
+
+  receiveMessage: function(msg) {
+    switch (msg.name) {
+      case "PageStyle:Switch":
+        this.markupDocumentViewer.authorStyleDisabled = false;
+        this._stylesheetSwitchAll(content, msg.data.title);
+        break;
+
+      case "PageStyle:Disable":
+        this.markupDocumentViewer.authorStyleDisabled = true;
+        break;
+    }
+  },
+
+  _stylesheetSwitchAll: function (frameset, title) {
+    if (!title || this._stylesheetInFrame(frameset, title)) {
+      this._stylesheetSwitchFrame(frameset, title);
+    }
+
+    for (let i = 0; i < frameset.frames.length; i++) {
+      // Recurse into sub-frames.
+      this._stylesheetSwitchAll(frameset.frames[i], title);
+    }
+  },
+
+  _stylesheetSwitchFrame: function (frame, title) {
+    var docStyleSheets = frame.document.styleSheets;
+
+    for (let i = 0; i < docStyleSheets.length; ++i) {
+      let docStyleSheet = docStyleSheets[i];
+      if (docStyleSheet.title) {
+        docStyleSheet.disabled = (docStyleSheet.title != title);
+      } else if (docStyleSheet.disabled) {
+        docStyleSheet.disabled = false;
+      }
+    }
+  },
+
+  _stylesheetInFrame: function (frame, title) {
+    return Array.some(frame.document.styleSheets, (styleSheet) => styleSheet.title == title);
+  },
+
+  _filterStyleSheets: function(styleSheets) {
+    let result = [];
+
+    for (let currentStyleSheet of styleSheets) {
+      if (!currentStyleSheet.title)
+        continue;
+
+      // Skip any stylesheets that don't match the screen media type.
+      if (currentStyleSheet.media.length > 0) {
+        let mediaQueryList = currentStyleSheet.media.mediaText;
+        if (!content.matchMedia(mediaQueryList).matches) {
+          continue;
+        }
+      }
+
+      result.push({title: currentStyleSheet.title,
+                   disabled: currentStyleSheet.disabled});
+    }
+
+    return result;
+  },
+};
+PageStyleHandler.init();
--- a/browser/base/content/socialchat.xml
+++ b/browser/base/content/socialchat.xml
@@ -29,17 +29,19 @@
     </content>
 
     <implementation implements="nsIDOMEventListener">
       <constructor><![CDATA[
         let Social = Components.utils.import("resource:///modules/Social.jsm", {}).Social;
         this.content.__defineGetter__("popupnotificationanchor",
                                       () => document.getAnonymousElementByAttribute(this, "anonid", "notification-icon"));
         Social.setErrorListener(this.content, function(aBrowser) {
-          aBrowser.webNavigation.loadURI("about:socialerror?mode=compactInfo", null, null, null, null);
+          aBrowser.webNavigation.loadURI("about:socialerror?mode=compactInfo&origin=" +
+                                 encodeURIComponent(aBrowser.getAttribute("origin")),
+                                 null, null, null, null);
         });
         if (!this.chatbar) {
           document.getAnonymousElementByAttribute(this, "anonid", "minimize").hidden = true;
           document.getAnonymousElementByAttribute(this, "anonid", "close").hidden = true;
         }
         let contentWindow = this.contentWindow;
         this.addEventListener("DOMContentLoaded", function DOMContentLoaded(event) {
           if (event.target != this.contentDocument)
@@ -146,17 +148,19 @@
           aTarget.src = this.src;
           aTarget.content.setAttribute("origin", this.content.getAttribute("origin"));
           aTarget.content.popupnotificationanchor.className = this.content.popupnotificationanchor.className;
           this.content.socialErrorListener.remove();
           aTarget.content.socialErrorListener.remove();
           this.content.swapDocShells(aTarget.content);
           Social.setErrorListener(this.content, function(aBrowser) {}); // 'this' will be destroyed soon.
           Social.setErrorListener(aTarget.content, function(aBrowser) {
-            aBrowser.webNavigation.loadURI("about:socialerror?mode=compactInfo", null, null, null, null);
+            aBrowser.webNavigation.loadURI("about:socialerror?mode=compactInfo&origin=" +
+                                 encodeURIComponent(aBrowser.getAttribute("origin")),
+                                 null, null, null, null);
           });
         ]]></body>
       </method>
 
       <method name="onTitlebarClick">
         <parameter name="aEvent"/>
         <body><![CDATA[
           if (!this.chatbar)
--- a/browser/base/content/test/general/browser_aboutAccounts.js
+++ b/browser/base/content/test/general/browser_aboutAccounts.js
@@ -29,17 +29,17 @@ let gTests = [
 {
   desc: "Test the remote commands",
   teardown: function* () {
     gBrowser.removeCurrentTab();
     yield fxAccounts.signOut();
   },
   run: function* ()
   {
-    setPref("identity.fxaccounts.remote.uri",
+    setPref("identity.fxaccounts.remote.signup.uri",
             "https://example.com/browser/browser/base/content/test/general/accounts_testRemoteCommands.html");
     yield promiseNewTabLoadEvent("about:accounts");
 
     let deferred = Promise.defer();
 
     let results = 0;
     try {
       let win = gBrowser.contentWindow;
@@ -110,17 +110,17 @@ let gTests = [
   }
 },
 {
   desc: "Test action=signup - no user logged in",
   teardown: () => gBrowser.removeCurrentTab(),
   run: function* ()
   {
     const expected_url = "https://example.com/?is_sign_up";
-    setPref("identity.fxaccounts.remote.uri", expected_url);
+    setPref("identity.fxaccounts.remote.signup.uri", expected_url);
     let [tab, url] = yield promiseNewTabWithIframeLoadEvent("about:accounts?action=signup");
     is(url, expected_url, "action=signup got the expected URL");
     // we expect the remote iframe to be shown.
     yield checkVisibilities(tab, {
       stage: false, // parent of 'manage' and 'intro'
       manage: false,
       intro: false, // this is  "get started"
       remote: true
@@ -128,17 +128,17 @@ let gTests = [
   },
 },
 {
   desc: "Test action=signup - user logged in",
   teardown: () => gBrowser.removeCurrentTab(),
   run: function* ()
   {
     const expected_url = "https://example.com/?is_sign_up";
-    setPref("identity.fxaccounts.remote.uri", expected_url);
+    setPref("identity.fxaccounts.remote.signup.uri", expected_url);
     yield setSignedInUser();
     let tab = yield promiseNewTabLoadEvent("about:accounts?action=signup");
     yield fxAccounts.getSignedInUser();
     // we expect "manage" to be shown.
     yield checkVisibilities(tab, {
       stage: true, // parent of 'manage' and 'intro'
       manage: true,
       intro: false, // this is  "get started"
@@ -174,17 +174,17 @@ let gTests = [
   },
 },
 {
   desc: "Test observers about:accounts",
   teardown: function() {
     gBrowser.removeCurrentTab();
   },
   run: function* () {
-    setPref("identity.fxaccounts.remote.uri", "https://example.com/");
+    setPref("identity.fxaccounts.remote.signup.uri", "https://example.com/");
     yield setSignedInUser();
     let tab = yield promiseNewTabLoadEvent("about:accounts");
     // sign the user out - the tab should have action=signin
     yield signOut();
     // wait for the new load.
     yield promiseOneMessage(tab, "test:document:load");
     is(tab.linkedBrowser.contentDocument.location.href, "about:accounts?action=signin");
   }
--- a/browser/base/content/test/social/browser_addons.js
+++ b/browser/base/content/test/social/browser_addons.js
@@ -319,17 +319,16 @@ var tests = {
 
     addTab(activationURL, function(tab) {
       let doc = tab.linkedBrowser.contentDocument;
       let installFrom = doc.nodePrincipal.origin;
       Services.prefs.setCharPref("social.whitelist", installFrom);
       Social.installProvider(doc, manifest2, function(addonManifest) {
         SocialService.addBuiltinProvider(addonManifest.origin, function(provider) {
           is(provider.manifest.version, 1, "manifest version is 1");
-          Social.enabled = true;
 
           // watch for the provider-update and test the new version
           SocialService.registerProviderListener(function providerListener(topic, origin, providers) {
             if (topic != "provider-update")
               return;
             is(origin, addonManifest.origin, "provider updated")
             SocialService.unregisterProviderListener(providerListener);
             Services.prefs.clearUserPref("social.whitelist");
@@ -337,24 +336,15 @@ var tests = {
             is(provider.manifest.version, 2, "manifest version is 2");
             Social.uninstallProvider(origin, function() {
               gBrowser.removeTab(tab);
               next();
             });
           });
 
           let port = provider.getWorkerPort();
-          port.onmessage = function (e) {
-            let topic = e.data.topic;
-            switch (topic) {
-              case "got-sidebar-message":
-                ok(true, "got the sidebar message from provider 1");
-                port.postMessage({topic: "worker.update", data: true});
-                break;
-            }
-          };
-          port.postMessage({topic: "test-init"});
+          port.postMessage({topic: "worker.update", data: true});
 
         });
       });
     });
   }
 }
--- a/browser/base/content/test/social/browser_chat_tearoff.js
+++ b/browser/base/content/test/social/browser_chat_tearoff.js
@@ -15,27 +15,29 @@ function test() {
   };
 
   let postSubTest = function(cb) {
     let chats = document.getElementById("pinnedchats");
     ok(chats.children.length == 0, "no chatty children left behind");
     cb();
   };
   runSocialTestWithProvider(manifest, function (finishcb) {
+    SocialSidebar.show();
+    ok(SocialSidebar.provider, "sidebar provider exists");
     runSocialTests(tests, undefined, postSubTest, function() {
       finishcb();
     });
   });
 }
 
 var tests = {
   testTearoffChat: function(next) {
     let chats = document.getElementById("pinnedchats");
     let chatTitle;
-    let port = Social.provider.getWorkerPort();
+    let port = SocialSidebar.provider.getWorkerPort();
     ok(port, "provider has a port");
     port.onmessage = function (e) {
       let topic = e.data.topic;
       switch (topic) {
         case "got-sidebar-message":
           port.postMessage({topic: "test-chatbox-open"});
           break;
         case "got-chatbox-visibility":
@@ -128,17 +130,17 @@ var tests = {
     });
 
     port.postMessage({topic: "test-init", data: { id: 1 }});
   },
 
   testCloseOnLogout: function(next) {
     let chats = document.getElementById("pinnedchats");
     const chatUrl = "https://example.com/browser/browser/base/content/test/social/social_chat.html";
-    let port = Social.provider.getWorkerPort();
+    let port = SocialSidebar.provider.getWorkerPort();
     ok(port, "provider has a port");
     port.postMessage({topic: "test-init"});
     port.onmessage = function (e) {
       let topic = e.data.topic;
       switch (topic) {
         case "got-chatbox-visibility":
           // chatbox is open, lets detach. The new chat window will be caught in
           // the window watcher below
@@ -199,17 +201,17 @@ var tests = {
 
     port.postMessage({topic: "test-worker-chat", data: chatUrl});
   },
 
   testReattachTwice: function(next) {
     let chats = document.getElementById("pinnedchats");
     const chatUrl = "https://example.com/browser/browser/base/content/test/social/social_chat.html";
     let chatBoxCount = 0, reattachCount = 0;
-    let port = Social.provider.getWorkerPort();
+    let port = SocialSidebar.provider.getWorkerPort();
     ok(port, "provider has a port");
     port.postMessage({topic: "test-init"});
     port.onmessage = function (e) {
       let topic = e.data.topic;
       switch (topic) {
         case "got-chatbox-visibility":
           // chatbox is open, lets detach. The new chat window will be caught in
           // the window watcher below
--- a/browser/base/content/test/social/browser_share.js
+++ b/browser/base/content/test/social/browser_share.js
@@ -98,22 +98,23 @@ function hasoptions(testOptions, options
       is(message_data, data, "option "+option);
     }
   }
 }
 
 var tests = {
   testSharePage: function(next) {
     let panel = document.getElementById("social-flyout-panel");
-    let port = Social.provider.getWorkerPort();
+    SocialSidebar.show();
+    let port = SocialSidebar.provider.getWorkerPort();
     ok(port, "provider has a port");
     let testTab;
     let testIndex = 0;
     let testData = corpus[testIndex++];
-    
+
     function runOneTest() {
       loadURLInTab(testData.url, function(tab) {
         testTab = tab;
         SocialShare.sharePage();
       });
     }
 
     port.onmessage = function (e) {
--- a/browser/base/content/test/social/browser_social_activation.js
+++ b/browser/base/content/test/social/browser_social_activation.js
@@ -2,17 +2,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 let SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).SocialService;
 
 let tabsToRemove = [];
 
 function postTestCleanup(callback) {
-  Social.provider = null;
   // any tabs opened by the test.
   for (let tab of tabsToRemove)
     gBrowser.removeTab(tab);
   tabsToRemove = [];
   // theses tests use the notification panel but don't bother waiting for it
   // to fully open - the end result is that the panel might stay open
   //SocialUI.activationPanel.hidePopup();
 
@@ -81,30 +80,31 @@ function activateIFrameProvider(domain, 
   let activationURL = domain+"/browser/browser/base/content/test/social/social_activate_iframe.html"
   addTab(activationURL, function(tab) {
     sendActivationEvent(tab, callback, false);
   });
 }
 
 function waitForProviderLoad(cb) {
   Services.obs.addObserver(function providerSet(subject, topic, data) {
-    Services.obs.removeObserver(providerSet, "social:provider-set");
-    info("social:provider-set observer was notified");
+    Services.obs.removeObserver(providerSet, "social:provider-enabled");
+    info("social:provider-enabled observer was notified");
     waitForCondition(function() {
       let sbrowser = document.getElementById("social-sidebar-browser");
-      return Social.provider &&
-             Social.provider.profile &&
-             Social.provider.profile.displayName &&
+      let provider = SocialSidebar.provider;
+      return provider &&
+             provider.profile &&
+             provider.profile.displayName &&
              sbrowser.docShellIsActive;
     }, function() {
       // executeSoon to let the browser UI observers run first
       executeSoon(cb);
     },
     "waitForProviderLoad: provider profile was not set");
-  }, "social:provider-set", false);
+  }, "social:provider-enabled", false);
 }
 
 
 function getAddonItemInList(aId, aList) {
   var item = aList.firstChild;
   while (item) {
     if ("mAddon" in item && item.mAddon.id == aId) {
       aList.ensureElementIsVisible(item);
@@ -152,17 +152,18 @@ function activateOneProvider(manifest, f
   });
 
   activateProvider(manifest.origin, function() {
     if (!finishActivation) {
       ok(panel.hidden, "activation panel is not showing");
       executeSoon(aCallback);
     } else {
       waitForProviderLoad(function() {
-        is(Social.provider.origin, manifest.origin, "new provider is active");
+        is(SocialSidebar.provider.origin, manifest.origin, "new provider is active");
+        ok(SocialSidebar.opened, "sidebar is open");
         checkSocialUI();
         executeSoon(aCallback);
       });
     }
   });
 }
 
 let gTestDomains = ["https://example.com", "https://test1.example.com", "https://test2.example.com"];
@@ -209,66 +210,65 @@ var tests = {
       next();
     });
   },
 
   testIFrameActivation: function(next) {
     Services.prefs.setCharPref("social.whitelist", gTestDomains.join(","));
     activateIFrameProvider(gTestDomains[0], function() {
       is(SocialUI.enabled, false, "SocialUI is not enabled");
-      ok(!Social.provider, "provider is not installed");
+      ok(!SocialSidebar.provider, "provider is not installed");
       let panel = document.getElementById("servicesInstall-notification");
       ok(panel.hidden, "activation panel still hidden");
       checkSocialUI();
       Services.prefs.clearUserPref("social.whitelist");
       next();
     });
   },
 
   testActivationFirstProvider: function(next) {
     Services.prefs.setCharPref("social.whitelist", gTestDomains.join(","));
     // first up we add a manifest entry for a single provider.
     activateOneProvider(gProviders[0], false, function() {
       // we deactivated leaving no providers left, so Social is disabled.
-      ok(!Social.provider, "should be no provider left after disabling");
+      ok(!SocialSidebar.provider, "should be no provider left after disabling");
       checkSocialUI();
       Services.prefs.clearUserPref("social.whitelist");
       next();
     });
   },
 
   testActivationBuiltin: function(next) {
     let prefname = addBuiltinManifest(gProviders[0]);
     is(SocialService.getOriginActivationType(gTestDomains[0]), "builtin", "manifest is builtin");
     // first up we add a manifest entry for a single provider.
     activateOneProvider(gProviders[0], false, function() {
       // we deactivated leaving no providers left, so Social is disabled.
-      ok(!Social.provider, "should be no provider left after disabling");
+      ok(!SocialSidebar.provider, "should be no provider left after disabling");
       checkSocialUI();
       resetBuiltinManifestPref(prefname);
       next();
     });
   },
 
   testActivationMultipleProvider: function(next) {
     // The trick with this test is to make sure that Social.providers[1] is
     // the current provider when doing the undo - this makes sure that the
     // Social code doesn't fallback to Social.providers[0], which it will
     // do in some cases (but those cases do not include what this test does)
     // first enable the 2 providers
     Services.prefs.setCharPref("social.whitelist", gTestDomains.join(","));
     SocialService.addProvider(gProviders[0], function() {
       SocialService.addProvider(gProviders[1], function() {
-        Social.provider = Social.providers[1];
         checkSocialUI();
         // activate the last provider.
         let prefname = addBuiltinManifest(gProviders[2]);
         activateOneProvider(gProviders[2], false, function() {
           // we deactivated - the first provider should be enabled.
-          is(Social.provider.origin, Social.providers[1].origin, "original provider should have been reactivated");
+          is(SocialSidebar.provider.origin, Social.providers[1].origin, "original provider should have been reactivated");
           checkSocialUI();
           Services.prefs.clearUserPref("social.whitelist");
           resetBuiltinManifestPref(prefname);
           next();
         });
       });
     });
   },
@@ -284,22 +284,24 @@ var tests = {
 
     gBrowser.selectedBrowser.addEventListener("load", function tabLoad() {
       gBrowser.selectedBrowser.removeEventListener("load", tabLoad, true);
       let browser = blanktab.linkedBrowser;
       is(browser.currentURI.spec, "about:addons", "about:addons should load into blank tab.");
 
       let prefname = addBuiltinManifest(gProviders[0]);
       activateOneProvider(gProviders[0], true, function() {
+        info("first activation completed");
         gBrowser.removeTab(gBrowser.selectedTab);
         tabsToRemove.pop();
         // uninstall the provider
         clickAddonRemoveButton(blanktab, function(addon) {
           checkSocialUI();
           activateOneProvider(gProviders[0], true, function() {
+            info("second activation completed");
 
             // after closing the addons tab, verify provider is still installed
             gBrowser.tabContainer.addEventListener("TabClose", function onTabClose() {
               gBrowser.tabContainer.removeEventListener("TabClose", onTabClose);
               AddonManager.getAddonsByTypes(["service"], function(aAddons) {
                 is(aAddons.length, 1, "there can be only one");
                 Services.prefs.clearUserPref("social.whitelist");
                 resetBuiltinManifestPref(prefname);
--- a/browser/base/content/test/social/browser_social_chatwindow.js
+++ b/browser/base/content/test/social/browser_social_chatwindow.js
@@ -39,23 +39,16 @@ function openChat(provider, callback) {
     }
   }
   let url = chatUrl + "?" + (chatId++);
   port.postMessage({topic: "test-init"});
   port.postMessage({topic: "test-worker-chat", data: url});
   gURLsNotRemembered.push(url);
 }
 
-function waitPrefChange(cb) {
-  Services.prefs.addObserver("social.enabled", function prefObserver(subject, topic, data) {
-    Services.prefs.removeObserver("social.enabled", prefObserver);
-    executeSoon(cb);
-  }, false);
-}
-
 function test() {
   requestLongerTimeout(2); // only debug builds seem to need more time...
   waitForExplicitFinish();
 
   let oldwidth = window.outerWidth; // we futz with these, so we restore them
   let oldleft = window.screenX;
   window.moveTo(0, window.screenY)
   let postSubTest = function(cb) {
@@ -63,28 +56,29 @@ function test() {
     ok(chats.children.length == 0, "no chatty children left behind");
     cb();
   };
   runSocialTestWithProvider(manifests, function (finishcb) {
     ok(Social.enabled, "Social is enabled");
     ok(Social.providers[0].getWorkerPort(), "provider 0 has port");
     ok(Social.providers[1].getWorkerPort(), "provider 1 has port");
     ok(Social.providers[2].getWorkerPort(), "provider 2 has port");
+    SocialSidebar.show();
     runSocialTests(tests, undefined, postSubTest, function() {
       window.moveTo(oldleft, window.screenY)
       window.resizeTo(oldwidth, window.outerHeight);
       finishcb();
     });
   });
 }
 
 var tests = {
   testOpenCloseChat: function(next) {
     let chats = document.getElementById("pinnedchats");
-    let port = Social.provider.getWorkerPort();
+    let port = SocialSidebar.provider.getWorkerPort();
     ok(port, "provider has a port");
     port.onmessage = function (e) {
       let topic = e.data.topic;
       switch (topic) {
         case "got-sidebar-message":
           port.postMessage({topic: "test-chatbox-open"});
           break;
         case "got-chatbox-visibility":
@@ -114,17 +108,17 @@ var tests = {
     port.postMessage({topic: "test-init", data: { id: 1 }});
   },
   testOpenMinimized: function(next) {
     // In this case the sidebar opens a chat (without specifying minimized).
     // We then minimize it and have the sidebar reopen the chat (again without
     // minimized).  On that second call the chat should open and no longer
     // be minimized.
     let chats = document.getElementById("pinnedchats");
-    let port = Social.provider.getWorkerPort();
+    let port = SocialSidebar.provider.getWorkerPort();
     let seen_opened = false;
     port.onmessage = function (e) {
       let topic = e.data.topic;
       switch (topic) {
         case "test-init-done":
           port.postMessage({topic: "test-chatbox-open"});
           break;
         case "chatbox-opened":
@@ -149,17 +143,17 @@ var tests = {
           }
       }
     }
     port.postMessage({topic: "test-init", data: { id: 1 }});
   },
   testManyChats: function(next) {
     // open enough chats to overflow the window, then check
     // if the menupopup is visible
-    let port = Social.provider.getWorkerPort();
+    let port = SocialSidebar.provider.getWorkerPort();
     let chats = document.getElementById("pinnedchats");
     ok(port, "provider has a port");
     ok(chats.menupopup.parentNode.collapsed, "popup nub collapsed at start");
     port.postMessage({topic: "test-init"});
     // we should *never* find a test box that needs more than this to cause
     // an overflow!
     let maxToOpen = 20;
     let numOpened = 0;
@@ -188,19 +182,19 @@ var tests = {
           port.close();
           next();
           break;
       }
     }
     maybeOpenAnother();
   },
   testWorkerChatWindow: function(next) {
-    const chatUrl = Social.provider.origin + "/browser/browser/base/content/test/social/social_chat.html";
+    const chatUrl = SocialSidebar.provider.origin + "/browser/browser/base/content/test/social/social_chat.html";
     let chats = document.getElementById("pinnedchats");
-    let port = Social.provider.getWorkerPort();
+    let port = SocialSidebar.provider.getWorkerPort();
     ok(port, "provider has a port");
     port.postMessage({topic: "test-init"});
     port.onmessage = function (e) {
       let topic = e.data.topic;
       switch (topic) {
         case "got-chatbox-message":
           ok(true, "got a chat window opened");
           ok(chats.selectedChat, "chatbox from worker opened");
@@ -214,17 +208,17 @@ var tests = {
           break;
       }
     }
     ok(!chats.selectedChat, "chats are all closed");
     port.postMessage({topic: "test-worker-chat", data: chatUrl});
   },
   testCloseSelf: function(next) {
     let chats = document.getElementById("pinnedchats");
-    let port = Social.provider.getWorkerPort();
+    let port = SocialSidebar.provider.getWorkerPort();
     ok(port, "provider has a port");
     port.onmessage = function (e) {
       let topic = e.data.topic;
       switch (topic) {
         case "test-init-done":
           port.postMessage({topic: "test-chatbox-open"});
           break;
         case "got-chatbox-visibility":
@@ -242,17 +236,17 @@ var tests = {
           next();
           break;
       }
     }
     port.postMessage({topic: "test-init", data: { id: 1 }});
   },
   testSameChatCallbacks: function(next) {
     let chats = document.getElementById("pinnedchats");
-    let port = Social.provider.getWorkerPort();
+    let port = SocialSidebar.provider.getWorkerPort();
     let seen_opened = false;
     port.onmessage = function (e) {
       let topic = e.data.topic;
       switch (topic) {
         case "test-init-done":
           port.postMessage({topic: "test-chatbox-open"});
           break;
         case "chatbox-opened":
@@ -273,17 +267,17 @@ var tests = {
           }
       }
     }
     port.postMessage({topic: "test-init", data: { id: 1 }});
   },
 
   // check removeAll does the right thing
   testRemoveAll: function(next, mode) {
-    let port = Social.provider.getWorkerPort();
+    let port = SocialSidebar.provider.getWorkerPort();
     port.postMessage({topic: "test-init"});
     get3ChatsForCollapsing(mode || "normal", function() {
       let chatbar = window.SocialChatBar.chatbar;
       chatbar.removeAll();
       // should be no evidence of any chats left.
       is(chatbar.childNodes.length, 0, "should be no chats left");
       checkPopup();
       is(chatbar.selectedChat, null, "nothing should be selected");
@@ -329,31 +323,31 @@ var tests = {
           closeAllChats();
           next();
         });
       });
     });
   },
 
   testShowWhenCollapsed: function(next) {
-    let port = Social.provider.getWorkerPort();
+    let port = SocialSidebar.provider.getWorkerPort();
     port.postMessage({topic: "test-init"});
     get3ChatsForCollapsing("normal", function(first, second, third) {
       let chatbar = window.SocialChatBar.chatbar;
       chatbar.showChat(first);
       ok(!first.collapsed, "first should no longer be collapsed");
       ok(second.collapsed ||  third.collapsed, false, "one of the others should be collapsed");
       closeAllChats();
       port.close();
       next();
     });
   },
 
   testActivity: function(next) {
-    let port = Social.provider.getWorkerPort();
+    let port = SocialSidebar.provider.getWorkerPort();
     port.postMessage({topic: "test-init"});
     get3ChatsForCollapsing("normal", function(first, second, third) {
       let chatbar = window.SocialChatBar.chatbar;
       is(chatbar.selectedChat, third, "third chat should be selected");
       ok(!chatbar.selectedChat.hasAttribute("activity"), "third chat should have no activity");
       // send an activity message to the second.
       ok(!second.hasAttribute("activity"), "second chat should have no activity");
       let chat2 = second.content;
@@ -369,17 +363,17 @@ var tests = {
       ok(!first.hasAttribute("activity"), "first chat should have no activity");
       let chat1 = first.content;
       let evt = chat1.contentDocument.createEvent("CustomEvent");
       evt.initCustomEvent("socialChatActivity", true, true, {});
       chat1.contentDocument.documentElement.dispatchEvent(evt);
       ok(first.hasAttribute("activity"), "first chat should now have activity");
       ok(chatbar.nub.hasAttribute("activity"), "nub should also have activity");
       // first is collapsed, so use openChat to get it.
-      chatbar.openChat(Social.provider, first.getAttribute("src"));
+      chatbar.openChat(SocialSidebar.provider, first.getAttribute("src"));
       ok(!first.hasAttribute("activity"), "first chat should no longer have activity");
       // The nub should lose the activity flag here too
       todo(!chatbar.nub.hasAttribute("activity"), "Bug 806266 - nub should no longer have activity");
       // TODO: tests for bug 806266 should arrange to have 2 chats collapsed
       // then open them checking the nub is updated correctly.
       // Now we will go and change the embedded browser in the second chat and
       // ensure the activity magic still works (ie, check that the unload for
       // the browser didn't cause our event handlers to be removed.)
@@ -399,17 +393,17 @@ var tests = {
         })
       })
       subiframe.setAttribute("src", "data:text/plain:new location for iframe");
     });
   },
 
   testOnlyOneCallback: function(next) {
     let chats = document.getElementById("pinnedchats");
-    let port = Social.provider.getWorkerPort();
+    let port = SocialSidebar.provider.getWorkerPort();
     let numOpened = 0;
     port.onmessage = function (e) {
       let topic = e.data.topic;
       switch (topic) {
         case "test-init-done":
           port.postMessage({topic: "test-chatbox-open"});
           break;
         case "chatbox-opened":
@@ -425,18 +419,18 @@ var tests = {
           });
       }
     }
     port.postMessage({topic: "test-init", data: { id: 1 }});
   },
 
   testSecondTopLevelWindow: function(next) {
     // Bug 817782 - check chats work in new top-level windows.
-    const chatUrl = Social.provider.origin + "/browser/browser/base/content/test/social/social_chat.html";
-    let port = Social.provider.getWorkerPort();
+    const chatUrl = SocialSidebar.provider.origin + "/browser/browser/base/content/test/social/social_chat.html";
+    let port = SocialSidebar.provider.getWorkerPort();
     let secondWindow;
     port.onmessage = function(e) {
       if (e.data.topic == "test-init-done") {
         secondWindow = OpenBrowserWindow();
         secondWindow.addEventListener("load", function loadListener() {
           secondWindow.removeEventListener("load", loadListener);
           port.postMessage({topic: "test-worker-chat", data: chatUrl});
         });
@@ -450,50 +444,50 @@ var tests = {
     port.postMessage({topic: "test-init"});
   },
 
   testChatWindowChooser: function(next) {
     // Tests that when a worker creates a chat, it is opened in the correct
     // window.
     // open a chat (it will open in the main window)
     ok(!window.SocialChatBar.hasChats, "first window should start with no chats");
-    openChat(Social.provider, function() {
+    openChat(SocialSidebar.provider, function() {
       ok(window.SocialChatBar.hasChats, "first window has the chat");
       // create a second window - this will be the "most recent" and will
       // therefore be the window that hosts the new chat (see bug 835111)
       let secondWindow = OpenBrowserWindow();
       secondWindow.addEventListener("load", function loadListener() {
         secondWindow.removeEventListener("load", loadListener);
         ok(!secondWindow.SocialChatBar.hasChats, "second window has no chats");
-        openChat(Social.provider, function() {
+        openChat(SocialSidebar.provider, function() {
           ok(secondWindow.SocialChatBar.hasChats, "second window now has chats");
           is(window.SocialChatBar.chatbar.childElementCount, 1, "first window still has 1 chat");
           window.SocialChatBar.chatbar.removeAll();
           // now open another chat - it should still open in the second.
-          openChat(Social.provider, function() {
+          openChat(SocialSidebar.provider, function() {
             ok(!window.SocialChatBar.hasChats, "first window has no chats");
             ok(secondWindow.SocialChatBar.hasChats, "second window has a chat");
 
             // focus the first window, and open yet another chat - it
             // should open in the first window.
             waitForFocus(function() {
-              openChat(Social.provider, function() {
+              openChat(SocialSidebar.provider, function() {
                 ok(window.SocialChatBar.hasChats, "first window has chats");
                 window.SocialChatBar.chatbar.removeAll();
                 ok(!window.SocialChatBar.hasChats, "first window has no chats");
 
                 let privateWindow = OpenBrowserWindow({private: true});
                 privateWindow.addEventListener("load", function loadListener() {
                   privateWindow.removeEventListener("load", loadListener);
 
                   // open a last chat - the focused window can't accept
                   // chats (it's a private window), so the chat should open
                   // in the window that was selected before. This is known
                   // to be broken on Linux.
-                  openChat(Social.provider, function() {
+                  openChat(SocialSidebar.provider, function() {
                     let os = Services.appinfo.OS;
                     const BROKEN_WM_Z_ORDER = os != "WINNT" && os != "Darwin";
                     let fn = BROKEN_WM_Z_ORDER ? todo : ok;
                     fn(window.SocialChatBar.hasChats, "first window has a chat");
                     window.SocialChatBar.chatbar.removeAll();
 
                     privateWindow.close();
                     secondWindow.close();
@@ -537,18 +531,18 @@ var tests = {
         });
       });
     });
   },
 
   // XXX - note this must be the last test until we restore the login state
   // between tests...
   testCloseOnLogout: function(next) {
-    const chatUrl = Social.provider.origin + "/browser/browser/base/content/test/social/social_chat.html";
-    let port = Social.provider.getWorkerPort();
+    const chatUrl = SocialSidebar.provider.origin + "/browser/browser/base/content/test/social/social_chat.html";
+    let port = SocialSidebar.provider.getWorkerPort();
     ok(port, "provider has a port");
     let opened = false;
     port.onmessage = function (e) {
       let topic = e.data.topic;
       switch (topic) {
         case "test-init-done":
           info("open first chat window");
           port.postMessage({topic: "test-worker-chat", data: chatUrl});
--- a/browser/base/content/test/social/browser_social_chatwindow_resize.js
+++ b/browser/base/content/test/social/browser_social_chatwindow_resize.js
@@ -20,25 +20,26 @@ function test() {
   window.moveTo(0, window.screenY)
   let postSubTest = function(cb) {
     let chats = document.getElementById("pinnedchats");
     ok(chats.children.length == 0, "no chatty children left behind");
     cb();
   };
 
   runSocialTestWithProvider(manifest, function (finishcb) {
-    let port = Social.provider.getWorkerPort();
+    SocialSidebar.show();
+    let port = SocialSidebar.provider.getWorkerPort();
     ok(port, "provider has a port");
     port.postMessage({topic: "test-init"});
     // we require a logged in user for chats, wait for that
     waitForCondition(function() {
       let sbrowser = document.getElementById("social-sidebar-browser");
-      return Social.provider &&
-             Social.provider.profile &&
-             Social.provider.profile.displayName &&
+      return SocialSidebar.provider &&
+             SocialSidebar.provider.profile &&
+             SocialSidebar.provider.profile.displayName &&
              sbrowser.docShellIsActive;
     }, function() {
       // executeSoon to let the browser UI observers run first
       runSocialTests(tests, undefined, postSubTest, function() {
         window.moveTo(oldleft, window.screenY)
         window.resizeTo(oldwidth, window.outerHeight);
         port.close();
         finishcb();
--- a/browser/base/content/test/social/browser_social_chatwindowfocus.js
+++ b/browser/base/content/test/social/browser_social_chatwindowfocus.js
@@ -35,32 +35,32 @@ function openChatViaWorkerMessage(port, 
   let chatbar = SocialChatBar.chatbar;
   let numExpected = chatbar.childElementCount + 1;
   port.postMessage({topic: "test-worker-chat", data: data});
   waitForCondition(function() chatbar.childElementCount == numExpected,
                    function() {
                       // so the child has been added, but we don't know if it
                       // has been intialized - re-request it and the callback
                       // means it's done.  Minimized, same as the worker.
-                      SocialChatBar.openChat(Social.provider,
+                      SocialChatBar.openChat(SocialSidebar.provider,
                                              data,
                                              function() {
                                                 callback();
                                              },
                                              "minimized");
                    },
                    "No new chat appeared");
 }
 
 
 let isSidebarLoaded = false;
 
 function startTestAndWaitForSidebar(callback) {
   let doneCallback;
-  let port = Social.provider.getWorkerPort();
+  let port = SocialSidebar.provider.getWorkerPort();
   function maybeCallback() {
     if (!doneCallback)
       callback(port);
     doneCallback = true;
   }
   port.onmessage = function(e) {
     let topic = e.data.topic;
     switch (topic) {
@@ -109,16 +109,17 @@ function test() {
       waitForCondition(function() isTabFocused(), cb, "tab should have focus");
     }
     let postSubTest = function(cb) {
       window.SocialChatBar.chatbar.removeAll();
       cb();
     }
     // and run the tests.
     runSocialTestWithProvider(manifest, function (finishcb) {
+      SocialSidebar.show();
       runSocialTests(tests, preSubTest, postSubTest, function () {
         finishcb();
       });
     });
   }, true);
   registerCleanupFunction(function() {
     gBrowser.removeTab(tab);
   });
--- a/browser/base/content/test/social/browser_social_errorPage.js
+++ b/browser/base/content/test/social/browser_social_errorPage.js
@@ -37,17 +37,17 @@ function openPanel(url, panelCallback, l
   SocialFlyout.panel.firstChild.addEventListener("load", function panelLoad() {
     SocialFlyout.panel.firstChild.removeEventListener("load", panelLoad, true);
     loadCallback();
   }, true);
 }
 
 function openChat(url, panelCallback, loadCallback) {
   // open a chat window
-  SocialChatBar.openChat(Social.provider, url, panelCallback);
+  SocialChatBar.openChat(SocialSidebar.provider, url, panelCallback);
   SocialChatBar.chatbar.firstChild.addEventListener("DOMContentLoaded", function panelLoad() {
     SocialChatBar.chatbar.firstChild.removeEventListener("DOMContentLoaded", panelLoad, true);
     loadCallback();
   }, true);
 }
 
 function onSidebarLoad(callback) {
   let sbrowser = document.getElementById("social-sidebar-browser");
@@ -74,21 +74,16 @@ let manifest = { // normal provider
   origin: "https://example.com",
   sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
   workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
   iconURL: "https://example.com/browser/browser/base/content/test/general/moz.png"
 };
 
 function test() {
   waitForExplicitFinish();
-  // we don't want the sidebar to auto-load in these tests..
-  Services.prefs.setBoolPref("social.sidebar.open", false);
-  registerCleanupFunction(function() {
-    Services.prefs.clearUserPref("social.sidebar.open");
-  });
 
   runSocialTestWithProvider(manifest, function (finishcb) {
     runSocialTests(tests, undefined, goOnline, finishcb);
   });
 }
 
 var tests = {
   testSidebar: function(next) {
@@ -108,20 +103,20 @@ var tests = {
           next();
         });
         sbrowser.contentDocument.getElementById("btnTryAgain").click();
       });
       sbrowser.contentDocument.getElementById("btnTryAgain").click();
     });
     // we want the worker to be fully loaded before going offline, otherwise
     // it might fail due to going offline.
-    ensureWorkerLoaded(Social.provider, function() {
+    ensureWorkerLoaded(SocialSidebar.provider, function() {
       // go offline then attempt to load the sidebar - it should fail.
       goOffline();
-      Services.prefs.setBoolPref("social.sidebar.open", true);
+      SocialSidebar.show();
   });
   },
 
   testFlyout: function(next) {
     let panelCallbackCount = 0;
     let panel = document.getElementById("social-flyout-panel");
     // go offline and open a flyout.
     goOffline();
--- a/browser/base/content/test/social/browser_social_flyout.js
+++ b/browser/base/content/test/social/browser_social_flyout.js
@@ -8,28 +8,29 @@ function test() {
   let manifest = { // normal provider
     name: "provider 1",
     origin: "https://example.com",
     sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
     workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
     iconURL: "https://example.com/browser/browser/base/content/test/general/moz.png"
   };
   runSocialTestWithProvider(manifest, function (finishcb) {
+    SocialSidebar.show();
     runSocialTests(tests, undefined, undefined, finishcb);
   });
 }
 
 var tests = {
   testOpenCloseFlyout: function(next) {
     let panel = document.getElementById("social-flyout-panel");
     panel.addEventListener("popupshowing", function onShowing() {
       panel.removeEventListener("popupshowing", onShowing);
       is(panel.firstChild.contentDocument.readyState, "complete", "panel is loaded prior to showing");
     });
-    let port = Social.provider.getWorkerPort();
+    let port = SocialSidebar.provider.getWorkerPort();
     ok(port, "provider has a port");
     port.onmessage = function (e) {
       let topic = e.data.topic;
       switch (topic) {
         case "got-sidebar-message":
           port.postMessage({topic: "test-flyout-open"});
           break;
         case "got-flyout-visibility":
@@ -48,17 +49,17 @@ var tests = {
           break;
       }
     }
     port.postMessage({topic: "test-init"});
   },
 
   testResizeFlyout: function(next) {
     let panel = document.getElementById("social-flyout-panel");
-    let port = Social.provider.getWorkerPort();
+    let port = SocialSidebar.provider.getWorkerPort();
     ok(port, "provider has a port");
     port.onmessage = function (e) {
       let topic = e.data.topic;
       switch (topic) {
         case "test-init-done":
           port.postMessage({topic: "test-flyout-open"});
           break;
         case "got-flyout-visibility":
@@ -96,20 +97,20 @@ var tests = {
   testCloseSelf: function(next) {
     // window.close is affected by the pref dom.allow_scripts_to_close_windows,
     // which defaults to false, but is set to true by the test harness.
     // so temporarily set it back.
     const ALLOW_SCRIPTS_TO_CLOSE_PREF = "dom.allow_scripts_to_close_windows";
     // note clearUserPref doesn't do what we expect, as the test harness itself
     // changes the pref value - so clearUserPref resets it to false rather than
     // the true setup by the test harness.
-    let oldAllowScriptsToClose = Services.prefs.getBoolPref(ALLOW_SCRIPTS_TO_CLOSE_PREF);    
+    let oldAllowScriptsToClose = Services.prefs.getBoolPref(ALLOW_SCRIPTS_TO_CLOSE_PREF);
     Services.prefs.setBoolPref(ALLOW_SCRIPTS_TO_CLOSE_PREF, false);
     let panel = document.getElementById("social-flyout-panel");
-    let port = Social.provider.getWorkerPort();
+    let port = SocialSidebar.provider.getWorkerPort();
     ok(port, "provider has a port");
     port.onmessage = function (e) {
       let topic = e.data.topic;
       switch (topic) {
         case "test-init-done":
           port.postMessage({topic: "test-flyout-open"});
           break;
         case "got-flyout-visibility":
@@ -137,17 +138,17 @@ var tests = {
       gBrowser.tabContainer.removeEventListener("TabOpen", onTabOpen, true);
       waitForCondition(function() { return panel.state == "closed" }, function() {
         gBrowser.removeTab(event.target);
         next();
       }, "panel should close after tab open");
     }
 
     let panel = document.getElementById("social-flyout-panel");
-    let port = Social.provider.getWorkerPort();
+    let port = SocialSidebar.provider.getWorkerPort();
     ok(port, "provider has a port");
     port.onmessage = function (e) {
       let topic = e.data.topic;
       switch (topic) {
         case "test-init-done":
           port.postMessage({topic: "test-flyout-open"});
           break;
         case "got-flyout-visibility":
--- a/browser/base/content/test/social/browser_social_isVisible.js
+++ b/browser/base/content/test/social/browser_social_isVisible.js
@@ -8,60 +8,60 @@ function test() {
   let manifest = { // normal provider
     name: "provider 1",
     origin: "https://example.com",
     sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
     workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
     iconURL: "https://example.com/browser/browser/base/content/test/general/moz.png"
   };
   runSocialTestWithProvider(manifest, function (finishcb) {
+    SocialSidebar.show();
     runSocialTests(tests, undefined, undefined, finishcb);
   });
 }
 
 var tests = {
   testSidebarMessage: function(next) {
-    let port = Social.provider.getWorkerPort();
+    let port = SocialSidebar.provider.getWorkerPort();
     ok(port, "provider has a port");
     port.postMessage({topic: "test-init"});
     port.onmessage = function (e) {
       let topic = e.data.topic;
       switch (topic) {
         case "got-sidebar-message":
           // The sidebar message will always come first, since it loads by default
           ok(true, "got sidebar message");
           port.close();
           next();
           break;
       }
     };
   },
   testIsVisible: function(next) {
-    let port = Social.provider.getWorkerPort();
+    let port = SocialSidebar.provider.getWorkerPort();
     port.postMessage({topic: "test-init"});
     port.onmessage = function (e) {
       let topic = e.data.topic;
       switch (topic) {
         case "got-isVisible-response":
           is(e.data.result, true, "Sidebar should be visible by default");
-          Social.toggleSidebar();
+          SocialSidebar.toggleSidebar();
           port.close();
           next();
       }
     };
     port.postMessage({topic: "test-isVisible"});
   },
   testIsNotVisible: function(next) {
-    let port = Social.provider.getWorkerPort();
+    let port = SocialSidebar.provider.getWorkerPort();
     port.postMessage({topic: "test-init"});
     port.onmessage = function (e) {
       let topic = e.data.topic;
       switch (topic) {
         case "got-isVisible-response":
           is(e.data.result, false, "Sidebar should be hidden");
-          Services.prefs.clearUserPref("social.sidebar.open");
           port.close();
           next();
       }
     };
     port.postMessage({topic: "test-isVisible"});
   }
 }
--- a/browser/base/content/test/social/browser_social_marks.js
+++ b/browser/base/content/test/social/browser_social_marks.js
@@ -52,24 +52,24 @@ function openWindowAndWaitForInit(callba
   }, topic, false);
 }
 
 function test() {
   waitForExplicitFinish();
 
   let toolbar = document.getElementById("nav-bar");
   let currentsetAtStart = toolbar.currentSet;
-  runSocialTestWithProvider(manifest, function () {
+  runSocialTestWithProvider(manifest, function (finishcb) {
     runSocialTests(tests, undefined, undefined, function () {
       Services.prefs.clearUserPref("social.remote-install.enabled");
       // just in case the tests failed, clear these here as well
       Services.prefs.clearUserPref("social.whitelist");
       ok(CustomizableUI.inDefaultState, "Should be in the default state when we finish");
       CustomizableUI.reset();
-      finish();
+      finishcb();
     });
   });
 }
 
 var tests = {
   testNoButtonOnEnable: function(next) {
     // we expect the addon install dialog to appear, we need to accept the
     // install from the dialog.
--- a/browser/base/content/test/social/browser_social_multiprovider.js
+++ b/browser/base/content/test/social/browser_social_multiprovider.js
@@ -1,96 +1,93 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 function test() {
   waitForExplicitFinish();
-
   runSocialTestWithProvider(gProviders, function (finishcb) {
+    SocialSidebar.provider = Social.providers[0];
+    SocialSidebar.show();
+    is(Social.providers[0].origin, SocialSidebar.provider.origin, "selected provider in sidebar");
     runSocialTests(tests, undefined, undefined, finishcb);
   });
 }
 
 let gProviders = [
   {
     name: "provider 1",
-    origin: "https://example.com",
-    sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html?provider1",
-    workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
+    origin: "https://test1.example.com",
+    sidebarURL: "https://test1.example.com/browser/browser/base/content/test/social/social_sidebar.html?provider1",
+    workerURL: "https://test1.example.com/browser/browser/base/content/test/social/social_worker.js",
     iconURL: "chrome://branding/content/icon48.png"
   },
   {
     name: "provider 2",
-    origin: "https://test1.example.com",
-    sidebarURL: "https://test1.example.com/browser/browser/base/content/test/social/social_sidebar.html?provider2",
-    workerURL: "https://test1.example.com/browser/browser/base/content/test/social/social_worker.js",
+    origin: "https://test2.example.com",
+    sidebarURL: "https://test2.example.com/browser/browser/base/content/test/social/social_sidebar.html?provider2",
+    workerURL: "https://test2.example.com/browser/browser/base/content/test/social/social_worker.js",
     iconURL: "chrome://branding/content/icon48.png"
   }
 ];
 
 var tests = {
   testProviderSwitch: function(next) {
     let menu = document.getElementById("social-statusarea-popup");
+    let button = document.getElementById("social-sidebar-button");
     function checkProviderMenu(selectedProvider) {
       let menuProviders = menu.querySelectorAll(".social-provider-menuitem");
       is(menuProviders.length, gProviders.length, "correct number of providers listed in the menu");
       // Find the selectedProvider's menu item
       let el = menu.getElementsByAttribute("origin", selectedProvider.origin);
       is(el.length, 1, "selected provider menu item exists");
       is(el[0].getAttribute("checked"), "true", "selected provider menu item is checked");
     }
 
     // the menu is not populated until onpopupshowing, so wait for popupshown
     function theTest() {
-      checkProviderMenu(gProviders[0]);
+      menu.removeEventListener("popupshown", theTest, true);
+      menu.hidePopup(); // doesn't need visibility
+      // first provider should already be visible in the sidebar
+      is(Social.providers[0].origin, SocialSidebar.provider.origin, "selected provider in sidebar");
+      checkProviderMenu(Social.providers[0]);
 
-      // Now wait for the initial provider profile to be set
-      waitForProviderLoad(function() {
-        menu.removeEventListener("popupshown", theTest, true);
-        checkUIStateMatchesProvider(gProviders[0]);
+      // Now activate "provider 2"
+      onSidebarLoad(function() {
+        checkUIStateMatchesProvider(Social.providers[1]);
 
-        // Now activate "provider 2"
-        observeProviderSet(function () {
-          waitForProviderLoad(function() {
-            checkUIStateMatchesProvider(gProviders[1]);
-            // disable social, click on the provider menuitem to switch providers
-            Social.enabled = false;
-            let el = menu.getElementsByAttribute("origin", gProviders[0].origin);
-            is(el.length, 1, "selected provider menu item exists");
-            el[0].click();
-            waitForProviderLoad(function() {
-              checkUIStateMatchesProvider(gProviders[0]);
-              next();
-            });
-          });
+        onSidebarLoad(function() {
+          checkUIStateMatchesProvider(Social.providers[0]);
+          next();
         });
-        Social.activateFromOrigin("https://test1.example.com");
+
+        // show the menu again so the menu is updated with the correct commands
+        function doClick() {
+          // click on the provider menuitem to switch providers
+          let el = menu.getElementsByAttribute("origin", Social.providers[0].origin);
+          is(el.length, 1, "selected provider menu item exists");
+          EventUtils.synthesizeMouseAtCenter(el[0], {});
+        }
+        menu.addEventListener("popupshown", doClick, true);
+        EventUtils.synthesizeMouseAtCenter(button, {});
+
       });
+      SocialSidebar.provider = Social.providers[1];
     };
     menu.addEventListener("popupshown", theTest, true);
-    let button = document.getElementById("social-sidebar-button");
     EventUtils.synthesizeMouseAtCenter(button, {});
   }
 }
 
 function checkUIStateMatchesProvider(provider) {
   // Sidebar
   is(document.getElementById("social-sidebar-browser").getAttribute("src"), provider.sidebarURL, "side bar URL is set");
 }
 
-function observeProviderSet(cb) {
-  Services.obs.addObserver(function providerSet(subject, topic, data) {
-    Services.obs.removeObserver(providerSet, "social:provider-set");
-    info("social:provider-set observer was notified");
-    // executeSoon to let the browser UI observers run first
-    executeSoon(cb);
-  }, "social:provider-set", false);
+function onSidebarLoad(callback) {
+  let sbrowser = document.getElementById("social-sidebar-browser");
+  sbrowser.addEventListener("load", function load() {
+    sbrowser.removeEventListener("load", load, true);
+    // give the load a chance to finish before pulling the rug (ie. calling
+    // next)
+    executeSoon(callback);
+  }, true);
 }
-
-function waitForProviderLoad(cb) {
-  waitForCondition(function() {
-    let sbrowser = document.getElementById("social-sidebar-browser");
-    return Social.provider.profile &&
-           Social.provider.profile.displayName &&
-           sbrowser.docShellIsActive;
-  }, cb, "waitForProviderLoad: provider profile was not set");
-}
--- a/browser/base/content/test/social/browser_social_multiworker.js
+++ b/browser/base/content/test/social/browser_social_multiworker.js
@@ -1,17 +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/. */
 
 function test() {
   waitForExplicitFinish();
 
   runSocialTestWithProvider(gProviders, function (finishcb) {
-    Social.enabled = true;
     runSocialTests(tests, undefined, undefined, function() {
       finishcb();
     });
   });
 }
 
 let gProviders = [
   {
@@ -52,18 +51,19 @@ var tests = {
     for (let p of Social.providers) {
       oneWorkerTest(p);
     }
 
     waitForCondition(function() messageReceived == Social.providers.length,
                      next, "received messages from all workers");
   },
 
-  testWorkerDisabling: function(next) {
-    Social.enabled = false;
-    is(Social.providers.length, gProviders.length, "providers still available");
-    for (let p of Social.providers) {
-      ok(!p.enabled, "provider disabled");
-      ok(!p.getWorkerPort(), "worker disabled");
-    }
-    next();
-  }
+   testMultipleWorkerEnabling: function(next) {
+     // test that all workers are enabled when we allow multiple workers
+     for (let p of Social.providers) {
+       ok(p.enabled, "provider enabled");
+       let port = p.getWorkerPort();
+       ok(port, "worker enabled");
+       port.close();
+     }
+     next();
+   }
 }
--- a/browser/base/content/test/social/browser_social_perwindowPB.js
+++ b/browser/base/content/test/social/browser_social_perwindowPB.js
@@ -50,17 +50,17 @@ function test() {
         finishcb();
       });
     });
   });
 }
 
 var tests = {
   testPrivateBrowsing: function(next) {
-    let port = Social.provider.getWorkerPort();
+    let port = SocialSidebar.provider.getWorkerPort();
     ok(port, "provider has a port");
     postAndReceive(port, "test-init", "test-init-done", function() {
       // social features should all be enabled in the existing window.
       info("checking main window ui");
       ok(window.SocialUI.enabled, "social is enabled in normal window");
       checkSocialUI(window);
       // open a new private-window
       openPBWindow(function(pbwin) {
--- a/browser/base/content/test/social/browser_social_sidebar.js
+++ b/browser/base/content/test/social/browser_social_sidebar.js
@@ -1,56 +1,58 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+let SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).SocialService;
+
+let manifest = { // normal provider
+  name: "provider 1",
+  origin: "https://example.com",
+  sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
+  workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
+  iconURL: "https://example.com/browser/browser/base/content/test/general/moz.png"
+};
+
 function test() {
   waitForExplicitFinish();
 
-  let manifest = { // normal provider
-    name: "provider 1",
-    origin: "https://example.com",
-    sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
-    workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
-    iconURL: "https://example.com/browser/browser/base/content/test/general/moz.png"
-  };
-  runSocialTestWithProvider(manifest, doTest);
+  SocialService.addProvider(manifest, function() {
+    // the test will remove the provider
+    doTest();
+  });
 }
 
-function doTest(finishcb) {
+function doTest() {
   ok(SocialSidebar.canShow, "social sidebar should be able to be shown");
-  ok(SocialSidebar.opened, "social sidebar should be open by default");
+  ok(!SocialSidebar.opened, "social sidebar should not be open by default");
+  SocialSidebar.show();
 
   let command = document.getElementById("Social:ToggleSidebar");
   let sidebar = document.getElementById("social-sidebar-box");
   let browser = sidebar.lastChild;
 
   function checkShown(shouldBeShown) {
     is(command.getAttribute("checked"), shouldBeShown ? "true" : "false",
        "toggle command should be " + (shouldBeShown ? "checked" : "unchecked"));
     is(sidebar.hidden, !shouldBeShown,
        "sidebar should be " + (shouldBeShown ? "visible" : "hidden"));
-    // The sidebar.open pref only reflects the actual state of the sidebar
-    // when social is enabled.
-    if (Social.enabled)
-      is(Services.prefs.getBoolPref("social.sidebar.open"), shouldBeShown,
-         "sidebar open pref should be " + shouldBeShown);
     if (shouldBeShown) {
-      is(browser.getAttribute('src'), Social.provider.sidebarURL, "sidebar url should be set");
+      is(browser.getAttribute('src'), SocialSidebar.provider.sidebarURL, "sidebar url should be set");
       // We don't currently check docShellIsActive as this is only set
       // after load event fires, and the tests below explicitly wait for this
       // anyway.
     }
     else {
       ok(!browser.docShellIsActive, "sidebar should have an inactive docshell");
       // sidebar will only be immediately unloaded (and thus set to
       // about:blank) when canShow is false.
       if (SocialSidebar.canShow) {
         // should not have unloaded so will still be the provider URL.
-        is(browser.getAttribute('src'), Social.provider.sidebarURL, "sidebar url should be set");
+        is(browser.getAttribute('src'), SocialSidebar.provider.sidebarURL, "sidebar url should be set");
       } else {
         // should have been an immediate unload.
         is(browser.getAttribute('src'), "about:blank", "sidebar url should be blank");
       }
     }
   }
 
   // First check the the sidebar is initially visible, and loaded
@@ -62,44 +64,36 @@ function doTest(finishcb) {
 
     checkShown(false);
 
     browser.addEventListener("socialFrameShow", function sidebarshow() {
       browser.removeEventListener("socialFrameShow", sidebarshow);
 
       checkShown(true);
 
-      // Set Social.enabled = false and check everything is as expected.
-      Social.enabled = false;
-      checkShown(false);
-
-      Social.enabled = true;
-      checkShown(true);
-
-      // And an edge-case - disable social and reset the provider.
-      Social.provider = null;
-      Social.enabled = false;
-      checkShown(false);
-
-      // Finish the test
-      finishcb();
+      // disable social.
+      SocialService.removeProvider(SocialSidebar.provider.origin, function() {
+        checkShown(false);
+        is(Social.providers.length, 0, "no providers left");
+        defaultFinishChecks();
+        // Finish the test
+        executeSoon(finish);
+      });
     });
 
     // Toggle it back on
     info("Toggling sidebar back on");
-    Social.toggleSidebar();
+    SocialSidebar.toggleSidebar();
   });
 
   // use port messaging to ensure that the sidebar is both loaded and
   // ready before we run other tests
-  let port = Social.provider.getWorkerPort();
+  let port = SocialSidebar.provider.getWorkerPort();
   port.postMessage({topic: "test-init"});
   port.onmessage = function (e) {
     let topic = e.data.topic;
     switch (topic) {
       case "got-sidebar-message":
         ok(true, "sidebar is loaded and ready");
-        Social.toggleSidebar();
+        SocialSidebar.toggleSidebar();
     }
   };
 }
-
-// XXX test sidebar in popup
--- a/browser/base/content/test/social/browser_social_window.js
+++ b/browser/base/content/test/social/browser_social_window.js
@@ -6,156 +6,200 @@
 
 let SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).SocialService;
 
 // This function should "reset" Social such that the next time Social.init()
 // is called (eg, when a new window is opened), it re-performs all
 // initialization.
 function resetSocial() {
   Social.initialized = false;
-  Social._provider = null;
   Social.providers = [];
   // *sob* - listeners keep getting added...
   SocialService._providerListeners.clear();
 }
 
 let createdWindows = [];
 
-function openWindowAndWaitForInit(callback) {
+function openWindowAndWaitForInit(parentWin, callback) {
   // this notification tells us SocialUI.init() has been run...
   let topic = "browser-delayed-startup-finished";
-  let w = OpenBrowserWindow();
+  let w = parentWin.OpenBrowserWindow();
   createdWindows.push(w);
   Services.obs.addObserver(function providerSet(subject, topic, data) {
     Services.obs.removeObserver(providerSet, topic);
     info(topic + " observer was notified - continuing test");
     executeSoon(() => callback(w));
   }, topic, false);
 }
 
 function closeOneWindow(cb) {
   let w = createdWindows.pop();
   if (!w) {
     cb();
     return;
   }
   waitForCondition(function() w.closed,
                    function() {
+                    info("window closed, " + createdWindows.length + " windows left");
                     closeOneWindow(cb);
                     }, "window did not close");
   w.close();
 }
 
 function postTestCleanup(cb) {
-  closeOneWindow(function() {
-    Services.prefs.clearUserPref("social.enabled");
-    cb();
-  });
+  closeOneWindow(cb);
 }
 
 let manifest = { // normal provider
   name: "provider 1",
   origin: "https://example.com",
   sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
   workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
   iconURL: "https://example.com/browser/browser/base/content/test/general/moz.png"
 };
+let manifest2 = { // used for testing install
+  name: "provider test1",
+  origin: "https://test1.example.com",
+  workerURL: "https://test1.example.com/browser/browser/base/content/test/social/social_worker.js",
+  sidebarURL: "https://test1.example.com/browser/browser/base/content/test/social/social_sidebar.html",
+  iconURL: "https://test1.example.com/browser/browser/base/content/test/general/moz.png",
+};
 
 function test() {
   waitForExplicitFinish();
   runSocialTests(tests, undefined, postTestCleanup);
 }
 
 let tests = {
   // check when social is totally disabled at startup (ie, no providers enabled)
   testInactiveStartup: function(cbnext) {
     is(Social.providers.length, 0, "needs zero providers to start this test.");
     ok(!SocialService.hasEnabledProviders, "no providers are enabled");
     resetSocial();
-    openWindowAndWaitForInit(function(w1) {
+    openWindowAndWaitForInit(window, function(w1) {
       checkSocialUI(w1);
       // Now social is (re-)initialized, open a secondary window and check that.
-      openWindowAndWaitForInit(function(w2) {
+      openWindowAndWaitForInit(window, function(w2) {
         checkSocialUI(w2);
         checkSocialUI(w1);
         cbnext();
       });
     });
   },
 
   // Check when providers are enabled and social is turned on at startup.
   testEnabledStartup: function(cbnext) {
-    runSocialTestWithProvider(manifest, function (finishcb) {
-      resetSocial();
-      openWindowAndWaitForInit(function(w1) {
-        ok(Social.enabled, "social is enabled");
-        ok(SocialService.hasEnabledProviders, "providers are enabled");
-        checkSocialUI(w1);
-        // now init is complete, open a second window
-        openWindowAndWaitForInit(function(w2) {
-          checkSocialUI(w2);
-          checkSocialUI(w1);
-          // disable social and re-check
-          Services.prefs.setBoolPref("social.enabled", false);
-          executeSoon(function() { // let all the UI observers run...
-            ok(!Social.enabled, "social is disabled");
-            checkSocialUI(w2);
+    setManifestPref("social.manifest.test", manifest);
+    ok(!SocialSidebar.opened, "sidebar is closed initially");
+    SocialService.addProvider(manifest, function() {
+      SocialService.addProvider(manifest2, function (provider) {
+        SocialSidebar.show();
+        waitForCondition(function() SocialSidebar.opened,
+                     function() {
+          ok(SocialSidebar.opened, "first window sidebar is open");
+          openWindowAndWaitForInit(window, function(w1) {
+            ok(w1.SocialSidebar.opened, "new window sidebar is open");
+            ok(SocialService.hasEnabledProviders, "providers are enabled");
             checkSocialUI(w1);
-            finishcb();
+            // now init is complete, open a second window
+            openWindowAndWaitForInit(window, function(w2) {
+              ok(w1.SocialSidebar.opened, "w1 sidebar is open");
+              ok(w2.SocialSidebar.opened, "w2 sidebar is open");
+              checkSocialUI(w2);
+              checkSocialUI(w1);
+
+              // disable social and re-check
+              SocialService.removeProvider(manifest.origin, function() {
+                SocialService.removeProvider(manifest2.origin, function() {
+                  ok(!Social.enabled, "social is disabled");
+                  is(Social.providers.length, 0, "no providers");
+                  ok(!w1.SocialSidebar.opened, "w1 sidebar is closed");
+                  ok(!w2.SocialSidebar.opened, "w2 sidebar is closed");
+                  checkSocialUI(w2);
+                  checkSocialUI(w1);
+                  Services.prefs.clearUserPref("social.manifest.test");
+                  cbnext();
+                });
+              });
+            });
           });
-        });
-      });
+        }, "sidebar did not open");
+      }, cbnext);
     }, cbnext);
   },
 
-  // Check when providers are enabled but social is turned off at startup.
-  testDisabledStartup: function(cbnext) {
-    setManifestPref("social.manifest.test", manifest);
-    SocialService.addProvider(manifest, function (provider) {
-      Services.prefs.setBoolPref("social.enabled", false);
-      resetSocial();
-      ok(SocialService.hasEnabledProviders, "providers are enabled");
-      openWindowAndWaitForInit(function(w1) {
-        ok(!Social.enabled, "social is disabled");
-        checkSocialUI(w1);
-        // now init is complete, open a second window
-        openWindowAndWaitForInit(function(w2) {
-          checkSocialUI(w2);
-          checkSocialUI(w1);
-          // enable social and re-check
-          Services.prefs.setBoolPref("social.enabled", true);
-          executeSoon(function() { // let all the UI observers run...
-            ok(Social.enabled, "social is enabled");
-            checkSocialUI(w2);
-            checkSocialUI(w1);
-            SocialService.removeProvider(manifest.origin, function() {
-              Services.prefs.clearUserPref("social.manifest.test");
-              cbnext();
-            });
-          });
+  // Check per window sidebar functionality, including migration from using
+  // prefs to using session state, and state inheritance of windows (new windows
+  // inherit state from the opener).
+  testPerWindowSidebar: function(cbnext) {
+    function finishCheck() {
+      // disable social and re-check
+      SocialService.removeProvider(manifest.origin, function() {
+        SocialService.removeProvider(manifest2.origin, function() {
+          ok(!Social.enabled, "social is disabled");
+          is(Social.providers.length, 0, "no providers");
+          Services.prefs.clearUserPref("social.manifest.test");
+          cbnext();
         });
       });
-    }, cbnext);
-  },
+    }
 
-  // Check when the last provider is disabled.
-  testRemoveProvider: function(cbnext) {
     setManifestPref("social.manifest.test", manifest);
-    SocialService.addProvider(manifest, function (provider) {
-      openWindowAndWaitForInit(function(w1) {
-        checkSocialUI(w1);
-        // now init is complete, open a second window
-        openWindowAndWaitForInit(function(w2) {
-          checkSocialUI(w2);
-          // disable the current provider.
-          SocialService.removeProvider(manifest.origin, function() {
-            ok(!Social.enabled, "social is disabled");
-            is(Social.providers.length, 0, "no providers");
+    ok(!SocialSidebar.opened, "sidebar is closed initially");
+    SocialService.addProvider(manifest, function() {
+      SocialService.addProvider(manifest2, function (provider) {
+        // test the migration of the social.sidebar.open pref. We'll set a user
+        // level pref to indicate it was open (along with the old
+        // social.provider.current pref), then we'll open a window. During the
+        // restoreState of the window, those prefs should be migrated, and the
+        // sidebar should be opened.  Both prefs are then removed.
+        Services.prefs.setCharPref("social.provider.current", "https://example.com");
+        Services.prefs.setBoolPref("social.sidebar.open", true);
+
+        openWindowAndWaitForInit(window, function(w1) {
+          ok(w1.SocialSidebar.opened, "new window sidebar is open");
+          ok(SocialService.hasEnabledProviders, "providers are enabled");
+          ok(!Services.prefs.prefHasUserValue("social.provider.current"), "social.provider.current pref removed");
+          ok(!Services.prefs.prefHasUserValue("social.sidebar.open"), "social.sidebar.open pref removed");
+          checkSocialUI(w1);
+          // now init is complete, open a second window, it's state should be the same as the opener
+          openWindowAndWaitForInit(w1, function(w2) {
+            ok(w1.SocialSidebar.opened, "w1 sidebar is open");
+            ok(w2.SocialSidebar.opened, "w2 sidebar is open");
             checkSocialUI(w2);
             checkSocialUI(w1);
-            Services.prefs.clearUserPref("social.manifest.test");
-            cbnext();
+
+            // change the sidebar in w2
+            w2.SocialSidebar.show(manifest2.origin);
+            let sbrowser1 = w1.document.getElementById("social-sidebar-browser");
+            is(manifest.origin, sbrowser1.getAttribute("origin"), "w1 sidebar origin matches");
+            let sbrowser2 = w2.document.getElementById("social-sidebar-browser");
+            is(manifest2.origin, sbrowser2.getAttribute("origin"), "w2 sidebar origin matches");
+
+            // hide sidebar, w1 sidebar should still be open
+            w2.SocialSidebar.hide();
+            ok(w1.SocialSidebar.opened, "w1 sidebar is opened");
+            ok(!w2.SocialSidebar.opened, "w2 sidebar is closed");
+            ok(sbrowser2.parentNode.hidden, "w2 sidebar is hidden");
+
+            // open a 3rd window from w2, it should inherit the state of w2
+            openWindowAndWaitForInit(w2, function(w3) {
+              // since the sidebar is not open, we need to ensure the provider
+              // is selected to test we inherited the provider from the opener
+              w3.SocialSidebar.ensureProvider();
+              is(w3.SocialSidebar.provider, w2.SocialSidebar.provider, "w3 has same provider as w2");
+              ok(!w3.SocialSidebar.opened, "w2 sidebar is closed");
+
+              // open a 4th window from w1, it should inherit the state of w1
+              openWindowAndWaitForInit(w1, function(w4) {
+                is(w4.SocialSidebar.provider, w1.SocialSidebar.provider, "w4 has same provider as w1");
+                ok(w4.SocialSidebar.opened, "w4 sidebar is opened");
+
+                finishCheck();
+              });
+            });
+
           });
         });
-      });
+      }, cbnext);
     }, cbnext);
-  },
+  }
 }
--- a/browser/base/content/test/social/browser_social_workercrash.js
+++ b/browser/base/content/test/social/browser_social_workercrash.js
@@ -12,20 +12,18 @@ let {getFrameWorkerHandle} = Cu.import("
 
 function test() {
   waitForExplicitFinish();
 
   // We need to ensure all our workers are in the same content process.
   Services.prefs.setIntPref("dom.ipc.processCount", 1);
 
   runSocialTestWithProvider(gProviders, function (finishcb) {
-    Social.enabled = true;
     runSocialTests(tests, undefined, undefined, function() {
       Services.prefs.clearUserPref("dom.ipc.processCount");
-      Services.prefs.clearUserPref("social.sidebar.open");
       finishcb();
     });
   });
 }
 
 let gProviders = [
   {
     name: "provider 1",
@@ -76,17 +74,17 @@ var tests = {
             sbrowser.contentDocument.getElementById("btnTryAgain").click();
           });
         });
         Services.obs.addObserver(observer, 'ipc:content-shutdown', false);
         // and cause the crash.
         mm.sendAsyncMessage("social-test:crash");
       });
     })
-    Services.prefs.setBoolPref("social.sidebar.open", true);
+    SocialSidebar.show();
   },
 }
 
 function onSidebarLoad(callback) {
   let sbrowser = document.getElementById("social-sidebar-browser");
   sbrowser.addEventListener("load", function load() {
     sbrowser.removeEventListener("load", load, true);
     callback();
--- a/browser/base/content/test/social/head.js
+++ b/browser/base/content/test/social/head.js
@@ -69,16 +69,18 @@ function defaultFinishChecks() {
 
 function runSocialTestWithProvider(manifest, callback, finishcallback) {
   let SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).SocialService;
 
   let manifests = Array.isArray(manifest) ? manifest : [manifest];
 
   // Check that none of the provider's content ends up in history.
   function finishCleanUp() {
+    ok(!SocialSidebar.provider, "no provider in sidebar");
+    SessionStore.setWindowValue(window, "socialSidebar", "");
     for (let i = 0; i < manifests.length; i++) {
       let m = manifests[i];
       for (let what of ['sidebarURL', 'workerURL', 'iconURL']) {
         if (m[what]) {
           yield promiseSocialUrlNotRemembered(m[what]);
         }
       };
     }
@@ -113,19 +115,16 @@ function runSocialTestWithProvider(manif
             info("Failed to clean up provider " + origin + ": " + ex);
           }
         }
       }
       removeProvider(m.origin, callback);
     });
   }
   function finishSocialTest(cleanup) {
-    // disable social before removing the providers to avoid providers
-    // being activated immediately before we get around to removing it.
-    Services.prefs.clearUserPref("social.enabled");
     removeAddedProviders(cleanup);
   }
 
   let providersAdded = 0;
   let firstProvider;
 
   manifests.forEach(function (m) {
     SocialService.addProvider(m, function(provider) {
@@ -136,23 +135,24 @@ function runSocialTestWithProvider(manif
       // we want to set the first specified provider as the UI's provider
       if (provider.origin == manifests[0].origin) {
         firstProvider = provider;
       }
 
       // If we've added all the providers we need, call the callback to start
       // the tests (and give it a callback it can call to finish them)
       if (providersAdded == manifests.length) {
-        // Set the UI's provider (which enables the feature)
-        Social.provider = firstProvider;
-
         registerCleanupFunction(function () {
           finishSocialTest(true);
         });
-        callback(finishSocialTest);
+        waitForCondition(function() provider.enabled,
+                         function() {
+          info("provider has been enabled");
+          callback(finishSocialTest);
+        }, "providers added and enabled");
       }
     });
   });
 }
 
 function runSocialTests(tests, cbPreTest, cbPostTest, cbFinish) {
   let testIter = Iterator(tests);
   let providersAtStart = Social.providers.length;
@@ -199,17 +199,16 @@ function runSocialTests(tests, cbPreTest
 }
 
 // A fairly large hammer which checks all aspects of the SocialUI for
 // internal consistency.
 function checkSocialUI(win) {
   let SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).SocialService;
   win = win || window;
   let doc = win.document;
-  let provider = Social.provider;
   let enabled = win.SocialUI.enabled;
   let active = Social.providers.length > 0 && !win.SocialUI._chromeless &&
                !PrivateBrowsingUtils.isWindowPrivate(win);
 
   // if we have enabled providers, we should also have instances of those
   // providers
   if (SocialService.hasEnabledProviders) {
     ok(Social.providers.length > 0, "providers are enabled");
@@ -232,62 +231,56 @@ function checkSocialUI(win) {
       is(a, b, msg)
     else
       ++numGoodTests;
   }
   function isbool(a, b, msg) {
     _is(!!a, !!b, msg);
   }
   isbool(win.SocialSidebar.canShow, enabled, "social sidebar active?");
-  if (enabled)
-    isbool(win.SocialSidebar.opened, enabled, "social sidebar open?");
   isbool(win.SocialChatBar.isAvailable, enabled, "chatbar available?");
   isbool(!win.SocialChatBar.chatbar.hidden, enabled, "chatbar visible?");
 
-  // the menus should always have the provider name
-  if (provider) {
-    let contextMenus = [
-      {
-        type: "link",
-        id: "context-marklinkMenu",
-        label: "social.marklinkMenu.label"
-      },
-      {
-        type: "page",
-        id: "context-markpageMenu",
-        label: "social.markpageMenu.label"
-      }
-    ];
+  let contextMenus = [
+    {
+      type: "link",
+      id: "context-marklinkMenu",
+      label: "social.marklinkMenu.label"
+    },
+    {
+      type: "page",
+      id: "context-markpageMenu",
+      label: "social.markpageMenu.label"
+    }
+  ];
 
-    for (let c of contextMenus) {
-      let leMenu = document.getElementById(c.id);
-      let parent, menus;
-      let markProviders = SocialMarks.getProviders();
-      if (markProviders.length > SocialMarks.MENU_LIMIT) {
-        // menus should be in a submenu, not in the top level of the context menu
-        parent = leMenu.firstChild;
-        menus = document.getElementsByClassName("context-mark" + c.type);
-        _is(menus.length, 0, "menu's are not in main context menu\n");
-        menus = parent.childNodes;
-        _is(menus.length, markProviders.length, c.id + " menu exists for each mark provider");
-      } else {
-        // menus should be in the top level of the context menu, not in a submenu
-        parent = leMenu.parentNode;
-        menus = document.getElementsByClassName("context-mark" + c.type);
-        _is(menus.length, markProviders.length, c.id + " menu exists for each mark provider");
-        menus = leMenu.firstChild.childNodes;
-        _is(menus.length, 0, "menu's are not in context submenu\n");
-      }
-      for (let m of menus)
-        _is(m.parentNode, parent, "menu has correct parent");
+  for (let c of contextMenus) {
+    let leMenu = document.getElementById(c.id);
+    let parent, menus;
+    let markProviders = SocialMarks.getProviders();
+    if (markProviders.length > SocialMarks.MENU_LIMIT) {
+      // menus should be in a submenu, not in the top level of the context menu
+      parent = leMenu.firstChild;
+      menus = document.getElementsByClassName("context-mark" + c.type);
+      _is(menus.length, 0, "menu's are not in main context menu\n");
+      menus = parent.childNodes;
+      _is(menus.length, markProviders.length, c.id + " menu exists for each mark provider");
+    } else {
+      // menus should be in the top level of the context menu, not in a submenu
+      parent = leMenu.parentNode;
+      menus = document.getElementsByClassName("context-mark" + c.type);
+      _is(menus.length, markProviders.length, c.id + " menu exists for each mark provider");
+      menus = leMenu.firstChild.childNodes;
+      _is(menus.length, 0, "menu's are not in context submenu\n");
     }
+    for (let m of menus)
+      _is(m.parentNode, parent, "menu has correct parent");
   }
 
   // and for good measure, check all the social commands.
-  isbool(!doc.getElementById("Social:Toggle").hidden, active, "Social:Toggle visible?");
   isbool(!doc.getElementById("Social:ToggleSidebar").hidden, enabled, "Social:ToggleSidebar visible?");
   isbool(!doc.getElementById("Social:ToggleNotifications").hidden, enabled, "Social:ToggleNotifications visible?");
   isbool(!doc.getElementById("Social:FocusChat").hidden, enabled, "Social:FocusChat visible?");
   isbool(doc.getElementById("Social:FocusChat").getAttribute("disabled"), enabled ? "false" : "true", "Social:FocusChat disabled?");
 
   // and report on overall success of failure of the various checks here.
   is(numGoodTests, numTests, "The Social UI tests succeeded.")
 }
@@ -446,17 +439,17 @@ function get3ChatsForCollapsing(mode, cb
         }, mode);
       }, mode);
     });
   }, mode);
 }
 
 function makeChat(mode, uniqueid, cb) {
   info("making a chat window '" + uniqueid +"'");
-  let provider = Social.provider;
+  let provider = SocialSidebar.provider;
   const chatUrl = provider.origin + "/browser/browser/base/content/test/social/social_chat.html";
   let isOpened = window.SocialChatBar.openChat(provider, chatUrl + "?id=" + uniqueid, function(chat) {
     info("chat window has opened");
     // we can't callback immediately or we might close the chat during
     // this event which upsets the implementation - it is only 1/2 way through
     // handling the load event.
     chat.document.title = uniqueid;
     executeSoon(cb);
--- a/browser/components/customizableui/src/CustomizableUI.jsm
+++ b/browser/components/customizableui/src/CustomizableUI.jsm
@@ -3444,17 +3444,22 @@ function OverflowableToolbar(aToolbarNod
 
   this._toolbar.setAttribute("overflowable", "true");
   let doc = this._toolbar.ownerDocument;
   this._target = this._toolbar.customizationTarget;
   this._list = doc.getElementById(this._toolbar.getAttribute("overflowtarget"));
   this._list.toolbox = this._toolbar.toolbox;
   this._list.customizationTarget = this._list;
 
-  Services.obs.addObserver(this, "browser-delayed-startup-finished", false);
+  let window = this._toolbar.ownerDocument.defaultView;
+  if (window.gBrowserInit.delayedStartupFinished) {
+    this.init();
+  } else {
+    Services.obs.addObserver(this, "browser-delayed-startup-finished", false);
+  }
 }
 
 OverflowableToolbar.prototype = {
   initialized: false,
   _forceOnOverflow: false,
 
   observe: function(aSubject, aTopic, aData) {
     if (aTopic == "browser-delayed-startup-finished" &&
@@ -3470,17 +3475,18 @@ OverflowableToolbar.prototype = {
     window.addEventListener("resize", this);
     window.gNavToolbox.addEventListener("customizationstarting", this);
     window.gNavToolbox.addEventListener("aftercustomization", this);
 
     let chevronId = this._toolbar.getAttribute("overflowbutton");
     this._chevron = doc.getElementById(chevronId);
     this._chevron.addEventListener("command", this);
 
-    this._panel = doc.getElementById("widget-overflow");
+    let panelId = this._toolbar.getAttribute("overflowpanel");
+    this._panel = doc.getElementById(panelId);
     this._panel.addEventListener("popuphiding", this);
     CustomizableUIInternal.addPanelCloseListeners(this._panel);
 
     CustomizableUI.addListener(this);
 
     // The 'overflow' event may have been fired before init was called.
     if (this._toolbar.overflowedDuringConstruction) {
       this.onOverflow(this._toolbar.overflowedDuringConstruction);
--- a/browser/components/customizableui/src/CustomizeMode.jsm
+++ b/browser/components/customizableui/src/CustomizeMode.jsm
@@ -332,16 +332,17 @@ CustomizeMode.prototype = {
     }
 
     this._removeExtraToolbarsIfEmpty();
 
     CustomizableUI.removeListener(this);
 
     this.document.removeEventListener("keypress", this);
     this.window.PanelUI.menuButton.removeEventListener("mousedown", this);
+    this.window.PanelUI.menuButton.open = false;
 
     this.window.PanelUI.beginBatchUpdate();
 
     this._removePanelCustomizationPlaceholders();
 
     let window = this.window;
     let document = this.document;
     let documentElement = document.documentElement;
@@ -420,17 +421,16 @@ CustomizeMode.prototype = {
       // And drop all area references.
       this.areas = [];
 
       // Let everybody in this window know that we're starting to
       // exit customization mode.
       CustomizableUI.dispatchToolboxEvent("customizationending", {}, window);
 
       window.PanelUI.setMainView(window.PanelUI.mainView);
-      window.PanelUI.menuButton.open = false;
       window.PanelUI.menuButton.disabled = false;
 
       let customizeButton = document.getElementById("PanelUI-customize");
       customizeButton.setAttribute("exitLabel", customizeButton.getAttribute("label"));
       customizeButton.setAttribute("label", customizeButton.getAttribute("enterLabel"));
       customizeButton.setAttribute("exitTooltiptext", customizeButton.getAttribute("tooltiptext"));
       customizeButton.setAttribute("tooltiptext", customizeButton.getAttribute("enterTooltiptext"));
 
@@ -661,17 +661,17 @@ CustomizeMode.prototype = {
   },
 
   //XXXunf Maybe this should use -moz-element instead of wrapping the node?
   //       Would ensure no weird interactions/event handling from original node,
   //       and makes it possible to put this in a lazy-loaded iframe/real tab
   //       while still getting rid of the need for overlays.
   makePaletteItem: function(aWidget, aPlace) {
     let widgetNode = aWidget.forWindow(this.window).node;
-    let wrapper = this.createWrapper(widgetNode, aPlace);
+    let wrapper = this.createOrUpdateWrapper(widgetNode, aPlace);
     wrapper.appendChild(widgetNode);
     return wrapper;
   },
 
   depopulatePalette: function() {
     return Task.spawn(function() {
       this.visiblePalette.hidden = true;
       let paletteChild = this.visiblePalette.firstChild;
@@ -723,36 +723,43 @@ CustomizeMode.prototype = {
 
     return deferred.promise;
   },
 
   wrapToolbarItem: function(aNode, aPlace) {
     if (!this.isCustomizableItem(aNode)) {
       return aNode;
     }
-    let wrapper = this.createWrapper(aNode, aPlace);
+    let wrapper = this.createOrUpdateWrapper(aNode, aPlace);
+
     // It's possible that this toolbar node is "mid-flight" and doesn't have
     // a parent, in which case we skip replacing it. This can happen if a
     // toolbar item has been dragged into the palette. In that case, we tell
     // CustomizableUI to remove the widget from its area before putting the
     // widget in the palette - so the node will have no parent.
     if (aNode.parentNode) {
       aNode = aNode.parentNode.replaceChild(wrapper, aNode);
     }
     wrapper.appendChild(aNode);
     return wrapper;
   },
 
-  createWrapper: function(aNode, aPlace) {
-    let wrapper = this.document.createElement("toolbarpaletteitem");
+  createOrUpdateWrapper: function(aNode, aPlace, aIsUpdate) {
+    let wrapper;
+    if (aIsUpdate && aNode.parentNode && aNode.parentNode.localName == "toolbarpaletteitem") {
+      wrapper = aNode.parentNode;
+      aPlace = wrapper.getAttribute("place");
+    } else {
+      wrapper = this.document.createElement("toolbarpaletteitem");
+      // "place" is used by toolkit to add the toolbarpaletteitem-palette
+      // binding to a toolbarpaletteitem, which gives it a label node for when
+      // it's sitting in the palette.
+      wrapper.setAttribute("place", aPlace);
+    }
 
-    // "place" is used by toolkit to add the toolbarpaletteitem-palette
-    // binding to a toolbarpaletteitem, which gives it a label node for when
-    // it's sitting in the palette.
-    wrapper.setAttribute("place", aPlace);
 
     // Ensure the wrapped item doesn't look like it's in any special state, and
     // can't be interactved with when in the customization palette.
     if (aNode.hasAttribute("command")) {
       wrapper.setAttribute("itemcommand", aNode.getAttribute("command"));
       aNode.removeAttribute("command");
     }
 
@@ -797,18 +804,21 @@ CustomizeMode.prototype = {
         currentContextMenu != contextMenuForPlace) {
       aNode.setAttribute("wrapped-context", currentContextMenu);
       aNode.setAttribute("wrapped-contextAttrName", contextMenuAttrName)
       aNode.removeAttribute(contextMenuAttrName);
     } else if (currentContextMenu == contextMenuForPlace) {
       aNode.removeAttribute(contextMenuAttrName);
     }
 
-    wrapper.addEventListener("mousedown", this);
-    wrapper.addEventListener("mouseup", this);
+    // Only add listeners for newly created wrappers:
+    if (!aIsUpdate) {
+      wrapper.addEventListener("mousedown", this);
+      wrapper.addEventListener("mouseup", this);
+    }
 
     return wrapper;
   },
 
   deferredUnwrapToolbarItem: function(aWrapper) {
     let deferred = Promise.defer();
     dispatchFunction(function() {
       let item = null;
@@ -1339,16 +1349,18 @@ CustomizeMode.prototype = {
 
     if (this._dragOverItem && dragOverItem != this._dragOverItem) {
       this._cancelDragActive(this._dragOverItem, dragOverItem);
     }
 
     if (dragOverItem != this._dragOverItem || dragValue != dragOverItem.getAttribute("dragover")) {
       if (dragOverItem != targetArea.customizationTarget) {
         this._setDragActive(dragOverItem, dragValue, draggedItemId, targetIsToolbar);
+      } else if (targetIsToolbar) {
+        this._updateToolbarCustomizationOutline(this.window, targetArea);
       }
       this._dragOverItem = dragOverItem;
     }
 
     aEvent.preventDefault();
     aEvent.stopPropagation();
   },
 
@@ -1738,27 +1750,25 @@ CustomizeMode.prototype = {
     let rect = aDraggedItem.parentNode.getBoundingClientRect();
     size = {width: rect.width, height: rect.height};
     // Cache the found value of size for this target.
     itemMap.set(targetArea, size);
 
     if (targetArea != currentArea) {
       this.unwrapToolbarItem(aDraggedItem.parentNode);
       // Put the item back into its previous position.
-      if (currentSibling)
-        currentParent.insertBefore(aDraggedItem, currentSibling);
-      else
-        currentParent.appendChild(aDraggedItem);
+      currentParent.insertBefore(aDraggedItem, currentSibling);
       // restore the areaType
       if (areaType) {
         if (currentType === false)
           aDraggedItem.removeAttribute(kAreaType);
         else
           aDraggedItem.setAttribute(kAreaType, currentType);
       }
+      this.createOrUpdateWrapper(aDraggedItem, null, true);
       CustomizableUI.onWidgetDrag(aDraggedItem.id);
     } else {
       aDraggedItem.parentNode.hidden = true;
     }
     return size;
   },
 
   _getCustomizableParent: function(aElement) {
--- a/browser/components/customizableui/test/browser.ini
+++ b/browser/components/customizableui/test/browser.ini
@@ -69,9 +69,10 @@ skip-if = os == "linux"
 [browser_968565_insert_before_hidden_items.js]
 [browser_969427_recreate_destroyed_widget_after_reset.js]
 [browser_969661_character_encoding_navbar_disabled.js]
 [browser_970511_undo_restore_default.js]
 [browser_972267_customizationchange_events.js]
 [browser_973932_addonbar_currentset.js]
 [browser_975719_customtoolbars_behaviour.js]
 [browser_978084_dragEnd_after_move.js]
+[browser_980155_add_overflow_toolbar.js]
 [browser_panel_toggle.js]
--- a/browser/components/customizableui/test/browser_880164_customization_context_menus.js
+++ b/browser/components/customizableui/test/browser_880164_customization_context_menus.js
@@ -335,8 +335,37 @@ add_task(function() {
   contextMenu.hidePopup();
   yield hiddenContextPromise;
 
   let hiddenPromise = promisePanelHidden(window);
   PanelUI.hide();
   yield hiddenPromise;
 });
 
+
+// Bug 982027 - moving icon around removes custom context menu.
+add_task(function() {
+  let widgetId = "custom-context-menu-toolbarbutton";
+  let expectedContext = "myfancycontext";
+  let widget = createDummyXULButton(widgetId, "Test ctxt menu");
+  widget.setAttribute("context", expectedContext);
+  CustomizableUI.addWidgetToArea(widgetId, CustomizableUI.AREA_NAVBAR);
+  is(widget.getAttribute("context"), expectedContext, "Should have context menu when added to the toolbar.");
+
+  yield startCustomizing();
+  is(widget.getAttribute("context"), "", "Should not have own context menu in the toolbar now that we're customizing.");
+  is(widget.getAttribute("wrapped-context"), expectedContext, "Should keep own context menu wrapped when in toolbar.");
+
+  let panel = PanelUI.contents;
+  simulateItemDrag(widget, panel);
+  is(widget.getAttribute("context"), "", "Should not have own context menu when in the panel.");
+  is(widget.getAttribute("wrapped-context"), expectedContext, "Should keep own context menu wrapped now that we're in the panel.");
+
+  simulateItemDrag(widget, document.getElementById("nav-bar").customizationTarget);
+  is(widget.getAttribute("context"), "", "Should not have own context menu when back in toolbar because we're still customizing.");
+  is(widget.getAttribute("wrapped-context"), expectedContext, "Should keep own context menu wrapped now that we're back in the toolbar.");
+
+  yield endCustomizing();
+  is(widget.getAttribute("context"), expectedContext, "Should have context menu again now that we're out of customize mode.");
+  CustomizableUI.removeWidgetFromArea(widgetId);
+  widget.remove();
+  ok(CustomizableUI.inDefaultState, "Should be in default state after removing button.");
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/customizableui/test/browser_980155_add_overflow_toolbar.js
@@ -0,0 +1,51 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const kToolbarName = "test-new-overflowable-toolbar";
+const kTestWidgetPrefix = "test-widget-for-overflowable-toolbar-";
+
+add_task(function addOverflowingToolbar() {
+  let originalWindowWidth = window.outerWidth;
+
+  let widgetIds = [];
+  for (let i = 0; i < 10; i++) {
+    let id = kTestWidgetPrefix + i;
+    widgetIds.push(id);
+    let spec = {id: id, type: "button", removable: true, label: "test", tooltiptext: "" + i};
+    CustomizableUI.createWidget(spec);
+  }
+
+  let toolbarNode = createOverflowableToolbarWithPlacements(kToolbarName, widgetIds);
+  assertAreaPlacements(kToolbarName, widgetIds);
+
+  for (let id of widgetIds) {
+    document.getElementById(id).style.minWidth = "200px";
+  }
+
+  isnot(toolbarNode.overflowable, null, "Toolbar should have overflowable controller");
+  isnot(toolbarNode.customizationTarget, null, "Toolbar should have customization target");
+  isnot(toolbarNode.customizationTarget, toolbarNode, "Customization target should not be toolbar node");
+
+  let oldChildCount = toolbarNode.customizationTarget.childElementCount;
+  let overflowableList = document.getElementById(kToolbarName + "-overflow-list");
+  let oldOverflowCount = overflowableList.childElementCount;
+
+  isnot(oldChildCount, 0, "Toolbar should have non-overflowing widgets");
+
+  window.resizeTo(400, window.outerHeight);
+  yield waitForCondition(() => toolbarNode.hasAttribute("overflowing"));
+  ok(toolbarNode.hasAttribute("overflowing"), "Should have an overflowing toolbar.");
+  ok(toolbarNode.customizationTarget.childElementCount < oldChildCount, "Should have fewer children.");
+  ok(overflowableList.childElementCount > oldOverflowCount, "Should have more overflowed widgets.");
+
+  window.resizeTo(originalWindowWidth, window.outerHeight);
+});
+
+
+add_task(function asyncCleanup() {
+  removeCustomToolbars();
+  yield resetCustomization();
+});
--- a/browser/components/customizableui/test/head.js
+++ b/browser/components/customizableui/test/head.js
@@ -40,21 +40,67 @@ function createToolbarWithPlacements(id,
   CustomizableUI.registerArea(id, {
     type: CustomizableUI.TYPE_TOOLBAR,
     defaultPlacements: placements
   });
   gNavToolbox.appendChild(tb);
   return tb;
 }
 
+function createOverflowableToolbarWithPlacements(id, placements) {
+  gAddedToolbars.add(id);
+
+  let tb = document.createElementNS(kNSXUL, "toolbar");
+  tb.id = id;
+  tb.setAttribute("customizationtarget", id + "-target");
+
+  let customizationtarget = document.createElementNS(kNSXUL, "hbox");
+  customizationtarget.id = id + "-target";
+  customizationtarget.setAttribute("flex", "1");
+  tb.appendChild(customizationtarget);
+
+  let overflowPanel = document.createElementNS(kNSXUL, "panel");
+  overflowPanel.id = id + "-overflow";
+  document.getElementById("mainPopupSet").appendChild(overflowPanel);
+
+  let overflowList = document.createElementNS(kNSXUL, "vbox");
+  overflowList.id = id + "-overflow-list";
+  overflowPanel.appendChild(overflowList);
+
+  let chevron = document.createElementNS(kNSXUL, "toolbarbutton");
+  chevron.id = id + "-chevron";
+  tb.appendChild(chevron);
+
+  CustomizableUI.registerArea(id, {
+    type: CustomizableUI.TYPE_TOOLBAR,
+    defaultPlacements: placements,
+    overflowable: true,
+  });
+
+  tb.setAttribute("customizable", "true");
+  tb.setAttribute("overflowable", "true");
+  tb.setAttribute("overflowpanel", overflowPanel.id);
+  tb.setAttribute("overflowtarget", overflowList.id);
+  tb.setAttribute("overflowbutton", chevron.id);
+
+  gNavToolbox.appendChild(tb);
+  return tb;
+}
+
 function removeCustomToolbars() {
   CustomizableUI.reset();
   for (let toolbarId of gAddedToolbars) {
     CustomizableUI.unregisterArea(toolbarId, true);
-    document.getElementById(toolbarId).remove();
+    let tb = document.getElementById(toolbarId);
+    if (tb.hasAttribute("overflowpanel")) {
+      let panel = document.getElementById(tb.getAttribute("overflowpanel"));
+      if (panel)
+        panel.remove();
+    }
+    tb.remove();
   }
   gAddedToolbars.clear();
 }
 
 function getToolboxCustomToolbarId(toolbarName) {
   return "__customToolbar_" + toolbarName.replace(" ", "_");
 }
 
--- a/browser/devtools/commandline/BuiltinCommands.jsm
+++ b/browser/devtools/commandline/BuiltinCommands.jsm
@@ -952,17 +952,17 @@ XPCOMUtils.defineLazyModuleGetter(this, 
   /**
    * Check if a given cookie matches a given host
    */
   function isCookieAtHost(cookie, host) {
     if (cookie.host == null) {
       return host == null;
     }
     if (cookie.host.startsWith(".")) {
-      return cookie.host === "." + host;
+      return host.endsWith(cookie.host);
     }
     else {
       return cookie.host == host;
     }
   }
 
   /**
    * 'cookie' command
--- a/browser/devtools/debugger/test/browser_dbg_breakpoints-break-on-last-line-of-script-on-reload.js
+++ b/browser/devtools/debugger/test/browser_dbg_breakpoints-break-on-last-line-of-script-on-reload.js
@@ -16,52 +16,69 @@ function test() {
 
   initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gThreadClient = gDebugger.gThreadClient;
     gEvents = gDebugger.EVENTS;
 
     Task.spawn(function* () {
-      yield waitForSourceShown(gPanel, CODE_URL);
+      try {
+
+        yield waitForSourceShown(gPanel, CODE_URL);
+
+        // Pause and set our breakpoints.
+        yield doInterrupt();
+        const [bp1, bp2, bp3] = yield promise.all([
+          setBreakpoint({
+            url: CODE_URL,
+            line: 2
+          }),
+          setBreakpoint({
+            url: CODE_URL,
+            line: 3
+          }),
+          setBreakpoint({
+            url: CODE_URL,
+            line: 4
+          })
+        ]);
+
+        // Should hit the first breakpoint on reload.
+        yield promise.all([
+          reloadActiveTab(gPanel, gEvents.SOURCE_SHOWN),
+          waitForCaretUpdated(gPanel, 2)
+        ]);
 
-      // Pause and set our breakpoints.
-      yield doInterrupt();
-      yield promise.all([
-        gPanel.addBreakpoint({
-          url: CODE_URL,
-          line: 2
-        }),
-        gPanel.addBreakpoint({
-          url: CODE_URL,
-          line: 3
-        }),
-        gPanel.addBreakpoint({
-          url: CODE_URL,
-          line: 4
-        })
-      ]);
+        // And should hit the other breakpoints as we resume.
+        yield promise.all([
+          doResume(),
+          waitForCaretUpdated(gPanel, 3)
+        ]);
+        yield promise.all([
+          doResume(),
+          waitForCaretUpdated(gPanel, 4)
+        ]);
 
-      // Should hit the first breakpoint on reload.
-      yield promise.all([
-        reloadActiveTab(gPanel, gEvents.SOURCE_SHOWN),
-        waitForCaretUpdated(gPanel, 2)
-      ]);
+        // Clean up the breakpoints.
+        yield promise.all([
+          rdpInvoke(bp1, bp1.remove),
+          rdpInvoke(bp2, bp1.remove),
+          rdpInvoke(bp3, bp1.remove),
+        ]);
 
-      // And should hit the other breakpoints as we resume.
-      yield promise.all([
-        doResume(),
-        waitForCaretUpdated(gPanel, 3)
-      ]);
-      yield promise.all([
-        doResume(),
-        waitForCaretUpdated(gPanel, 4)
-      ]);
+        yield resumeDebuggerThenCloseAndFinish(gPanel);
 
-      yield resumeDebuggerThenCloseAndFinish(gPanel);
+      } catch (e) {
+        DevToolsUtils.reportException(
+          "browser_dbg_breakpoints-break-on-last-line-of-script-on-reload.js",
+          e
+        );
+        ok(false);
+      }
     });
   });
 
   function rdpInvoke(obj, method) {
     return promiseInvoke(obj, method)
       .then(({error, message }) => {
         if (error) {
           throw new Error(error + ": " + message);
@@ -71,9 +88,20 @@ function test() {
 
   function doResume() {
     return rdpInvoke(gThreadClient, gThreadClient.resume);
   }
 
   function doInterrupt() {
     return rdpInvoke(gThreadClient, gThreadClient.interrupt);
   }
+
+  function setBreakpoint(location) {
+    let deferred = promise.defer();
+    gThreadClient.setBreakpoint(location, ({ error, message }, bpClient) => {
+      if (error) {
+        deferred.reject(error + ": " + message);
+      }
+      deferred.resolve(bpClient);
+    });
+    return deferred.promise;
+  }
 }
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -437,25 +437,16 @@ fullscreenButton.tooltip=Display the win
 service.toolbarbutton.label=Services
 service.toolbarbutton.tooltiptext=Services
 
 # LOCALIZATION NOTE (social.install.description): %1$S is the hostname of the social provider, %2$S is brandShortName (e.g. Firefox)
 service.install.description=Would you like to enable services from %1$S to display in your %2$S toolbar and sidebar?
 service.install.ok.label=Enable Services
 service.install.ok.accesskey=E
 
-# LOCALIZATION NOTE (social.turnOff.label): %S is the name of the social provider
-social.turnOff.label=Turn off %S
-social.turnOff.accesskey=T
-# LOCALIZATION NOTE (social.turnOn.label): %S is the name of the social provider
-social.turnOn.label=Turn on %S
-social.turnOn.accesskey=T
-social.turnOffAll.label=Turn off all Services
-social.turnOnAll.label=Turn on all Services
-
 # LOCALIZATION NOTE (social.markpageMenu.label): %S is the name of the social provider
 social.markpageMenu.label=Save Page to %S
 # LOCALIZATION NOTE (social.marklinkMenu.label): %S is the name of the social provider
 social.marklinkMenu.label=Save Link to %S
 
 # LOCALIZATION NOTE (social.error.message): %1$S is brandShortName (e.g. Firefox), %2$S is the name of the social provider
 social.error.message=%1$S is unable to connect with %2$S right now.
 social.error.tryAgain.label=Try Again
--- a/browser/metro/base/content/ContextUI.js
+++ b/browser/metro/base/content/ContextUI.js
@@ -168,21 +168,30 @@ var ContextUI = {
     aDelay = aDelay || kForegroundTabAnimationDelay;
     this._clearDelayedTimeout();
     this._lastTimeoutDelay = aDelay;
     this._hidingId = setTimeout(function () {
         ContextUI.dismissTabs();
       }, aDelay);
   },
 
-  // Display the nav bar
+  /*
+   * Display the nav bar.
+   *
+   * @return false if we were already visible, and didn't do anything.
+   */
   displayNavbar: function () {
+    if (Elements.chromeState.getAttribute("navbar") == "visible") {
+      return false;
+    }
+
     Elements.navbar.show();
     Elements.chromeState.setAttribute("navbar", "visible");
     ContentAreaObserver.updateContentArea();
+    return true;
   },
 
   // Display the tab tray
   displayTabs: function () {
     this._clearDelayedTimeout();
     this._setIsExpanded(true);
   },
 
--- a/browser/metro/base/content/bindings/urlbar.xml
+++ b/browser/metro/base/content/bindings/urlbar.xml
@@ -279,18 +279,25 @@
         <body>
           <![CDATA[
             if (this.isEditing)
               return;
 
             Elements.urlbarState.setAttribute("editing", true);
             this._lastKnownGoodURL = this.value;
 
-            if (!this.focused)
+            if (!this.focused) {
               this.focus();
+              // If we force focus, ensure we're visible.
+              if (ContextUI.displayNavbar()) {
+                // If we forced visibility, ensure we're positioned above keyboard.
+                // (Previous "blur" may have forced us down behind it.)
+                ContentAreaObserver.updateAppBarPosition();
+              }
+            }
 
             this._clearFormatting();
             this.select();
 
             if (aShouldDismiss)
               ContextUI.dismissTabs();
 
             if (!InputSourceHelper.isPrecise) {
--- a/browser/metro/base/content/contenthandlers/SelectionHandler.js
+++ b/browser/metro/base/content/contenthandlers/SelectionHandler.js
@@ -113,23 +113,34 @@ var SelectionHandler = {
     }
     
     // Sanity check to be sure we are initialized
     if (!this._targetElement) {
       this._onFail("not initialized");
       return;
     }
 
-    // Similar to _onSelectionStart - we need to create initial selection
-    // but without the initialization bits.
-    let framePoint = this._clientPointToFramePoint({ xPos: aX, yPos: aY });
-    if (!this._domWinUtils.selectAtPoint(framePoint.xPos, framePoint.yPos,
-                                         Ci.nsIDOMWindowUtils.SELECT_CHARACTER)) {
-      this._onFail("failed to set selection at point");
-      return;
+    // Only use selectAtPoint for editable content and avoid that for inputs,
+    // as we can expand caret to selection manually more precisely. We can use
+    // selectAtPoint for inputs too though, but only once bug 881938 is fully
+    // resolved.
+    if(Util.isEditableContent(this._targetElement)) {
+      // Similar to _onSelectionStart - we need to create initial selection
+      // but without the initialization bits.
+      let framePoint = this._clientPointToFramePoint({ xPos: aX, yPos: aY });
+      if (!this._domWinUtils.selectAtPoint(framePoint.xPos, framePoint.yPos,
+                                           Ci.nsIDOMWindowUtils.SELECT_CHARACTER)) {
+        this._onFail("failed to set selection at point");
+        return;
+      }
+    } else if (this._targetElement.selectionStart == 0 || aMarker == "end") {
+      // Expand caret forward or backward depending on direction
+      this._targetElement.selectionEnd++;
+    } else {
+      this._targetElement.selectionStart--;
     }
 
     // We bail if things get out of sync here implying we missed a message.
     this._selectionMoveActive = true;
 
     // Update the position of the selection marker that is *not*
     // being dragged.
     this._updateSelectionUI("update", aMarker == "end", aMarker == "start");
@@ -173,49 +184,34 @@ var SelectionHandler = {
       return;
     }
 
     if (!this._selectionMoveActive) {
       this._onFail("mouse isn't down for drag move?");
       return;
     }
 
-    // Update selection in the doc
-    let pos = null;
-    if (aMsg.change == "start") {
-      pos = aMsg.start;
-    } else {
-      pos = aMsg.end;
-    }
-    this._handleSelectionPoint(aMsg.change, pos, false);
+    this._handleSelectionPoint(aMsg, false);
   },
 
   /*
    * Selection monocle move finished event handler
    */
   _onSelectionMoveEnd: function _onSelectionMoveComplete(aMsg) {
     if (!this._contentWindow) {
       this._onFail("_onSelectionMove was called without proper view set up");
       return;
     }
 
     if (!this._selectionMoveActive) {
       this._onFail("mouse isn't down for drag move?");
       return;
     }
 
-    // Update selection in the doc
-    let pos = null;
-    if (aMsg.change == "start") {
-      pos = aMsg.start;
-    } else {
-      pos = aMsg.end;
-    }
-
-    this._handleSelectionPoint(aMsg.change, pos, true);
+    this._handleSelectionPoint(aMsg, true);
     this._selectionMoveActive = false;
     
     // _handleSelectionPoint may set a scroll timer, so this must
     // be reset after the last call.
     this._clearTimers();
 
     // Update the position of our selection monocles
     this._updateSelectionUI("end", true, true);
--- a/browser/metro/base/content/helperui/ChromeSelectionHandler.js
+++ b/browser/metro/base/content/helperui/ChromeSelectionHandler.js
@@ -107,49 +107,34 @@ var ChromeSelectionHandler = {
       return;
     }
 
     if (!this._selectionMoveActive) {
       this._onFail("mouse isn't down for drag move?");
       return;
     }
 
-    // Update selection in the doc
-    let pos = null;
-    if (aMsg.change == "start") {
-      pos = aMsg.start;
-    } else {
-      pos = aMsg.end;
-    }
-    this._handleSelectionPoint(aMsg.change, pos, false);
+    this._handleSelectionPoint(aMsg, false);
   },
 
   /*
    * Selection monocle move finished event handler
    */
   _onSelectionMoveEnd: function _onSelectionMoveComplete(aMsg) {
     if (!this.targetIsEditable) {
       this._onFail("_onSelectionMoveEnd with bad targetElement.");
       return;
     }
 
     if (!this._selectionMoveActive) {
       this._onFail("mouse isn't down for drag move?");
       return;
     }
 
-    // Update selection in the doc
-    let pos = null;
-    if (aMsg.change == "start") {
-      pos = aMsg.start;
-    } else {
-      pos = aMsg.end;
-    }
-
-    this._handleSelectionPoint(aMsg.change, pos, true);
+    this._handleSelectionPoint(aMsg, true);
     this._selectionMoveActive = false;
     
     // Clear any existing scroll timers
     this._clearTimers();
 
     // Update the position of our selection monocles
     this._updateSelectionUI("end", true, true);
   },
--- a/browser/metro/base/content/helperui/SelectionHelperUI.js
+++ b/browser/metro/base/content/helperui/SelectionHelperUI.js
@@ -106,21 +106,22 @@ Marker.prototype = {
   _selectionHelperUI: null,
   _xPos: 0,
   _yPos: 0,
   _xDrag: 0,
   _yDrag: 0,
   _tag: "",
   _hPlane: 0,
   _vPlane: 0,
+  _restrictedToBounds: false,
 
   // Tweak me if the monocle graphics change in any way
   _monocleRadius: 8,
   _monocleXHitTextAdjust: -2, 
-  _monocleYHitTextAdjust: -10, 
+  _monocleYHitTextAdjust: -10,
 
   get xPos() {
     return this._xPos;
   },
 
   get yPos() {
     return this._yPos;
   },
@@ -132,16 +133,23 @@ Marker.prototype = {
   set tag(aVal) {
     this._tag = aVal;
   },
 
   get dragging() {
     return this._element.customDragger.dragging;
   },
 
+  // Indicates that marker's position doesn't reflect real selection boundary
+  // but rather boundary of input control while actual selection boundaries are
+  // not visible (ex. due scrolled content).
+  get restrictedToBounds() {
+    return this._restrictedToBounds;
+  },
+
   shutdown: function shutdown() {
     this._element.hidden = true;
     this._element.customDragger.shutdown = true;
     delete this._element.customDragger;
     this._selectionHelperUI = null;
     this._element = null;
   },
 
@@ -158,19 +166,20 @@ Marker.prototype = {
   hide: function hide() {
     this._element.hidden = true;
   },
 
   get visible() {
     return this._element.hidden == false;
   },
 
-  position: function position(aX, aY) {
+  position: function position(aX, aY, aRestrictedToBounds) {
     this._xPos = aX;
     this._yPos = aY;
+    this._restrictedToBounds = !!aRestrictedToBounds;
     this._setPosition();
   },
 
   _setPosition: function _setPosition() {
     this._element.left = this._xPos + "px";
     this._element.top = this._yPos + "px";
   },
 
@@ -578,16 +587,17 @@ var SelectionHelperUI = {
 
     // SelectionHandler messages
     messageManager.addMessageListener("Content:SelectionRange", this);
     messageManager.addMessageListener("Content:SelectionCopied", this);
     messageManager.addMessageListener("Content:SelectionFail", this);
     messageManager.addMessageListener("Content:SelectionDebugRect", this);
     messageManager.addMessageListener("Content:HandlerShutdown", this);
     messageManager.addMessageListener("Content:SelectionHandlerPong", this);
+    messageManager.addMessageListener("Content:SelectionSwap", this);
 
     // capture phase
     window.addEventListener("keypress", this, true);
     window.addEventListener("MozPrecisePointer", this, true);
     window.addEventListener("MozDeckOffsetChanging", this, true);
     window.addEventListener("MozDeckOffsetChanged", this, true);
     window.addEventListener("KeyboardChanged", this, true);
 
@@ -607,16 +617,17 @@ var SelectionHelperUI = {
 
   _shutdown: function _shutdown() {
     messageManager.removeMessageListener("Content:SelectionRange", this);
     messageManager.removeMessageListener("Content:SelectionCopied", this);
     messageManager.removeMessageListener("Content:SelectionFail", this);
     messageManager.removeMessageListener("Content:SelectionDebugRect", this);
     messageManager.removeMessageListener("Content:HandlerShutdown", this);
     messageManager.removeMessageListener("Content:SelectionHandlerPong", this);
+    messageManager.removeMessageListener("Content:SelectionSwap", this);
 
     window.removeEventListener("keypress", this, true);
     window.removeEventListener("MozPrecisePointer", this, true);
     window.removeEventListener("MozDeckOffsetChanging", this, true);
     window.removeEventListener("MozDeckOffsetChanged", this, true);
     window.removeEventListener("KeyboardChanged", this, true);
 
     window.removeEventListener("click", this, false);
@@ -932,37 +943,43 @@ var SelectionHelperUI = {
     this.overlay.addDebugRect(aMsg.left, aMsg.top, aMsg.right, aMsg.bottom,
                               aMsg.color, aMsg.fill, aMsg.id);
   },
 
   _selectionHandlerShutdown: function _selectionHandlerShutdown() {
     this._shutdown();
   },
 
+  _selectionSwap: function _selectionSwap() {
+    [this.startMark.tag, this.endMark.tag] = [this.endMark.tag,
+        this.startMark.tag];
+    [this._startMark, this._endMark] = [this.endMark, this.startMark];
+  },
+
   /*
    * Message handlers
    */
 
   _onSelectionCopied: function _onSelectionCopied(json) {
     this.closeEditSession(true);
   },
 
   _onSelectionRangeChange: function _onSelectionRangeChange(json) {
     let haveSelectionRect = true;
 
     if (json.updateStart) {
       let x = this._msgTarget.btocx(json.start.xPos, true);
       let y = this._msgTarget.btocy(json.start.yPos, true);
-      this.startMark.position(x, y);
+      this.startMark.position(x, y, json.start.restrictedToBounds);
     }
 
     if (json.updateEnd) {
       let x = this._msgTarget.btocx(json.end.xPos, true);
       let y = this._msgTarget.btocy(json.end.yPos, true);
-      this.endMark.position(x, y);
+      this.endMark.position(x, y, json.end.restrictedToBounds);
     }
 
     if (json.updateCaret) {
       let x = this._msgTarget.btocx(json.caret.xPos, true);
       let y = this._msgTarget.btocy(json.caret.yPos, true);
       // If selectionRangeFound is set SelectionHelper found a range we can
       // attach to. If not, there's no text in the control, and hence no caret
       // position information we can use.
@@ -1085,78 +1102,81 @@ var SelectionHelperUI = {
         this._onSelectionCopied(json);
         break;
       case "Content:SelectionDebugRect":
         this._onDebugRectRequest(json);
         break;
       case "Content:HandlerShutdown":
         this._selectionHandlerShutdown();
         break;
+      case "Content:SelectionSwap":
+        this._selectionSwap();
+        break;
       case "Content:SelectionHandlerPong":
         this._onPong(json.id);
         break;
     }
   },
 
   /*
    * Callbacks from markers
    */
 
   _getMarkerBaseMessage: function _getMarkerBaseMessage(aMarkerTag) {
     return {
       change: aMarkerTag,
       start: {
         xPos: this._msgTarget.ctobx(this.startMark.xPos, true),
-        yPos: this._msgTarget.ctoby(this.startMark.yPos, true)
+        yPos: this._msgTarget.ctoby(this.startMark.yPos, true),
+        restrictedToBounds: this.startMark.restrictedToBounds
       },
       end: {
         xPos: this._msgTarget.ctobx(this.endMark.xPos, true),
-        yPos: this._msgTarget.ctoby(this.endMark.yPos, true)
+        yPos: this._msgTarget.ctoby(this.endMark.yPos, true),
+        restrictedToBounds: this.endMark.restrictedToBounds
       },
       caret: {
         xPos: this._msgTarget.ctobx(this.caretMark.xPos, true),
         yPos: this._msgTarget.ctoby(this.caretMark.yPos, true)
       },
     };
   },
 
   markerDragStart: function markerDragStart(aMarker) {
     let json = this._getMarkerBaseMessage(aMarker.tag);
     if (aMarker.tag == "caret") {
-      this._cachedCaretPos = null;
-      this._sendAsyncMessage("Browser:CaretMove", json);
+      // Cache for when we start the drag in _transitionFromCaretToSelection.
+      if (!this._cachedCaretPos) {
+        this._cachedCaretPos = this._getMarkerBaseMessage(aMarker.tag).caret;
+      }
       return;
     }
     this._sendAsyncMessage("Browser:SelectionMoveStart", json);
   },
 
   markerDragStop: function markerDragStop(aMarker) {
     let json = this._getMarkerBaseMessage(aMarker.tag);
     if (aMarker.tag == "caret") {
-      this._sendAsyncMessage("Browser:CaretUpdate", json);
+      this._cachedCaretPos = null;
       return;
     }
     this._sendAsyncMessage("Browser:SelectionMoveEnd", json);
   },
 
   markerDragMove: function markerDragMove(aMarker, aDirection) {
     if (aMarker.tag == "caret") {
       // If direction is "tbd" the drag monocle hasn't determined which
       // direction the user is dragging.
       if (aDirection != "tbd") {
         // We are going to transition from caret browsing mode to selection
         // mode on drag. So swap the caret monocle for a start or end monocle
         // depending on the direction of the drag, and start selecting text.
         this._transitionFromCaretToSelection(aDirection);
         return false;
       }
-      // Cache for when we start the drag in _transitionFromCaretToSelection.
-      if (!this._cachedCaretPos) {
-        this._cachedCaretPos = this._getMarkerBaseMessage(aMarker.tag).caret;
-      }
       return true;
     }
     this._cachedCaretPos = null;
 
     // We'll re-display these after the drag is complete.
     this._hideMonocles();
 
     let json = this._getMarkerBaseMessage(aMarker.tag);
--- a/browser/metro/base/content/library/SelectionPrototype.js
+++ b/browser/metro/base/content/library/SelectionPrototype.js
@@ -237,55 +237,117 @@ SelectionPrototype.prototype = {
     this._cache.updateCaret = aUpdateCaret || false;
     this._cache.targetIsEditable = this._targetIsEditable;
 
     // Get monocles positioned correctly
     this.sendAsync("Content:SelectionRange", this._cache);
   },
 
   /*
-   * _handleSelectionPoint(aMarker, aPoint, aEndOfSelection) 
+   * _handleSelectionPoint(aSelectionInfo, aEndOfSelection)
    *
    * After a monocle moves to a new point in the document, determines
    * what the target is and acts on its selection accordingly. If the
    * monocle is within the bounds of the target, adds or subtracts selection
    * at the monocle coordinates appropriately and then merges selection ranges
    * into a single continuous selection. If the monocle is outside the bounds
    * of the target and the underlying target is editable, uses the selection
    * controller to advance selection and visibility within the control.
    */
-  _handleSelectionPoint: function _handleSelectionPoint(aMarker, aClientPoint,
+  _handleSelectionPoint: function _handleSelectionPoint(aSelectionInfo,
                                                         aEndOfSelection) {
     let selection = this._getSelection();
 
-    let clientPoint = { xPos: aClientPoint.xPos, yPos: aClientPoint.yPos };
+    let markerToChange = aSelectionInfo.change == "start" ?
+        aSelectionInfo.start : aSelectionInfo.end;
 
     if (selection.rangeCount == 0) {
       this._onFail("selection.rangeCount == 0");
       return;
     }
 
     // We expect continuous selection ranges.
     if (selection.rangeCount > 1) {
       this._setContinuousSelection();
     }
 
     // Adjust our y position up such that we are sending coordinates on
     // the text line vs. below it where the monocle is positioned.
-    let halfLineHeight = this._queryHalfLineHeight(aMarker, selection);
-    clientPoint.yPos -= halfLineHeight;
+    let halfLineHeight = this._queryHalfLineHeight(aSelectionInfo.start,
+        selection);
+    aSelectionInfo.start.yPos -= halfLineHeight;
+    aSelectionInfo.end.yPos -= halfLineHeight;
+
+    let isSwapNeeded = false;
+    if (this._isSelectionSwapNeeded(aSelectionInfo.start, aSelectionInfo.end,
+        halfLineHeight)) {
+      [aSelectionInfo.start, aSelectionInfo.end] =
+          [aSelectionInfo.end, aSelectionInfo.start];
+
+      isSwapNeeded = true;
+      this.sendAsync("Content:SelectionSwap");
+    }
 
     // Modify selection based on monocle movement
     if (this._targetIsEditable && !Util.isEditableContent(this._targetElement)) {
-      this._adjustEditableSelection(aMarker, clientPoint, aEndOfSelection);
+      if (isSwapNeeded) {
+        this._adjustEditableSelection("start", aSelectionInfo.start,
+            aEndOfSelection);
+        this._adjustEditableSelection("end", aSelectionInfo.end,
+            aEndOfSelection);
+      } else {
+        this._adjustEditableSelection(aSelectionInfo.change, markerToChange,
+            aEndOfSelection);
+      }
     } else {
-      this._adjustSelectionAtPoint(aMarker, clientPoint, aEndOfSelection);
+      if (isSwapNeeded) {
+        this._adjustSelectionAtPoint("start", aSelectionInfo.start,
+            aEndOfSelection, true);
+        this._adjustSelectionAtPoint("end", aSelectionInfo.end,
+            aEndOfSelection, true);
+      } else {
+        this._adjustSelectionAtPoint(aSelectionInfo.change, markerToChange,
+            aEndOfSelection);
+      }
     }
   },
 
+  /**
+   * Checks whether we need to swap start and end markers depending the target
+   * element and monocle position.
+   * @param aStart Start monocle coordinates.
+   * @param aEnd End monocle coordinates
+   * @param aYThreshold Y-coordinate threshold used to eliminate slight
+   * differences in monocle vertical positions.
+   */
+  _isSelectionSwapNeeded: function(aStart, aEnd, aYThreshold) {
+    let isSwapNeededByX = aStart.xPos > aEnd.xPos;
+    let isSwapNeededByY = aStart.yPos - aEnd.yPos > aYThreshold;
+    let onTheSameLine = Math.abs(aStart.yPos - aEnd.yPos) <= aYThreshold;
+
+    if (this._targetIsEditable &&
+        !Util.isEditableContent(this._targetElement)) {
+
+      // If one of the markers is restricted to edit bounds, then we shouldn't
+      // swap it until we know its real position
+      if (aStart.restrictedToBounds && aEnd.restrictedToBounds) {
+        return false;
+      }
+
+      // For multi line we should respect Y-coordinate
+      if (Util.isMultilineInput(this._targetElement)) {
+        return isSwapNeededByY || (isSwapNeededByX && onTheSameLine);
+      }
+
+      return isSwapNeededByX;
+    }
+
+    return isSwapNeededByY || (isSwapNeededByX && onTheSameLine);
+  },
+
   /*
    * _handleSelectionPoint helper methods
    */
 
   /*
    * _adjustEditableSelection
    *
    * Based on a monocle marker and position, adds or subtracts from the
@@ -370,19 +432,22 @@ SelectionPrototype.prototype = {
    * Note: we are trying to move away from this api due to the overhead. 
    *
    * @param the marker currently being manipulated
    * @param aClientPoint the point designating the new start or end
    * position for the selection.
    * @param aEndOfSelection indicates if this is the end of a selection
    * move, in which case we may want to snap to the end of a word or
    * sentence.
+   * @param suppressSelectionUIUpdate Indicates that we don't want to update
+   * static monocle automatically as it's going to be be updated explicitly like
+   * in case with monocle swapping.
    */
-  _adjustSelectionAtPoint: function _adjustSelectionAtPoint(aMarker, aClientPoint,
-                                                            aEndOfSelection) {
+  _adjustSelectionAtPoint: function _adjustSelectionAtPoint(aMarker,
+      aClientPoint, aEndOfSelection, suppressSelectionUIUpdate) {
     // Make a copy of the existing range, we may need to reset it.
     this._backupRangeList();
 
     // shrinkSelectionFromPoint takes sub-frame relative coordinates.
     let framePoint = this._clientPointToFramePoint(aClientPoint);
 
     // Tests to see if the user is trying to shrink the selection, and if so
     // collapses it down to the appropriate side such that our calls below
@@ -392,20 +457,41 @@ SelectionPrototype.prototype = {
     let selectResult = false;
     try {
       // If we're at the end of a selection (touchend) snap to the word.
       let type = ((aEndOfSelection && this._snap) ?
         Ci.nsIDOMWindowUtils.SELECT_WORD :
         Ci.nsIDOMWindowUtils.SELECT_CHARACTER);
 
       // Select a character at the point.
-      selectResult = 
-        this._domWinUtils.selectAtPoint(framePoint.xPos,
-                                        framePoint.yPos,
-                                        type);
+      selectResult = this._domWinUtils.selectAtPoint(framePoint.xPos,
+          framePoint.yPos, type);
+
+      // selectAtPoint selects char back and forward and apparently can select
+      // content that is beyond selection boundaries that we had before it was
+      // shrunk that forces selection to always move forward or backward
+      // preventing monocle swapping.
+      if (selectResult && shrunk && this._rangeBackup) {
+        let selection = this._getSelection();
+
+        let currentSelection = this._extractUIRects(
+            selection.getRangeAt(selection.rangeCount - 1)).selection;
+        let previousSelection = this._extractUIRects(
+            this._rangeBackup[0]).selection;
+
+        if (aMarker == "start" &&
+            currentSelection.right > previousSelection.right) {
+          selectResult = false;
+        }
+
+        if (aMarker == "end"  &&
+            currentSelection.left < previousSelection.left) {
+          selectResult = false;
+        }
+      }
     } catch (ex) {
     }
 
     // If selectAtPoint failed (which can happen if there's nothing to select)
     // reset our range back before we shrunk it.
     if (!selectResult) {
       this._restoreRangeList();
     }
@@ -413,17 +499,19 @@ SelectionPrototype.prototype = {
     this._freeRangeList();
 
     // Smooth over the selection between all existing ranges.
     this._setContinuousSelection();
 
     // Update the other monocle's position. We do this because the dragging
     // monocle may reset the static monocle to a new position if the dragging
     // monocle drags ahead or behind the other.
-    this._updateSelectionUI("update", aMarker == "end", aMarker == "start");
+    this._updateSelectionUI("update",
+      aMarker == "end" && !suppressSelectionUIUpdate,
+      aMarker == "start" && !suppressSelectionUIUpdate);
   },
 
   /*
    * _backupRangeList, _restoreRangeList, and _freeRangeList
    *
    * Utilities that manage a cloned copy of the existing selection.
    */
 
@@ -432,16 +520,20 @@ SelectionPrototype.prototype = {
     for (let idx = 0; idx < this._getSelection().rangeCount; idx++) {
       this._rangeBackup.push(this._getSelection().getRangeAt(idx).cloneRange());
     }
   },
 
   _restoreRangeList: function _restoreRangeList() {
     if (this._rangeBackup == null)
       return;
+
+    // Remove every previously created selection range
+    this._getSelection().removeAllRanges();
+
     for (let idx = 0; idx < this._rangeBackup.length; idx++) {
       this._getSelection().addRange(this._rangeBackup[idx]);
     }
     this._freeRangeList();
   },
 
   _freeRangeList: function _restoreRangeList() {
     this._rangeBackup = null;
@@ -813,51 +905,60 @@ SelectionPrototype.prototype = {
     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;
   },
 
+  /**
+   * Updates point's coordinate with max\min available in accordance with the
+   * bounding rectangle. Returns true if point coordinates were actually updated,
+   * and false - otherwise.
+   * @param aPoint Target point which coordinates will be analyzed.
+   * @param aRectangle Target rectangle to bound to.
+   */
+  _restrictPointToRectangle: function(aPoint, aRectangle) {
+    let restrictionWasRequired = false;
+
+    if (aPoint.xPos < aRectangle.left) {
+      aPoint.xPos = aRectangle.left;
+      restrictionWasRequired = true;
+    } else if (aPoint.xPos > aRectangle.right) {
+      aPoint.xPos = aRectangle.right;
+      restrictionWasRequired = true;
+    }
+
+    if (aPoint.yPos < aRectangle.top) {
+      aPoint.yPos = aRectangle.top;
+      restrictionWasRequired = true;
+    } else if (aPoint.yPos > aRectangle.bottom) {
+      aPoint.yPos = aRectangle.bottom;
+      restrictionWasRequired = true;
+    }
+
+    return restrictionWasRequired;
+  },
+
   /*
    * 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._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;
-    if (this._cache.end.xPos > bounds.right)
-      this._cache.end.xPos = bounds.right;
-    if (this._cache.caret.xPos > bounds.right)
-      this._cache.caret.xPos = bounds.right;
-
-    if (this._cache.start.yPos < bounds.top)
-      this._cache.start.yPos = bounds.top;
-    if (this._cache.end.yPos < bounds.top)
-      this._cache.end.yPos = bounds.top;
-    if (this._cache.caret.yPos < bounds.top)
-      this._cache.caret.yPos = bounds.top;
-    if (this._cache.start.yPos > bounds.bottom)
-      this._cache.start.yPos = bounds.bottom;
-    if (this._cache.end.yPos > bounds.bottom)
-      this._cache.end.yPos = bounds.bottom;
-    if (this._cache.caret.yPos > bounds.bottom)
-      this._cache.caret.yPos = bounds.bottom;
+    let targetRectangle = this._getTargetBrowserRect();
+    this._cache.start.restrictedToBounds = this._restrictPointToRectangle(
+        this._cache.start, targetRectangle);
+    this._cache.end.restrictedToBounds = this._restrictPointToRectangle(
+        this._cache.end, targetRectangle);
+    this._restrictPointToRectangle(this._cache.caret, targetRectangle);
   },
 
   _restrictCoordinateToEditBounds: function _restrictCoordinateToEditBounds(aX, aY) {
     let result = {
       xPos: aX,
       yPos: aY
     };
     if (!this._targetIsEditable)
--- a/browser/metro/base/tests/mochitest/browser_selection_contenteditable.js
+++ b/browser/metro/base/tests/mochitest/browser_selection_contenteditable.js
@@ -32,17 +32,17 @@ gTests.push({
 });
 
 gTests.push({
   desc: "simple test to make sure content editable selection works",
   run: function test() {
     let div = gWindow.document.getElementById("testdiv");
     ok(div, "have the div");
 
-    sendElementTap(gWindow, div, 287); // end of 'outlook.com'
+    sendElementTap(gWindow, div, 284); // end of 'outlook.com'
 
     yield waitForCondition(function () {
         return SelectionHelperUI.isCaretUIVisible;
       }, kCommonWaitMs, kCommonPollMs);
 
     let xpos = SelectionHelperUI.caretMark.xPos;
     let ypos = SelectionHelperUI.caretMark.yPos + 10;
     var touchdrag = new TouchDragAndHold();
--- a/browser/metro/base/tests/mochitest/browser_selection_inputs.js
+++ b/browser/metro/base/tests/mochitest/browser_selection_inputs.js
@@ -190,16 +190,106 @@ gTests.push({
     yield touchdrag.move(135, ystartpos);
     touchdrag.end();
 
     yield SelectionHelperUI.pingSelectionHandler();
     is(getTrimmedSelection(gInput).toString(), "straight on like a tunnel for", "selection test");
   },
 });
 
+gTests.push({
+  desc: "Bug 858206 - Drag selection monocles should not push other monocles " +
+        "out of the way.",
+  setUp: setUpAndTearDown,
+  tearDown: setUpAndTearDown,
+  run: function test() {
+    let inputOriginalValue = gInput.value;
+
+    gInput.value = "The rabbit-hole went straight on";
+
+    let promise = waitForEvent(document, "popupshown");
+    sendContextMenuClickToElement(gWindow, gInput, 150);
+    yield promise;
+
+    // Make initial selection
+    promise = waitForEvent(document, "popuphidden");
+    sendElementTap(gWindow, document.getElementById("context-select"));
+    yield promise;
+
+    yield waitForCondition(() => SelectionHelperUI.isSelectionUIVisible,
+        kCommonWaitMs, kCommonPollMs);
+    is(getTrimmedSelection(gInput).toString(), "straight");
+
+    // Swap monocles when dragging with end monocle
+    let startXPos = SelectionHelperUI.endMark.xPos;
+    let startYPos = SelectionHelperUI.endMark.yPos + 10;
+    let touchDrag = new TouchDragAndHold();
+    yield touchDrag.start(gWindow, startXPos, startYPos, startXPos - 300,
+        startYPos);
+
+    yield waitForCondition(() => getTrimmedSelection(gInput).toString() ==
+        "The rabbit-hole went", kCommonWaitMs, kCommonPollMs);
+    touchDrag.end();
+    yield waitForCondition(() => !SelectionHelperUI.hasActiveDrag,
+        kCommonWaitMs, kCommonPollMs);
+    yield SelectionHelperUI.pingSelectionHandler();
+
+     // Swap monocles when dragging with start monocle
+    startXPos = SelectionHelperUI.startMark.xPos;
+    startYPos = SelectionHelperUI.startMark.yPos + 10;
+    yield touchDrag.start(gWindow, startXPos, startYPos, startXPos + 300,
+        startYPos);
+    yield waitForCondition(() => getTrimmedSelection(gInput).toString() ==
+        "straight on", kCommonWaitMs, kCommonPollMs);
+    touchDrag.end();
+    yield waitForCondition(() => !SelectionHelperUI.hasActiveDrag,
+        kCommonWaitMs, kCommonPollMs);
+    yield SelectionHelperUI.pingSelectionHandler();
+
+    // Swap monocles right after caret-to-selection mode switch from start
+    gInput.selectionStart = gInput.selectionEnd = 0;
+    sendElementTap(gWindow, gInput, 0, 0);
+
+    yield waitForCondition(() => !SelectionHelperUI.isSelectionUIVisible &&
+        SelectionHelperUI.isCaretUIVisible);
+
+    startXPos = SelectionHelperUI.caretMark.xPos;
+    startYPos = SelectionHelperUI.caretMark.yPos + 10;
+
+    yield touchDrag.start(gWindow, startXPos, startYPos, startXPos + 300,
+        startYPos);
+    yield waitForCondition(() => getTrimmedSelection(gInput).toString() ==
+        "The rabbit-hole went straight on", kCommonWaitMs, kCommonPollMs);
+    touchDrag.end();
+
+    sendTap(gWindow, 10, 10);
+    yield waitForCondition(() => !SelectionHelperUI.isSelectionUIVisible);
+
+    // Swap monocles right after caret-to-selection mode switch from end
+    gInput.selectionStart = gInput.selectionEnd = gInput.value.length;
+    let inputSelectionRectangle = gInput.QueryInterface(Ci.nsIDOMNSEditableElement).
+        editor.selection.getRangeAt(0).getClientRects()[0];
+    sendTap(gWindow, inputSelectionRectangle.right,
+        inputSelectionRectangle.top);
+
+    yield waitForCondition(() => SelectionHelperUI.isCaretUIVisible);
+
+    startXPos = SelectionHelperUI.caretMark.xPos;
+    startYPos = SelectionHelperUI.caretMark.yPos + 10;
+
+    yield touchDrag.start(gWindow, startXPos, startYPos, startXPos - 300,
+        startYPos);
+    yield waitForCondition(() => getTrimmedSelection(gInput).toString() ==
+        "The rabbit-hole went straight on", kCommonWaitMs, kCommonPollMs);
+    touchDrag.end();
+
+    gInput.value = inputOriginalValue;
+  }
+});
+
 function test() {
   if (!isLandscapeMode()) {
     todo(false, "browser_selection_tests need landscape mode to run.");
     return;
   }
   // XXX need this until bugs 886624 and 859742 are fully resolved
   setDevPixelEqualToPx();
   runTests();
--- a/browser/metro/base/tests/mochitest/browser_selection_urlbar.js
+++ b/browser/metro/base/tests/mochitest/browser_selection_urlbar.js
@@ -310,18 +310,104 @@ gTests.push({
     ok(!SelectHelperUI.isSelectionUIVisible && !SelectHelperUI.isCaretUIVisible,
         "Neither CaretUI nor SelectionUI is visible on empty input.");
 
     inputField.value = "Test text";
 
     sendTap(window, inputFieldRectangle.left + 10, inputFieldRectangle.top + 5);
 
     yield waitForCondition(() => SelectionHelperUI.isCaretUIVisible);
+    chromeHandlerSpy.restore();
+    inputField.blur();
+  }
+});
 
-    chromeHandlerSpy.restore();
+gTests.push({
+  desc: "Bug 858206 - Drag selection monocles should not push other monocles " +
+        "out of the way.",
+  run: function test() {
+    yield showNavBar();
+
+    let edit = document.getElementById("urlbar-edit");
+    edit.value = "about:mozilla";
+
+    let editRectangle = edit.getBoundingClientRect();
+
+    sendTap(window, editRectangle.left, editRectangle.top);
+
+    yield waitForCondition(() => SelectionHelperUI.isSelectionUIVisible);
+
+    let selection = edit.QueryInterface(
+        Components.interfaces.nsIDOMXULTextBoxElement).editor.selection;
+    let selectionRectangle = selection.getRangeAt(0).getClientRects()[0];
+
+    // Place caret to the input start
+    sendTap(window, selectionRectangle.left + 2, selectionRectangle.top + 2);
+    yield waitForCondition(() => !SelectionHelperUI.isSelectionUIVisible &&
+        SelectionHelperUI.isCaretUIVisible);
+
+    let startXPos = SelectionHelperUI.caretMark.xPos;
+    let startYPos = SelectionHelperUI.caretMark.yPos + 10;
+    let touchDrag = new TouchDragAndHold();
+    yield touchDrag.start(gWindow, startXPos, startYPos, startXPos + 200,
+        startYPos);
+    yield waitForCondition(() => getTrimmedSelection(edit).toString() ==
+        "about:mozilla", kCommonWaitMs, kCommonPollMs);
+
+    touchDrag.end();
+    yield waitForCondition(() => !SelectionHelperUI.hasActiveDrag,
+        kCommonWaitMs, kCommonPollMs);
+
+    // Place caret to the input end
+    sendTap(window, selectionRectangle.right - 2, selectionRectangle.top + 2);
+    yield waitForCondition(() => !SelectionHelperUI.isSelectionUIVisible &&
+        SelectionHelperUI.isCaretUIVisible);
+
+    startXPos = SelectionHelperUI.caretMark.xPos;
+    startYPos = SelectionHelperUI.caretMark.yPos + 10;
+    yield touchDrag.start(gWindow, startXPos, startYPos, startXPos - 200,
+        startYPos);
+    yield waitForCondition(() => getTrimmedSelection(edit).toString() ==
+        "about:mozilla", kCommonWaitMs, kCommonPollMs);
+
+    touchDrag.end();
+    yield waitForCondition(() => !SelectionHelperUI.hasActiveDrag,
+        kCommonWaitMs, kCommonPollMs);
+
+    // Place caret in the middle
+    let midX = Math.ceil(((selectionRectangle.right - selectionRectangle.left) *
+        .5) + selectionRectangle.left);
+    let midY = Math.ceil(((selectionRectangle.bottom - selectionRectangle.top) *
+        .5) + selectionRectangle.top);
+
+    sendTap(window, midX, midY);
+    yield waitForCondition(() => !SelectionHelperUI.isSelectionUIVisible &&
+        SelectionHelperUI.isCaretUIVisible);
+
+    startXPos = SelectionHelperUI.caretMark.xPos;
+    startYPos = SelectionHelperUI.caretMark.yPos + 10;
+    yield touchDrag.start(gWindow, startXPos, startYPos, startXPos - 200,
+        startYPos);
+    yield waitForCondition(() => getTrimmedSelection(edit).toString() ==
+        "about:", kCommonWaitMs, kCommonPollMs);
+
+    touchDrag.end();
+    yield waitForCondition(() => !SelectionHelperUI.hasActiveDrag,
+        kCommonWaitMs, kCommonPollMs);
+
+    // Now try to swap monocles
+    startXPos = SelectionHelperUI.startMark.xPos;
+    startYPos = SelectionHelperUI.startMark.yPos + 10;
+    yield touchDrag.start(gWindow, startXPos, startYPos, startXPos + 200,
+        startYPos);
+    yield waitForCondition(() => getTrimmedSelection(edit).toString() ==
+        "mozilla", kCommonWaitMs, kCommonPollMs);
+      touchDrag.end();
+    yield waitForCondition(() => !SelectionHelperUI.hasActiveDrag &&
+        SelectionHelperUI.isSelectionUIVisible, kCommonWaitMs, kCommonPollMs);
   }
 });
 
 function test() {
   if (!isLandscapeMode()) {
     todo(false, "browser_selection_tests need landscape mode to run.");
     return;
   }
--- a/browser/modules/NetworkPrioritizer.jsm
+++ b/browser/modules/NetworkPrioritizer.jsm
@@ -75,28 +75,22 @@ let BrowserHelper = {
     let windowEntry = WindowHelper.getEntry(aBrowser.ownerDocument.defaultView);
     if (windowEntry.lastSelectedBrowser)
       this.decreasePriority(windowEntry.lastSelectedBrowser);
     this.increasePriority(aBrowser);
 
     windowEntry.lastSelectedBrowser = aBrowser;
   },
 
-  // Auxiliary methods
-  getLoadgroup: function NP_BH_getLoadgroup(aBrowser) {
-    return aBrowser.webNavigation.QueryInterface(Ci.nsIDocumentLoader)
-                   .loadGroup.QueryInterface(Ci.nsISupportsPriority);
-  },
-
   increasePriority: function NP_BH_increasePriority(aBrowser) {
-    this.getLoadgroup(aBrowser).adjustPriority(PRIORITY_DELTA);
+    aBrowser.adjustPriority(PRIORITY_DELTA);
   },
 
   decreasePriority: function NP_BH_decreasePriority(aBrowser) {
-    this.getLoadgroup(aBrowser).adjustPriority(PRIORITY_DELTA * -1);
+    aBrowser.adjustPriority(PRIORITY_DELTA * -1);
   }
 };
 
 
 // Methods that impact a window. Put into single object for organization.
 let WindowHelper = {
   addWindow: function NP_WH_addWindow(aWindow) {
     // Build internal data object
--- a/browser/modules/Social.jsm
+++ b/browser/modules/Social.jsm
@@ -29,36 +29,16 @@ XPCOMUtils.defineLazyModuleGetter(this, 
   "resource://gre/modules/PrivateBrowsingUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Promise",
   "resource://gre/modules/Promise.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "unescapeService",
                                    "@mozilla.org/feed-unescapehtml;1",
                                    "nsIScriptableUnescapeHTML");
 
-// Add a pref observer for the enabled state
-function prefObserver(subject, topic, data) {
-  let enable = Services.prefs.getBoolPref("social.enabled");
-  if (enable && !Social.provider) {
-    // this will result in setting Social.provider
-    SocialService.getOrderedProviderList(function(providers) {
-      Social.enabled = true;
-      Social._updateProviderCache(providers);
-    });
-  } else if (!enable && Social.provider) {
-    Social.provider = null;
-  }
-}
-
-Services.prefs.addObserver("social.enabled", prefObserver, false);
-Services.obs.addObserver(function xpcomShutdown() {
-  Services.obs.removeObserver(xpcomShutdown, "xpcom-shutdown");
-  Services.prefs.removeObserver("social.enabled", prefObserver);
-}, "xpcom-shutdown", false);
-
 function promiseSetAnnotation(aURI, providerList) {
   let deferred = Promise.defer();
 
   // Delaying to catch issues with asynchronous behavior while waiting
   // to implement asynchronous annotations in bug 699844.
   Services.tm.mainThread.dispatch(function() {
     try {
       if (providerList && providerList.length > 0) {
@@ -95,172 +75,91 @@ function promiseGetAnnotation(aURI) {
 }
 
 this.Social = {
   initialized: false,
   lastEventReceived: 0,
   providers: [],
   _disabledForSafeMode: false,
 
-  get _currentProviderPref() {
-    try {
-      return Services.prefs.getComplexValue("social.provider.current",
-                                            Ci.nsISupportsString).data;
-    } catch (ex) {}
-    return null;
-  },
-  set _currentProviderPref(val) {
-    let string = Cc["@mozilla.org/supports-string;1"].
-                 createInstance(Ci.nsISupportsString);
-    string.data = val;
-    Services.prefs.setComplexValue("social.provider.current",
-                                   Ci.nsISupportsString, string);
-  },
-
-  _provider: null,
-  get provider() {
-    return this._provider;
-  },
-  set provider(val) {
-    this._setProvider(val);
-  },
-
-  // Sets the current provider and notifies observers of the change.
-  _setProvider: function (provider) {
-    if (this._provider == provider)
-      return;
-
-    this._provider = provider;
-
-    if (this._provider) {
-      this._provider.enabled = true;
-      this._currentProviderPref = this._provider.origin;
-    }
-    let enabled = !!provider;
-    if (enabled != SocialService.enabled) {
-      SocialService.enabled = enabled;
-      this._updateWorkerState(enabled);
-    }
-
-    let origin = this._provider && this._provider.origin;
-    Services.obs.notifyObservers(null, "social:provider-set", origin);
-  },
-
-  get defaultProvider() {
-    if (this.providers.length == 0)
-      return null;
-    let provider = this._getProviderFromOrigin(this._currentProviderPref);
-    return provider || this.providers[0];
-  },
-
   init: function Social_init() {
     this._disabledForSafeMode = Services.appinfo.inSafeMode && this.enabled;
+    let deferred = Promise.defer();
 
     if (this.initialized) {
-      return;
+      deferred.resolve(true);
+      return deferred.promise;
     }
     this.initialized = true;
     // if SocialService.hasEnabledProviders, retreive the providers so the
     // front-end can generate UI
     if (SocialService.hasEnabledProviders) {
       // Retrieve the current set of providers, and set the current provider.
       SocialService.getOrderedProviderList(function (providers) {
         Social._updateProviderCache(providers);
         Social._updateWorkerState(SocialService.enabled);
+        deferred.resolve(false);
       });
+    } else {
+      deferred.resolve(false);
     }
 
     // Register an observer for changes to the provider list
     SocialService.registerProviderListener(function providerListener(topic, origin, providers) {
       // An engine change caused by adding/removing a provider should notify.
       // any providers we receive are enabled in the AddonsManager
       if (topic == "provider-installed" || topic == "provider-uninstalled") {
         // installed/uninstalled do not send the providers param
         Services.obs.notifyObservers(null, "social:" + topic, origin);
         return;
       }
       if (topic == "provider-enabled") {
         Social._updateProviderCache(providers);
-        Social._updateWorkerState(Social.enabled);
+        Social._updateWorkerState(true);
         Services.obs.notifyObservers(null, "social:" + topic, origin);
         return;
       }
       if (topic == "provider-disabled") {
         // a provider was removed from the list of providers, that does not
         // affect worker state for other providers
         Social._updateProviderCache(providers);
+        Social._updateWorkerState(providers.length > 0);
         Services.obs.notifyObservers(null, "social:" + topic, origin);
         return;
       }
       if (topic == "provider-update") {
         // a provider has self-updated its manifest, we need to update our cache
         // and reload the provider.
         Social._updateProviderCache(providers);
         let provider = Social._getProviderFromOrigin(origin);
         provider.reload();
       }
     });
+    return deferred.promise;
   },
 
   _updateWorkerState: function(enable) {
     [p.enabled = enable for (p of Social.providers) if (p.enabled != enable)];
   },
 
   // Called to update our cache of providers and set the current provider
   _updateProviderCache: function (providers) {
     this.providers = providers;
     Services.obs.notifyObservers(null, "social:providers-changed", null);
-
-    // If social is currently disabled there's nothing else to do other than
-    // to notify about the lack of a provider.
-    if (!SocialService.enabled) {
-      Services.obs.notifyObservers(null, "social:provider-set", null);
-      return;
-    }
-    // Otherwise set the provider.
-    this._setProvider(this.defaultProvider);
-  },
-
-  set enabled(val) {
-    // Setting .enabled is just a shortcut for setting the provider to either
-    // the default provider or null...
-
-    this._updateWorkerState(val);
-
-    if (val) {
-      if (!this.provider)
-        this.provider = this.defaultProvider;
-    } else {
-      this.provider = null;
-    }
   },
 
   get enabled() {
-    return this.provider != null;
-  },
-
-  toggle: function Social_toggle() {
-    this.enabled = this._disabledForSafeMode ? false : !this.enabled;
-    this._disabledForSafeMode = false;
-  },
-
-  toggleSidebar: function SocialSidebar_toggle() {
-    let prefValue = Services.prefs.getBoolPref("social.sidebar.open");
-    Services.prefs.setBoolPref("social.sidebar.open", !prefValue);
+    return !this._disabledForSafeMode && this.providers.length > 0;
   },
 
   toggleNotifications: function SocialNotifications_toggle() {
     let prefValue = Services.prefs.getBoolPref("social.toast-notifications.enabled");
     Services.prefs.setBoolPref("social.toast-notifications.enabled", !prefValue);
   },
 
-  setProviderByOrigin: function (origin) {
-    this.provider = this._getProviderFromOrigin(origin);
-  },
-
   _getProviderFromOrigin: function (origin) {
     for (let p of this.providers) {
       if (p.origin == origin) {
         return p;
       }
     }
     return null;
   },
@@ -276,37 +175,17 @@ this.Social = {
   uninstallProvider: function(origin, aCallback) {
     SocialService.uninstallProvider(origin, aCallback);
   },
 
   // Activation functionality
   activateFromOrigin: function (origin, callback) {
     // For now only "builtin" providers can be activated.  It's OK if the
     // provider has already been activated - we still get called back with it.
-    SocialService.addBuiltinProvider(origin, function(provider) {
-      if (provider) {
-        // No need to activate again if we're already active
-        if (provider == this.provider)
-          return;
-        this.provider = provider;
-      }
-      if (callback)
-        callback(provider);
-    }.bind(this));
-  },
-
-  deactivateFromOrigin: function (origin, oldOrigin) {
-    // if we have the old provider, always set that before trying removal
-    let provider = this._getProviderFromOrigin(origin);
-    let oldProvider = this._getProviderFromOrigin(oldOrigin);
-    if (!oldProvider && this.providers.length)
-      oldProvider = this.providers[0];
-    this.provider = oldProvider;
-    if (provider)
-      SocialService.removeProvider(origin);
+    SocialService.addBuiltinProvider(origin, callback);
   },
 
   // Page Marking functionality
   isURIMarked: function(origin, aURI, aCallback) {
     promiseGetAnnotation(aURI).then(function(val) {
       if (val) {
         let providerList = JSON.parse(val);
         val = providerList.indexOf(origin) >= 0;
@@ -479,27 +358,29 @@ SocialErrorListener.prototype = {
         } catch (e) {}
       }
     }
 
     // Calling cancel() will raise some OnStateChange notifications by itself,
     // so avoid doing that more than once
     if (failure && aStatus != Components.results.NS_BINDING_ABORTED) {
       aRequest.cancel(Components.results.NS_BINDING_ABORTED);
-      Social.provider.errorState = "content-error";
+      let provider = Social._getProviderFromOrigin(this.iframe.getAttribute("origin"));
+      provider.errorState = "content-error";
       this.setErrorMessage(aWebProgress.QueryInterface(Ci.nsIDocShell)
                               .chromeEventHandler);
     }
   },
 
   onLocationChange: function SPL_onLocationChange(aWebProgress, aRequest, aLocation, aFlags) {
     if (aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE) {
       aRequest.cancel(Components.results.NS_BINDING_ABORTED);
-      if (!Social.provider.errorState)
-        Social.provider.errorState = "content-error";
+      let provider = Social._getProviderFromOrigin(this.iframe.getAttribute("origin"));
+      if (!provider.errorState)
+        provider.errorState = "content-error";
       schedule(function() {
         this.setErrorMessage(aWebProgress.QueryInterface(Ci.nsIDocShell)
                               .chromeEventHandler);
       }.bind(this));
     }
   },
 
   onProgressChange: function SPL_onProgressChange() {},
--- a/browser/modules/test/unit/social/head.js
+++ b/browser/modules/test/unit/social/head.js
@@ -80,16 +80,23 @@ function initApp() {
   createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
   // prepare a blocklist file for the blocklist service
   var blocklistFile = gProfD.clone();
   blocklistFile.append("blocklist.xml");
   if (blocklistFile.exists())
     blocklistFile.remove(false);
   var source = do_get_file("blocklist.xml");
   source.copyTo(gProfD, "blocklist.xml");
+
+
+  let internalManager = Cc["@mozilla.org/addons/integration;1"].
+                     getService(Ci.nsIObserver).
+                     QueryInterface(Ci.nsITimerCallback);
+
+  internalManager.observe(null, "addons-startup", null);
 }
 
 function setManifestPref(manifest) {
   let string = Cc["@mozilla.org/supports-string;1"].
                createInstance(Ci.nsISupportsString);
   string.data = JSON.stringify(manifest);
   Services.prefs.setComplexValue("social.manifest." + manifest.origin, Ci.nsISupportsString, string);
 }
@@ -97,50 +104,61 @@ function setManifestPref(manifest) {
 function do_wait_observer(topic, cb) {
   function observer(subject, topic, data) {
     Services.obs.removeObserver(observer, topic);
     cb();
   }
   Services.obs.addObserver(observer, topic, false);
 }
 
+function do_add_providers(cb) {
+  // run only after social is already initialized
+  SocialService.addProvider(manifests[0], function() {
+    do_wait_observer("social:providers-changed", function() {
+      do_check_eq(Social.providers.length, 2, "2 providers installed");
+      do_execute_soon(cb);
+    });
+    SocialService.addProvider(manifests[1]);
+  });
+}
+
 function do_initialize_social(enabledOnStartup, cb) {
   initApp();
 
-  manifests.forEach(function (manifest) {
-    setManifestPref(manifest);
-  });
-  // Set both providers active and flag the first one as "current"
-  let activeVal = Cc["@mozilla.org/supports-string;1"].
-             createInstance(Ci.nsISupportsString);
-  let active = {};
-  for (let m of manifests)
-    active[m.origin] = 1;
-  activeVal.data = JSON.stringify(active);
-  Services.prefs.setComplexValue("social.activeProviders",
-                                 Ci.nsISupportsString, activeVal);
-  Services.prefs.setCharPref("social.provider.current", manifests[0].origin);
-  Services.prefs.setBoolPref("social.enabled", enabledOnStartup);
+  if (enabledOnStartup) {
+    // set prefs before initializing social
+    manifests.forEach(function (manifest) {
+      setManifestPref(manifest);
+    });
+    // Set both providers active and flag the first one as "current"
+    let activeVal = Cc["@mozilla.org/supports-string;1"].
+               createInstance(Ci.nsISupportsString);
+    let active = {};
+    for (let m of manifests)
+      active[m.origin] = 1;
+    activeVal.data = JSON.stringify(active);
+    Services.prefs.setComplexValue("social.activeProviders",
+                                   Ci.nsISupportsString, activeVal);
 
-  do_register_cleanup(function() {
-    manifests.forEach(function (manifest) {
-      Services.prefs.clearUserPref("social.manifest." + manifest.origin);
+    do_register_cleanup(function() {
+      manifests.forEach(function (manifest) {
+        Services.prefs.clearUserPref("social.manifest." + manifest.origin);
+      });
+      Services.prefs.clearUserPref("social.activeProviders");
     });
-    Services.prefs.clearUserPref("social.enabled");
-    Services.prefs.clearUserPref("social.provider.current");
-    Services.prefs.clearUserPref("social.activeProviders");
-  });
 
-  // expecting 2 providers installed
-  do_wait_observer("social:providers-changed", function() {
-    do_check_eq(Social.providers.length, 2, "2 providers installed");
-    cb();
-  });
+    // expecting 2 providers installed
+    do_wait_observer("social:providers-changed", function() {
+      do_check_eq(Social.providers.length, 2, "2 providers installed");
+      do_execute_soon(cb);
+    });
+  }
 
   // import and initialize everything
   SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).SocialService;
-  do_check_eq(SocialService.enabled, enabledOnStartup, "service is doing its thing");
-  do_check_true(SocialService.hasEnabledProviders, "Service has enabled providers");
+  do_check_eq(enabledOnStartup, SocialService.hasEnabledProviders, "Service has enabled providers");
   Social = Cu.import("resource:///modules/Social.jsm", {}).Social;
   do_check_false(Social.initialized, "Social is not initialized");
   Social.init();
   do_check_true(Social.initialized, "Social is initialized");
+  if (!enabledOnStartup)
+    do_execute_soon(cb);
 }
--- a/browser/modules/test/unit/social/test_social.js
+++ b/browser/modules/test/unit/social/test_social.js
@@ -8,23 +8,25 @@ function run_test() {
   add_test(testStartupEnabled);
   add_test(testDisableAfterStartup);
   do_initialize_social(true, run_next_test);
 }
 
 function testStartupEnabled() {
   // wait on startup before continuing
   do_check_eq(Social.providers.length, 2, "two social providers enabled");
-  do_check_true(Social.providers[0].enabled, "provider is enabled");
-  do_check_true(Social.providers[1].enabled, "provider is enabled");
+  do_check_true(Social.providers[0].enabled, "provider 0 is enabled");
+  do_check_true(Social.providers[1].enabled, "provider 1 is enabled");
   run_next_test();
 }
 
 function testDisableAfterStartup() {
-  do_wait_observer("social:provider-set", function() {
-    do_check_eq(Social.enabled, false, "Social is disabled");
-    do_check_false(Social.providers[0].enabled, "provider is enabled");
-    do_check_false(Social.providers[1].enabled, "provider is enabled");
-    do_test_finished();
-    run_next_test();
+  let SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).SocialService;
+  SocialService.removeProvider(Social.providers[0].origin, function() {
+    do_wait_observer("social:providers-changed", function() {
+      do_check_eq(Social.enabled, false, "Social is disabled");
+      do_check_eq(Social.providers.length, 0, "no social providers available");
+      do_test_finished();
+      run_next_test();
+    });
+    SocialService.removeProvider(Social.providers[0].origin)
   });
-  Social.enabled = false;
 }
--- a/browser/modules/test/unit/social/test_socialDisabledStartup.js
+++ b/browser/modules/test/unit/social/test_socialDisabledStartup.js
@@ -7,25 +7,23 @@ function run_test() {
   do_test_pending();
   add_test(testStartupDisabled);
   add_test(testEnableAfterStartup);
   do_initialize_social(false, run_next_test);
 }
 
 function testStartupDisabled() {
   // wait on startup before continuing
-  do_check_eq(Social.providers.length, 2, "two social providers available");
-  do_check_false(Social.providers[0].enabled, "provider is enabled");
-  do_check_false(Social.providers[1].enabled, "provider is enabled");
+  do_check_false(Social.enabled, "Social is disabled");
+  do_check_eq(Social.providers.length, 0, "zero social providers available");
   run_next_test();
 }
 
 function testEnableAfterStartup() {
-  do_wait_observer("social:provider-set", function() {
+  do_add_providers(function () {
     do_check_true(Social.enabled, "Social is enabled");
     do_check_eq(Social.providers.length, 2, "two social providers available");
-    do_check_true(Social.providers[0].enabled, "provider is enabled");
-    do_check_true(Social.providers[1].enabled, "provider is enabled");
+    do_check_true(Social.providers[0].enabled, "provider 0 is enabled");
+    do_check_true(Social.providers[1].enabled, "provider 1 is enabled");
     do_test_finished();
     run_next_test();
   });
-  Social.enabled = true;
 }
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -169,20 +169,16 @@ toolbarpaletteitem[place="palette"] > #p
   animation: animation-bookmarkAdded 800ms;
   animation-timing-function: ease, ease, ease;
 }
 
 #bookmarked-notification-anchor[notification="finish"][in-bookmarks-toolbar=true] > #bookmarked-notification {
   animation: animation-bookmarkAddedToBookmarksBar 800ms;
 }
 
-#bookmarks-menu-button[notification="finish"] {
-  pointer-events: none;
-}
-
 #bookmarks-menu-button[notification="finish"] > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon {
   animation: animation-bookmarkPulse 300ms;
   animation-delay: 600ms;
   animation-timing-function: ease-out;
 }
 
 /* Bookmark menus */
 menu.bookmark-item,
--- a/browser/themes/linux/customizableui/panelUIOverlay.css
+++ b/browser/themes/linux/customizableui/panelUIOverlay.css
@@ -36,14 +36,24 @@ toolbarbutton.social-provider-menuitem >
   width: 16px;
   height: 16px;
 }
 
 .subviewbutton:-moz-any([image],[targetURI],.cui-withicon, .restoreallitem, .bookmark-item)[checked="true"] > .toolbarbutton-icon {
   visibility: hidden;
 }
 
+menu.subviewbutton > .menu-right {
+  -moz-appearance: none;
+  list-style-image: url(chrome://browser/skin/places/bookmarks-menu-arrow.png);
+  -moz-image-region: rect(0, 16px, 16px, 0);
+}
+
+menu[disabled="true"].subviewbutton > .menu-right {
+  -moz-image-region: rect(0, 32px, 16px, 16px);
+}
+
 .PanelUI-subView toolbarseparator,
 .PanelUI-subView menuseparator,
 .cui-widget-panelview menuseparator,
 #PanelUI-footer-inner > toolbarseparator {
   -moz-appearance: none !important;
 }
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -100,18 +100,19 @@ browser.jar:
   skin/classic/browser/feeds/audioFeedIcon.png        (feeds/feedIcon.png)
   skin/classic/browser/feeds/audioFeedIcon16.png      (feeds/feedIcon16.png)
   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/places/bookmarksMenu.png       (places/bookmarksMenu.png)
   skin/classic/browser/places/bookmarksToolbar.png    (places/bookmarksToolbar.png)
+  skin/classic/browser/places/bookmarksToolbar-menuPanel.png (places/bookmarksToolbar-menuPanel.png)
   skin/classic/browser/places/bookmarks-notification-finish.png  (places/bookmarks-notification-finish.png)
-  skin/classic/browser/places/bookmarksToolbar-menuPanel.png (places/bookmarksToolbar-menuPanel.png)
+  skin/classic/browser/places/bookmarks-menu-arrow.png           (places/bookmarks-menu-arrow.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/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)
new file mode 100644
index 0000000000000000000000000000000000000000..616f16b7fface8c53d164bb8fd7a221d646b4bc7
GIT binary patch
literal 183
zc%17D@N?(olHy`uVBq!ia0vp^3P3Et!3HGD8EPYeRGp`bV~EG`<OB)UW>E<V366k8
z`xh>3RQk&!B_$PZYHIrQ<kkC^ZWV4_y3}=^5c?nYN8+zuy;6H(x!|+?1wnnOs3;$e
zLI#d{R;xqvZP-ix<>$TpaqGsVhyI<+4nOoS2v$C^V0hKB-&c2TrSmmLW+_(YSsd(U
h(tH(;CR{IA8BR~l5VXvgyB6pY22WQ%mvv4FO#q;wKPUhI
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -399,30 +399,22 @@ toolbarpaletteitem[place="palette"] > #p
 }
 
 #bookmarked-notification-anchor[notification="finish"] > #bookmarked-notification {
   background-image: url("chrome://browser/skin/places/bookmarks-notification-finish.png");
   animation: animation-bookmarkAdded 800ms;
   animation-timing-function: ease, ease, ease;
 }
 
-#bookmarked-notification-anchor[notification="finish"][in-bookmarks-toolbar=true] > #bookmarked-notification {
-  animation: animation-bookmarkAdded 800ms;
-}
-
 @media (min-resolution: 2dppx) {
   #bookmarked-notification-anchor[notification="finish"] > #bookmarked-notification {
     background-image: url("chrome://browser/skin/places/bookmarks-notification-finish@2x.png");
   }
 }
 
-#bookmarks-menu-button[notification="finish"] {
-  pointer-events: none;
-}
-
 #bookmarks-menu-button[notification="finish"] > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon {
   animation: animation-bookmarkPulse 300ms;
   animation-delay: 600ms;
   animation-timing-function: ease-out;
 }
 
 /* ----- BOOKMARK MENUS ----- */
 
--- a/browser/themes/osx/customizableui/panelUIOverlay.css
+++ b/browser/themes/osx/customizableui/panelUIOverlay.css
@@ -55,17 +55,17 @@
 
   #PanelUI-customize:hover:active,
   #PanelUI-help:not([disabled]):hover:active,
   #PanelUI-quit:not([disabled]):hover:active {
     -moz-image-region: rect(0, 96px, 32px, 64px);
   }
 
   .subviewbutton[checked="true"] {
-    background-image: url("chrome://global/skin/menu/menu-check@2x.png");
+    background-image: url("chrome://global/skin/menu/shared-menu-check@2x.png");
   }
 
 }
 
 .panelUI-grid .toolbarbutton-1 {
   margin-right: 0;
   margin-left: 0;
   margin-bottom: 0;
--- a/browser/themes/shared/customizableui/panelUIOverlay.inc.css
+++ b/browser/themes/shared/customizableui/panelUIOverlay.inc.css
@@ -887,17 +887,17 @@ toolbaritem[overflowedItem=true],
   background-clip: padding-box;
   background-position: center;
   background-repeat: no-repeat;
   background-size: 1px 18px;
   box-shadow: 0 0 0 1px hsla(0,0%,100%,.2);
 }
 
 .subviewbutton[checked="true"] {
-  background: url("chrome://global/skin/menu/menu-check.png") top 7px left 7px / 11px 11px no-repeat transparent;
+  background: url("chrome://global/skin/menu/shared-menu-check.png") top 7px left 7px / 11px 11px no-repeat transparent;
 }
 
 .PanelUI-subView > menu > .menu-iconic-left,
 .PanelUI-subView > menuitem > .menu-iconic-left {
   -moz-appearance: none;
   -moz-margin-end: 3px;
 }
 
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -359,20 +359,16 @@ toolbarpaletteitem[place="palette"] > #p
 }
 
 #bookmarked-notification-anchor[notification="finish"] > #bookmarked-notification {
   background-image: url("chrome://browser/skin/places/bookmarks-notification-finish.png");
   animation: animation-bookmarkAdded 800ms;
   animation-timing-function: ease, ease, ease;
 }
 
-#bookmarks-menu-button[notification="finish"] {
-  pointer-events: none;
-}
-
 #bookmarks-menu-button[notification="finish"] > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon {
   animation: animation-bookmarkPulse 300ms;
   animation-delay: 600ms;
   animation-timing-function: ease-out;
 }
 
 /* ::::: bookmark menus ::::: */
 
--- a/browser/themes/windows/customizableui/panelUIOverlay.css
+++ b/browser/themes/windows/customizableui/panelUIOverlay.css
@@ -54,16 +54,26 @@ toolbarbutton.social-provider-menuitem >
   width: 16px;
   height: 16px;
 }
 
 .subviewbutton:-moz-any([image],[targetURI],.cui-withicon, .restoreallitem, .bookmark-item)[checked="true"] > .toolbarbutton-icon {
   visibility: hidden;
 }
 
+menu.subviewbutton > .menu-right {
+  -moz-appearance: none;
+  list-style-image: url(chrome://browser/skin/places/bookmarks-menu-arrow.png);
+  -moz-image-region: rect(0, 16px, 16px, 0);
+}
+
+menu[disabled="true"].subviewbutton > .menu-right {
+  -moz-image-region: rect(0, 32px, 16px, 16px);
+}
+
 %ifdef WINDOWS_AERO
 /* Win8 and beyond. */
 @media not all and (-moz-os-version: windows-vista) {
   @media not all and (-moz-os-version: windows-win7) {
     panelview .toolbarbutton-1,
     .subviewbutton,
     .widget-overflow-list .toolbarbutton-1,
     .panelUI-grid .toolbarbutton-1 > .toolbarbutton-menubutton-button,
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -125,16 +125,17 @@ browser.jar:
         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/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/bookmarksToolbar-menuPanel.png   (places/bookmarksToolbar-menuPanel.png)
         skin/classic/browser/places/bookmarks-notification-finish.png (places/bookmarks-notification-finish.png)
+        skin/classic/browser/places/bookmarks-menu-arrow.png         (places/bookmarks-menu-arrow.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)
         skin/classic/browser/places/unstarred48.png                  (places/unstarred48.png)
         skin/classic/browser/places/tag.png                          (places/tag.png)
         skin/classic/browser/places/history.png                      (places/history.png)
@@ -453,16 +454,17 @@ browser.jar:
 *       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/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/bookmarksToolbar-menuPanel.png (places/bookmarksToolbar-menuPanel-aero.png)
         skin/classic/aero/browser/places/bookmarks-notification-finish.png   (places/bookmarks-notification-finish.png)
+        skin/classic/aero/browser/places/bookmarks-menu-arrow.png    (places/bookmarks-menu-arrow.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)
         skin/classic/aero/browser/places/unstarred48.png             (places/unstarred48.png)
         skin/classic/aero/browser/places/tag.png                     (places/tag-aero.png)
         skin/classic/aero/browser/places/history.png                 (places/history-aero.png)
new file mode 100644
index 0000000000000000000000000000000000000000..616f16b7fface8c53d164bb8fd7a221d646b4bc7
GIT binary patch
literal 183
zc%17D@N?(olHy`uVBq!ia0vp^3P3Et!3HGD8EPYeRGp`bV~EG`<OB)UW>E<V366k8
z`xh>3RQk&!B_$PZYHIrQ<kkC^ZWV4_y3}=^5c?nYN8+zuy;6H(x!|+?1wnnOs3;$e
zLI#d{R;xqvZP-ix<>$TpaqGsVhyI<+4nOoS2v$C^V0hKB-&c2TrSmmLW+_(YSsd(U
h(tH(;CR{IA8BR~l5VXvgyB6pY22WQ%mvv4FO#q;wKPUhI
--- a/build/valgrind/mach_commands.py
+++ b/build/valgrind/mach_commands.py
@@ -100,17 +100,17 @@ class MachCommands(MachCommandBase):
             if not os.path.exists(valgrind):
                 valgrind = findInPath(valgrind)
 
             valgrind_args = [
                 valgrind,
                 '--smc-check=all-non-file',
                 '--vex-iropt-register-updates=allregs-at-mem-access',
                 '--gen-suppressions=all',
-                '--num-callers=20',
+                '--num-callers=36',
                 '--leak-check=full',
                 '--show-possibly-lost=no',
                 '--track-origins=yes'
             ]
 
             for s in suppressions:
                 valgrind_args.append('--suppressions=' + s)
 
--- a/content/canvas/src/WebGLFramebuffer.cpp
+++ b/content/canvas/src/WebGLFramebuffer.cpp
@@ -91,16 +91,27 @@ WebGLFramebuffer::Attachment::IsReadable
 
 void
 WebGLFramebuffer::Attachment::SetTexImage(WebGLTexture* tex, GLenum target, GLint level)
 {
     mTexturePtr = tex;
     mRenderbufferPtr = nullptr;
     mTexImageTarget = target;
     mTexImageLevel = level;
+
+    mNeedsFinalize = true;
+}
+
+void
+WebGLFramebuffer::Attachment::SetRenderbuffer(WebGLRenderbuffer* rb)
+{
+    mTexturePtr = nullptr;
+    mRenderbufferPtr = rb;
+
+    mNeedsFinalize = true;
 }
 
 bool
 WebGLFramebuffer::Attachment::HasUninitializedImageData() const
 {
     if (!HasImage())
         return false;
 
@@ -310,22 +321,41 @@ WebGLFramebuffer::Attachment::IsComplete
         return false;
     }
 
     MOZ_ASSERT(false, "Should not get here.");
     return false;
 }
 
 void
-WebGLFramebuffer::Attachment::FinalizeAttachment(GLenum attachmentLoc) const
+WebGLFramebuffer::Attachment::FinalizeAttachment(GLContext* gl, GLenum attachmentLoc) const
 {
+    if (!mNeedsFinalize)
+        return;
+
+    mNeedsFinalize = false;
+
+    if (!HasImage()) {
+        if (attachmentLoc == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
+            gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT,
+                                         LOCAL_GL_RENDERBUFFER, 0);
+            gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_STENCIL_ATTACHMENT,
+                                         LOCAL_GL_RENDERBUFFER, 0);
+        } else {
+            gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, attachmentLoc,
+                                         LOCAL_GL_RENDERBUFFER, 0);
+        }
+
+        return;
+    }
     MOZ_ASSERT(HasImage());
 
     if (Texture()) {
-        GLContext* gl = Texture()->Context()->gl;
+        MOZ_ASSERT(gl == Texture()->Context()->gl);
+
         if (attachmentLoc == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
             gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT,
                                       TexImageTarget(), Texture()->GLName(), TexImageLevel());
             gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_STENCIL_ATTACHMENT,
                                       TexImageTarget(), Texture()->GLName(), TexImageLevel());
         } else {
             gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, attachmentLoc,
                                       TexImageTarget(), Texture()->GLName(), TexImageLevel());
@@ -827,32 +857,28 @@ FinalizeDrawAndReadBuffers(GLContext* aG
     GLenum colorBufferSource = aColorBufferDefined ? LOCAL_GL_COLOR_ATTACHMENT0 : LOCAL_GL_NONE;
     aGL->fDrawBuffer(colorBufferSource);
     aGL->fReadBuffer(colorBufferSource);
 }
 
 void
 WebGLFramebuffer::FinalizeAttachments() const
 {
+    GLContext* gl = mContext->gl;
+
     size_t count = ColorAttachmentCount();
     for (size_t i = 0; i < count; i++) {
-        if (ColorAttachment(i).IsDefined())
-            ColorAttachment(i).FinalizeAttachment(LOCAL_GL_COLOR_ATTACHMENT0 + i);
+        ColorAttachment(i).FinalizeAttachment(gl, LOCAL_GL_COLOR_ATTACHMENT0 + i);
     }
 
-    if (DepthAttachment().IsDefined())
-        DepthAttachment().FinalizeAttachment(LOCAL_GL_DEPTH_ATTACHMENT);
+    DepthAttachment().FinalizeAttachment(gl, LOCAL_GL_DEPTH_ATTACHMENT);
+    StencilAttachment().FinalizeAttachment(gl, LOCAL_GL_STENCIL_ATTACHMENT);
+    DepthStencilAttachment().FinalizeAttachment(gl, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT);
 
-    if (StencilAttachment().IsDefined())
-        StencilAttachment().FinalizeAttachment(LOCAL_GL_STENCIL_ATTACHMENT);
-
-    if (DepthStencilAttachment().IsDefined())
-        DepthStencilAttachment().FinalizeAttachment(LOCAL_GL_DEPTH_STENCIL_ATTACHMENT);
-
-    FinalizeDrawAndReadBuffers(mContext->gl, ColorAttachment(0).IsDefined());
+    FinalizeDrawAndReadBuffers(gl, ColorAttachment(0).IsDefined());
 }
 
 inline void
 ImplCycleCollectionUnlink(mozilla::WebGLFramebuffer::Attachment& aField)
 {
     aField.mTexturePtr = nullptr;
     aField.mRenderbufferPtr = nullptr;
 }
--- a/content/canvas/src/WebGLFramebuffer.h
+++ b/content/canvas/src/WebGLFramebuffer.h
@@ -36,35 +36,35 @@ public:
     struct Attachment
     {
         // deleting a texture or renderbuffer immediately detaches it
         WebGLRefPtr<WebGLTexture> mTexturePtr;
         WebGLRefPtr<WebGLRenderbuffer> mRenderbufferPtr;
         GLenum mAttachmentPoint;
         GLenum mTexImageTarget;
         GLint mTexImageLevel;
+        mutable bool mNeedsFinalize;
 
         Attachment(GLenum aAttachmentPoint = LOCAL_GL_COLOR_ATTACHMENT0)
             : mAttachmentPoint(aAttachmentPoint)
+            , mNeedsFinalize(false)
         {}
 
         bool IsDefined() const {
             return Texture() || Renderbuffer();
         }
 
         bool IsDeleteRequested() const;
 
         bool HasAlpha() const;
         bool IsReadableFloat() const;
 
         void SetTexImage(WebGLTexture* tex, GLenum target, GLint level);
-        void SetRenderbuffer(WebGLRenderbuffer* rb) {
-            mTexturePtr = nullptr;
-            mRenderbufferPtr = rb;
-        }
+        void SetRenderbuffer(WebGLRenderbuffer* rb);
+
         const WebGLTexture* Texture() const {
             return mTexturePtr;
         }
         WebGLTexture* Texture() {
             return mTexturePtr;
         }
         const WebGLRenderbuffer* Renderbuffer() const {
             return mRenderbufferPtr;
@@ -87,17 +87,17 @@ public:
             mRenderbufferPtr = nullptr;
         }
 
         const WebGLRectangleObject& RectangleObject() const;
 
         bool HasImage() const;
         bool IsComplete() const;
 
-        void FinalizeAttachment(GLenum attachmentLoc) const;
+        void FinalizeAttachment(gl::GLContext* gl, GLenum attachmentLoc) const;
     };
 
     void Delete();
 
     bool HasEverBeenBound() { return mHasEverBeenBound; }
     void SetHasEverBeenBound(bool x) { mHasEverBeenBound = x; }
     GLuint GLName() { return mGLName; }
 
--- a/content/media/BufferMediaResource.h
+++ b/content/media/BufferMediaResource.h
@@ -137,16 +137,34 @@ public:
 
   bool IsTransportSeekable() MOZ_OVERRIDE { return true; }
 
   virtual const nsCString& GetContentType() const MOZ_OVERRIDE
   {
     return mContentType;
   }
 
+  virtual size_t SizeOfExcludingThis(
+                        MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    // Not owned:
+    // - mBuffer
+    // - mPrincipal
+    size_t size = MediaResource::SizeOfExcludingThis(aMallocSizeOf);
+    size += mContentType.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+
+    return size;
+  }
+
+  virtual size_t SizeOfIncludingThis(
+                        MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
 private:
   const uint8_t * mBuffer;
   uint32_t mLength;
   uint32_t mOffset;
   nsCOMPtr<nsIPrincipal> mPrincipal;
   const nsAutoCString mContentType;
 };
 
--- a/content/media/MediaCache.cpp
+++ b/content/media/MediaCache.cpp
@@ -381,16 +381,38 @@ MediaCacheStream::MediaCacheStream(Chann
     mPlaybackBytesPerSecond(10000),
     mPinCount(0),
     mCurrentMode(MODE_PLAYBACK),
     mMetadataInPartialBlockBuffer(false),
     mPartialBlockBuffer(new int64_t[BLOCK_SIZE/sizeof(int64_t)])
 {
 }
 
+size_t MediaCacheStream::SizeOfExcludingThis(
+                                MallocSizeOf aMallocSizeOf) const
+{
+  // Looks like these are not owned:
+  // - mClient
+  // - mPrincipal
+  size_t size = mBlocks.SizeOfExcludingThis(aMallocSizeOf);
+  size += mReadaheadBlocks.SizeOfExcludingThis(aMallocSizeOf);
+  size += mMetadataBlocks.SizeOfExcludingThis(aMallocSizeOf);
+  size += mPlayedBlocks.SizeOfExcludingThis(aMallocSizeOf);
+  size += mPartialBlockBuffer.SizeOfExcludingThis(aMallocSizeOf);
+
+  return size;
+}
+
+size_t MediaCacheStream::BlockList::SizeOfExcludingThis(
+                                MallocSizeOf aMallocSizeOf) const
+{
+  return mEntries.SizeOfExcludingThis(/* sizeOfEntryExcludingThis = */ nullptr,
+                                      aMallocSizeOf);
+}
+
 void MediaCacheStream::BlockList::AddFirstBlock(int32_t aBlock)
 {
   NS_ASSERTION(!mEntries.GetEntry(aBlock), "Block already in list");
   Entry* entry = mEntries.PutEntry(aBlock);
 
   if (mFirstBlock < 0) {
     entry->mNextBlock = entry->mPrevBlock = aBlock;
   } else {
--- a/content/media/MediaCache.h
+++ b/content/media/MediaCache.h
@@ -337,16 +337,19 @@ public:
   // be less than aCount. If the first byte of data is not in the cache,
   // this will block until the data is available or the stream is
   // closed, otherwise it won't block.
   nsresult Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes);
   // Seeks to aOffset in the stream then performs a Read operation. See
   // 'Read' for argument and return details.
   nsresult ReadAt(int64_t aOffset, char* aBuffer,
                   uint32_t aCount, uint32_t* aBytes);
+
+  size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const;
+
 private:
   friend class MediaCache;
 
   /**
    * A doubly-linked list of blocks. Add/Remove/Get methods are all
    * constant time. We declare this here so that a stream can contain a
    * BlockList of its read-ahead blocks. Blocks are referred to by index
    * into the MediaCache::mIndex array.
@@ -380,16 +383,18 @@ private:
     void NotifyBlockSwapped(int32_t aBlockIndex1, int32_t aBlockIndex2);
 #ifdef DEBUG
     // Verify linked-list invariants
     void Verify();
 #else
     void Verify() {}
 #endif
 
+    size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
   private:
     struct Entry : public nsUint32HashKey {
       Entry(KeyTypePointer aKey) : nsUint32HashKey(aKey) { }
       Entry(const Entry& toCopy) : nsUint32HashKey(&toCopy.GetKey()),
         mNextBlock(toCopy.mNextBlock), mPrevBlock(toCopy.mPrevBlock) {}
 
       int32_t mNextBlock;
       int32_t mPrevBlock;
--- a/content/media/MediaDecoder.cpp
+++ b/content/media/MediaDecoder.cpp
@@ -54,16 +54,18 @@ PRLogModuleInfo* gMediaDecoderLog;
 #define DECODER_LOG(type, msg)
 #endif
 
 class MediaMemoryTracker : public nsIMemoryReporter
 {
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIMEMORYREPORTER
 
+  MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf);
+
   MediaMemoryTracker();
   virtual ~MediaMemoryTracker();
   void InitMemoryReporter();
 
   static StaticRefPtr<MediaMemoryTracker> sUniqueInstance;
 
   static MediaMemoryTracker* UniqueInstance() {
     if (!sUniqueInstance) {
@@ -1825,37 +1827,47 @@ MediaDecoder::IsAppleMP3Enabled()
 }
 #endif
 
 NS_IMETHODIMP
 MediaMemoryTracker::CollectReports(nsIHandleReportCallback* aHandleReport,
                                    nsISupports* aData)
 {
   int64_t video = 0, audio = 0;
+  size_t resources = 0;
   DecodersArray& decoders = Decoders();
   for (size_t i = 0; i < decoders.Length(); ++i) {
-    video += decoders[i]->VideoQueueMemoryInUse();
-    audio += decoders[i]->SizeOfAudioQueue();
+    MediaDecoder* decoder = decoders[i];
+    video += decoder->VideoQueueMemoryInUse();
+    audio += decoder->SizeOfAudioQueue();
+
+    if (decoder->GetResource()) {
+      resources += decoder->GetResource()->SizeOfIncludingThis(MallocSizeOf);
+    }
   }
 
 #define REPORT(_path, _amount, _desc)                                         \
   do {                                                                        \
       nsresult rv;                                                            \
       rv = aHandleReport->Callback(EmptyCString(), NS_LITERAL_CSTRING(_path), \
                                    KIND_HEAP, UNITS_BYTES, _amount,           \
                                    NS_LITERAL_CSTRING(_desc), aData);         \
       NS_ENSURE_SUCCESS(rv, rv);                                              \
   } while (0)
 
-  REPORT("explicit/media/decoded-video", video,
+  REPORT("explicit/media/decoded/video", video,
          "Memory used by decoded video frames.");
 
-  REPORT("explicit/media/decoded-audio", audio,
+  REPORT("explicit/media/decoded/audio", audio,
          "Memory used by decoded audio chunks.");
 
+  REPORT("explicit/media/resources", resources,
+         "Memory used by media resources including streaming buffers, caches, "
+         "etc.");
+
   return NS_OK;
 }
 
 MediaDecoderOwner*
 MediaDecoder::GetOwner()
 {
   MOZ_ASSERT(NS_IsMainThread());
   return mOwner;
--- a/content/media/MediaResource.cpp
+++ b/content/media/MediaResource.cpp
@@ -1243,16 +1243,30 @@ public:
   }
   virtual bool    IsDataCachedToEndOfResource(int64_t aOffset) { return true; }
   virtual bool    IsSuspendedByCache() { return false; }
   virtual bool    IsSuspended() { return false; }
   virtual bool    IsTransportSeekable() MOZ_OVERRIDE { return true; }
 
   nsresult GetCachedRanges(nsTArray<MediaByteRange>& aRanges);
 
+  virtual size_t SizeOfExcludingThis(
+                        MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    // Might be useful to track in the future:
+    // - mInput
+    return BaseMediaResource::SizeOfExcludingThis(aMallocSizeOf);
+  }
+
+  virtual size_t SizeOfIncludingThis(
+                        MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
 protected:
   // These Unsafe variants of Read and Seek perform their operations
   // without acquiring mLock. The caller must obtain the lock before
   // calling. The implmentation of Read, Seek and ReadAt obtains the
   // lock before calling these Unsafe variants to read or seek.
   nsresult UnsafeRead(char* aBuffer, uint32_t aCount, uint32_t* aBytes);
   nsresult UnsafeSeek(int32_t aWhence, int64_t aOffset);
 private:
--- a/content/media/MediaResource.h
+++ b/content/media/MediaResource.h
@@ -388,28 +388,56 @@ public:
     return nullptr;
   }
 
   // Return true if the stream is a live stream
   virtual bool IsRealTime() {
     return false;
   }
 
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
+    return 0;
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
 protected:
   virtual ~MediaResource() {};
 
 private:
   void Destroy();
 };
 
 class BaseMediaResource : public MediaResource {
 public:
   virtual nsIURI* URI() const { return mURI; }
   virtual void MoveLoadsToBackground();
 
+  virtual size_t SizeOfExcludingThis(
+                  MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    // Might be useful to track in the future:
+    // - mChannel
+    // - mURI (possibly owned, looks like just a ref from mChannel)
+    // Not owned:
+    // - mDecoder
+    size_t size = MediaResource::SizeOfExcludingThis(aMallocSizeOf);
+    size += mContentType.SizeOfIncludingThisIfUnshared(aMallocSizeOf);
+
+    return size;
+  }
+
+  virtual size_t SizeOfIncludingThis(
+                  MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
 protected:
   BaseMediaResource(MediaDecoder* aDecoder,
                     nsIChannel* aChannel,
                     nsIURI* aURI,
                     const nsACString& aContentType) :
     mDecoder(aDecoder),
     mChannel(aChannel),
     mURI(aURI),
@@ -551,16 +579,34 @@ public:
   virtual int64_t GetLength();
   virtual int64_t GetNextCachedData(int64_t aOffset);
   virtual int64_t GetCachedDataEnd(int64_t aOffset);
   virtual bool    IsDataCachedToEndOfResource(int64_t aOffset);
   virtual bool    IsSuspendedByCache();
   virtual bool    IsSuspended();
   virtual bool    IsTransportSeekable() MOZ_OVERRIDE;
 
+  virtual size_t SizeOfExcludingThis(
+                      MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE {
+    // Might be useful to track in the future:
+    //   - mListener (seems minor)
+    //   - mChannelStatistics (seems minor)
+    //     owned if RecordStatisticsTo is not called
+    //   - mDataReceivedEvent (seems minor)
+    size_t size = BaseMediaResource::SizeOfExcludingThis(aMallocSizeOf);
+    size += mCacheStream.SizeOfExcludingThis(aMallocSizeOf);
+
+    return size;
+  }
+
+  virtual size_t SizeOfIncludingThis(
+                      MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
   class Listener MOZ_FINAL : public nsIStreamListener,
                              public nsIInterfaceRequestor,
                              public nsIChannelEventSink
   {
   public:
     Listener(ChannelMediaResource* aResource) : mResource(aResource) {}
     ~Listener() {}
 
--- a/content/media/MediaStreamGraph.cpp
+++ b/content/media/MediaStreamGraph.cpp
@@ -1139,28 +1139,42 @@ MediaStreamGraphImpl::ResumeAllAudioOutp
   for (uint32_t i = 0; i < mStreams.Length(); ++i) {
     MediaStream* s = mStreams[i];
     for (uint32_t j = 0; j < s->mAudioOutputStreams.Length(); ++j) {
       s->mAudioOutputStreams[j].mStream->Resume();
     }
   }
 }
 
+struct AutoProfilerUnregisterThread
+{
+  // The empty ctor is used to silence a pre-4.8.0 GCC unused variable warning.
+  AutoProfilerUnregisterThread()
+  {
+  }
+
+  ~AutoProfilerUnregisterThread()
+  {
+    profiler_unregister_thread();
+  }
+};
+
 void
 MediaStreamGraphImpl::RunThread()
 {
   nsTArray<MessageBlock> messageQueue;
   {
     MonitorAutoLock lock(mMonitor);
     messageQueue.SwapElements(mMessageQueue);
   }
   NS_ASSERTION(!messageQueue.IsEmpty(),
                "Shouldn't have started a graph with empty message queue!");
 
   uint32_t ticksProcessed = 0;
+  AutoProfilerUnregisterThread autoUnregister;
 
   for (;;) {
     // Update mCurrentTime to the min of the playing audio times, or using the
     // wall-clock time change if no audio is playing.
     UpdateCurrentTime();
 
     // Calculate independent action times for each batch of messages (each
     // batch corresponding to an event loop task). This isolates the performance
@@ -1315,18 +1329,16 @@ MediaStreamGraphImpl::RunThread()
           ResumeAllAudioOutputs();
         }
       }
       mWaitState = WAITSTATE_RUNNING;
       mNeedAnotherIteration = false;
       messageQueue.SwapElements(mMessageQueue);
     }
   }
-
-  profiler_unregister_thread();
 }
 
 void
 MediaStreamGraphImpl::ApplyStreamUpdate(StreamUpdate* aUpdate)
 {
   mMonitor.AssertCurrentThreadOwns();
 
   MediaStream* stream = aUpdate->mStream;
--- a/content/media/RtspMediaResource.cpp
+++ b/content/media/RtspMediaResource.cpp
@@ -64,16 +64,27 @@ public:
     MOZ_ASSERT(mSlotSize < UINT32_MAX / BUFFER_SLOT_NUM);
     mRingBuffer = new uint8_t[mTotalBufferSize];
     Reset();
   };
   ~RtspTrackBuffer() {
     MOZ_COUNT_DTOR(RtspTrackBuffer);
     mRingBuffer = nullptr;
   };
+
+  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
+    // including this
+    size_t size = aMallocSizeOf(this);
+
+    // excluding this
+    size += mRingBuffer.SizeOfExcludingThis(aMallocSizeOf);
+
+    return size;
+  }
+
   void Start() {
     MonitorAutoLock monitor(mMonitor);
     mIsStarted = true;
     mFrameType = 0;
   }
   void Stop() {
     MonitorAutoLock monitor(mMonitor);
     mIsStarted = false;
@@ -364,16 +375,33 @@ RtspMediaResource::~RtspMediaResource()
 {
   RTSPMLOG("~RtspMediaResource");
   if (mListener) {
     // Kill its reference to us since we're going away
     mListener->Revoke();
   }
 }
 
+size_t
+RtspMediaResource::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  size_t size = BaseMediaResource::SizeOfExcludingThis(aMallocSizeOf);
+  size += mTrackBuffer.SizeOfExcludingThis(aMallocSizeOf);
+
+  // Include the size of each track buffer.
+  for (size_t i = 0; i < mTrackBuffer.Length(); i++) {
+    size += mTrackBuffer[i]->SizeOfIncludingThis(aMallocSizeOf);
+  }
+
+  // Could add in the future:
+  // - mMediaStreamController
+
+  return size;
+}
+
 NS_IMPL_ISUPPORTS2(RtspMediaResource::Listener,
                    nsIInterfaceRequestor, nsIStreamingProtocolListener);
 
 nsresult
 RtspMediaResource::Listener::OnMediaDataAvailable(uint8_t aTrackIdx,
                                                   const nsACString &data,
                                                   uint32_t length,
                                                   uint32_t offset,
--- a/content/media/RtspMediaResource.h
+++ b/content/media/RtspMediaResource.h
@@ -180,16 +180,24 @@ public:
     return nullptr;
   }
   // dummy
   virtual nsresult ReadFromCache(char* aBuffer, int64_t aOffset,
                                  uint32_t aCount) MOZ_OVERRIDE {
     return NS_ERROR_FAILURE;
   }
 
+  virtual size_t SizeOfExcludingThis(
+                      MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+
+  virtual size_t SizeOfIncludingThis(
+                      MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
   // Listener implements nsIStreamingProtocolListener as
   // mMediaStreamController's callback function.
   // It holds RtspMediaResource reference to notify the connection status and
   // data arrival. The Revoke function releases the reference when
   // RtspMediaResource is destroyed.
   class Listener MOZ_FINAL : public nsIInterfaceRequestor,
                              public nsIStreamingProtocolListener
   {
--- a/content/media/mediasource/MediaSourceDecoder.h
+++ b/content/media/mediasource/MediaSourceDecoder.h
@@ -128,15 +128,30 @@ public:
   {
     aRanges.AppendElement(MediaByteRange(0, GetLength()));
     return NS_OK;
   }
 
   virtual bool IsTransportSeekable() MOZ_OVERRIDE { return true; }
   virtual const nsCString& GetContentType() const MOZ_OVERRIDE { return mType; }
 
+  virtual size_t SizeOfExcludingThis(
+                      MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    size_t size = MediaResource::SizeOfExcludingThis(aMallocSizeOf);
+    size += mType.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+
+    return size;
+  }
+
+  virtual size_t SizeOfIncludingThis(
+                      MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
 private:
   const nsAutoCString mType;
 };
 
 } // namespace mozilla
 
 #endif /* MOZILLA_MEDIASOURCEDECODER_H_ */
--- a/content/media/mediasource/SourceBufferResource.h
+++ b/content/media/mediasource/SourceBufferResource.h
@@ -47,16 +47,26 @@ private:
   // (done in SourceBuffer::AppendData) which then requests
   // all SourceBuffers to evict data up to approximately
   // the same timepoint.
   struct ResourceItem {
     ResourceItem(uint8_t const* aData, uint32_t aSize) {
       mData.AppendElements(aData, aSize);
     }
     nsTArray<uint8_t> mData;
+
+    size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
+      // size including this
+      size_t size = aMallocSizeOf(this);
+
+      // size excluding this
+      size += mData.SizeOfExcludingThis(aMallocSizeOf);
+
+      return size;
+    }
   };
 
   class ResourceQueueDeallocator : public nsDequeFunctor {
     virtual void* operator() (void* anObject) {
       delete static_cast<ResourceItem*>(anObject);
       return nullptr;
     }
   };
@@ -170,16 +180,30 @@ private:
           break;
         }
         mOffset += item->mData.Length();
         delete PopFront();
         evicted = true;
       }
       return evicted;
     }
+
+    size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
+      // Calculate the size of the internal deque.
+      size_t size = nsDeque::SizeOfExcludingThis(aMallocSizeOf);
+
+      // Sum the ResourceItems.
+      for (int32_t i = 0; i < nsDeque::GetSize(); ++i) {
+        const ResourceItem* item =
+            static_cast<const ResourceItem*>(nsDeque::ObjectAt(i));
+        size += item->SizeOfIncludingThis(aMallocSizeOf);
+      }
+
+      return size;
+    }
   };
 
 public:
   SourceBufferResource(nsIPrincipal* aPrincipal,
                        const nsACString& aType);
   ~SourceBufferResource();
 
   virtual nsresult Close() MOZ_OVERRIDE;
@@ -221,16 +245,36 @@ public:
   {
     aRanges.AppendElement(MediaByteRange(mInputBuffer.GetOffset(),
                                          mInputBuffer.GetLength()));
     return NS_OK;
   }
 
   virtual const nsCString& GetContentType() const MOZ_OVERRIDE { return mType; }
 
+  virtual size_t SizeOfExcludingThis(
+                      MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    ReentrantMonitorAutoEnter mon(mMonitor);
+
+    // Not owned:
+    // - mPrincipal
+    size_t size = MediaResource::SizeOfExcludingThis(aMallocSizeOf);
+    size += mType.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+    size += mInputBuffer.SizeOfExcludingThis(aMallocSizeOf);
+
+    return size;
+  }
+
+  virtual size_t SizeOfIncludingThis(
+                      MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
   // Used by SourceBuffer.
   void AppendData(const uint8_t* aData, uint32_t aLength);
   void Ended();
   // Remove data from resource if it holds more than the threshold
   // number of bytes. Returns true if some data was evicted.
   bool EvictData(uint32_t aThreshold);
 
   // Remove data from resource before the given offset.
@@ -239,17 +283,17 @@ public:
 private:
   nsCOMPtr<nsIPrincipal> mPrincipal;
   const nsAutoCString mType;
 
   // Provides synchronization between SourceBuffers and InputAdapters.
   // Protects all of the member variables below.  Read() will await a
   // Notify() (from Seek, AppendData, Ended, or Close) when insufficient
   // data is available in mData.
-  ReentrantMonitor mMonitor;
+  mutable ReentrantMonitor mMonitor;
 
   // The buffer holding resource data is a queue of ResourceItem's.
   ResourceQueue mInputBuffer;
 
   uint64_t mOffset;
   bool mClosed;
   bool mEnded;
 };
--- a/content/media/ogg/OggWriter.cpp
+++ b/content/media/ogg/OggWriter.cpp
@@ -17,16 +17,24 @@ namespace mozilla {
 
 OggWriter::OggWriter() : ContainerWriter()
 {
   if (NS_FAILED(Init())) {
     LOG("ERROR! Fail to initialize the OggWriter.");
   }
 }
 
+OggWriter::~OggWriter()
+{
+  if (mInitialized) {
+    ogg_stream_clear(&mOggStreamState);
+  }
+  // mPacket's data was always owned by us, no need to ogg_packet_clear.
+}
+
 nsresult
 OggWriter::Init()
 {
   MOZ_ASSERT(!mInitialized);
 
   // The serial number (serialno) should be a random number, for the current
   // implementation where the output file contains only a single stream, this
   // serialno is used to differentiate between files.
--- a/content/media/ogg/OggWriter.h
+++ b/content/media/ogg/OggWriter.h
@@ -17,16 +17,17 @@ namespace mozilla {
  * to the Ogg stream.
  * For more details, please reference:
  * http://www.xiph.org/ogg/doc/libogg/encoding.html
  */
 class OggWriter : public ContainerWriter
 {
 public:
   OggWriter();
+  ~OggWriter();
 
   nsresult WriteEncodedTrack(const EncodedFrameContainer &aData,
                              uint32_t aFlags = 0) MOZ_OVERRIDE;
 
   nsresult GetContainerData(nsTArray<nsTArray<uint8_t> >* aOutputBufs,
                             uint32_t aFlags = 0) MOZ_OVERRIDE;
 
   // Check metadata type integrity and reject unacceptable track encoder.
--- a/content/svg/content/src/nsSVGElement.cpp
+++ b/content/svg/content/src/nsSVGElement.cpp
@@ -1200,17 +1200,17 @@ MappedAttrParser::ParseMappedAttrValue(n
   if (!mDecl) {
     mDecl = new css::Declaration();
     mDecl->InitializeEmpty();
   }
 
   // Get the nsCSSProperty ID for our mapped attribute.
   nsCSSProperty propertyID =
     nsCSSProps::LookupProperty(nsDependentAtomString(aMappedAttrName),
-                               nsCSSProps::eEnabledForAllContent);
+                               nsCSSProps::eEnabled);
   if (propertyID != eCSSProperty_UNKNOWN) {
     bool changed; // outparam for ParseProperty. (ignored)
     mParser.ParseProperty(propertyID, aMappedAttrValue, mDocURI, mBaseURI,
                           mNodePrincipal, mDecl, &changed, false, true);
     return;
   }
   NS_ABORT_IF_FALSE(aMappedAttrName == nsGkAtoms::lang,
                     "Only 'lang' should be unrecognized!");
@@ -2534,17 +2534,17 @@ nsSVGElement::GetAnimatedAttr(int32_t aN
     // targeting width/height on outer-<svg> don't appear to be ignored
     // because we returned a nsISMILAttr for the corresponding
     // SVGAnimatedLength.
 
     // Mapped attributes:
     if (IsAttributeMapped(aName)) {
       nsCSSProperty prop =
         nsCSSProps::LookupProperty(nsDependentAtomString(aName),
-                                   nsCSSProps::eEnabledForAllContent);
+                                   nsCSSProps::eEnabled);
       // Check IsPropertyAnimatable to avoid attributes that...
       //  - map to explicitly unanimatable properties (e.g. 'direction')
       //  - map to unsupported attributes (e.g. 'glyph-orientation-horizontal')
       if (nsSMILCSSProperty::IsPropertyAnimatable(prop)) {
         return new nsSMILMappedAttribute(prop, this);
       }
     }
 
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -2662,18 +2662,18 @@ nsDOMWindowUtils::ComputeAnimationDistan
   }
 
   nsresult rv;
   nsCOMPtr<nsIContent> content = do_QueryInterface(aElement, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Convert direction-dependent properties as appropriate, e.g.,
   // border-left to border-left-value.
-  nsCSSProperty property =
-    nsCSSProps::LookupProperty(aProperty, nsCSSProps::eIgnoreEnabledState);
+  nsCSSProperty property = nsCSSProps::LookupProperty(aProperty,
+                                                      nsCSSProps::eAny);
   if (property != eCSSProperty_UNKNOWN && nsCSSProps::IsShorthand(property)) {
     nsCSSProperty subprop0 = *nsCSSProps::SubpropertyEntryFor(property);
     if (nsCSSProps::PropHasFlags(subprop0, CSS_PROPERTY_REPORT_OTHER_NAME) &&
         nsCSSProps::OtherNameFor(subprop0) == property) {
       property = subprop0;
     } else {
       property = eCSSProperty_UNKNOWN;
     }
--- a/dom/bindings/GenerateCSS2PropertiesWebIDL.py
+++ b/dom/bindings/GenerateCSS2PropertiesWebIDL.py
@@ -2,28 +2,19 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this file,
 # You can obtain one at http://mozilla.org/MPL/2.0/.
 
 import sys
 import string
 
 propList = eval(sys.stdin.read())
 props = ""
-for [prop, id, flags, pref] in propList:
+for [prop, pref] in propList:
     extendedAttrs = ["Throws", "TreatNullAs=EmptyString"]
-    # To limit the overhead of Func= annotations, we only generate them when
-    # necessary, which is when the
-    # CSS_PROPERTY_ALWAYS_ENABLED_IN_CHROME_OR_CERTIFIED_APP flag is set.
-    # Otherwise, we try to get by with just a Pref= annotation or no annotation
-    # at all.
-    if "CSS_PROPERTY_ALWAYS_ENABLED_IN_CHROME_OR_CERTIFIED_APP" in flags:
-        extendedAttrs.append('Func="IsCSSPropertyExposedToJS<eCSSProperty_%s>"' % id)
-    # The following is an 'elif' because it is the responsibility of
-    # IsCSSPropertyExposedToJS to handle the pref if there is one.
-    elif pref is not "":
+    if pref is not "":
         extendedAttrs.append('Pref="%s"' % pref)
     if not prop.startswith("Moz"):
         prop = prop[0].lower() + prop[1:]
     # Unfortunately, even some of the getters here are fallible
     # (e.g. on nsComputedDOMStyle).
     props += "  [%s] attribute DOMString %s;\n" % (", ".join(extendedAttrs),
                                                    prop)
 
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -282,16 +282,17 @@ TabChild::TabChild(ContentChild* aManage
   , mDidFakeShow(false)
   , mNotified(false)
   , mContentDocumentIsDisplayed(false)
   , mTriedBrowserInit(false)
   , mOrientation(eScreenOrientation_PortraitPrimary)
   , mUpdateHitRegion(false)
   , mContextMenuHandled(false)
   , mWaitingTouchListeners(false)
+  , mIgnoreKeyPressEvent(false)
 {
   if (!sActiveDurationMsSet) {
     Preferences::AddIntVarCache(&sActiveDurationMs,
                                 "ui.touch_activation.duration_ms",
                                 sActiveDurationMs);
     sActiveDurationMsSet = true;
   }
 }
@@ -1905,18 +1906,29 @@ TabChild::RecvRealTouchMoveEvent(const W
                                  const ScrollableLayerGuid& aGuid)
 {
   return RecvRealTouchEvent(aEvent, aGuid);
 }
 
 bool
 TabChild::RecvRealKeyEvent(const WidgetKeyboardEvent& event)
 {
+  // If content code called preventDefault() on a keydown event, then we don't
+  // want to process any following keypress events.
+  if (event.message == NS_KEY_PRESS && mIgnoreKeyPressEvent) {
+    return true;
+  }
+
   WidgetKeyboardEvent localEvent(event);
-  DispatchWidgetEvent(localEvent);
+  nsEventStatus status = DispatchWidgetEvent(localEvent);
+
+  if (event.message == NS_KEY_DOWN) {
+    mIgnoreKeyPressEvent = status == nsEventStatus_eConsumeNoDefault;
+  }
+
   return true;
 }
 
 bool
 TabChild::RecvKeyEvent(const nsString& aType,
                        const int32_t& aKeyCode,
                        const int32_t& aCharCode,
                        const int32_t& aModifiers,
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -503,15 +503,17 @@ private:
     bool mContentDocumentIsDisplayed;
     bool mTriedBrowserInit;
     ScreenOrientation mOrientation;
     bool mUpdateHitRegion;
     bool mContextMenuHandled;
     bool mWaitingTouchListeners;
     void FireSingleTapEvent(LayoutDevicePoint aPoint);
 
+    bool mIgnoreKeyPressEvent;
+
     DISALLOW_EVIL_CONSTRUCTORS(TabChild);
 };
 
 }
 }
 
 #endif // mozilla_dom_TabChild_h
--- a/dom/smil/nsSMILAnimationController.cpp
+++ b/dom/smil/nsSMILAnimationController.cpp
@@ -789,17 +789,17 @@ nsSMILAnimationController::GetTargetIden
       // width/height are special as they may be attributes or for
       // outer-<svg> elements, mapped into style.
       if (attributeName == nsGkAtoms::width ||
           attributeName == nsGkAtoms::height) {
         isCSS = targetElem->GetNameSpaceID() != kNameSpaceID_SVG;
       } else {
         nsCSSProperty prop =
           nsCSSProps::LookupProperty(nsDependentAtomString(attributeName),
-                                     nsCSSProps::eEnabledForAllContent);
+                                     nsCSSProps::eEnabled);
         isCSS = nsSMILCSSProperty::IsPropertyAnimatable(prop);
       }
     }
   } else {
     isCSS = (attributeType == eSMILTargetAttrType_CSS);
   }
 
   // Construct the key
--- a/dom/smil/nsSMILCompositor.cpp
+++ b/dom/smil/nsSMILCompositor.cpp
@@ -120,17 +120,17 @@ nsSMILCompositor::ClearAnimationEffects(
 // Protected Helper Functions
 // --------------------------
 nsISMILAttr*
 nsSMILCompositor::CreateSMILAttr()
 {
   if (mKey.mIsCSS) {
     nsCSSProperty propId =
       nsCSSProps::LookupProperty(nsDependentAtomString(mKey.mAttributeName),
-                                 nsCSSProps::eEnabledForAllContent);
+                                 nsCSSProps::eEnabled);
     if (nsSMILCSSProperty::IsPropertyAnimatable(propId)) {
       return new nsSMILCSSProperty(propId, mKey.mElement.get());
     }
   } else {
     return mKey.mElement->GetAnimatedAttr(mKey.mAttributeNamespaceID,
                                           mKey.mAttributeName);
   }
   return nullptr;
--- a/dom/webidl/CSS2PropertiesProps.h
+++ b/dom/webidl/CSS2PropertiesProps.h
@@ -1,39 +1,34 @@
 /* A file meant as input to the preprocessor only */
 
 /* DO_PROP serves as an extra level of indirection to allow expansion
    of CSS_PROP_DOMPROP_PREFIXED */
 
 [
 
-#define PROP_STRINGIFY_INTERNAL(X) #X
-#define PROP_STRINGIFY(X) PROP_STRINGIFY_INTERNAL(X)
-
-#define DO_PROP(method, id, flags, pref) \
-  [ #method, #id, PROP_STRINGIFY(flags), pref ],
+#define DO_PROP(method, pref) \
+  [ #method, pref ],
 #define CSS_PROP(name, id, method, flags, pref, parsevariant, kwtable, \
 		 stylestruct, stylestructofset, animtype) \
-  DO_PROP(method, id, flags, pref)
+  DO_PROP(method, pref)
 #define CSS_PROP_SHORTHAND(name, id, method, flags, pref) \
-  DO_PROP(method, id, flags, pref)
+  DO_PROP(method, pref)
 #define CSS_PROP_PUBLIC_OR_PRIVATE(publicname_, privatename_) publicname_
 #define CSS_PROP_LIST_EXCLUDE_INTERNAL
 
 #include "nsCSSPropList.h"
 
 #undef CSS_PROP_LIST_EXCLUDE_INTERNAL
 #undef CSS_PROP_PUBLIC_OR_PRIVATE
 #undef CSS_PROP_SHORTHAND
 #undef CSS_PROP
 
 #define CSS_PROP_ALIAS(name, id, method, pref) \
-  DO_PROP(method, id, 0, pref)
+  DO_PROP(method, pref)
 
 #include "nsCSSPropAliasList.h"
 
 #undef CSS_PROP_ALIAS
 
 #undef DO_PROP
-#undef PROP_STRINGIFY
-#undef PROP_STRINGIFY_INTERNAL
 
 ]
--- a/editor/libeditor/html/nsHTMLCSSUtils.cpp
+++ b/editor/libeditor/html/nsHTMLCSSUtils.cpp
@@ -560,17 +560,17 @@ nsHTMLCSSUtils::GetCSSInlinePropertyBase
 
   MOZ_ASSERT(aStyleType == eSpecified);
   nsRefPtr<css::StyleRule> rule = element->GetInlineStyleRule();
   if (!rule) {
     return NS_OK;
   }
   nsCSSProperty prop =
     nsCSSProps::LookupProperty(nsDependentAtomString(aProperty),
-                               nsCSSProps::eEnabledForAllContent);
+                               nsCSSProps::eEnabled);
   MOZ_ASSERT(prop != eCSSProperty_UNKNOWN);
   rule->GetDeclaration()->GetValue(prop, aValue);
 
   return NS_OK;
 }
 
 already_AddRefed<nsComputedDOMStyle>
 nsHTMLCSSUtils::GetComputedStyle(nsIDOMElement* aElement)
--- a/gfx/layers/Compositor.h
+++ b/gfx/layers/Compositor.h
@@ -12,16 +12,17 @@
 #include "mozilla/gfx/Point.h"          // for IntSize, Point
 #include "mozilla/gfx/Rect.h"           // for Rect, IntRect
 #include "mozilla/gfx/Types.h"          // for Float
 #include "mozilla/layers/CompositorTypes.h"  // for DiagnosticTypes, etc
 #include "mozilla/layers/LayersTypes.h"  // for LayersBackend
 #include "nsISupportsImpl.h"            // for MOZ_COUNT_CTOR, etc
 #include "nsRegion.h"
 #include <vector>
+#include "mozilla/WidgetUtils.h"
 
 /**
  * Different elements of a web pages are rendered into separate "layers" before
  * they are flattened into the final image that is brought to the screen.
  * See Layers.h for more informations about layers and why we use retained
  * structures.
  * Most of the documentation for layers is directly in the source code in the
  * form of doc comments. An overview can also be found in the the wiki:
@@ -196,16 +197,17 @@ public:
 class Compositor : public RefCounted<Compositor>
 {
 public:
   MOZ_DECLARE_REFCOUNTED_TYPENAME(Compositor)
   Compositor(PCompositorParent* aParent = nullptr)
     : mCompositorID(0)
     , mDiagnosticTypes(DIAGNOSTIC_NONE)
     , mParent(aParent)
+    , mScreenRotation(ROTATION_0)
   {
     MOZ_COUNT_CTOR(Compositor);
   }
   virtual ~Compositor()
   {
     MOZ_COUNT_DTOR(Compositor);
   }
 
@@ -475,16 +477,40 @@ public:
     }
     return fillRatio;
   }
 
   virtual CompositorBackendSpecificData* GetCompositorBackendSpecificData() {
     return nullptr;
   }
 
+  ScreenRotation GetScreenRotation() const {
+    return mScreenRotation;
+  }
+
+  void SetScreenRotation(ScreenRotation aRotation) {
+    mScreenRotation = aRotation;
+  }
+
+  // On b2g the clip rect is in the coordinate space of the physical screen
+  // independently of its rotation, while the coordinate space of the layers,
+  // on the other hand, depends on the screen orientation.
+  // This only applies to b2g as with other platforms, orientation is handled
+  // at the OS level rather than in Gecko.
+  gfx::Rect ClipRectInLayersCoordinates(gfx::Rect aClip) const {
+    switch (mScreenRotation) {
+      case ROTATION_90:
+      case ROTATION_270:
+        return gfx::Rect(aClip.y, aClip.x, aClip.height, aClip.width);
+      case ROTATION_0:
+      case ROTATION_180:
+      default:
+        return aClip;
+    }
+  }
 protected:
   void DrawDiagnosticsInternal(DiagnosticFlags aFlags,
                                const gfx::Rect& aVisibleRect,
                                const gfx::Rect& aClipRect,
                                const gfx::Matrix4x4& transform);
 
   bool ShouldDrawDiagnostics(DiagnosticFlags);
 
@@ -500,16 +526,18 @@ protected:
   /**
    * We keep track of the total number of pixels filled as we composite the
    * current frame. This value is an approximation and is not accurate,
    * especially in the presence of transforms.
    */
   size_t mPixelsPerFrame;
   size_t mPixelsFilled;
 
+  ScreenRotation mScreenRotation;
+
 private:
   static LayersBackend sBackend;
 
 };
 
 } // namespace layers
 } // namespace mozilla
 
--- a/gfx/layers/ImageContainer.cpp
+++ b/gfx/layers/ImageContainer.cpp
@@ -276,67 +276,16 @@ ImageContainer::LockCurrentImage()
   }
 
   EnsureActiveImage();
 
   nsRefPtr<Image> retval = mActiveImage;
   return retval.forget();
 }
 
-already_AddRefed<gfxASurface>
-ImageContainer::DeprecatedLockCurrentAsSurface(gfx::IntSize *aSize, Image** aCurrentImage)
-{
-  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-
-  if (mRemoteData) {
-    NS_ASSERTION(mRemoteDataMutex, "Should have remote data mutex when having remote data!");
-    mRemoteDataMutex->Lock();
-
-    EnsureActiveImage();
-
-    if (aCurrentImage) {
-      NS_IF_ADDREF(mActiveImage);
-      *aCurrentImage = mActiveImage.get();
-    }
-
-    if (!mActiveImage) {
-      return nullptr;
-    } 
-
-    if (mActiveImage->GetFormat() == ImageFormat::REMOTE_IMAGE_BITMAP) {
-      nsRefPtr<gfxImageSurface> newSurf =
-        new gfxImageSurface(mRemoteData->mBitmap.mData,
-                            ThebesIntSize(mRemoteData->mSize),
-                            mRemoteData->mBitmap.mStride,
-                            mRemoteData->mFormat == RemoteImageData::BGRX32 ?
-                                                   gfxImageFormat::ARGB32 :
-                                                   gfxImageFormat::RGB24);
-
-      *aSize = newSurf->GetSize().ToIntSize();
-    
-      return newSurf.forget();
-    }
-
-    *aSize = mActiveImage->GetSize();
-    return mActiveImage->DeprecatedGetAsSurface();
-  }
-
-  if (aCurrentImage) {
-    NS_IF_ADDREF(mActiveImage);
-    *aCurrentImage = mActiveImage.get();
-  }
-
-  if (!mActiveImage) {
-    return nullptr;
-  }
-
-  *aSize = mActiveImage->GetSize();
-  return mActiveImage->DeprecatedGetAsSurface();
-}
-
 TemporaryRef<gfx::SourceSurface>
 ImageContainer::LockCurrentAsSourceSurface(gfx::IntSize *aSize, Image** aCurrentImage)
 {
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
 
   if (mRemoteData) {
     NS_ASSERTION(mRemoteDataMutex, "Should have remote data mutex when having remote data!");
     mRemoteDataMutex->Lock();
--- a/gfx/layers/ImageContainer.h
+++ b/gfx/layers/ImageContainer.h
@@ -518,27 +518,16 @@ public:
    * may be shared accross processes, calling this function will make
    * a copy of the image data while holding the mRemoteDataMutex. If possible,
    * the lock methods should be used to avoid the copy, however this should be
    * avoided if the surface is required for a long period of time.
    */
   already_AddRefed<gfxASurface> DeprecatedGetCurrentAsSurface(gfx::IntSize* aSizeResult);
 
   /**
-   * This is similar to GetCurrentAsSurface, however this does not make a copy
-   * of the image data and requires the user to call UnlockCurrentImage when
-   * done with the image data. Once UnlockCurrentImage has been called the
-   * surface returned by this function is no longer valid! This works for any
-   * type of image. Optionally a pointer can be passed to receive the current
-   * image.
-   */
-  already_AddRefed<gfxASurface> DeprecatedLockCurrentAsSurface(gfx::IntSize* aSizeResult,
-                                                               Image** aCurrentImage = nullptr);
-
-  /**
    * Same as GetCurrentAsSurface but for Moz2D
    */
   TemporaryRef<gfx::SourceSurface> GetCurrentAsSourceSurface(gfx::IntSize* aSizeResult);
 
   /**
    * Same as LockCurrentAsSurface but for Moz2D
    */
   TemporaryRef<gfx::SourceSurface> LockCurrentAsSourceSurface(gfx::IntSize* aSizeResult,
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -1303,16 +1303,23 @@ public:
    */
   void Log(const char* aPrefix="");
   /**
    * Log information about just this layer manager itself to the NSPR
    * log (if enabled for "Layers").
    */
   void LogSelf(const char* aPrefix="");
 
+  // Print interesting information about this into aTo.  Internally
+  // used to implement Dump*() and Log*().  If subclasses have
+  // additional interesting properties, they should override this with
+  // an implementation that first calls the base implementation then
+  // appends additional info to aTo.
+  virtual nsACString& PrintInfo(nsACString& aTo, const char* aPrefix);
+
   static bool IsLogEnabled() { return LayerManager::IsLogEnabled(); }
 
   /**
    * Returns the current area of the layer (in layer-space coordinates)
    * marked as needed to be recomposited.
    */
   const nsIntRegion& GetInvalidRegion() { return mInvalidRegion; }
   const void SetInvalidRegion(const nsIntRegion& aRect) { mInvalidRegion = aRect; }
@@ -1346,23 +1353,16 @@ public:
   void Mutated()
   {
     mManager->Mutated(this);
   }
 
 protected:
   Layer(LayerManager* aManager, void* aImplData);
 
-  // Print interesting information about this into aTo.  Internally
-  // used to implement Dump*() and Log*().  If subclasses have
-  // additional interesting properties, they should override this with
-  // an implementation that first calls the base implementation then
-  // appends additional info to aTo.
-  virtual nsACString& PrintInfo(nsACString& aTo, const char* aPrefix);
-
   /**
    * We can snap layer transforms for two reasons:
    * 1) To avoid unnecessary resampling when a transform is a translation
    * by a non-integer number of pixels.
    * Snapping the translation to an integer number of pixels avoids
    * blurring the layer and can be faster to composite.
    * 2) When a layer is used to render a rectangular object, we need to
    * emulate the rendering of rectangular inactive content and snap the
--- a/gfx/layers/composite/APZCTreeManager.cpp
+++ b/gfx/layers/composite/APZCTreeManager.cpp
@@ -398,17 +398,17 @@ APZCTreeManager::ReceiveInputEvent(const
         } else {
           NS_WARNING("Got an unexpected touchend/touchcancel");
           mTouchCount = 0;
         }
         // If we have an mApzcForInputBlock and it's the end of the touch sequence
         // then null it out so we don't keep a dangling reference and leak things.
         if (mTouchCount == 0) {
           mApzcForInputBlock = nullptr;
-          mOverscrollHandoffChain.clear();
+          ClearOverscrollHandoffChain();
         }
       }
       break;
     } case PINCHGESTURE_INPUT: {
       const PinchGestureInput& pinchInput = aEvent.AsPinchGestureInput();
       nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(pinchInput.mFocusPoint);
       if (apzc) {
         apzc->GetGuid(aOutTargetGuid);
@@ -512,17 +512,17 @@ APZCTreeManager::ProcessTouchEvent(Widge
       // NS_TOUCH_END event contains only released touches thus decrementing.
       mTouchCount -= aEvent.touches.Length();
     } else {
       NS_WARNING("Got an unexpected touchend/touchcancel");
       mTouchCount = 0;
     }
     if (mTouchCount == 0) {
       mApzcForInputBlock = nullptr;
-      mOverscrollHandoffChain.clear();
+      ClearOverscrollHandoffChain();
     }
   }
   return ret;
 }
 
 void
 APZCTreeManager::TransformCoordinateToGecko(const ScreenIntPoint& aPoint,
                                             LayoutDeviceIntPoint* aOutTransformedPoint)
@@ -672,59 +672,148 @@ APZCTreeManager::ClearTree()
   nsTArray< nsRefPtr<AsyncPanZoomController> > apzcsToDestroy;
   Collect(mRootApzc, &apzcsToDestroy);
   for (size_t i = 0; i < apzcsToDestroy.Length(); i++) {
     apzcsToDestroy[i]->Destroy();
   }
   mRootApzc = nullptr;
 }
 
+/**
+ * Transform a displacement from the screen coordinates of a source APZC to
+ * the screen coordinates of a target APZC.
+ * @param aTreeManager the tree manager for the APZC tree containing |aSource|
+ *                     and |aTarget|
+ * @param aSource the source APZC
+ * @param aTarget the target APZC
+ * @param aStartPoint the start point of the displacement
+ * @param aEndPoint the end point of the displacement
+ */
+static void
+TransformDisplacement(APZCTreeManager* aTreeManager,
+                      AsyncPanZoomController* aSource,
+                      AsyncPanZoomController* aTarget,
+                      ScreenPoint& aStartPoint,
+                      ScreenPoint& aEndPoint) {
+  gfx3DMatrix transformToApzc;
+  gfx3DMatrix transformToGecko;  // ignored
+
+  // Convert start and end points to untransformed screen coordinates.
+  aTreeManager->GetInputTransforms(aSource, transformToApzc, transformToGecko);
+  ApplyTransform(&aStartPoint, transformToApzc.Inverse());
+  ApplyTransform(&aEndPoint, transformToApzc.Inverse());
+
+  // Convert start and end points to aTarget's transformed screen coordinates.
+  aTreeManager->GetInputTransforms(aTarget, transformToApzc, transformToGecko);
+  ApplyTransform(&aStartPoint, transformToApzc);
+  ApplyTransform(&aEndPoint, transformToApzc);
+}
+
 void
 APZCTreeManager::DispatchScroll(AsyncPanZoomController* aPrev, ScreenPoint aStartPoint, ScreenPoint aEndPoint,
                                 uint32_t aOverscrollHandoffChainIndex)
 {
-  // If we have reached the end of the overscroll handoff chain, there is
-  // nothing more to scroll, so we ignore the rest of the pan gesture.
-  if (aOverscrollHandoffChainIndex >= mOverscrollHandoffChain.length()) {
-    // Nothing more to scroll - ignore the rest of the pan gesture.
-    return;
+  nsRefPtr<AsyncPanZoomController> next;
+  {
+    // Grab tree lock to protect mOverscrollHandoffChain from concurrent
+    // access from the input and compositor threads.
+    // Release it before calling TransformDisplacement() as that grabs the
+    // lock itself.
+    MonitorAutoLock lock(mTreeLock);
+
+    // If we have reached the end of the overscroll handoff chain, there is
+    // nothing more to scroll, so we ignore the rest of the pan gesture.
+    if (aOverscrollHandoffChainIndex >= mOverscrollHandoffChain.length()) {
+      // Nothing more to scroll - ignore the rest of the pan gesture.
+      return;
+    }
+
+    next = mOverscrollHandoffChain[aOverscrollHandoffChainIndex];
   }
 
-  nsRefPtr<AsyncPanZoomController> next = mOverscrollHandoffChain[aOverscrollHandoffChainIndex];
   if (next == nullptr)
     return;
 
   // Convert the start and end points from |aPrev|'s coordinate space to
   // |next|'s coordinate space. Since |aPrev| may be the same as |next|
   // (if |aPrev| is the APZC that is initiating the scroll and there is no
   // scroll grabbing to grab the scroll from it), don't bother doing the
   // transformations in that case.
   if (next != aPrev) {
-    gfx3DMatrix transformToApzc;
-    gfx3DMatrix transformToGecko;  // ignored
-
-    // Convert start and end points to untransformed screen coordinates.
-    GetInputTransforms(aPrev, transformToApzc, transformToGecko);
-    ApplyTransform(&aStartPoint, transformToApzc.Inverse());
-    ApplyTransform(&aEndPoint, transformToApzc.Inverse());
-
-    // Convert start and end points to next's transformed screen coordinates.
-    GetInputTransforms(next, transformToApzc, transformToGecko);
-    ApplyTransform(&aStartPoint, transformToApzc);
-    ApplyTransform(&aEndPoint, transformToApzc);
+    TransformDisplacement(this, aPrev, next, aStartPoint, aEndPoint);
   }
 
   // Scroll |next|. If this causes overscroll, it will call DispatchScroll()
   // again with an incremented index.
   next->AttemptScroll(aStartPoint, aEndPoint, aOverscrollHandoffChainIndex);
 }
 
+void
+APZCTreeManager::HandOffFling(AsyncPanZoomController* aPrev, ScreenPoint aVelocity)
+{
+  // Build the overscroll handoff chain. This is necessary because it is
+  // otherwise built on touch-start and cleared on touch-end, and a fling
+  // happens after touch-end. Note that, unlike DispatchScroll() which is
+  // called on every touch-move during overscroll panning,
+  // HandleFlingOverscroll() is only called once during a fling handoff,
+  // so it's not worth trying to avoid building the handoff chain here.
+  BuildOverscrollHandoffChain(aPrev);
+
+  nsRefPtr<AsyncPanZoomController> next;  // will be used outside monitor block
+  {
+    // Grab tree lock to protect mOverscrollHandoffChain from concurrent
+    // access from the input and compositor threads.
+    // Release it before calling GetInputTransforms() as that grabs the
+    // lock itself.
+    MonitorAutoLock lock(mTreeLock);
+
+    // Find |aPrev| in the handoff chain.
+    uint32_t i;
+    for (i = 0; i < mOverscrollHandoffChain.length(); ++i) {
+      if (mOverscrollHandoffChain[i] == aPrev) {
+        break;
+      }
+    }
+
+    // Get the next APZC in the handoff chain, if any.
+    if (i + 1 < mOverscrollHandoffChain.length()) {
+      next = mOverscrollHandoffChain[i + 1];
+    }
+
+    // Clear the handoff chain so we don't maintain references to APZCs
+    // unnecessarily.
+    mOverscrollHandoffChain.clear();
+  }
+
+  // Nothing to hand off fling to.
+  if (next == nullptr) {
+    return;
+  }
+
+  // The fling's velocity needs to be transformed from the screen coordinates
+  // of |aPrev| to the screen coordinates of |next|. To transform a velocity
+  // correctly, we need to convert it to a displacement. For now, we do this
+  // by anchoring it to a start point of (0, 0).
+  // TODO: For this to be correct in the presence of 3D transforms, we should
+  // use the end point of the touch that started the fling as the start point
+  // rather than (0, 0).
+  ScreenPoint startPoint;  // (0, 0)
+  ScreenPoint endPoint = startPoint + aVelocity;
+  TransformDisplacement(this, aPrev, next, startPoint, endPoint);
+  ScreenPoint transformedVelocity = endPoint - startPoint;
+
+  // Tell |next| to start a fling with the transformed velocity.
+  next->TakeOverFling(transformedVelocity);
+}
+
+
 bool
 APZCTreeManager::FlushRepaintsForOverscrollHandoffChain()
 {
+  MonitorAutoLock lock(mTreeLock);  // to access mOverscrollHandoffChain
   if (mOverscrollHandoffChain.length() == 0) {
     return false;
   }
   for (uint32_t i = 0; i < mOverscrollHandoffChain.length(); i++) {
     nsRefPtr<AsyncPanZoomController> item = mOverscrollHandoffChain[i];
     if (item) {
       item->FlushRepaintForOverscrollHandoff();
     }
@@ -794,16 +883,20 @@ APZCTreeManager::BuildOverscrollHandoffC
   // frame at the touch point (which is what GetTargetAPZC finds), but
   // something higher up in the tree.
   // It's not sufficient to just find the initial target, however, as
   // overscroll can be handed off to another APZC. Without scroll grabbing,
   // handoff just occurs from child to parent. With scroll grabbing, the
   // handoff order can be different, so we build a chain of APZCs in the
   // order in which scroll will be handed off to them.
 
+  // Grab tree lock to protect mOverscrollHandoffChain from concurrent
+  // access between the input and compositor threads.
+  MonitorAutoLock lock(mTreeLock);
+
   mOverscrollHandoffChain.clear();
 
   // Start with the child -> parent chain.
   for (AsyncPanZoomController* apzc = aInitialTarget; apzc; apzc = apzc->GetParent()) {
     if (!mOverscrollHandoffChain.append(apzc)) {
       NS_WARNING("Vector::append failed");
       mOverscrollHandoffChain.clear();
       return;
@@ -817,16 +910,23 @@ APZCTreeManager::BuildOverscrollHandoffC
   // non-scrollgrabbing APZCs remains child -> parent.
   // (The relative order between scrollgrabbing APZCs will also remain
   // child -> parent, though that's just an artefact of the implementation
   // and users of 'scrollgrab' should not rely on this.)
   std::stable_sort(mOverscrollHandoffChain.begin(), mOverscrollHandoffChain.end(),
                    CompareByScrollPriority());
 }
 
+void
+APZCTreeManager::ClearOverscrollHandoffChain()
+{
+  MonitorAutoLock lock(mTreeLock);
+  mOverscrollHandoffChain.clear();
+}
+
 AsyncPanZoomController*
 APZCTreeManager::FindTargetAPZC(AsyncPanZoomController* aApzc, const ScrollableLayerGuid& aGuid)
 {
   mTreeLock.AssertCurrentThreadOwns();
 
   // This walks the tree in depth-first, reverse order, so that it encounters
   // APZCs front-to-back on the screen.
   for (AsyncPanZoomController* child = aApzc->GetLastChild(); child; child = child->GetPrevSibling()) {
--- a/gfx/layers/composite/APZCTreeManager.h
+++ b/gfx/layers/composite/APZCTreeManager.h
@@ -37,16 +37,32 @@ enum AllowedTouchBehavior {
   UNKNOWN =            1 << 3
 };
 
 class Layer;
 class AsyncPanZoomController;
 class CompositorParent;
 
 /**
+ * ****************** NOTE ON LOCK ORDERING IN APZ **************************
+ *
+ * There are two kinds of locks used by APZ: APZCTreeManager::mTreeLock
+ * ("the tree lock") and AsyncPanZoomController::mMonitor ("APZC locks").
+ *
+ * To avoid deadlock, we impose a lock ordering between these locks, which is:
+ *
+ *      tree lock -> APZC locks
+ *
+ * The interpretation of the lock ordering is that if lock A precedes lock B
+ * in the ordering sequence, then you must NOT wait on A while holding B.
+ *
+ * **************************************************************************
+ */
+
+/**
  * This class manages the tree of AsyncPanZoomController instances. There is one
  * instance of this class owned by each CompositorParent, and it contains as
  * many AsyncPanZoomController instances as there are scrollable container layers.
  * This class generally lives on the compositor thread, although some functions
  * may be called from other threads as noted; thread safety is ensured internally.
  *
  * The bulk of the work of this class happens as part of the UpdatePanZoomControllerTree
  * function, which is when a layer tree update is received by the compositor.
@@ -234,20 +250,32 @@ public:
    *   - C.TrackTouch() calls TM.DispatchScroll() with index = 0.
    *   - TM.DispatchScroll() calls B.AttemptScroll() (since B is at index 0 in the chain).
    *   - B.AttemptScroll() scrolls B. If there is overscroll, it calls TM.DispatchScroll() with index = 1.
    *   - TM.DispatchScroll() calls C.AttemptScroll() (since C is at index 1 in the chain)
    *   - C.AttemptScroll() scrolls C. If there is overscroll, it calls TM.DispatchScroll() with index = 2.
    *   - TM.DispatchScroll() calls A.AttemptScroll() (since A is at index 2 in the chain)
    *   - A.AttemptScroll() scrolls A. If there is overscroll, it calls TM.DispatchScroll() with index = 3.
    *   - TM.DispatchScroll() discards the rest of the scroll as there are no more elements in the chain.
+   *
+   * Note: this should be used for panning only. For handing off overscroll for
+   *       a fling, use HandOffFling().
    */
   void DispatchScroll(AsyncPanZoomController* aAPZC, ScreenPoint aStartPoint, ScreenPoint aEndPoint,
                       uint32_t aOverscrollHandoffChainIndex);
 
+  /**
+   * This is a callback for AsyncPanZoomController to call when it wants to
+   * hand off overscroll from a fling.
+   * @param aApzc the APZC that is handing off the fling
+   * @param aVelocity the current velocity of the fling, in |aApzc|'s screen
+   *                  pixels per millisecond
+   */
+  void HandOffFling(AsyncPanZoomController* aApzc, ScreenPoint aVelocity);
+
   bool FlushRepaintsForOverscrollHandoffChain();
 
 protected:
   /**
    * Debug-build assertion that can be called to ensure code is running on the
    * compositor thread.
    */
   virtual void AssertOnCompositorThread();
@@ -274,16 +302,17 @@ private:
   already_AddRefed<AsyncPanZoomController> CommonAncestor(AsyncPanZoomController* aApzc1, AsyncPanZoomController* aApzc2);
   already_AddRefed<AsyncPanZoomController> RootAPZCForLayersId(AsyncPanZoomController* aApzc);
   already_AddRefed<AsyncPanZoomController> GetTouchInputBlockAPZC(const WidgetTouchEvent& aEvent);
   nsEventStatus ProcessTouchEvent(WidgetTouchEvent& touchEvent, ScrollableLayerGuid* aOutTargetGuid);
   nsEventStatus ProcessMouseEvent(WidgetMouseEvent& mouseEvent, ScrollableLayerGuid* aOutTargetGuid);
   nsEventStatus ProcessEvent(WidgetInputEvent& inputEvent, ScrollableLayerGuid* aOutTargetGuid);
   void UpdateZoomConstraintsRecursively(AsyncPanZoomController* aApzc,
                                         const ZoomConstraints& aConstraints);
+  void ClearOverscrollHandoffChain();
 
   /**
    * Recursive helper function to build the APZC tree. The tree of APZC instances has
    * the same shape as the layer tree, but excludes all the layers that are not scrollable.
    * Note that this means APZCs corresponding to layers at different depths in the tree
    * may end up becoming siblings. It also means that the "root" APZC may have siblings.
    * This function walks the layer tree backwards through siblings and constructs the APZC
    * tree also as a last-child-prev-sibling tree because that simplifies the hit detection
@@ -298,17 +327,19 @@ private:
                                                       uint64_t aFirstPaintLayersId,
                                                       nsTArray< nsRefPtr<AsyncPanZoomController> >* aApzcsToDestroy);
 
 private:
   /* Whenever walking or mutating the tree rooted at mRootApzc, mTreeLock must be held.
    * This lock does not need to be held while manipulating a single APZC instance in
    * isolation (that is, if its tree pointers are not being accessed or mutated). The
    * lock also needs to be held when accessing the mRootApzc instance variable, as that
-   * is considered part of the APZC tree management state. */
+   * is considered part of the APZC tree management state.
+   * Finally, the lock needs to be held when accessing mOverscrollHandoffChain.
+   * IMPORTANT: See the note about lock ordering at the top of this file. */
   mozilla::Monitor mTreeLock;
   nsRefPtr<AsyncPanZoomController> mRootApzc;
   /* This tracks the APZC that should receive all inputs for the current input event block.
    * This allows touch points to move outside the thing they started on, but still have the
    * touch events delivered to the same initial APZC. This will only ever be touched on the
    * input delivery thread, and so does not require locking.
    */
   nsRefPtr<AsyncPanZoomController> mApzcForInputBlock;
--- a/gfx/layers/composite/ContainerLayerComposite.cpp
+++ b/gfx/layers/composite/ContainerLayerComposite.cpp
@@ -26,16 +26,17 @@
 #include "nsAutoPtr.h"                  // for nsRefPtr
 #include "nsDebug.h"                    // for NS_ASSERTION
 #include "nsISupportsImpl.h"            // for MOZ_COUNT_CTOR, etc
 #include "nsISupportsUtils.h"           // for NS_ADDREF, NS_RELEASE
 #include "nsPoint.h"                    // for nsIntPoint
 #include "nsRect.h"                     // for nsIntRect
 #include "nsRegion.h"                   // for nsIntRegion
 #include "nsTArray.h"                   // for nsAutoTArray
+#include "TextRenderer.h"               // for TextRenderer
 #include <vector>
 
 namespace mozilla {
 namespace layers {
 
 // HasOpaqueAncestorLayer and ContainerRender are shared between RefLayer and ContainerLayer
 static bool
 HasOpaqueAncestorLayer(Layer* aLayer)
@@ -115,16 +116,42 @@ static gfx::Point GetScrollData(Layer* a
   if (aLayer->GetLocalTransform().Is2D(&matrix)) {
     return matrix.GetTranslation();
   }
 
   gfx::Point origin;
   return origin;
 }
 
+static void DrawLayerInfo(const nsIntRect& aClipRect,
+                          LayerManagerComposite* aManager,
+                          Layer* aLayer)
+{
+
+  if (aLayer->GetType() == Layer::LayerType::TYPE_CONTAINER) {
+    // XXX - should figure out a way to render this, but for now this
+    // is hard to do, since it will often get superimposed over the first
+    // child of the layer, which is bad.
+    return;
+  }
+
+  nsAutoCString layerInfo;
+  aLayer->PrintInfo(layerInfo, "");
+
+  nsIntRegion visibleRegion = aLayer->GetVisibleRegion();
+
+  uint32_t maxWidth = std::min<uint32_t>(visibleRegion.GetBounds().width, 500);
+
+  nsIntPoint topLeft = visibleRegion.GetBounds().TopLeft();
+  aManager->GetTextRenderer()->RenderText(layerInfo.get(), gfx::IntPoint(topLeft.x, topLeft.y),
+                                          aLayer->GetEffectiveTransform(), 16,
+                                          maxWidth);
+
+}
+
 static LayerVelocityUserData* GetVelocityData(Layer* aLayer) {
   static char sLayerVelocityUserDataKey;
   void* key = reinterpret_cast<void*>(&sLayerVelocityUserDataKey);
   if (!aLayer->HasUserData(key)) {
     LayerVelocityUserData* newData = new LayerVelocityUserData();
     aLayer->SetUserData(key, newData);
   }
 
@@ -356,16 +383,20 @@ ContainerRender(ContainerT* aContainer,
     if (restoreVisibleRegion) {
       // Restore the region in case it's not covered by opaque content next time
       layerToRender->SetShadowVisibleRegion(savedVisibleRegion);
     }
 
     if (gfxPrefs::LayersScrollGraph()) {
       DrawVelGraph(clipRect, aManager, layerToRender->GetLayer());
     }
+
+    if (gfxPrefs::DrawLayerInfo()) {
+      DrawLayerInfo(clipRect, aManager, layerToRender->GetLayer());
+    }
     // invariant: our GL context should be current here, I don't think we can
     // assert it though
   }
 
   if (needsSurface) {
     // Unbind the current surface and rebind the previous one.
 #ifdef MOZ_DUMP_PAINTING
     if (gfxUtils::sDumpPainting) {
new file mode 100644
--- /dev/null
+++ b/gfx/layers/composite/FontData.h
@@ -0,0 +1,13 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// This is explicitly not guarded as we want only 1 file to include this and
+// it's good if things break if someone else does.
+const unsigned char sFontPNG[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x8, 0x0, 0x0, 0x0, 0x0, 0x79, 0x19, 0xf7, 0xba, 0x0, 0x0, 0xb, 0xfe, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xed, 0x5d, 0xd1, 0xb6, 0xe3, 0x20, 0x8, 0x74, 0x72, 0xf2, 0xff, 0xbf, 0x3c, 0xfb, 0xd0, 0xbb, 0xbd, 0x51, 0x8, 0x48, 0xd4, 0xc4, 0xde, 0xda, 0xb3, 0x67, 0x77, 0x6d, 0x13, 0x63, 0x10, 0x81, 0x41, 0x40, 0x30, 0x7d, 0xf7, 0x67, 0x4b, 0x8b, 0x0, 0xdf, 0xfd, 0xd9, 0x2f, 0xdc, 0x3, 0xc6, 0xda, 0x76, 0x67, 0x29, 0xa5, 0x94, 0xb8, 0x38, 0x20, 0x42, 0x33, 0xc0, 0x68, 0xaa, 0x37, 0x64, 0x14, 0x47, 0xc2, 0x67, 0x13, 0x80, 0x5, 0xbf, 0x91, 0xb0, 0x5f, 0x9f, 0x73, 0x71, 0x40, 0x39, 0x61, 0x1c, 0xcd, 0xff, 0x39, 0x7d, 0x8, 0x1e, 0x29, 0x72, 0x3f, 0x1, 0x90, 0x12, 0x8a, 0xf9, 0xc, 0x52, 0x20, 0x9f, 0x51, 0x2, 0x8e, 0xd0, 0x99, 0x8d, 0x3, 0x7a, 0x2f, 0x2, 0x7a, 0x4, 0xa4, 0x90, 0xba, 0x8, 0x68, 0x1, 0x28, 0x32, 0x1a, 0x87, 0x6f, 0x50, 0x71, 0x7f, 0x5f, 0x29, 0xf, 0x85, 0xa3, 0x18, 0x5b, 0x10, 0xc, 0x10, 0x80, 0xf2, 0x1d, 0x79, 0x4e, 0x5e, 0x8f, 0xfc, 0xaf, 0x1, 0xb0, 0x60, 0x50, 0x4, 0x17, 0x40, 0xc6, 0xd3, 0xf4, 0xd5, 0x2e, 0x3a, 0xdb, 0x1, 0x5d, 0x85, 0x78, 0x8a, 0xbd, 0x7e, 0x33, 0xc3, 0x24, 0xe4, 0x52, 0x70, 0xff, 0xbc, 0xf5, 0x8f, 0xf0, 0x82, 0xe2, 0x5c, 0x1c, 0xd0, 0x9b, 0x83, 0x1c, 0x7a, 0x50, 0xb4, 0x33, 0xa9, 0xf9, 0xf5, 0x58, 0x0, 0xb, 0xe, 0x2f, 0x2, 0x2c, 0x2, 0x7c, 0x3d, 0x1, 0xea, 0x8c, 0xb9, 0xc, 0x4f, 0x16, 0x70, 0x14, 0x21, 0xc5, 0x8e, 0x60, 0xfb, 0x1, 0xe, 0x80, 0xf5, 0xc2, 0x52, 0x8c, 0xb6, 0x9, 0x52, 0xdc, 0xfb, 0xc6, 0x35, 0x4, 0x40, 0xa, 0x81, 0x8d, 0x66, 0xbd, 0xce, 0xc9, 0x8, 0x80, 0x6f, 0x93, 0x1, 0x6d, 0xef, 0xcf, 0xf1, 0xf4, 0xc2, 0xc0, 0xf9, 0xd9, 0xdb, 0xe7, 0x7f, 0x30, 0x5, 0x50, 0x98, 0x6b, 0x48, 0xe8, 0x9, 0xaf, 0x77, 0x14, 0xf8, 0xbe, 0xf5, 0x7d, 0xa0, 0xfa, 0xb, 0x18, 0xa2, 0x6f, 0x3e, 0x9e, 0xc2, 0x5c, 0x65, 0x5f, 0xe3, 0x75, 0xa7, 0x30, 0x87, 0x3d, 0xa, 0xd8, 0x42, 0x8b, 0x8a, 0x3f, 0x81, 0xb6, 0x7f, 0xa1, 0xa6, 0x8f, 0x80, 0x7, 0xa2, 0x5d, 0x8, 0xd2, 0xd1, 0xfb, 0x7f, 0x4a, 0x4a, 0x6e, 0xfe, 0xc, 0x3c, 0xab, 0xa6, 0x9e, 0x31, 0x84, 0x4c, 0x9e, 0xa3, 0xfb, 0x45, 0x47, 0x7f, 0xc5, 0xc2, 0x2, 0xcb, 0x1f, 0xb0, 0x38, 0x60, 0x11, 0x60, 0x7a, 0x2, 0xe0, 0x6f, 0x11, 0x0, 0x1e, 0x5a, 0x85, 0x3, 0x97, 0x5f, 0xde, 0x1, 0x94, 0x5f, 0x9c, 0x93, 0x2b, 0xf2, 0x73, 0x77, 0x68, 0xd1, 0xce, 0x1, 0x60, 0x6e, 0x3b, 0x82, 0x44, 0x26, 0x5c, 0xe1, 0x6d, 0x5f, 0xd3, 0xb2, 0x43, 0x91, 0xb4, 0xcd, 0xb0, 0x27, 0x97, 0x0, 0x12, 0x4, 0x53, 0xc, 0x54, 0x25, 0x44, 0x6f, 0x4d, 0x95, 0x77, 0xe7, 0x13, 0x80, 0xd2, 0x30, 0xf4, 0x2d, 0x1f, 0xf0, 0xb4, 0x45, 0x47, 0x11, 0x13, 0xb7, 0xda, 0x49, 0x7b, 0xf7, 0x29, 0x22, 0x12, 0xc1, 0x23, 0xa2, 0x3, 0x1b, 0xec, 0xd, 0x30, 0xc7, 0x7f, 0x3f, 0x4b, 0x82, 0x9d, 0x18, 0xe0, 0x2, 0x1, 0xca, 0xdd, 0xd9, 0x32, 0xe4, 0xe2, 0x35, 0xb6, 0xdf, 0xab, 0xd0, 0x86, 0xaf, 0x59, 0xf0, 0x80, 0x4b, 0xcc, 0xe0, 0xd6, 0xe1, 0x7e, 0x41, 0x4, 0xb8, 0xf, 0x11, 0xf8, 0x9a, 0x37, 0x1a, 0x9c, 0xc1, 0x27, 0x6d, 0xd1, 0xe, 0x5e, 0x22, 0x80, 0x21, 0x99, 0xcc, 0x89, 0xc, 0xee, 0x72, 0x28, 0xbe, 0x1d, 0x10, 0x17, 0xd3, 0x60, 0x4a, 0x7c, 0x7, 0xee, 0x10, 0x0, 0x81, 0x71, 0x73, 0x1c, 0xdd, 0x86, 0xe8, 0xf, 0x86, 0x1a, 0xfb, 0x50, 0xc8, 0x77, 0xe3, 0xe3, 0xb7, 0xfb, 0x57, 0xdd, 0x5c, 0x78, 0x76, 0x86, 0xd5, 0x99, 0x8d, 0xe1, 0xee, 0x1, 0x2d, 0x7f, 0xc0, 0x82, 0xc3, 0x8b, 0x0, 0xdf, 0xfd, 0xd9, 0x15, 0xcb, 0x1a, 0x22, 0xb6, 0x94, 0x56, 0xbb, 0x94, 0x62, 0xa5, 0x62, 0x80, 0x68, 0x9a, 0xbf, 0xf7, 0xdd, 0xf9, 0xd2, 0xed, 0x82, 0x2c, 0x52, 0x54, 0x40, 0x15, 0xc8, 0xb7, 0xcb, 0xd0, 0x88, 0xb0, 0xc6, 0x6d, 0x6c, 0x5c, 0x5e, 0xcf, 0xe2, 0xe, 0xbf, 0xff, 0x36, 0x59, 0xe, 0x7b, 0x3c, 0x9b, 0x7b, 0x3, 0x3b, 0x68, 0xfd, 0x96, 0x94, 0x92, 0x8a, 0x0, 0x12, 0x33, 0x5c, 0xbc, 0xb4, 0x42, 0xe9, 0x81, 0xa1, 0xf0, 0xb, 0x96, 0x70, 0x4f, 0x2e, 0xa9, 0xd4, 0x64, 0xeb, 0x79, 0x33, 0x80, 0xa4, 0xf0, 0x63, 0xc6, 0x72, 0x16, 0x76, 0xdb, 0x2f, 0x30, 0x98, 0x83, 0x5, 0x29, 0x4d, 0x1b, 0x7b, 0x86, 0xc2, 0xd6, 0x7d, 0x88, 0x63, 0x93, 0x8c, 0xa7, 0x6, 0x63, 0x4, 0x90, 0x8f, 0x28, 0xd7, 0xac, 0x39, 0x0, 0xfa, 0xbd, 0x83, 0x85, 0x43, 0x25, 0x6, 0xc6, 0xfc, 0x68, 0xf1, 0x73, 0x7, 0xd5, 0xde, 0x24, 0x60, 0xfe, 0xcf, 0x57, 0xc8, 0x9, 0x1, 0xc6, 0x58, 0xde, 0x5f, 0x22, 0xd9, 0xe3, 0xc5, 0x12, 0x40, 0x63, 0xb0, 0xb4, 0x97, 0x14, 0xc7, 0xce, 0xc6, 0xfc, 0x85, 0xfb, 0x33, 0x96, 0xa4, 0xdb, 0x9f, 0x92, 0xae, 0x60, 0xe9, 0x69, 0x57, 0x6f, 0x2b, 0x5a, 0xd1, 0x6e, 0x33, 0xa5, 0xa6, 0xeb, 0x5b, 0xad, 0x84, 0x47, 0xc1, 0xd7, 0x74, 0xf8, 0x6d, 0xfb, 0xf2, 0xf7, 0x5f, 0x70, 0x78, 0xa1, 0xc1, 0x45, 0x80, 0x45, 0x80, 0xaf, 0xf7, 0x7, 0x28, 0xd1, 0x9c, 0xb4, 0xc0, 0x45, 0x91, 0x4c, 0xaa, 0xda, 0xd, 0x87, 0x6f, 0xdc, 0xfb, 0x99, 0x3f, 0x12, 0x34, 0xf1, 0xbb, 0x7b, 0xfd, 0xff, 0x76, 0xfe, 0x6f, 0xb9, 0xbd, 0xc8, 0x23, 0x1, 0xde, 0x9b, 0x78, 0xbf, 0xf0, 0x4e, 0xdb, 0xeb, 0xcb, 0x9e, 0x50, 0x98, 0x5e, 0x25, 0xe0, 0x37, 0xd1, 0x1b, 0x63, 0xda, 0x47, 0xcb, 0x2c, 0xb5, 0x6e, 0xff, 0x79, 0x63, 0xdb, 0x90, 0x7a, 0x7f, 0xb9, 0xfd, 0xfe, 0xbf, 0x12, 0x82, 0xbc, 0x2e, 0xf7, 0xf6, 0xb4, 0x83, 0xf7, 0x8f, 0x54, 0xde, 0x25, 0x5a, 0x67, 0xf6, 0xe5, 0xf6, 0x42, 0x63, 0xc7, 0x98, 0x87, 0x32, 0xe4, 0xe3, 0xd3, 0x6c, 0x21, 0x22, 0x42, 0xa2, 0xfd, 0x8d, 0x48, 0x6d, 0x8f, 0x4c, 0x4, 0xed, 0x29, 0x42, 0x41, 0x1, 0xfc, 0xbc, 0x84, 0xfe, 0xaf, 0xdc, 0x60, 0xee, 0xcd, 0xee, 0xa, 0x53, 0x6a, 0x3e, 0xbe, 0xd0, 0x9a, 0xad, 0xf0, 0x50, 0x1c, 0x1e, 0xc2, 0xe0, 0x4b, 0x81, 0x2e, 0xb, 0x50, 0x44, 0x30, 0xb8, 0x70, 0x18, 0x85, 0x2c, 0x9c, 0xd8, 0x44, 0x66, 0xd7, 0xfd, 0xf6, 0x2d, 0xfd, 0x6c, 0x60, 0x57, 0xf7, 0x49, 0x28, 0x33, 0x16, 0x61, 0x10, 0x84, 0x6e, 0x40, 0x8d, 0x60, 0xf3, 0x78, 0xfe, 0x78, 0x79, 0xf1, 0xfc, 0xfd, 0xcd, 0x84, 0xb9, 0xfa, 0x3c, 0xf4, 0x51, 0xae, 0x69, 0xc2, 0x5e, 0xe3, 0x5e, 0xcd, 0x8, 0x22, 0x22, 0x53, 0xa8, 0x6f, 0x13, 0x14, 0xe3, 0x63, 0x15, 0xd5, 0xb5, 0xe7, 0x1f, 0x63, 0x79, 0xbe, 0xd2, 0x12, 0xfc, 0x55, 0x0, 0x5f, 0x8a, 0x8b, 0x97, 0x3f, 0x60, 0xa1, 0xc1, 0x45, 0x80, 0x45, 0x80, 0x2f, 0xf7, 0x7, 0x54, 0xe1, 0xfd, 0xf3, 0x8d, 0x82, 0x12, 0xfe, 0x97, 0x96, 0xa4, 0x5, 0xcf, 0x7f, 0x77, 0xc5, 0x60, 0xf8, 0x7, 0xe4, 0xe3, 0x2d, 0xbf, 0x3e, 0x82, 0x7b, 0xd1, 0x7b, 0x1d, 0xde, 0x17, 0xe, 0x88, 0x77, 0xdb, 0xdb, 0xcd, 0x67, 0x79, 0x7d, 0xd1, 0x13, 0xe5, 0xde, 0x20, 0xa5, 0xbb, 0xc1, 0xc1, 0x16, 0x96, 0xf7, 0xc0, 0x89, 0xd3, 0x3d, 0x8b, 0xf, 0xf0, 0xf3, 0x67, 0xdb, 0x94, 0x2f, 0x8e, 0x86, 0x99, 0x48, 0x99, 0x8, 0xc4, 0x4e, 0xb6, 0x22, 0xf7, 0xf1, 0x32, 0x20, 0x6, 0xcf, 0x47, 0x18, 0x3a, 0x74, 0x6a, 0x89, 0x79, 0x78, 0xbf, 0xf8, 0x9d, 0x68, 0xe3, 0x80, 0x17, 0x58, 0x3d, 0x40, 0x56, 0x5, 0xba, 0x34, 0x6, 0xcf, 0xc6, 0x84, 0x60, 0x45, 0x3d, 0x38, 0xb0, 0xc5, 0x3f, 0x50, 0xb1, 0x1e, 0x22, 0xc1, 0xf7, 0x21, 0x77, 0x87, 0xcc, 0x37, 0xb8, 0xb4, 0x4, 0x1a, 0x53, 0x1e, 0x30, 0xb3, 0xc1, 0xbd, 0x55, 0xe1, 0xf5, 0x23, 0x5, 0xd0, 0xba, 0xe6, 0xf9, 0x2b, 0xfd, 0x6b, 0x16, 0x29, 0x38, 0x74, 0x7a, 0xf6, 0x3a, 0xbc, 0x7e, 0xe0, 0x33, 0x57, 0x6, 0x8, 0x4, 0x4f, 0xd7, 0x89, 0x6d, 0x5f, 0x31, 0x14, 0xaa, 0xea, 0x4f, 0x9e, 0x19, 0x23, 0x82, 0xad, 0x17, 0x54, 0xc8, 0x80, 0xbe, 0x19, 0x1e, 0x93, 0x9b, 0xc2, 0x3, 0xec, 0x9c, 0xc1, 0x2c, 0x60, 0x8e, 0xae, 0x32, 0x81, 0x86, 0xcb, 0x21, 0xb2, 0xd0, 0xe0, 0x22, 0xc0, 0xf, 0x1, 0xf0, 0xed, 0x4, 0xf0, 0xec, 0x88, 0xba, 0xd2, 0x4f, 0xd7, 0x3b, 0x40, 0xe7, 0x76, 0x7c, 0x9, 0xc, 0x16, 0x83, 0x51, 0x31, 0xdb, 0x23, 0x3c, 0xbf, 0x4d, 0x6, 0x88, 0x3, 0xb, 0x0, 0xf3, 0x77, 0x79, 0x79, 0x79, 0x9e, 0x81, 0xe8, 0xce, 0xea, 0xa0, 0x66, 0x86, 0x4d, 0xd3, 0x39, 0x1, 0xc7, 0x3e, 0x61, 0x8f, 0x7f, 0x57, 0x67, 0xcc, 0xa, 0x77, 0x57, 0x32, 0x3c, 0x60, 0xdc, 0x4f, 0x25, 0x40, 0x43, 0x44, 0x2c, 0x4, 0xb9, 0xc4, 0x45, 0x8f, 0x38, 0x42, 0x58, 0x7b, 0xfc, 0x15, 0xc9, 0xd3, 0x74, 0xd9, 0xb4, 0xad, 0xd4, 0x35, 0x3b, 0x2f, 0x2a, 0x25, 0xe1, 0x3e, 0x66, 0x9, 0xb6, 0xaa, 0x85, 0xd1, 0x6a, 0x65, 0x78, 0x41, 0x45, 0x36, 0x3e, 0x63, 0xf8, 0x89, 0x19, 0x5d, 0xcd, 0xd7, 0x8a, 0xa2, 0xaa, 0x70, 0x27, 0x1, 0x8d, 0x9a, 0xef, 0x82, 0x8b, 0x65, 0x20, 0x7, 0x10, 0x5, 0x8d, 0x8b, 0x9c, 0x94, 0x12, 0xee, 0x97, 0x29, 0x2e, 0x74, 0xf2, 0xfe, 0xa2, 0x29, 0x31, 0xe7, 0x3e, 0x95, 0xeb, 0xd4, 0xe3, 0xd3, 0xc8, 0x1f, 0x1c, 0x73, 0xed, 0x47, 0x60, 0x81, 0xc9, 0x4c, 0xef, 0x7, 0x38, 0x20, 0x96, 0x1a, 0xb, 0xfe, 0x39, 0x2, 0x2c, 0x38, 0xbc, 0x8, 0xb0, 0x8, 0xf0, 0xa7, 0x8, 0x70, 0xbb, 0x54, 0xb7, 0x4f, 0x33, 0x88, 0xfa, 0xb3, 0x3f, 0xef, 0x98, 0x1d, 0x17, 0xa, 0x79, 0xa5, 0xc6, 0x52, 0x51, 0x4e, 0x4f, 0xc1, 0xcb, 0x79, 0x81, 0x3, 0x17, 0xef, 0xcb, 0x19, 0xc1, 0x69, 0xf3, 0xdd, 0x86, 0x71, 0x7d, 0x51, 0x8f, 0x12, 0x23, 0xeb, 0x50, 0xed, 0x1a, 0x5e, 0x36, 0x2b, 0xe, 0x78, 0x78, 0xff, 0x82, 0x59, 0xb, 0xaf, 0x58, 0x39, 0xda, 0xd6, 0xa8, 0xc5, 0x15, 0xaa, 0x4b, 0x8c, 0x41, 0x2a, 0x6, 0x4d, 0x71, 0x58, 0xfb, 0xc5, 0xda, 0xde, 0x6a, 0x13, 0x8d, 0x41, 0x5a, 0x15, 0x26, 0xb6, 0xb, 0x6f, 0x0, 0x44, 0x5f, 0xb8, 0xd, 0xd9, 0x4, 0x93, 0x9d, 0x29, 0x18, 0xd8, 0x3a, 0xd3, 0xf2, 0x82, 0x16, 0xf0, 0xce, 0xf8, 0x8c, 0x4f, 0x11, 0x4c, 0x8a, 0xd9, 0x45, 0x57, 0x5b, 0x8d, 0xdb, 0x2d, 0x5c, 0xc8, 0x16, 0x35, 0xab, 0xce, 0x99, 0xf0, 0x88, 0xe0, 0xc0, 0xf0, 0x9a, 0xa2, 0x12, 0xef, 0x9b, 0x54, 0xd6, 0xfc, 0x1, 0xc9, 0xf4, 0xf, 0xb8, 0x27, 0x82, 0xd2, 0x3c, 0x42, 0x62, 0x6c, 0x7c, 0xc0, 0x9e, 0x3c, 0x17, 0xa7, 0xeb, 0x24, 0x55, 0xda, 0xf4, 0xae, 0xe7, 0x69, 0xb3, 0xa7, 0xc0, 0xad, 0x61, 0xcf, 0x9, 0xd, 0xa1, 0xb1, 0x3c, 0xcf, 0xd9, 0x2d, 0x41, 0xb7, 0x1e, 0xb, 0xbd, 0xf8, 0x0, 0x4e, 0x44, 0xee, 0x85, 0x6, 0x3b, 0x31, 0x5, 0xc6, 0xdd, 0xff, 0x11, 0x1c, 0x30, 0xb2, 0x32, 0xcf, 0xf3, 0x4, 0x78, 0x78, 0x4, 0xfb, 0x15, 0x3d, 0x1b, 0x14, 0x34, 0x66, 0x21, 0x20, 0x50, 0xad, 0x54, 0x86, 0xf3, 0x2, 0x5, 0xe7, 0x8a, 0x8d, 0xa7, 0xbf, 0x5a, 0xa9, 0xb3, 0xd1, 0x74, 0xfe, 0xce, 0x73, 0x46, 0x59, 0x6a, 0x2e, 0x87, 0xa7, 0x6e, 0x2a, 0xaf, 0x53, 0x80, 0xd0, 0x41, 0x93, 0xfb, 0x18, 0x6b, 0x23, 0x9f, 0x81, 0xb2, 0xd6, 0x17, 0x12, 0xa3, 0x24, 0x66, 0xcb, 0x74, 0x98, 0xd9, 0xe3, 0xf1, 0xc3, 0xdb, 0x82, 0xb1, 0xcf, 0x32, 0xd8, 0x7c, 0xaa, 0x52, 0xc8, 0x7b, 0xdc, 0xa1, 0x31, 0xb6, 0x54, 0xfa, 0xeb, 0x78, 0x2, 0xbc, 0x32, 0xec, 0x99, 0x64, 0x6c, 0x32, 0x4e, 0x57, 0x3a, 0xaf, 0x50, 0xec, 0xa, 0x7, 0x8c, 0x3e, 0x81, 0x13, 0xc4, 0xfb, 0xaf, 0x54, 0xe6, 0x27, 0x9c, 0x3c, 0xfd, 0xb2, 0xa2, 0xdb, 0x98, 0xc8, 0x4f, 0xb2, 0x6, 0x7b, 0x57, 0xf7, 0xd8, 0xaa, 0x41, 0x7e, 0x37, 0xa1, 0x8, 0xc7, 0x75, 0xad, 0xa0, 0x61, 0x8b, 0x2, 0x6e, 0x98, 0x9f, 0x8b, 0xc6, 0x11, 0xe4, 0x20, 0xe1, 0xb0, 0x53, 0x3c, 0x78, 0x99, 0x6a, 0x8a, 0x15, 0x4, 0x7c, 0xa9, 0xe5, 0x5f, 0x19, 0x30, 0xdc, 0xe, 0xe8, 0xae, 0x2, 0x59, 0x1, 0xf2, 0x3d, 0xbc, 0x7a, 0x2c, 0xe9, 0xd0, 0xe8, 0xa2, 0x65, 0x13, 0x83, 0x5c, 0x60, 0x80, 0xf, 0xfb, 0x80, 0xe3, 0xef, 0xf8, 0xd3, 0x40, 0x6b, 0xf9, 0x3, 0x16, 0x1, 0x3e, 0x90, 0xeb, 0x8f, 0xba, 0x75, 0x9b, 0x60, 0x38, 0x68, 0xbc, 0x20, 0xfa, 0x80, 0xcc, 0x6d, 0xed, 0x9f, 0x3d, 0xee, 0x9d, 0x3f, 0xd8, 0x38, 0x3e, 0x69, 0x46, 0xbc, 0x77, 0xa7, 0xd1, 0x7, 0x7b, 0x50, 0x3b, 0xc, 0x0, 0xe7, 0x4, 0x70, 0x2d, 0xa7, 0xbe, 0x72, 0x53, 0x3f, 0xc4, 0x17, 0xae, 0x7d, 0x13, 0xb5, 0x2c, 0x72, 0xcb, 0x11, 0x2c, 0xfc, 0x1, 0xd7, 0xd5, 0xc4, 0x18, 0x3b, 0x80, 0xe0, 0x7b, 0xcb, 0xd0, 0x3, 0x6b, 0x48, 0x66, 0x78, 0xaa, 0xb0, 0x4, 0xf3, 0xfd, 0x78, 0xf7, 0x7c, 0x1, 0x3a, 0x33, 0x46, 0xaf, 0xde, 0x7f, 0xf, 0x45, 0x6d, 0x55, 0xb3, 0x7f, 0x7b, 0xb4, 0xc, 0x9c, 0x88, 0x3b, 0xb5, 0x0, 0xba, 0x40, 0x2b, 0x9e, 0x1e, 0xd2, 0x78, 0x1, 0xe, 0xca, 0xd3, 0xef, 0x52, 0xaa, 0x5e, 0x2, 0x1d, 0xe8, 0x91, 0xbd, 0x82, 0x5f, 0xe, 0x84, 0x87, 0x3f, 0xa9, 0x36, 0x17, 0xfb, 0x72, 0xfe, 0xf9, 0x3e, 0x60, 0xf9, 0x9a, 0xf9, 0x1a, 0x57, 0x8a, 0xe7, 0x57, 0x84, 0x48, 0x5c, 0x77, 0x88, 0xc, 0x10, 0x60, 0x1c, 0xc1, 0x42, 0xfe, 0xfb, 0xe0, 0xaa, 0x3f, 0xe0, 0x3, 0xc, 0xb7, 0xae, 0x5a, 0xc0, 0x96, 0xa9, 0xb, 0xb, 0x2c, 0x2, 0x2c, 0x2, 0x2c, 0x2, 0x2c, 0x2, 0x7c, 0xd3, 0x67, 0xff, 0xb6, 0x17, 0xbe, 0x52, 0x43, 0xc4, 0x2f, 0xd9, 0x27, 0x2e, 0x86, 0xb1, 0x97, 0x57, 0xe6, 0xed, 0x81, 0x96, 0xe2, 0x6e, 0x87, 0x56, 0xf6, 0xc6, 0x40, 0xcd, 0xe6, 0x72, 0x92, 0x25, 0xc, 0x3, 0xdb, 0xf5, 0xce, 0x79, 0x7f, 0x17, 0x28, 0x78, 0x5, 0x9, 0x9d, 0x76, 0xe2, 0x9f, 0x3c, 0xd, 0x27, 0x7d, 0x1c, 0x45, 0x82, 0x41, 0x4d, 0xe9, 0x30, 0xfb, 0xfd, 0x81, 0x63, 0xb0, 0x69, 0x23, 0xb4, 0x14, 0xfd, 0xb9, 0x4, 0xa0, 0x8e, 0xce, 0x2c, 0xe0, 0x1a, 0xf2, 0x89, 0xb8, 0xd1, 0xe6, 0xc8, 0xa2, 0xb1, 0x65, 0xd7, 0x71, 0xbc, 0x6d, 0x45, 0x77, 0xef, 0xe1, 0x45, 0x81, 0x82, 0x27, 0x50, 0x54, 0x7b, 0x87, 0x13, 0x41, 0x51, 0x75, 0xa4, 0x26, 0x4f, 0xfc, 0x37, 0x49, 0xd6, 0x5b, 0xa8, 0xa8, 0x8b, 0xdd, 0x78, 0xe0, 0xa2, 0xe5, 0x4e, 0x50, 0xc7, 0xe, 0xb6, 0xe1, 0xdf, 0x54, 0xd4, 0x14, 0x35, 0xdf, 0x5f, 0x1c, 0x4f, 0xa0, 0xbc, 0x2d, 0xfd, 0x18, 0x21, 0x1b, 0xdf, 0x9b, 0x24, 0x64, 0x99, 0x72, 0xe3, 0x48, 0x6e, 0x2f, 0xbb, 0xdc, 0xb9, 0xc0, 0x5d, 0x71, 0x3c, 0x19, 0x63, 0xb5, 0x16, 0x50, 0x17, 0x9d, 0xa1, 0x5, 0xf0, 0xa, 0x6a, 0x39, 0xed, 0x41, 0xf5, 0xdf, 0x44, 0xb4, 0x80, 0x26, 0xd6, 0x58, 0xdf, 0xa1, 0xd3, 0x5f, 0xd, 0x1, 0x9c, 0x73, 0x85, 0x9d, 0x3e, 0xa3, 0x6a, 0x30, 0x35, 0xd6, 0x2c, 0x6d, 0xb6, 0x3, 0x7a, 0x7b, 0x8, 0xbc, 0x83, 0x93, 0xbe, 0xcf, 0x14, 0x9e, 0xcb, 0x5, 0xb3, 0x4d, 0x3e, 0xbe, 0xf1, 0xd8, 0x60, 0xb9, 0xc4, 0x16, 0x1, 0x16, 0x1, 0xfa, 0x20, 0xec, 0xcf, 0x25, 0x80, 0x13, 0xb6, 0xe8, 0xd4, 0x92, 0xab, 0x88, 0x33, 0xb, 0xa6, 0x7f, 0x47, 0xb, 0x2, 0x94, 0xf9, 0xe6, 0x79, 0xfb, 0xff, 0xb3, 0x71, 0x2a, 0x4, 0xa5, 0x61, 0xe5, 0x5, 0x36, 0x22, 0x18, 0x5c, 0x1e, 0xb, 0x71, 0x80, 0x63, 0xfb, 0xeb, 0xd7, 0x83, 0x67, 0x6d, 0xc7, 0xf4, 0xaa, 0x58, 0x2, 0x22, 0x82, 0x83, 0xee, 0x84, 0xb4, 0xac, 0xa, 0x36, 0x5e, 0x2f, 0xec, 0x2e, 0xd5, 0xb6, 0x3f, 0x10, 0x80, 0x88, 0x15, 0x77, 0xe7, 0xc3, 0x87, 0x11, 0xaa, 0xee, 0x0, 0x83, 0x49, 0x49, 0x24, 0x3, 0xa0, 0x8a, 0x50, 0x59, 0x14, 0xc6, 0xaa, 0x80, 0xff, 0xd1, 0x39, 0x8a, 0xd2, 0xab, 0xf0, 0x26, 0x88, 0x3c, 0xc9, 0x12, 0xe, 0xe2, 0x10, 0x5d, 0xfc, 0xdb, 0x3e, 0x34, 0xff, 0x3, 0x66, 0xdd, 0xc9, 0xb6, 0x2b, 0x1c, 0x85, 0xce, 0xdb, 0xdb, 0x8, 0x49, 0x10, 0xf7, 0x81, 0x5, 0x5, 0xf8, 0x23, 0xc9, 0x90, 0xb5, 0x7f, 0x4f, 0x8d, 0x4b, 0xc5, 0xef, 0x8f, 0xdb, 0x1, 0xcd, 0xa6, 0x67, 0x9d, 0xd4, 0x36, 0xfc, 0xb2, 0x59, 0x73, 0x1b, 0xb4, 0x4c, 0x87, 0x1a, 0xef, 0x9a, 0x58, 0xb3, 0x95, 0x33, 0x4e, 0x4f, 0xa1, 0xdd, 0x4f, 0x84, 0x38, 0x5b, 0x5e, 0x9f, 0x23, 0x21, 0x6, 0xb5, 0xb2, 0x2e, 0x2c, 0xce, 0xd9, 0xcd, 0x24, 0x57, 0xf9, 0xfb, 0x60, 0x30, 0xa4, 0x79, 0x18, 0xa6, 0xca, 0x5d, 0xcd, 0x17, 0xc8, 0xf3, 0x5b, 0x63, 0xb7, 0xe7, 0x1b, 0xe4, 0x3e, 0xc7, 0x3b, 0x38, 0x60, 0x6a, 0x40, 0xbe, 0xfc, 0x1, 0xaa, 0xd4, 0xe, 0xc6, 0x4b, 0x77, 0x57, 0x1a, 0x5e, 0xd5, 0xda, 0x5e, 0x37, 0xff, 0x27, 0x0, 0x1d, 0x1d, 0x22, 0x4c, 0x19, 0x11, 0x7f, 0x8d, 0xc8, 0x33, 0x5d, 0x5b, 0xd1, 0x7b, 0x7e, 0x76, 0x6c, 0x73, 0xcb, 0xcd, 0x83, 0x96, 0xc0, 0x47, 0xe5, 0x51, 0xa9, 0xc9, 0xf3, 0xdd, 0xdb, 0xb9, 0x11, 0xa8, 0x9d, 0x67, 0x78, 0x7e, 0x80, 0xa1, 0xdb, 0x6e, 0x1b, 0xdc, 0xae, 0xb1, 0x74, 0xe7, 0x76, 0xa1, 0xea, 0xd4, 0xf3, 0xc, 0x3, 0x60, 0xa0, 0x2f, 0x77, 0x6d, 0x77, 0x71, 0x59, 0xd1, 0x9a, 0x66, 0x8d, 0x6c, 0xf2, 0xb0, 0xaf, 0xde, 0xed, 0xb9, 0x3f, 0xfb, 0x5d, 0x62, 0x26, 0x55, 0x7, 0xf3, 0x46, 0xdb, 0xed, 0x4, 0xe8, 0x7d, 0xcc, 0x89, 0xd6, 0xe6, 0x75, 0x7, 0xc0, 0x58, 0x19, 0xb0, 0xcb, 0x53, 0x5e, 0x47, 0xb4, 0xe7, 0x5d, 0x12, 0xf, 0x38, 0x44, 0x90, 0x66, 0xa2, 0xc8, 0xaf, 0xef, 0x6c, 0xe8, 0xbf, 0x98, 0xd6, 0xe, 0x78, 0xc4, 0x66, 0x9b, 0xc9, 0x50, 0x7c, 0x64, 0x6f, 0x70, 0x26, 0x2d, 0xf9, 0x8, 0x1, 0x66, 0xe2, 0x80, 0xe5, 0xf, 0xd0, 0x25, 0xa3, 0x3, 0x6f, 0xe1, 0x41, 0xf0, 0xd6, 0x7c, 0xef, 0x26, 0x88, 0x1f, 0xd2, 0x31, 0x5b, 0xd, 0x9c, 0x75, 0xfc, 0x5, 0x25, 0xe6, 0x76, 0xfd, 0x5, 0x51, 0x19, 0x11, 0x84, 0xf8, 0x33, 0x9d, 0xe1, 0xf2, 0x9, 0x58, 0x40, 0x98, 0xda, 0x22, 0xb0, 0x50, 0xc4, 0xcb, 0x37, 0xb6, 0x35, 0x86, 0x65, 0x51, 0x75, 0xa8, 0xb7, 0x65, 0x7a, 0x3a, 0x9c, 0x3d, 0xa9, 0xdb, 0xe9, 0x96, 0xcf, 0xa4, 0xb5, 0x5d, 0xe5, 0x93, 0x69, 0xd1, 0xb1, 0x8c, 0x3c, 0x6e, 0x93, 0xab, 0x4e, 0xdd, 0xcc, 0x3c, 0xc4, 0xef, 0xb7, 0xb6, 0xb, 0x21, 0xf5, 0x33, 0xa0, 0x7e, 0xa6, 0x41, 0x99, 0xb0, 0x60, 0x47, 0xa8, 0xdc, 0xbf, 0x31, 0xa2, 0x9d, 0x27, 0x30, 0xb6, 0x40, 0xdf, 0x99, 0x53, 0xe6, 0xc5, 0x1, 0x4a, 0x69, 0xb3, 0xc1, 0x86, 0xda, 0xf0, 0xf7, 0xf, 0xd5, 0xeb, 0xdf, 0xa8, 0x64, 0x7b, 0x73, 0xf0, 0xf6, 0xee, 0xd3, 0x96, 0xf0, 0xf1, 0x64, 0xf1, 0xfb, 0x97, 0x80, 0xac, 0xae, 0x3f, 0x58, 0x15, 0x2b, 0x1e, 0xa4, 0xc3, 0xf6, 0xf5, 0xa6, 0x8e, 0x41, 0xca, 0xc4, 0xdf, 0xb, 0x9a, 0xdb, 0x49, 0xd4, 0x34, 0x91, 0xd7, 0x67, 0x87, 0x98, 0xc4, 0xda, 0x72, 0xbe, 0x69, 0x95, 0x8c, 0x4, 0x9f, 0xb6, 0x3, 0x5e, 0x23, 0xb0, 0xc2, 0x90, 0xa2, 0x6d, 0xbb, 0xe0, 0xc4, 0xab, 0x82, 0xe2, 0x3a, 0x7c, 0xfd, 0x51, 0x38, 0xbc, 0x8, 0xb0, 0x8, 0xb0, 0x8, 0xf0, 0xa7, 0x9, 0x80, 0x4f, 0x23, 0x40, 0xe7, 0x0, 0x11, 0xc4, 0x83, 0x63, 0x1b, 0x1c, 0x40, 0x3d, 0x22, 0x44, 0x9a, 0x3, 0x44, 0x10, 0x33, 0xc5, 0x9b, 0x1d, 0x40, 0xb0, 0xcc, 0xf6, 0xd0, 0xdd, 0xcb, 0xe, 0xf8, 0x39, 0x61, 0xa2, 0xc9, 0xf0, 0xaa, 0x68, 0x67, 0xb1, 0x79, 0x9d, 0xfb, 0x6f, 0x8e, 0x10, 0xd1, 0x22, 0x8b, 0x7b, 0xb7, 0x8f, 0x5f, 0xf6, 0xee, 0xbf, 0x59, 0x8, 0xb6, 0xee, 0x4e, 0x5f, 0xd9, 0xcd, 0xe6, 0x6c, 0x4b, 0xc0, 0x11, 0xfa, 0x1d, 0xda, 0xa8, 0x97, 0xe3, 0xd1, 0x76, 0x2b, 0x1, 0x40, 0xe9, 0xa3, 0xeb, 0xdd, 0xb6, 0xe1, 0x6a, 0x7, 0x96, 0x6a, 0x34, 0x84, 0x46, 0xf3, 0x80, 0x60, 0x2, 0xcc, 0xc3, 0x4, 0xa0, 0x88, 0xef, 0xef, 0xdf, 0x4e, 0x19, 0x4, 0x9f, 0x4b, 0xb, 0x3c, 0x61, 0x7, 0x4c, 0x65, 0x7b, 0x3c, 0x1, 0x86, 0xa6, 0xa, 0xa2, 0x7b, 0x82, 0x0, 0x53, 0x71, 0xc0, 0x72, 0x89, 0x2d, 0x7f, 0xc0, 0x22, 0x80, 0x62, 0x9a, 0xc5, 0x2, 0x44, 0x7a, 0x27, 0x94, 0xdc, 0x19, 0x20, 0xf2, 0x3a, 0x78, 0xd9, 0xc6, 0xfb, 0xbd, 0xfd, 0x5, 0x63, 0xfd, 0x3, 0xe2, 0x7a, 0x67, 0x7a, 0x3a, 0x63, 0xab, 0x9, 0x5, 0xaf, 0xf3, 0xb4, 0x4d, 0x46, 0xf8, 0x24, 0xa7, 0x66, 0x44, 0x6b, 0x5b, 0x24, 0x7e, 0xe6, 0x17, 0x88, 0xf8, 0x81, 0x58, 0x5b, 0xa9, 0x17, 0x21, 0xba, 0x3f, 0xfe, 0xfe, 0x8a, 0x16, 0x2f, 0xf7, 0x92, 0x46, 0xfa, 0x7, 0xb4, 0x19, 0xba, 0x3f, 0xd9, 0xd8, 0x40, 0x83, 0xe9, 0x86, 0xbd, 0x5a, 0xc5, 0x67, 0xd4, 0xcf, 0xcc, 0xb4, 0xb, 0x0, 0x94, 0xbf, 0xdf, 0xbe, 0x3d, 0xae, 0xd6, 0x96, 0x7b, 0xd0, 0x36, 0x9e, 0xc0, 0xe, 0xe8, 0x1e, 0x1f, 0x62, 0xf7, 0x58, 0xfc, 0xfe, 0x8, 0x16, 0xf0, 0x78, 0xa2, 0xaf, 0x7b, 0x0, 0xae, 0x84, 0x70, 0xaa, 0xe6, 0xf6, 0xf6, 0x12, 0x2b, 0xe3, 0x2b, 0xae, 0x2f, 0xea, 0x33, 0x6, 0xdb, 0x22, 0x20, 0xa, 0x91, 0xf0, 0x81, 0xbf, 0xf0, 0xf1, 0x2, 0x70, 0xc6, 0x97, 0xd0, 0x98, 0x5b, 0xa6, 0x38, 0x25, 0x34, 0x3e, 0xfe, 0xf5, 0x13, 0x43, 0xbf, 0x7f, 0xbd, 0x3f, 0xe0, 0x1f, 0x5f, 0x4e, 0x4b, 0x19, 0x56, 0xcb, 0xb5, 0x20, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 };
+const unsigned short sGlyphWidths[256] = { 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 4, 3, 5, 7, 7, 12, 9, 2, 4, 4, 5, 8, 4, 4, 4, 4, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 4, 4, 8, 8, 8, 7, 13, 9, 9, 9, 9, 9, 8, 10, 9, 3, 6, 9, 7, 11, 9, 10, 9, 10, 9, 9, 7, 9, 9, 13, 7, 9, 7, 4, 4, 4, 5, 7, 4, 7, 7, 7, 7, 7, 3, 7, 7, 3, 3, 7, 3, 11, 7, 7, 7, 7, 4, 7, 4, 7, 5, 9, 7, 7, 7, 4, 3, 4, 8, 10, 7, 10, 3, 7, 4, 13, 7, 7, 4, 14, 9, 4, 13, 10, 7, 10, 10, 3, 3, 4, 4, 5, 7, 13, 4, 13, 7, 4, 12, 10, 7, 9, 4, 3, 7, 7, 7, 7, 3, 7, 4, 10, 4, 7, 8, 4, 10, 7, 5, 7, 4, 4, 4, 7, 7, 4, 4, 4, 5, 7, 11, 11, 11, 8, 9, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9, 3, 3, 3, 3, 9, 9, 10, 10, 10, 10, 10, 8, 10, 9, 9, 9, 9, 9, 9, 9, 7, 7, 7, 7, 7, 7, 12, 7, 7, 7, 7, 7, 3, 3, 3, 3, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 };
+const unsigned int sTextureWidth = 256;
+const unsigned int sTextureHeight = 256;
+const unsigned int sCellWidth = 16;
+const unsigned int sCellHeight = 16;
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -46,16 +46,17 @@
 #include "nsIWidget.h"                  // for nsIWidget
 #include "nsPoint.h"                    // for nsIntPoint
 #include "nsRect.h"                     // for nsIntRect
 #include "nsRegion.h"                   // for nsIntRegion, etc
 #ifdef MOZ_WIDGET_ANDROID
 #include <android/log.h>
 #endif
 #include "GeckoProfiler.h"
+#include "TextRenderer.h"               // for TextRenderer
 
 class gfxASurface;
 class gfxContext;
 struct nsIntSize;
 
 
 namespace mozilla {
 namespace layers {
@@ -99,16 +100,17 @@ LayerManagerComposite::ClearCachedResour
  * LayerManagerComposite
  */
 LayerManagerComposite::LayerManagerComposite(Compositor* aCompositor)
 : mCompositor(aCompositor)
 , mInTransaction(false)
 , mIsCompositorReady(false)
 , mDebugOverlayWantsNextFrame(false)
 {
+  mTextRenderer = new TextRenderer(aCompositor);
   MOZ_ASSERT(aCompositor);
 }
 
 LayerManagerComposite::~LayerManagerComposite()
 {
   Destroy();
 }
 
--- a/gfx/layers/composite/LayerManagerComposite.h
+++ b/gfx/layers/composite/LayerManagerComposite.h
@@ -56,16 +56,17 @@ class ContainerLayerComposite;
 class EffectChain;
 class ImageLayer;
 class ImageLayerComposite;
 class LayerComposite;
 class RefLayerComposite;
 class SurfaceDescriptor;
 class ThebesLayerComposite;
 class TiledLayerComposer;
+class TextRenderer;
 struct FPSState;
 
 class LayerManagerComposite : public LayerManager
 {
 public:
   LayerManagerComposite(Compositor* aCompositor);
   ~LayerManagerComposite();
   
@@ -222,16 +223,18 @@ public:
    * that can request a next frame.
    */
   bool DebugOverlayWantsNextFrame() { return mDebugOverlayWantsNextFrame; }
   void SetDebugOverlayWantsNextFrame(bool aVal)
   { mDebugOverlayWantsNextFrame = aVal; }
 
   void NotifyShadowTreeTransaction();
 
+  TextRenderer* GetTextRenderer() { return mTextRenderer; }
+
 private:
   /** Region we're clipping our current drawing to. */
   nsIntRegion mClippingRegion;
   nsIntRect mRenderBounds;
 
   /** Current root layer. */
   LayerComposite* RootLayer() const;
 
@@ -268,16 +271,18 @@ private:
 
   gfx::Matrix mWorldMatrix;
   nsIntRegion mInvalidRegion;
   nsAutoPtr<FPSState> mFPS;
 
   bool mInTransaction;
   bool mIsCompositorReady;
   bool mDebugOverlayWantsNextFrame;
+
+  RefPtr<TextRenderer> mTextRenderer;
 };
 
 /**
  * Composite layers are for use with OMTC on the compositor thread only. There
  * must be corresponding Basic layers on the content thread. For composite
  * layers, the layer manager only maintains the layer tree, all rendering is
  * done by a Compositor (see Compositor.h). As such, composite layers are
  * platform-independent and can be used on any platform for which there is a
new file mode 100644
--- /dev/null
+++ b/gfx/layers/composite/TextRenderer.cpp
@@ -0,0 +1,163 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TextRenderer.h"
+#include "FontData.h"
+#include "png.h"
+#include "mozilla/Base64.h"
+#include "mozilla/layers/Compositor.h"
+#include "mozilla/layers/TextureHost.h"
+#include "mozilla/layers/Effects.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace gfx;
+using namespace std;
+
+const Float sBackgroundOpacity = 0.6f;
+const SurfaceFormat sTextureFormat = SurfaceFormat::B8G8R8A8;
+
+static void PNGAPI info_callback(png_structp png_ptr, png_infop info_ptr)
+{
+  png_read_update_info(png_ptr, info_ptr);
+}
+
+static void PNGAPI row_callback(png_structp png_ptr, png_bytep new_row, png_uint_32 row_num, int pass)
+{
+  MOZ_ASSERT(sTextureFormat == SurfaceFormat::B8G8R8A8);
+
+  DataSourceSurface::MappedSurface map = static_cast<TextRenderer*>(png_get_progressive_ptr(png_ptr))->GetSurfaceMap();
+
+  uint32_t* dst = (uint32_t*)(map.mData + map.mStride * row_num);
+
+  for (uint32_t x = 0; x < sTextureWidth; x++) {
+    // We blend to a transparent white background, this will make text readable
+    // even if it's on a dark background. Without hurting our ability to
+    // interact with the content behind the text.
+    Float alphaValue = Float(0xFF - new_row[x]) / 255.0f;
+    Float baseValue = sBackgroundOpacity * (1.0f - alphaValue);
+    Color pixelColor(baseValue, baseValue, baseValue, baseValue + alphaValue);
+    dst[x] = pixelColor.ToABGR();
+  }
+}
+
+TextRenderer::~TextRenderer()
+{
+  if (mGlyphBitmaps) {
+    mGlyphBitmaps->Unmap();
+  }
+}
+
+void
+TextRenderer::RenderText(const string& aText, const IntPoint& aOrigin,
+                         const Matrix4x4& aTransform, uint32_t aTextSize,
+                         uint32_t aTargetPixelWidth)
+{
+  EnsureInitialized();
+
+  // For now we only have a bitmap font with a 16px cell size, so we just
+  // scale it up if the user wants larger text.
+  Float scaleFactor = Float(aTextSize) / Float(sCellHeight);
+
+  aTargetPixelWidth /= scaleFactor;
+
+  uint32_t numLines = 1;
+  uint32_t maxWidth = 0;
+  uint32_t lineWidth = 0;
+  // Calculate the size of the surface needed to draw all the glyphs.
+  for (uint32_t i = 0; i < aText.length(); i++) {
+    // Insert a line break if we go past the TargetPixelWidth.
+    // XXX - this has the downside of overrunning the intended width, causing
+    // things at the edge of a window to be cut off.
+    if (aText[i] == '\n' || (aText[i] == ' ' && lineWidth > aTargetPixelWidth)) {
+      numLines++;
+      lineWidth = 0;
+      continue;
+    }
+
+    lineWidth += sGlyphWidths[uint32_t(aText[i])];
+    maxWidth = std::max(lineWidth, maxWidth);
+  }
+
+  // Create a surface to draw our glyphs to.
+  RefPtr<DataSourceSurface> textSurf =
+    Factory::CreateDataSourceSurface(IntSize(maxWidth, numLines * sCellHeight), sTextureFormat);
+
+  DataSourceSurface::MappedSurface map;
+  textSurf->Map(DataSourceSurface::MapType::READ_WRITE, &map);
+
+  // Initialize the surface to transparent white.
+  memset(map.mData, uint8_t(sBackgroundOpacity * 255.0f),
+         numLines * sCellHeight * map.mStride);
+
+  uint32_t currentXPos = 0;
+  uint32_t currentYPos = 0;
+
+  // Copy our glyphs onto the surface.
+  for (uint32_t i = 0; i < aText.length(); i++) {
+    if (aText[i] == '\n' || (aText[i] == ' ' && currentXPos > aTargetPixelWidth)) {
+      currentYPos += sCellHeight;
+      currentXPos = 0;
+      continue;
+    }
+
+    uint32_t glyphXOffset = aText[i] % (sTextureWidth / sCellWidth) * sCellWidth * BytesPerPixel(sTextureFormat);
+    uint32_t truncatedLine = aText[i] / (sTextureWidth / sCellWidth);
+    uint32_t glyphYOffset =  truncatedLine * sCellHeight * mMap.mStride;
+
+    for (int y = 0; y < 16; y++) {
+      memcpy(map.mData + (y + currentYPos) * map.mStride + currentXPos * BytesPerPixel(sTextureFormat),
+             mMap.mData + glyphYOffset + y * mMap.mStride + glyphXOffset,
+             sGlyphWidths[uint32_t(aText[i])] * BytesPerPixel(sTextureFormat));
+    }
+
+    currentXPos += sGlyphWidths[uint32_t(aText[i])];
+  }
+
+  textSurf->Unmap();
+
+  RefPtr<DataTextureSource> src = mCompositor->CreateDataTextureSource();
+
+  if (!src->Update(textSurf)) {
+    // Upload failed.
+    return;
+  }
+
+  RefPtr<EffectRGB> effect = new EffectRGB(src, true, Filter::LINEAR);
+  EffectChain chain;
+  chain.mPrimaryEffect = effect;
+
+  Matrix4x4 transform = aTransform;
+  transform.Scale(scaleFactor, scaleFactor, 1.0f);
+  mCompositor->DrawQuad(Rect(aOrigin.x, aOrigin.y, maxWidth, numLines * 16),
+                        Rect(-10000, -10000, 20000, 20000), chain, 1.0f, transform);
+}
+
+void
+TextRenderer::EnsureInitialized()
+{
+  if (mGlyphBitmaps) {
+    return;
+  }
+
+  mGlyphBitmaps = Factory::CreateDataSourceSurface(IntSize(sTextureWidth, sTextureHeight), sTextureFormat);
+
+  mGlyphBitmaps->Map(DataSourceSurface::MapType::READ_WRITE, &mMap);
+
+  png_structp png_ptr = NULL;
+  png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
+
+  png_set_progressive_read_fn(png_ptr, this, info_callback, row_callback, nullptr);
+  png_infop info_ptr = NULL;
+  info_ptr = png_create_info_struct(png_ptr);
+
+  png_process_data(png_ptr, info_ptr, (uint8_t*)sFontPNG, sizeof(sFontPNG));
+
+  png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
+}
+
+}
+}
new file mode 100644
--- /dev/null
+++ b/gfx/layers/composite/TextRenderer.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_TextRenderer_H
+#define GFX_TextRenderer_H
+
+#include "mozilla/gfx/2D.h"
+#include <string>
+
+namespace mozilla {
+namespace layers {
+
+class Compositor;
+
+class TextRenderer : public RefCounted<TextRenderer>
+{
+public:
+  MOZ_DECLARE_REFCOUNTED_TYPENAME(TextRenderer)
+  TextRenderer(Compositor *aCompositor)
+    : mCompositor(aCompositor)
+  {
+  }
+
+  ~TextRenderer();
+
+  void RenderText(const std::string& aText, const gfx::IntPoint& aOrigin,
+                  const gfx::Matrix4x4& aTransform, uint32_t aTextSize,
+                  uint32_t aTargetPixelWidth);
+
+  gfx::DataSourceSurface::MappedSurface& GetSurfaceMap() { return mMap; }
+
+private:
+
+  void EnsureInitialized();
+
+  RefPtr<Compositor> mCompositor;
+  RefPtr<gfx::DataSourceSurface> mGlyphBitmaps;
+  gfx::DataSourceSurface::MappedSurface mMap;
+};
+
+}
+}
+
+#endif
--- a/gfx/layers/composite/ThebesLayerComposite.cpp
+++ b/gfx/layers/composite/ThebesLayerComposite.cpp
@@ -21,16 +21,17 @@
 #include "nsAString.h"
 #include "nsAutoPtr.h"                  // for nsRefPtr
 #include "nsISupportsImpl.h"            // for MOZ_COUNT_CTOR, etc
 #include "nsMathUtils.h"                // for NS_lround
 #include "nsPoint.h"                    // for nsIntPoint
 #include "nsRect.h"                     // for nsIntRect
 #include "nsSize.h"                     // for nsIntSize
 #include "nsString.h"                   // for nsAutoCString
+#include "TextRenderer.h"
 #include "GeckoProfiler.h"
 
 namespace mozilla {
 namespace layers {
 
 class TiledLayerComposer;
 
 ThebesLayerComposite::ThebesLayerComposite(LayerManagerComposite *aManager)
--- a/gfx/layers/composite/TiledContentHost.cpp
+++ b/gfx/layers/composite/TiledContentHost.cpp
@@ -317,17 +317,17 @@ TiledContentHost::RenderTile(const TileH
     return;
   }
 
   nsIntRect screenBounds = aScreenRegion.GetBounds();
   Matrix mat = aTransform.As2D();
   Rect quad(screenBounds.x, screenBounds.y, screenBounds.width, screenBounds.height);
   quad = mat.TransformBounds(quad);
 
-  if (!quad.Intersects(aClipRect)) {
+  if (!quad.Intersects(mCompositor->ClipRectInLayersCoordinates(aClipRect))) {
     return;
   }
 
   AutoLockTextureHost autoLock(aTile.mTextureHost);
   if (autoLock.Failed()) {
     NS_WARNING("Failed to lock tile");
     return;
   }
--- a/gfx/layers/ipc/AsyncPanZoomController.cpp
+++ b/gfx/layers/ipc/AsyncPanZoomController.cpp
@@ -307,16 +307,23 @@ static bool IsCloseToHorizontal(float aA
 }
 
 // As above, but for the vertical axis.
 static bool IsCloseToVertical(float aAngle, float aThreshold)
 {
   return (fabs(aAngle - (M_PI / 2)) < aThreshold);
 }
 
+template <typename Units>
+static bool IsZero(const gfx::PointTyped<Units>& aPoint)
+{
+  return FuzzyEqualsMultiplicative(aPoint.x, 0.0f)
+      && FuzzyEqualsMultiplicative(aPoint.y, 0.0f);
+}
+
 static inline void LogRendertraceRect(const ScrollableLayerGuid& aGuid, const char* aDesc, const char* aColor, const CSSRect& aRect)
 {
 #ifdef APZC_ENABLE_RENDERTRACE
   static const TimeStamp sRenderStart = TimeStamp::Now();
   TimeDuration delta = TimeStamp::Now() - sRenderStart;
   printf_stderr("(%llu,%lu,%llu)%s RENDERTRACE %f rect %s %f %f %f %f\n",
     aGuid.mLayersId, aGuid.mPresShellId, aGuid.mScrollId,
     aDesc, delta.ToMilliseconds(), aColor,
@@ -334,33 +341,31 @@ GetFrameTime() {
   if (sFrameTime.IsNull()) {
     return TimeStamp::Now();
   }
   return sFrameTime;
 }
 
 class FlingAnimation: public AsyncPanZoomAnimation {
 public:
-  FlingAnimation(AxisX& aX, AxisY& aY)
+  FlingAnimation(AsyncPanZoomController& aApzc)
     : AsyncPanZoomAnimation(TimeDuration::FromMilliseconds(gFlingRepaintInterval))
-    , mX(aX)
-    , mY(aY)
+    , mApzc(aApzc)
   {}
   /**
    * Advances a fling by an interpolated amount based on the passed in |aDelta|.
    * This should be called whenever sampling the content transform for this
    * frame. Returns true if the fling animation should be advanced by one frame,
    * or false if there is no fling or the fling has ended.
    */
   virtual bool Sample(FrameMetrics& aFrameMetrics,
                       const TimeDuration& aDelta);
 
 private:
-  AxisX& mX;
-  AxisY& mY;
+  AsyncPanZoomController& mApzc;
 };
 
 class ZoomAnimation: public AsyncPanZoomAnimation {
 public:
   ZoomAnimation(CSSPoint aStartOffset, CSSToScreenScale aStartZoom,
                 CSSPoint aEndOffset, CSSToScreenScale aEndZoom)
     : mStartOffset(aStartOffset)
     , mStartZoom(aStartZoom)
@@ -745,31 +750,32 @@ nsEventStatus AsyncPanZoomController::On
     SetState(NOTHING);
     return nsEventStatus_eIgnore;
 
   case PANNING:
   case PANNING_LOCKED_X:
   case PANNING_LOCKED_Y:
     {
       // Make a local copy of the tree manager pointer and check if it's not
-      // null before calling HandleOverscroll(). This is necessary because
-      // Destroy(), which nulls out mTreeManager, could be called concurrently.
+      // null before calling FlushRepaintsForOverscrollHandoffChain().
+      // This is necessary because Destroy(), which nulls out mTreeManager,
+      // could be called concurrently.
       APZCTreeManager* treeManagerLocal = mTreeManager;
       if (treeManagerLocal) {
         if (!treeManagerLocal->FlushRepaintsForOverscrollHandoffChain()) {
           NS_WARNING("Overscroll handoff chain was empty during panning! This should not be the case.");
           // Graceful handling of error condition
           FlushRepaintForOverscrollHandoff();
         }
       }
     }
     mX.EndTouch();
     mY.EndTouch();
     SetState(FLING);
-    StartAnimation(new FlingAnimation(mX, mY));
+    StartAnimation(new FlingAnimation(*this));
     return nsEventStatus_eConsumeNoDefault;
 
   case PINCHING:
     SetState(NOTHING);
     // Scale gesture listener should have handled this.
     NS_WARNING("Gesture listener should have handled pinching in OnTouchEnd.");
     return nsEventStatus_eIgnore;
 
@@ -1147,49 +1153,58 @@ void AsyncPanZoomController::AttemptScro
 
   ScreenPoint overscroll;  // will be used outside monitor block
   {
     ReentrantMonitorAutoEnter lock(mMonitor);
 
     CSSToScreenScale zoom = mFrameMetrics.mZoom;
 
     // Inversely scale the offset by the resolution (when you're zoomed further in,
-    // a larger swipe should move you a shorter distance).
+    // the same swipe should move you a shorter distance).
     CSSPoint cssDisplacement = displacement / zoom;
 
     CSSPoint cssOverscroll;
-    gfx::Point scrollOffset(mX.AdjustDisplacement(cssDisplacement.x,
-                                                  cssOverscroll.x),
-                            mY.AdjustDisplacement(cssDisplacement.y,
-                                                  cssOverscroll.y));
+    CSSPoint allowedDisplacement(mX.AdjustDisplacement(cssDisplacement.x,
+                                                       cssOverscroll.x),
+                                 mY.AdjustDisplacement(cssDisplacement.y,
+                                                       cssOverscroll.y));
     overscroll = cssOverscroll * zoom;
 
-    if (fabs(scrollOffset.x) > EPSILON || fabs(scrollOffset.y) > EPSILON) {
-      ScrollBy(CSSPoint::FromUnknownPoint(scrollOffset));
+    if (!IsZero(allowedDisplacement)) {
+      ScrollBy(allowedDisplacement);
       ScheduleComposite();
 
       TimeDuration timePaintDelta = mPaintThrottler.TimeSinceLastRequest(GetFrameTime());
       if (timePaintDelta.ToMilliseconds() > gPanRepaintInterval) {
         RequestContentRepaint();
       }
       UpdateSharedCompositorFrameMetrics();
     }
   }
 
-  if (fabs(overscroll.x) > EPSILON || fabs(overscroll.y) > EPSILON) {
+  if (!IsZero(overscroll)) {
     // "+ overscroll" rather than "- overscroll" because "overscroll" is what's
     // left of "displacement", and "displacement" is "start - end".
     CallDispatchScroll(aEndPoint + overscroll, aEndPoint, aOverscrollHandoffChainIndex + 1);
   }
 }
 
+void AsyncPanZoomController::TakeOverFling(ScreenPoint aVelocity) {
+  // We may have a pre-existing velocity for whatever reason (for example,
+  // a previously handed off fling). We don't want to clobber that.
+  mX.SetVelocity(mX.GetVelocity() + aVelocity.x);
+  mY.SetVelocity(mY.GetVelocity() + aVelocity.y);
+  SetState(FLING);
+  StartAnimation(new FlingAnimation(*this));
+}
+
 void AsyncPanZoomController::CallDispatchScroll(const ScreenPoint& aStartPoint, const ScreenPoint& aEndPoint,
                                                 uint32_t aOverscrollHandoffChainIndex) {
   // Make a local copy of the tree manager pointer and check if it's not
-  // null before calling HandleOverscroll(). This is necessary because
+  // null before calling DispatchScroll(). This is necessary because
   // Destroy(), which nulls out mTreeManager, could be called concurrently.
   APZCTreeManager* treeManagerLocal = mTreeManager;
   if (treeManagerLocal) {
     treeManagerLocal->DispatchScroll(this, aStartPoint, aEndPoint,
                                      aOverscrollHandoffChainIndex);
   }
 }
 
@@ -1235,34 +1250,86 @@ void AsyncPanZoomController::TrackTouch(
 }
 
 ScreenIntPoint& AsyncPanZoomController::GetFirstTouchScreenPoint(const MultiTouchInput& aEvent) {
   return ((SingleTouchData&)aEvent.mTouches[0]).mScreenPoint;
 }
 
 bool FlingAnimation::Sample(FrameMetrics& aFrameMetrics,
                             const TimeDuration& aDelta) {
-  bool shouldContinueFlingX = mX.FlingApplyFrictionOrCancel(aDelta),
-       shouldContinueFlingY = mY.FlingApplyFrictionOrCancel(aDelta);
+
+  // If the fling is handed off to our APZC from a child, on the first call to
+  // Sample() aDelta might be negative because it's computed as the sample time
+  // from SampleContentTransformForFrame() minus our APZC's mLastSampleTime
+  // which is the time the child handed off the fling from its call to
+  // SampleContentTransformForFrame() with the same sample time. If we allow
+  // the negative aDelta to be processed, it will yield a displacement in the
+  // direction opposite to the fling, which can cause us to overscroll and
+  // hand off the fling to _our_ parent, which effectively kills the fling.
+  if (aDelta.ToMilliseconds() <= 0) {
+    return true;
+  }
+
+  bool shouldContinueFlingX = mApzc.mX.FlingApplyFrictionOrCancel(aDelta),
+       shouldContinueFlingY = mApzc.mY.FlingApplyFrictionOrCancel(aDelta);
   // If we shouldn't continue the fling, let's just stop and repaint.
   if (!shouldContinueFlingX && !shouldContinueFlingY) {
     return false;
   }
 
-  CSSPoint overscroll; // overscroll is ignored for flings
-  ScreenPoint offset(aDelta.ToMilliseconds() * mX.GetVelocity(),
-                     aDelta.ToMilliseconds() * mY.GetVelocity());
+  // AdjustDisplacement() zeroes out the Axis velocity if we're in overscroll.
+  // Since we need to hand off the velocity to the tree manager in such a case,
+  // we save it here. Would be ScreenVector instead of ScreenPoint if we had
+  // vector classes.
+  ScreenPoint velocity(mApzc.mX.GetVelocity(), mApzc.mY.GetVelocity());
+
+  ScreenPoint offset = velocity * aDelta.ToMilliseconds();
 
   // Inversely scale the offset by the resolution (when you're zoomed further in,
-  // a larger swipe should move you a shorter distance).
+  // the same swipe should move you a shorter distance).
   CSSPoint cssOffset = offset / aFrameMetrics.mZoom;
-  aFrameMetrics.mScrollOffset += CSSPoint::FromUnknownPoint(gfx::Point(
-    mX.AdjustDisplacement(cssOffset.x, overscroll.x),
-    mY.AdjustDisplacement(cssOffset.y, overscroll.y)
-  ));
+  CSSPoint overscroll;
+  aFrameMetrics.mScrollOffset += CSSPoint(
+    mApzc.mX.AdjustDisplacement(cssOffset.x, overscroll.x),
+    mApzc.mY.AdjustDisplacement(cssOffset.y, overscroll.y)
+  );
+
+  // If the fling has caused us to reach the end of our scroll range, hand
+  // off the fling to the next APZC in the overscroll handoff chain.
+  if (!IsZero(overscroll)) {
+    // We may have reached the end of the scroll range along one axis but
+    // not the other. In such a case we only want to hand off the relevant
+    // component of the fling.
+    if (FuzzyEqualsMultiplicative(overscroll.x, 0.0f)) {
+      velocity.x = 0;
+    } else if (FuzzyEqualsMultiplicative(overscroll.y, 0.0f)) {
+      velocity.y = 0;
+    }
+
+    // To hand off the fling, we call APZCTreeManager::HandleFlingOverscroll()
+    // which starts a new fling in the next APZC in the handoff chain with
+    // the same velocity. For simplicity, the actual overscroll of the current
+    // sample is discarded rather than being handed off. The compositor should
+    // sample animations sufficiently frequently that this is not noticeable.
+
+    // Make a local copy of the tree manager pointer and check if it's not
+    // null before calling HandleFlingOverscroll(). This is necessary because
+    // Destroy(), which nulls out mTreeManager, could be called concurrently.
+    APZCTreeManager* treeManagerLocal = mApzc.mTreeManager;
+    if (treeManagerLocal) {
+      // APZC is holding mMonitor, so directly calling HandleFlingOverscroll()
+      // (which acquires the tree lock) would violate the lock ordering. Instead
+      // we schedule HandleFlingOverscroll() to be called after mMonitor is
+      // released.
+      mDeferredTasks.append(NewRunnableMethod(treeManagerLocal,
+                                              &APZCTreeManager::HandOffFling,
+                                              &mApzc,
+                                              velocity));
+    }
+  }
 
   return true;
 }
 
 void AsyncPanZoomController::StartAnimation(AsyncPanZoomAnimation* aAnimation)
 {
   ReentrantMonitorAutoEnter lock(mMonitor);
   mAnimation = aAnimation;
@@ -1530,16 +1597,22 @@ bool AsyncPanZoomController::SampleConte
 
     LogRendertraceRect(GetGuid(), "viewport", "red",
       CSSRect(mFrameMetrics.mScrollOffset,
               ParentLayerSize(mFrameMetrics.mCompositionBounds.Size()) / mFrameMetrics.GetZoomToParent()));
 
     mCurrentAsyncScrollOffset = mFrameMetrics.mScrollOffset;
   }
 
+  // Execute tasks queued up by mAnimation's Sample() (called by
+  // UpdateAnimation()) for execution after mMonitor has been released.
+  if (mAnimation) {
+    mAnimation->ExecuteDeferredTasks();
+  }
+
   // Cancel the mAsyncScrollTimeoutTask because we will fire a
   // mozbrowserasyncscroll event or renew the mAsyncScrollTimeoutTask again.
   if (mAsyncScrollTimeoutTask) {
     mAsyncScrollTimeoutTask->Cancel();
     mAsyncScrollTimeoutTask = nullptr;
   }
   // Fire the mozbrowserasyncscroll event immediately if it's been
   // sAsyncScrollThrottleTime ms since the last time we fired the event and the
--- a/gfx/layers/ipc/AsyncPanZoomController.h
+++ b/gfx/layers/ipc/AsyncPanZoomController.h
@@ -35,16 +35,17 @@ namespace layers {
 struct ScrollableLayerGuid;
 class CompositorParent;
 class GestureEventListener;
 class ContainerLayer;
 class PCompositorParent;
 class ViewTransform;
 class APZCTreeManager;
 class AsyncPanZoomAnimation;
+class FlingAnimation;
 
 /**
  * Controller for all panning and zooming logic. Any time a user input is
  * detected and it must be processed in some way to affect what the user sees,
  * it goes through here. Listens for any input event from InputData and can
  * optionally handle WidgetGUIEvent-derived touch events, but this must be done
  * on the main thread. Note that this class completely cross-platform.
  *
@@ -297,16 +298,22 @@ public:
    * of which APZC to hand off the overscroll to; this function increments it
    * and passes it on to APZCTreeManager::DispatchScroll() in the event of
    * overscroll.
    */
   void AttemptScroll(const ScreenPoint& aStartPoint, const ScreenPoint& aEndPoint,
                      uint32_t aOverscrollHandoffChainIndex = 0);
 
   /**
+   * Take over a fling with the given velocity from another APZC. Used for
+   * during overscroll handoff for a fling.
+   */
+  void TakeOverFling(ScreenPoint aVelocity);
+
+  /**
    * Returns allowed touch behavior for the given point on the scrollable layer.
    * Internally performs a kind of hit testing based on the regions constructed
    * on the main thread and attached to the current scrollable layer. Each of such regions
    * contains info about allowed touch behavior. If regions info isn't enough it returns
    * UNKNOWN value and we should switch to the fallback approach - asking content.
    * TODO: for now it's only a stub and returns hardcoded magic value. As soon as bug 928833
    * is done we should integrate its logic here.
    */
@@ -645,16 +652,17 @@ protected:
   // Both |mFrameMetrics| and |mLastContentPaintMetrics| are protected by the
   // monitor. Do not read from or modify either of them without locking.
   FrameMetrics mFrameMetrics;
 
   // Protects |mFrameMetrics|, |mLastContentPaintMetrics|, and |mState|.
   // Before manipulating |mFrameMetrics| or |mLastContentPaintMetrics|, the
   // monitor should be held. When setting |mState|, either the SetState()
   // function can be used, or the monitor can be held and then |mState| updated.
+  // IMPORTANT: See the note about lock ordering at the top of APZCTreeManager.h.
   ReentrantMonitor mMonitor;
 
   // Specifies whether we should use touch-action css property. Initialized from
   // the preferences. This property (in comparison with the global one) simplifies
   // testing apzc with (and without) touch-action property enabled concurrently
   // (e.g. with the gtest framework).
   bool mTouchActionPropertyEnabled;
 
@@ -742,16 +750,17 @@ private:
   bool mPreventDefaultSet;
 
   // Extra offset to add in SampleContentTransformForFrame for testing
   CSSPoint mTestAsyncScrollOffset;
 
   RefPtr<AsyncPanZoomAnimation> mAnimation;
 
   friend class Axis;
+  friend class FlingAnimation;
 
   /* The functions and members in this section are used to build a tree
    * structure out of APZC instances. This tree can only be walked or
    * manipulated while holding the lock in the associated APZCTreeManager
    * instance.
    */
 public:
   void SetLastChild(AsyncPanZoomController* child) {
@@ -860,19 +869,38 @@ public:
 
   virtual ~AsyncPanZoomAnimation()
   { }
 
   virtual bool Sample(FrameMetrics& aFrameMetrics,
                       const TimeDuration& aDelta) = 0;
 
   /**
+   * Execute the tasks in |mDeferredTasks| in order. See |mDeferredTasks|
+   * for more information.
+   */
+  void ExecuteDeferredTasks() {
+    for (uint32_t i = 0; i < mDeferredTasks.length(); ++i) {
+      mDeferredTasks[i]->Run();
+    }
+    mDeferredTasks.clear();
+  }
+
+  /**
    * Specifies how frequently (at most) we want to do repaints during the
    * animation sequence. TimeDuration::Forever() will cause it to only repaint
    * at the end of the animation.
    */
   TimeDuration mRepaintInterval;
+
+protected:
+  /**
+   * Tasks scheduled for execution after the APZC's mMonitor is released.
+   * Derived classes can add tasks here in Sample(), and the APZC can call
+   * ExecuteDeferredTasks() to execute them.
+   */
+  Vector<Task*> mDeferredTasks;
 };
 
 }
 }
 
 #endif // mozilla_layers_PanZoomController_h
--- a/gfx/layers/ipc/Axis.cpp
+++ b/gfx/layers/ipc/Axis.cpp
@@ -249,16 +249,20 @@ float Axis::ScaleWillOverscrollAmount(fl
   }
   return 0;
 }
 
 float Axis::GetVelocity() {
   return mAxisLocked ? 0 : mVelocity;
 }
 
+void Axis::SetVelocity(float aVelocity) {
+  mVelocity = aVelocity;
+}
+
 float Axis::GetCompositionEnd() {
   return GetOrigin() + GetCompositionLength();
 }
 
 float Axis::GetPageEnd() {
   return GetPageStart() + GetPageLength();
 }
 
--- a/gfx/layers/ipc/Axis.h
+++ b/gfx/layers/ipc/Axis.h
@@ -124,16 +124,26 @@ public:
   float GetExcess();
 
   /**
    * Gets the raw velocity of this axis at this moment.
    */
   float GetVelocity();
 
   /**
+   * Sets the raw velocity of this axis at this moment.
+   * Intended to be called only when the axis "takes over" a velocity from
+   * another APZC, in which case there are no touch points available to call
+   * UpdateWithTouchAtDevicePoint. In other circumstances,
+   * UpdateWithTouchAtDevicePoint should be used and the velocity calculated
+   * there.
+   */
+  void SetVelocity(float aVelocity);
+
+  /**
    * Gets the overscroll state of the axis given an additional displacement.
    * That is to say, if the given displacement is applied, this will tell you
    * whether or not it will overscroll, and in what direction.
    */
   Overscroll DisplacementWillOverscroll(float aDisplacement);
 
   /**
    * If a displacement will overscroll the axis, this returns the amount and in
--- a/gfx/layers/ipc/LayerTransactionParent.cpp
+++ b/gfx/layers/ipc/LayerTransactionParent.cpp
@@ -206,16 +206,21 @@ LayerTransactionParent::RecvUpdate(const
 #endif
 
   MOZ_LAYERS_LOG(("[ParentSide] received txn with %d edits", cset.Length()));
 
   if (mDestroyed || !layer_manager() || layer_manager()->IsDestroyed()) {
     return true;
   }
 
+  if (mLayerManager && mLayerManager->GetCompositor() &&
+      !targetConfig.naturalBounds().IsEmpty()) {
+    mLayerManager->GetCompositor()->SetScreenRotation(targetConfig.rotation());
+  }
+
   // Clear fence handles used in previsou transaction.
   ClearPrevFenceHandles();
 
   EditReplyVector replyv;
 
   {
     AutoResolveRefLayers resolve(mShadowLayersManager->GetCompositionManager(this));
     layer_manager()->BeginTransaction();
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -246,16 +246,17 @@ UNIFIED_SOURCES += [
     'composite/CanvasLayerComposite.cpp',
     'composite/ColorLayerComposite.cpp',
     'composite/CompositableHost.cpp',
     'composite/ContainerLayerComposite.cpp',
     'composite/ContentHost.cpp',
     'composite/ImageHost.cpp',
     'composite/ImageLayerComposite.cpp',
     'composite/LayerManagerComposite.cpp',
+    'composite/TextRenderer.cpp',
     'composite/TextureHost.cpp',
     'composite/ThebesLayerComposite.cpp',
     'composite/TiledContentHost.cpp',
     'Compositor.cpp',
     'CopyableCanvasLayer.cpp',
     'Effects.cpp',
     'ImageDataSerializer.cpp',
     'ImageLayers.cpp',
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -149,16 +149,17 @@ private:
 #else
   // If MOZ_GFX_OPTIMIZE_MOBILE is not defined, we actually take the
   // preference value, defaulting to true.
   DECL_GFX_PREF(Once, "layers.componentalpha.enabled",         ComponentAlphaEnabled, bool, true);
 #endif
   DECL_GFX_PREF(Live, "layers.draw-bigimage-borders",          DrawBigImageBorders, bool, false);
   DECL_GFX_PREF(Live, "layers.draw-borders",                   DrawLayerBorders, bool, false);
   DECL_GFX_PREF(Live, "layers.draw-tile-borders",              DrawTileBorders, bool, false);
+  DECL_GFX_PREF(Live, "layers.draw-layer-info",                DrawLayerInfo, bool, false);
   DECL_GFX_PREF(Once, "layers.dump",                           LayersDump, bool, false);
   DECL_GFX_PREF(Once, "layers.enable-tiles",                   LayersTilesEnabled, bool, false);
   DECL_GFX_PREF(Once, "layers.simple-tiles",                   LayersUseSimpleTiles, bool, false);
   DECL_GFX_PREF(Once, "layers.force-per-tile-drawing",         PerTileDrawing, bool, false);
   DECL_GFX_PREF(Once, "layers.overzealous-gralloc-unlocking",  OverzealousGrallocUnlocking, bool, false);
   DECL_GFX_PREF(Once, "layers.force-shmem-tiles",              ForceShmemTiles, bool, false);
   DECL_GFX_PREF(Live, "layers.frame-counter",                  DrawFrameCounter, bool, false);
   DECL_GFX_PREF(Live, "layers.low-precision-buffer",           UseLowPrecisionBuffer, bool, false);
--- a/js/src/jit/AsmJSModule.cpp
+++ b/js/src/jit/AsmJSModule.cpp
@@ -205,16 +205,24 @@ static void *
 RedirectCall(void *fun, ABIFunctionType type)
 {
 #ifdef JS_ARM_SIMULATOR
     fun = Simulator::RedirectNativeFunction(fun, type);
 #endif
     return fun;
 }
 
+#ifdef DEBUG
+static void
+AssumeUnreachable()
+{
+    MOZ_CRASH("Reached unreachable code in asm.js");
+}
+#endif
+
 static void *
 AddressOf(AsmJSImmKind kind, ExclusiveContext *cx)
 {
     switch (kind) {
       case AsmJSImm_Runtime:
         return cx->runtimeAddressForJit();
       case AsmJSImm_StackLimit:
         return cx->stackLimitAddressForJitCode(StackForUntrustedScript);
@@ -269,16 +277,20 @@ AddressOf(AsmJSImmKind kind, ExclusiveCo
       case AsmJSImm_ExpD:
         return RedirectCall(FuncCast<double (double)>(exp), Args_Double_Double);
       case AsmJSImm_LogD:
         return RedirectCall(FuncCast<double (double)>(log), Args_Double_Double);
       case AsmJSImm_PowD:
         return RedirectCall(FuncCast(ecmaPow), Args_Double_DoubleDouble);
       case AsmJSImm_ATan2D:
         return RedirectCall(FuncCast(ecmaAtan2), Args_Double_DoubleDouble);
+#ifdef DEBUG
+      case AsmJSImm_AssumeUnreachable:
+        return RedirectCall(FuncCast(AssumeUnreachable), Args_General0);
+#endif
       case AsmJSImm_Invalid:
         break;
     }
 
     MOZ_ASSUME_UNREACHABLE("Bad AsmJSImmKind");
     return nullptr;
 }
 
--- a/js/src/jit/IonMacroAssembler.cpp
+++ b/js/src/jit/IonMacroAssembler.cpp
@@ -1274,30 +1274,34 @@ AssumeUnreachable_(const char *output) {
     MOZ_ReportAssertionFailure(output, __FILE__, __LINE__);
 }
 #endif
 
 void
 MacroAssembler::assumeUnreachable(const char *output)
 {
 #ifdef DEBUG
-    // AsmJS forbids use of ImmPtr.
-    if (!IsCompilingAsmJS()) {
-        RegisterSet regs = RegisterSet::Volatile();
-        PushRegsInMask(regs);
+    RegisterSet regs = RegisterSet::Volatile();
+    PushRegsInMask(regs);
+    Register temp = regs.takeGeneral();
 
-        Register temp = regs.takeGeneral();
-
+    // With ASLR, we can't rely on 'output' to point to the
+    // same char array after serialization/deserialization.
+    // It is not possible until we modify AsmJsImmPtr and
+    // the underlying "patching" mechanism.
+    if (IsCompilingAsmJS()) {
+        setupUnalignedABICall(0, temp);
+        callWithABINoProfiling(AsmJSImm_AssumeUnreachable);
+    } else {
         setupUnalignedABICall(1, temp);
         movePtr(ImmPtr(output), temp);
         passABIArg(temp);
         callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, AssumeUnreachable_));
-
-        PopRegsInMask(RegisterSet::Volatile());
     }
+    PopRegsInMask(RegisterSet::Volatile());
 #endif
 
     breakpoint();
 }
 
 static void
 Printf0_(const char *output) {
     printf("%s", output);
--- a/js/src/jit/shared/Assembler-shared.h
+++ b/js/src/jit/shared/Assembler-shared.h
@@ -699,16 +699,19 @@ enum AsmJSImmKind
     AsmJSImm_CeilD,
     AsmJSImm_CeilF,
     AsmJSImm_FloorD,
     AsmJSImm_FloorF,
     AsmJSImm_ExpD,
     AsmJSImm_LogD,
     AsmJSImm_PowD,
     AsmJSImm_ATan2D,
+#ifdef DEBUG
+    AsmJSImm_AssumeUnreachable,
+#endif
     AsmJSImm_Invalid
 };
 
 // Pointer to be embedded as an immediate in asm.js code.
 class AsmJSImmPtr
 {
     AsmJSImmKind kind_;
   public:
--- a/layout/base/nsBidiPresUtils.cpp
+++ b/layout/base/nsBidiPresUtils.cpp
@@ -1223,31 +1223,33 @@ nsBidiPresUtils::ResolveParagraphWithinB
                                              BidiParagraphData* aBpd)
 {
   aBpd->ClearBidiControls();
   ResolveParagraph(aBlockFrame, aBpd);
   aBpd->ResetData();
 }
 
 void
-nsBidiPresUtils::ReorderFrames(nsIFrame*            aFirstFrameOnLine,
-                               int32_t              aNumFramesOnLine)
+nsBidiPresUtils::ReorderFrames(nsIFrame*   aFirstFrameOnLine,
+                               int32_t     aNumFramesOnLine,
+                               WritingMode aLineWM,
+                               nscoord&    aLineWidth)
 {
   // If this line consists of a line frame, reorder the line frame's children.
   if (aFirstFrameOnLine->GetType() == nsGkAtoms::lineFrame) {
     aFirstFrameOnLine = aFirstFrameOnLine->GetFirstPrincipalChild();
     if (!aFirstFrameOnLine)
       return;
     // All children of the line frame are on the first line. Setting aNumFramesOnLine
     // to -1 makes InitLogicalArrayFromLine look at all of them.
     aNumFramesOnLine = -1;
   }
 
   BidiLineData bld(aFirstFrameOnLine, aNumFramesOnLine);
-  RepositionInlineFrames(&bld, aFirstFrameOnLine);
+  RepositionInlineFrames(&bld, aFirstFrameOnLine, aLineWM, aLineWidth);
 }
 
 nsIFrame*
 nsBidiPresUtils::GetFirstLeaf(nsIFrame* aFrame)
 {
   nsIFrame* firstLeaf = aFrame;
   while (!IsBidiLeaf(firstLeaf)) {
     nsIFrame* firstChild = firstLeaf->GetFirstPrincipalChild();
@@ -1277,32 +1279,30 @@ nsBidiPresUtils::GetFrameBaseLevel(nsIFr
   nsIFrame* firstLeaf = aFrame;
   while (!IsBidiLeaf(firstLeaf)) {
     firstLeaf = firstLeaf->GetFirstPrincipalChild();
   }
   return NS_GET_BASE_LEVEL(firstLeaf);
 }
 
 void
-nsBidiPresUtils::IsLeftOrRightMost(nsIFrame*              aFrame,
-                                   nsContinuationStates*  aContinuationStates,
-                                   bool&                aIsLeftMost /* out */,
-                                   bool&                aIsRightMost /* out */)
+nsBidiPresUtils::IsFirstOrLast(nsIFrame*             aFrame,
+                               nsContinuationStates* aContinuationStates,
+                               bool&                 aIsFirst /* out */,
+                               bool&                 aIsLast /* out */)
 {
-  const nsStyleVisibility* vis = aFrame->StyleVisibility();
-  bool isLTR = (NS_STYLE_DIRECTION_LTR == vis->mDirection);
-
   /*
-   * Since we lay out frames from left to right (in both LTR and RTL), visiting a
-   * frame with 'mFirstVisualFrame == nullptr', means it's the first appearance of
-   * one of its continuation chain frames on the line.
-   * To determine if it's the last visual frame of its continuation chain on the line
-   * or not, we count the number of frames of the chain on the line, and then reduce
-   * it when we lay out a frame of the chain. If this value becomes 1 it means
-   * that it's the last visual frame of its continuation chain on this line.
+   * Since we lay out frames in the line's direction, visiting a frame with
+   * 'mFirstVisualFrame == nullptr', means it's the first appearance of one
+   * of its continuation chain frames on the line.
+   * To determine if it's the last visual frame of its continuation chain on
+   * the line or not, we count the number of frames of the chain on the line,
+   * and then reduce it when we lay out a frame of the chain. If this value
+   * becomes 1 it means that it's the last visual frame of its continuation
+   * chain on this line.
    */
 
   nsFrameContinuationState* frameState = aContinuationStates->GetEntry(aFrame);
   nsFrameContinuationState* firstFrameState;
 
   if (!frameState->mFirstVisualFrame) {
     // aFrame is the first visual frame of its continuation chain
     nsFrameContinuationState* contState;
@@ -1329,142 +1329,152 @@ nsBidiPresUtils::IsLeftOrRightMost(nsIFr
     for (frame = aFrame->GetNextContinuation();
          frame && (contState = aContinuationStates->GetEntry(frame));
          frame = frame->GetNextContinuation()) {
       frameState->mFrameCount++;
       contState->mFirstVisualFrame = aFrame;
     }
     frameState->mHasContOnNextLines = (frame != nullptr);
 
-    aIsLeftMost = isLTR ? !frameState->mHasContOnPrevLines
-                        : !frameState->mHasContOnNextLines;
+    aIsFirst = !frameState->mHasContOnPrevLines;
     firstFrameState = frameState;
   } else {
     // aFrame is not the first visual frame of its continuation chain
-    aIsLeftMost = false;
+    aIsFirst = false;
     firstFrameState = aContinuationStates->GetEntry(frameState->mFirstVisualFrame);
   }
 
-  aIsRightMost = (firstFrameState->mFrameCount == 1) &&
-                 (isLTR ? !firstFrameState->mHasContOnNextLines
-                        : !firstFrameState->mHasContOnPrevLines);
+  aIsLast = (firstFrameState->mFrameCount == 1 &&
+             !firstFrameState->mHasContOnNextLines);
 
-  if ((aIsLeftMost || aIsRightMost) &&
+  if ((aIsFirst || aIsLast) &&
       (aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
     // For ib splits, don't treat anything except the last part as
     // endmost or anything except the first part as startmost.
     // As an optimization, only get the first continuation once.
     nsIFrame* firstContinuation = aFrame->FirstContinuation();
     if (firstContinuation->FrameIsNonLastInIBSplit()) {
       // We are not endmost
-      if (isLTR) {
-        aIsRightMost = false;
-      } else {
-        aIsLeftMost = false;
-      }
+      aIsLast = false;
     }
     if (firstContinuation->FrameIsNonFirstInIBSplit()) {
       // We are not startmost
-      if (isLTR) {
-        aIsLeftMost = false;
-      } else {
-        aIsRightMost = false;
-      }
+      aIsFirst = false;
     }
   }
 
   // Reduce number of remaining frames of the continuation chain on the line.
   firstFrameState->mFrameCount--;
 }
 
 void
-nsBidiPresUtils::RepositionFrame(nsIFrame*              aFrame,
-                                 bool                   aIsOddLevel,
-                                 nscoord&               aLeft,
-                                 nsContinuationStates*  aContinuationStates)
+nsBidiPresUtils::RepositionFrame(nsIFrame*             aFrame,
+                                 bool                  aIsEvenLevel,
+                                 nscoord&              aStart,
+                                 nsContinuationStates* aContinuationStates,
+                                 WritingMode           aLineWM,
+                                 nscoord&              aLineWidth)
 {
   if (!aFrame)
     return;
 
-  bool isLeftMost, isRightMost;
-  IsLeftOrRightMost(aFrame,
-                    aContinuationStates,
-                    isLeftMost /* out */,
-                    isRightMost /* out */);
+  bool isFirst, isLast;
+  IsFirstOrLast(aFrame,
+                aContinuationStates,
+                isFirst /* out */,
+                isLast /* out */);
 
+  WritingMode frameWM = aFrame->GetWritingMode();
   nsInlineFrame* testFrame = do_QueryFrame(aFrame);
+
+  //XXX temporary until GetSkipSides is logicalized
+  bool isLeftMost = false, isRightMost = false;
   if (testFrame) {
     aFrame->AddStateBits(NS_INLINE_FRAME_BIDI_VISUAL_STATE_IS_SET);
 
+    isLeftMost = ((isFirst && frameWM.IsBidiLTR()) ||
+                 (isLast && !frameWM.IsBidiLTR()));
     if (isLeftMost)
       aFrame->AddStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_LEFT_MOST);
     else
       aFrame->RemoveStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_LEFT_MOST);
 
+    isRightMost =  ((isLast && frameWM.IsBidiLTR()) ||
+                    (isFirst && !frameWM.IsBidiLTR()));
     if (isRightMost)
       aFrame->AddStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_RIGHT_MOST);
     else
       aFrame->RemoveStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_RIGHT_MOST);
   }
   // This method is called from nsBlockFrame::PlaceLine via the call to
   // bidiUtils->ReorderFrames, so this is guaranteed to be after the inlines
   // have been reflowed, which is required for GetUsedMargin/Border/Padding
-  nsMargin margin = aFrame->GetUsedMargin();
-  if (isLeftMost)
-    aLeft += margin.left;
+  LogicalMargin margin(frameWM, aFrame->GetUsedMargin());
+  if (isFirst) {
+    aStart += margin.IStart(frameWM);
+  }
 
-  nscoord start = aLeft;
+  nscoord start = aStart;
+  nscoord frameWidth = aFrame->GetSize().width;
 
   if (!IsBidiLeaf(aFrame))
   {
-    nscoord x = 0;
-    nsMargin borderPadding = aFrame->GetUsedBorderAndPadding();
-    if (isLeftMost) {
-      x += borderPadding.left;
+    nscoord iCoord = 0;
+    LogicalMargin borderPadding(frameWM, aFrame->GetUsedBorderAndPadding());
+    if (isFirst) {
+      iCoord += borderPadding.IStart(frameWM);
     }
 
-    // If aIsOddLevel is true, so we need to traverse the child list
-    // in reverse order, to make it O(n) we store the list locally and
-    // iterate the list reversely
+    // If the resolved direction of the container is different from the
+    // direction of the frame, we need to traverse the child list in reverse
+    // order, to make it O(n) we store the list locally and iterate the list
+    // in reverse
+    bool reverseOrder = aIsEvenLevel != frameWM.IsBidiLTR();
     nsTArray<nsIFrame*> childList;
     nsIFrame *frame = aFrame->GetFirstPrincipalChild();
-    if (frame && aIsOddLevel) {
+    if (frame && reverseOrder) {
       childList.AppendElement((nsIFrame*)nullptr);
       while (frame) {
         childList.AppendElement(frame);
         frame = frame->GetNextSibling();
       }
       frame = childList[childList.Length() - 1];
     }
 
     // Reposition the child frames
     int32_t index = 0;
     while (frame) {
       RepositionFrame(frame,
-                      aIsOddLevel,
-                      x,
-                      aContinuationStates);
+                      aIsEvenLevel,
+                      iCoord,
+                      aContinuationStates,
+                      frameWM,
+                      frameWidth);
       index++;
-      frame = aIsOddLevel ?
+      frame = reverseOrder ?
                 childList[childList.Length() - index - 1] :
                 frame->GetNextSibling();
     }
 
-    if (isRightMost) {
-      x += borderPadding.right;
+    if (isLast) {
+      iCoord += borderPadding.IEnd(frameWM);
     }
-    aLeft += x;
+    aStart += iCoord;
   } else {
-    aLeft += aFrame->GetSize().width;
+    aStart += frameWidth;
   }
-  nsRect rect = aFrame->GetRect();
-  aFrame->SetRect(nsRect(start, rect.y, aLeft - start, rect.height));
 
-  if (isRightMost)
-    aLeft += margin.right;
+  LogicalRect logicalRect(aLineWM, aFrame->GetRect(), aLineWidth);
+  logicalRect.IStart(aLineWM) = start;
+  logicalRect.ISize(aLineWM) = aStart - start;
+  aFrame->SetRect(aLineWM, logicalRect, aLineWidth);
+
+  if (isLast) {
+    aStart += margin.IEnd(frameWM);
+  }
 }
 
 void
 nsBidiPresUtils::InitContinuationStates(nsIFrame*              aFrame,
                                         nsContinuationStates*  aContinuationStates)
 {
   nsFrameContinuationState* state = aContinuationStates->PutEntry(aFrame);
   state->mFirstVisualFrame = nullptr;
@@ -1479,50 +1489,64 @@ nsBidiPresUtils::InitContinuationStates(
       InitContinuationStates(frame,
                              aContinuationStates);
     }
   }
 }
 
 void
 nsBidiPresUtils::RepositionInlineFrames(BidiLineData *aBld,
-                                        nsIFrame* aFirstChild)
+                                        nsIFrame* aFirstChild,
+                                        WritingMode aLineWM,
+                                        nscoord& aLineWidth)
 {
-  const nsStyleVisibility* vis = aFirstChild->StyleVisibility();
-  bool isLTR = (NS_STYLE_DIRECTION_LTR == vis->mDirection);
-  nscoord leftSpace = 0;
+  nscoord startSpace = 0;
 
   // This method is called from nsBlockFrame::PlaceLine via the call to
   // bidiUtils->ReorderFrames, so this is guaranteed to be after the inlines
   // have been reflowed, which is required for GetUsedMargin/Border/Padding
-  nsMargin margin = aFirstChild->GetUsedMargin();
+  WritingMode frameWM = aFirstChild->GetWritingMode();
+  LogicalMargin margin(frameWM, aFirstChild->GetUsedMargin());
   if (!aFirstChild->GetPrevContinuation() &&
       !aFirstChild->FrameIsNonFirstInIBSplit())
-    leftSpace = isLTR ? margin.left : margin.right;
+    startSpace = margin.IStart(frameWM);
 
-  nscoord left = aFirstChild->GetPosition().x - leftSpace;
+  nscoord start = LogicalRect(aLineWM, aFirstChild->GetRect(),
+                              aLineWidth).IStart(aLineWM) - startSpace;
   nsIFrame* frame;
   int32_t count = aBld->mVisualFrames.Length();
   int32_t index;
   nsContinuationStates continuationStates;
 
   // Initialize continuation states to (nullptr, 0) for
   // each frame on the line.
   for (index = 0; index < count; index++) {
     InitContinuationStates(aBld->VisualFrameAt(index), &continuationStates);
   }
 
   // Reposition frames in visual order
-  for (index = 0; index < count; index++) {
+  int32_t step, limit;
+  if (aLineWM.IsBidiLTR()) {
+    index = 0;
+    step = 1;
+    limit = count;
+  } else {
+    index = count - 1;
+    step = -1;
+    limit = -1;
+  }
+  for (; index != limit; index += step) {
     frame = aBld->VisualFrameAt(index);
     RepositionFrame(frame,
-                    (aBld->mLevels[aBld->mIndexMap[index]] & 1),
-                    left,
-                    &continuationStates);
-  } // for
+                    !(aBld->mLevels[aBld->mIndexMap[index]] & 1),
+                    start,
+                    &continuationStates,
+                    aLineWM,
+                    aLineWidth);
+  }
 }
 
 bool
 nsBidiPresUtils::CheckLineOrder(nsIFrame*  aFirstFrameOnLine,
                                 int32_t    aNumFramesOnLine,
                                 nsIFrame** aFirstVisual,
                                 nsIFrame** aLastVisual)
 {
--- a/layout/base/nsBidiPresUtils.h
+++ b/layout/base/nsBidiPresUtils.h
@@ -22,16 +22,17 @@ struct BidiParagraphData;
 struct BidiLineData;
 class nsIFrame;
 class nsBlockFrame;
 class nsPresContext;
 class nsRenderingContext;
 class nsBlockInFlowLineIterator;
 class nsStyleContext;
 template<class T> class nsTHashtable;
+namespace mozilla { class WritingMode; }
 
 /**
  * A structure representing some continuation state for each frame on the line,
  * used to determine the first and the last continuation frame for each
  * continuation chain on the line.
  */
 struct nsFrameContinuationState : public nsVoidPtrHashKey
 {
@@ -154,17 +155,19 @@ public:
 
   /**
    * Reorder this line using Bidi engine.
    * Update frame array, following the new visual sequence.
    * 
    * @lina 05/02/2000
    */
   static void ReorderFrames(nsIFrame*            aFirstFrameOnLine,
-                            int32_t              aNumFramesOnLine);
+                            int32_t              aNumFramesOnLine,
+                            mozilla::WritingMode aLineWM,
+                            nscoord&             aLineWidth);
 
   /**
    * Format Unicode text, taking into account bidi capabilities
    * of the platform. The formatting includes: reordering, Arabic shaping,
    * symmetric and numeric swapping, removing control characters.
    *
    * @lina 06/18/2000 
    */
@@ -372,34 +375,37 @@ private:
    *  If we encounter content that requires us to split the element into more
    *  than one paragraph for bidi resolution, resolve the paragraph up to that
    *  point.
    */
   static void TraverseFrames(nsBlockFrame*              aBlockFrame,
                              nsBlockInFlowLineIterator* aLineIter,
                              nsIFrame*                  aCurrentFrame,
                              BidiParagraphData*         aBpd);
-  
+
   /*
-   * Position aFrame and it's descendants to their visual places. Also if aFrame
-   * is not leaf, resize it to embrace it's children.
+   * Position aFrame and its descendants to their visual places. Also if aFrame
+   * is not leaf, resize it to embrace its children.
    *
-   * @param aFrame               The frame which itself and its children are going
-   *                             to be repositioned
-   * @param aIsOddLevel          TRUE means the embedding level of this frame is odd
-   * @param[in,out] aLeft        IN value is the starting position of aFrame(without
-   *                             considering its left margin)
-   *                             OUT value will be the ending position of aFrame(after
-   *                             adding its right margin)
+   * @param aFrame               The frame which itself and its children are
+   *                             going to be repositioned
+   * @param aIsEvenLevel         TRUE means the embedding level of this frame
+   *                             is even (LTR)
+   * @param[in,out] aStart       IN value is the starting position of aFrame
+   *                             (without considering its inline-start margin)
+   *                             OUT value will be the ending position of aFrame
+   *                             (after adding its inline-end margin)
    * @param aContinuationStates  A map from nsIFrame* to nsFrameContinuationState
    */
   static void RepositionFrame(nsIFrame*              aFrame,
-                              bool                   aIsOddLevel,
-                              nscoord&               aLeft,
-                              nsContinuationStates*  aContinuationStates);
+                              bool                   aIsEvenLevel,
+                              nscoord&               aStart,
+                              nsContinuationStates*  aContinuationStates,
+                              mozilla::WritingMode   aLineWM,
+                              nscoord&               aLineWidth);
 
   /*
    * Initialize the continuation state(nsFrameContinuationState) to
    * (nullptr, 0) for aFrame and its descendants.
    *
    * @param aFrame               The frame which itself and its descendants will
    *                             be initialized
    * @param aContinuationStates  A map from nsIFrame* to nsFrameContinuationState
@@ -417,30 +423,32 @@ private:
    * A frame is rightmost if it's the last appearance of its continuation chain
    * on the line and the chain is on its first line if it's RTL or the chain is
    * on its last line if it's LTR.
    *
    * @param aContinuationStates  A map from nsIFrame* to nsFrameContinuationState
    * @param[out] aIsLeftMost     TRUE means aFrame is leftmost frame or continuation
    * @param[out] aIsRightMost    TRUE means aFrame is rightmost frame or continuation
    */
-   static void IsLeftOrRightMost(nsIFrame*              aFrame,
-                                 nsContinuationStates*  aContinuationStates,
-                                 bool&                aIsLeftMost /* out */,
-                                 bool&                aIsRightMost /* out */);
+   static void IsFirstOrLast(nsIFrame*              aFrame,
+                             nsContinuationStates*  aContinuationStates,
+                             bool&                  aIsFirst /* out */,
+                             bool&                  aIsLast /* out */);
 
   /**
    *  Adjust frame positions following their visual order
    *
    *  @param aFirstChild the first kid
    *
    *  @lina 04/11/2000
    */
   static void RepositionInlineFrames(BidiLineData* aBld,
-                                     nsIFrame* aFirstChild);
+                                     nsIFrame* aFirstChild,
+                                     mozilla::WritingMode aLineWM,
+                                     nscoord& aLineWidth);
   
   /**
    * Helper method for Resolve()
    * Truncate a text frame to the end of a single-directional run and possibly
    * create a continuation frame for the remainder of its content.
    *
    * @param aFrame       the original frame
    * @param aNewFrame    [OUT] the new frame that was created
--- a/layout/generic/WritingModes.h
+++ b/layout/generic/WritingModes.h
@@ -828,16 +828,22 @@ public:
    */
   LogicalMargin ConvertTo(WritingMode aToMode, WritingMode aFromMode) const
   {
     CHECK_WRITING_MODE(aFromMode);
     return aToMode == aFromMode ?
       *this : LogicalMargin(aToMode, GetPhysicalMargin(aFromMode));
   }
 
+  bool IsEmpty() const
+  {
+    return (mMargin.left == 0 && mMargin.top == 0 &&
+            mMargin.right == 0 && mMargin.bottom == 0);
+  }
+
 private:
   friend class LogicalRect;
 
   LogicalMargin() MOZ_DELETE;
 
 #ifdef DEBUG
   WritingMode GetWritingMode() const { return mWritingMode; }
 #else
@@ -1118,16 +1124,27 @@ public:
   }
 
   nscoord YMost(WritingMode aWritingMode) const
   {
     CHECK_WRITING_MODE(aWritingMode);
     return aWritingMode.IsVertical() ? mRect.XMost() : mRect.YMost();
   }
 
+  bool IsEmpty() const
+  {
+    return (mRect.x == 0 && mRect.y == 0 &&
+            mRect.width == 0 && mRect.height == 0);
+  }
+
+  bool IsZeroSize() const
+  {
+    return (mRect.width == 0 && mRect.height == 0);
+  }
+
 /* XXX are these correct?
   nscoord ILeft(WritingMode aWritingMode) const
   {
     CHECK_WRITING_MODE(aWritingMode);
     return aWritingMode.IsBidiLTR() ? IStart() : IEnd();
   }
   nscoord IRight(WritingMode aWritingMode) const
   {
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -1445,17 +1445,17 @@ nsBlockFrame::ComputeFinalSize(const nsH
     NS_FRAME_SET_OVERFLOW_INCOMPLETE(aState.mReflowStatus);
   }
 
   // Screen out negative heights --- can happen due to integer overflows :-(
   aMetrics.Height() = std::max(0, aMetrics.Height());
   *aBottomEdgeOfChildren = bottomEdgeOfChildren;
 
 #ifdef DEBUG_blocks
-  if (CRAZY_WIDTH(aMetrics.Width()) || CRAZY_HEIGHT(aMetrics.Height())) {
+  if (CRAZY_SIZE(aMetrics.Width()) || CRAZY_SIZE(aMetrics.Height())) {
     ListTag(stdout);
     printf(": WARNING: desired:%d,%d\n", aMetrics.Width(), aMetrics.Height());
   }
 #endif
 }
 
 void
 nsBlockFrame::ComputeOverflowAreas(const nsRect&         aBounds,
@@ -3443,45 +3443,42 @@ nsBlockFrame::DoReflowInlineFrames(nsBlo
   // are impacted by floats.
   if (aFloatAvailableSpace.mHasFloats)
     aLine->SetLineIsImpactedByFloat(true);
 #ifdef REALLY_NOISY_REFLOW
   printf("nsBlockFrame::DoReflowInlineFrames %p impacted = %d\n",
          this, aFloatAvailableSpace.mHasFloats);
 #endif
 
-  nscoord x = aFloatAvailableSpace.mRect.x;
-  nscoord availWidth = aFloatAvailableSpace.mRect.width;
-  nscoord availHeight;
+  WritingMode wm = GetWritingMode(aLine->mFirstChild);
+  nscoord lineWidth = aFloatAvailableSpace.mRect.width +
+                      aState.BorderPadding().LeftRight();
+  LogicalRect lineRect(wm, aFloatAvailableSpace.mRect, lineWidth);
+
+  nscoord iStart = lineRect.IStart(wm);
+
+  nscoord availISize = lineRect.ISize(wm);
+  nscoord availBSize;
   if (aState.GetFlag(BRS_UNCONSTRAINEDHEIGHT)) {
-    availHeight = NS_UNCONSTRAINEDSIZE;
+    availBSize = NS_UNCONSTRAINEDSIZE;
   }
   else {
     /* XXX get the height right! */
-    availHeight = aFloatAvailableSpace.mRect.height;
+    availBSize = lineRect.BSize(wm);
   }
 
   // Make sure to enable resize optimization before we call BeginLineReflow
   // because it might get disabled there
   aLine->EnableResizeReflowOptimization();
 
-  // For unicode-bidi: plaintext, we need to get the direction of the line from
-  // the resolved paragraph level of the first frame on the line, not the block
-  // frame, because the block frame could be split by hard line breaks into
-  // multiple paragraphs with different base direction
-  uint8_t direction =
-    (StyleTextReset()->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_PLAINTEXT) ?
-      nsBidiPresUtils::GetFrameBaseLevel(aLine->mFirstChild) & 1 :
-      StyleVisibility()->mDirection;
-
-  aLineLayout.BeginLineReflow(x, aState.mY,
-                              availWidth, availHeight,
+  aLineLayout.BeginLineReflow(iStart, aState.mY,
+                              availISize, availBSize,
                               aFloatAvailableSpace.mHasFloats,
                               false, /*XXX isTopOfPage*/
-                              direction);
+                              wm, lineWidth);
 
   aState.SetFlag(BRS_LINE_LAYOUT_EMPTY, false);
 
   // XXX Unfortunately we need to know this before reflowing the first
   // inline frame in the line. FIX ME.
   if ((0 == aLineLayout.GetLineNumber()) &&
       (NS_BLOCK_HAS_FIRST_LETTER_CHILD & mState) &&
       (NS_BLOCK_HAS_FIRST_LETTER_STYLE & mState)) {
@@ -4047,29 +4044,29 @@ nsBlockFrame::PlaceLine(nsBlockReflowSta
   // first line box.
   //
   // There are exactly two places a bullet can be placed: near the
   // first or second line. It's only placed on the second line in a
   // rare case: when the first line is empty.
   bool addedBullet = false;
   if (HasOutsideBullet() &&
       ((aLine == mLines.front() &&
-        (!aLineLayout.IsZeroHeight() || (aLine == mLines.back()))) ||
+        (!aLineLayout.IsZeroBSize() || (aLine == mLines.back()))) ||
        (mLines.front() != mLines.back() &&
         0 == mLines.front()->mBounds.height &&
         aLine == mLines.begin().next()))) {
     nsHTMLReflowMetrics metrics(aState.mReflowState);
     nsIFrame* bullet = GetOutsideBullet();
     ReflowBullet(bullet, aState, metrics, aState.mY);
     NS_ASSERTION(!BulletIsEmpty() || metrics.Height() == 0,
                  "empty bullet took up space");
     aLineLayout.AddBulletFrame(bullet, metrics);
     addedBullet = true;
   }
-  aLineLayout.VerticalAlignLine();
+  aLineLayout.BlockDirAlignLine();
 
   // We want to compare to the available space that we would have had in
   // the line's height *before* we placed any floats in the line itself.
   // Floats that are in the line are handled during line reflow (and may
   // result in floats being pushed to below the line or (I HOPE???) in a
   // reflow with a forced break position).
   nsRect oldFloatAvailableSpace(aFloatAvailableSpace);
   // As we redo for floats, we can't reduce the amount of height we're
@@ -4087,19 +4084,19 @@ nsBlockFrame::PlaceLine(nsBlockReflowSta
   // LINE_REFLOW_REDO_MORE_FLOATS).
   if (AvailableSpaceShrunk(oldFloatAvailableSpace, aFloatAvailableSpace)) {
     return false;
   }
 
 #ifdef DEBUG
   {
     static nscoord lastHeight = 0;
-    if (CRAZY_HEIGHT(aLine->mBounds.y)) {
+    if (CRAZY_SIZE(aLine->mBounds.y)) {
       lastHeight = aLine->mBounds.y;
-      if (abs(aLine->mBounds.y - lastHeight) > CRAZY_H/10) {
+      if (abs(aLine->mBounds.y - lastHeight) > CRAZY_COORD/10) {
         nsFrame::ListTag(stdout);
         printf(": line=%p y=%d line.bounds.height=%d\n",
                static_cast<void*>(aLine.get()),
                aLine->mBounds.y, aLine->mBounds.height);
       }
     }
     else {
       lastHeight = 0;
@@ -4120,30 +4117,19 @@ nsBlockFrame::PlaceLine(nsBlockReflowSta
    * In other words, isLastLine really means isLastLineAndWeCare.
    */
   bool isLastLine =
     !IsSVGText() &&
     ((NS_STYLE_TEXT_ALIGN_AUTO != styleText->mTextAlignLast ||
       NS_STYLE_TEXT_ALIGN_JUSTIFY == styleText->mTextAlign) &&
      (aLineLayout.GetLineEndsInBR() ||
       IsLastLine(aState, aLine)));
-  aLineLayout.HorizontalAlignFrames(aLine->mBounds, isLastLine);
-  // XXX: not only bidi: right alignment can be broken after
-  // RelativePositionFrames!!!
-  // XXXldb Is something here considering relatively positioned frames at
-  // other than their original positions?
-#ifdef IBMBIDI
-  // XXXldb Why don't we do this earlier?
-  if (aState.mPresContext->BidiEnabled()) {
-    if (!aState.mPresContext->IsVisualMode() ||
-        StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) {
-      nsBidiPresUtils::ReorderFrames(aLine->mFirstChild, aLine->GetChildCount());
-    } // not visual mode
-  } // bidi enabled
-#endif // IBMBIDI
+
+  aLineLayout.InlineDirAlignFrames(aLine->mBounds, isLastLine,
+                                   aLine->GetChildCount());
 
   // From here on, pfd->mBounds rectangles are incorrect because bidi
   // might have moved frames around!
   nsOverflowAreas overflowAreas;
   aLineLayout.RelativePositionFrames(overflowAreas);
   aLine->SetOverflowAreas(overflowAreas);
   if (addedBullet) {
     aLineLayout.RemoveBulletFrame(GetOutsideBullet());
--- a/layout/generic/nsBlockReflowContext.cpp
+++ b/layout/generic/nsBlockReflowContext.cpp
@@ -257,17 +257,17 @@ nsBlockReflowContext::ReflowBlock(const 
 #endif
 
   mOuterReflowState.mFloatManager->Translate(tx, ty);
   rv = mFrame->Reflow(mPresContext, mMetrics, aFrameRS, aFrameReflowStatus);
   mOuterReflowState.mFloatManager->Translate(-tx, -ty);
 
 #ifdef DEBUG
   if (!NS_INLINE_IS_BREAK_BEFORE(aFrameReflowStatus)) {
-    if (CRAZY_WIDTH(mMetrics.Width()) || CRAZY_HEIGHT(mMetrics.Height())) {
+    if (CRAZY_SIZE(mMetrics.Width()) || CRAZY_SIZE(mMetrics.Height())) {
       printf("nsBlockReflowContext: ");
       nsFrame::ListTag(stdout, mFrame);
       printf(" metrics=%d,%d!\n", mMetrics.Width(), mMetrics.Height());
     }
     if ((mMetrics.Width() == nscoord(0xdeadbeef)) ||
         (mMetrics.Height() == nscoord(0xdeadbeef))) {
       printf("nsBlockReflowContext: ");
       nsFrame::ListTag(stdout, mFrame);
--- a/layout/generic/nsContainerFrame.h
+++ b/layout/generic/nsContainerFrame.h
@@ -29,21 +29,18 @@ class FramePropertyTable;
 }
 
 // Some macros for container classes to do sanity checking on
 // width/height/x/y values computed during reflow.
 // NOTE: AppUnitsPerCSSPixel value hardwired here to remove the
 // dependency on nsDeviceContext.h.  It doesn't matter if it's a
 // little off.
 #ifdef DEBUG
-#define CRAZY_W (1000000*60)
-#define CRAZY_H CRAZY_W
-
-#define CRAZY_WIDTH(_x) (((_x) < -CRAZY_W) || ((_x) > CRAZY_W))
-#define CRAZY_HEIGHT(_y) (((_y) < -CRAZY_H) || ((_y) > CRAZY_H))
+#define CRAZY_COORD (1000000*60)
+#define CRAZY_SIZE(_x) (((_x) < -CRAZY_COORD) || ((_x) > CRAZY_COORD))
 #endif
 
 /**
  * Implementation of a container frame.
  */
 class nsContainerFrame : public nsSplittableFrame
 {
 public:
--- a/layout/generic/nsFirstLetterFrame.cpp
+++ b/layout/generic/nsFirstLetterFrame.cpp
@@ -183,31 +183,20 @@ nsFirstLetterFrame::Reflow(nsPresContext
   // Reflow the child
   if (!aReflowState.mLineLayout) {
     // When there is no lineLayout provided, we provide our own. The
     // only time that the first-letter-frame is not reflowing in a
     // line context is when its floating.
     nsHTMLReflowState rs(aPresContext, aReflowState, kid, availSize);
     nsLineLayout ll(aPresContext, nullptr, &aReflowState, nullptr);
 
-    // For unicode-bidi: plaintext, we need to get the direction of the line
-    // from the resolved paragraph level of the child, not the block frame,
-    // because the block frame could be split by hard line breaks into
-    // multiple paragraphs with different base direction
-    uint8_t direction;
-    nsIFrame* containerFrame = ll.LineContainerFrame();
-    if (containerFrame->StyleTextReset()->mUnicodeBidi &
-        NS_STYLE_UNICODE_BIDI_PLAINTEXT) {
-      FramePropertyTable *propTable = aPresContext->PropertyTable();
-      direction = NS_PTR_TO_INT32(propTable->Get(kid, BaseLevelProperty())) & 1;
-    } else {
-      direction = containerFrame->StyleVisibility()->mDirection;
-    }
     ll.BeginLineReflow(bp.left, bp.top, availSize.width, NS_UNCONSTRAINEDSIZE,
-                       false, true, direction);
+                       false, true,
+                       ll.LineContainerFrame()->GetWritingMode(kid),
+                       aReflowState.AvailableWidth());
     rs.mLineLayout = &ll;
     ll.SetInFirstLetter(true);
     ll.SetFirstLetterStyleOK(true);
 
     kid->WillReflow(aPresContext);
     kid->Reflow(aPresContext, aMetrics, rs, aReflowStatus);
 
     ll.EndLineReflow();
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -970,16 +970,30 @@ nsIFrame::GetPaddingRectRelativeToSelf()
 }
 
 nsRect
 nsIFrame::GetPaddingRect() const
 {
   return GetPaddingRectRelativeToSelf() + GetPosition();
 }
 
+WritingMode
+nsIFrame::GetWritingMode(nsIFrame* aSubFrame) const
+{
+  WritingMode writingMode = GetWritingMode();
+
+  if (!writingMode.IsVertical() &&
+      (StyleTextReset()->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_PLAINTEXT)) {
+    nsBidiLevel frameLevel = nsBidiPresUtils::GetFrameBaseLevel(aSubFrame);
+    writingMode.SetDirectionFromBidiLevel(frameLevel);
+  }
+
+  return writingMode;
+}
+
 nsRect
 nsIFrame::GetMarginRectRelativeToSelf() const
 {
   nsMargin m = GetUsedMargin();
   ApplySkipSides(m);
   nsRect r(0, 0, mRect.width, mRect.height);
   r.Inflate(m);
   return r;
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -668,16 +668,25 @@ public:
   /**
    * The frame's writing-mode, used for logical layout computations.
    */
   mozilla::WritingMode GetWritingMode() const {
     return mozilla::WritingMode(StyleVisibility());
   }
 
   /**
+   * Get the writing mode of this frame, but if it is styled with
+   * unicode-bidi: plaintext, reset the direction to the resolved paragraph
+   * level of the given subframe (typically the first frame on the line),
+   * not this frame's writing mode, because the container frame could be split
+   * by hard line breaks into multiple paragraphs with different base direction.
+   */
+  mozilla::WritingMode GetWritingMode(nsIFrame* aSubFrame) const;
+
+  /**
    * Bounding rect of the frame. The values are in app units, and the origin is
    * relative to the upper-left of the geometric parent. The size includes the
    * content area, borders, and padding.
    *
    * Note: moving or sizing the frame does not affect the view's size or
    * position.
    */
   nsRect GetRect() const { return mRect; }
--- a/layout/generic/nsInlineFrame.cpp
+++ b/layout/generic/nsInlineFrame.cpp
@@ -482,33 +482,31 @@ nsInlineFrame::ReflowFrames(nsPresContex
                             nsReflowStatus& aStatus)
 {
   nsresult rv = NS_OK;
   aStatus = NS_FRAME_COMPLETE;
 
   nsLineLayout* lineLayout = aReflowState.mLineLayout;
   bool inFirstLine = aReflowState.mLineLayout->GetInFirstLine();
   RestyleManager* restyleManager = aPresContext->RestyleManager();
-  bool ltr = (NS_STYLE_DIRECTION_LTR == aReflowState.mStyleVisibility->mDirection);
-  nscoord leftEdge = 0;
+  WritingMode wm = aReflowState.GetWritingMode();
+  nscoord startEdge = 0;
   // Don't offset by our start borderpadding if we have a prev continuation or
   // if we're in a part of an {ib} split other than the first one.
   if (!GetPrevContinuation() && !FrameIsNonFirstInIBSplit()) {
-    leftEdge = ltr ? aReflowState.ComputedPhysicalBorderPadding().left
-                   : aReflowState.ComputedPhysicalBorderPadding().right;
+    startEdge = aReflowState.ComputedLogicalBorderPadding().IStart(wm);
   }
-  nscoord availableWidth = aReflowState.AvailableWidth();
-  NS_ASSERTION(availableWidth != NS_UNCONSTRAINEDSIZE,
+  nscoord availableISize = aReflowState.AvailableISize();
+  NS_ASSERTION(availableISize != NS_UNCONSTRAINEDSIZE,
                "should no longer use available widths");
-  // Subtract off left and right border+padding from availableWidth
-  availableWidth -= leftEdge;
-  availableWidth -= ltr ? aReflowState.ComputedPhysicalBorderPadding().right
-                        : aReflowState.ComputedPhysicalBorderPadding().left;
-  lineLayout->BeginSpan(this, &aReflowState, leftEdge,
-                        leftEdge + availableWidth, &mBaseline);
+  // Subtract off inline axis border+padding from availableISize
+  availableISize -= startEdge;
+  availableISize -= aReflowState.ComputedLogicalBorderPadding().IEnd(wm);
+  lineLayout->BeginSpan(this, &aReflowState, startEdge,
+                        startEdge + availableISize, &mBaseline);
 
   // First reflow our principal children.
   nsIFrame* frame = mFrames.FirstChild();
   bool done = false;
   while (frame) {
     // Check if we should lazily set the child frame's parent pointer.
     if (irs.mSetParentPointer) {
       bool havePrevBlock =
@@ -641,40 +639,38 @@ nsInlineFrame::ReflowFrames(nsPresContex
 
   // If after reflowing our children they take up no area then make
   // sure that we don't either.
   //
   // Note: CSS demands that empty inline elements still affect the
   // line-height calculations. However, continuations of an inline
   // that are empty we force to empty so that things like collapsed
   // whitespace in an inline element don't affect the line-height.
-  aMetrics.Width() = lineLayout->EndSpan(this);
+  aMetrics.ISize() = lineLayout->EndSpan(this);
 
   // Compute final width.
 
   // Make sure to not include our start border and padding if we have a prev
   // continuation or if we're in a part of an {ib} split other than the first
   // one.
   if (!GetPrevContinuation() && !FrameIsNonFirstInIBSplit()) {
-    aMetrics.Width() += ltr ? aReflowState.ComputedPhysicalBorderPadding().left
-                          : aReflowState.ComputedPhysicalBorderPadding().right;
+    aMetrics.ISize() += aReflowState.ComputedLogicalBorderPadding().IStart(wm);
   }
 
   /*
    * We want to only apply the end border and padding if we're the last
    * continuation and either not in an {ib} split or the last part of it.  To
    * be the last continuation we have to be complete (so that we won't get a
    * next-in-flow) and have no non-fluid continuations on our continuation
    * chain.
    */
   if (NS_FRAME_IS_COMPLETE(aStatus) &&
       !LastInFlow()->GetNextContinuation() &&
       !FrameIsNonLastInIBSplit()) {
-    aMetrics.Width() += ltr ? aReflowState.ComputedPhysicalBorderPadding().right
-                          : aReflowState.ComputedPhysicalBorderPadding().left;
+    aMetrics.Width() += aReflowState.ComputedLogicalBorderPadding().IEnd(wm);
   }
 
   nsRefPtr<nsFontMetrics> fm;
   float inflation = nsLayoutUtils::FontSizeInflationFor(this);
   nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm), inflation);
   aReflowState.rendContext->SetFont(fm);
 
   if (fm) {
--- a/layout/generic/nsLineLayout.cpp
+++ b/layout/generic/nsLineLayout.cpp
@@ -18,22 +18,23 @@
 #include "nsStyleContext.h"
 #include "nsPresContext.h"
 #include "nsRenderingContext.h"
 #include "nsGkAtoms.h"
 #include "nsIContent.h"
 #include "nsLayoutUtils.h"
 #include "nsTextFrame.h"
 #include "nsStyleStructInlines.h"
+#include "nsBidiPresUtils.h"
 #include <algorithm>
 
 #ifdef DEBUG
-#undef  NOISY_HORIZONTAL_ALIGN
-#undef  NOISY_VERTICAL_ALIGN
-#undef  REALLY_NOISY_VERTICAL_ALIGN
+#undef  NOISY_INLINEDIR_ALIGN
+#undef  NOISY_BLOCKDIR_ALIGN
+#undef  REALLY_NOISY_BLOCKDIR_ALIGN
 #undef  NOISY_REFLOW
 #undef  REALLY_NOISY_REFLOW
 #undef  NOISY_PUSHING
 #undef  REALLY_NOISY_PUSHING
 #undef  DEBUG_ADD_TEXT
 #undef  NOISY_MAX_ELEMENT_SIZE
 #undef  REALLY_NOISY_MAX_ELEMENT_SIZE
 #undef  NOISY_CAN_PLACE_FRAME
@@ -55,17 +56,17 @@ nsLineLayout::nsLineLayout(nsPresContext
     mFloatManager(aFloatManager),
     mBlockReflowState(aOuterReflowState),
     mLastOptionalBreakContent(nullptr),
     mForceBreakContent(nullptr),
     mBlockRS(nullptr),/* XXX temporary */
     mLastOptionalBreakPriority(gfxBreakPriority::eNoBreak),
     mLastOptionalBreakContentOffset(-1),
     mForceBreakContentOffset(-1),
-    mMinLineHeight(0),
+    mMinLineBSize(0),
     mTextIndent(0),
     mFirstLetterStyleOK(false),
     mIsTopOfPage(false),
     mImpactedByFloats(false),
     mLastFloatWasLetterFrame(false),
     mLineIsEmpty(false),
     mLineEndsInBR(false),
     mNeedBackup(false),
@@ -86,17 +87,17 @@ nsLineLayout::nsLineLayout(nsPresContext
   nsBlockFrame* blockFrame = do_QueryFrame(aOuterReflowState->frame);
   if (blockFrame)
     mStyleText = blockFrame->StyleTextForLineLayout();
   else
     mStyleText = aOuterReflowState->frame->StyleText();
 
   mLineNumber = 0;
   mTotalPlacedFrames = 0;
-  mTopEdge = 0;
+  mBStartEdge = 0;
   mTrimmableWidth = 0;
 
   mInflationMinFontSize =
     nsLayoutUtils::InflationMinFontSizeFor(aOuterReflowState->frame);
 
   // Instead of always pre-initializing the free-lists for frames and
   // spans, we do it on demand so that situations that only use a few
   // frames and spans won't waste a lot of time in unneeded
@@ -128,94 +129,95 @@ nsLineLayout::~nsLineLayout()
 inline bool
 HasPrevInFlow(nsIFrame *aFrame)
 {
   nsIFrame *prevInFlow = aFrame->GetPrevInFlow();
   return prevInFlow != nullptr;
 }
 
 void
-nsLineLayout::BeginLineReflow(nscoord aX, nscoord aY,
-                              nscoord aWidth, nscoord aHeight,
+nsLineLayout::BeginLineReflow(nscoord aICoord, nscoord aBCoord,
+                              nscoord aISize, nscoord aBSize,
                               bool aImpactedByFloats,
                               bool aIsTopOfPage,
-                              uint8_t aDirection)
+                              WritingMode aWritingMode,
+                              nscoord aContainerWidth)
 {
   NS_ASSERTION(nullptr == mRootSpan, "bad linelayout user");
-  NS_WARN_IF_FALSE(aWidth != NS_UNCONSTRAINEDSIZE,
+  NS_WARN_IF_FALSE(aISize != NS_UNCONSTRAINEDSIZE,
                    "have unconstrained width; this should only result from "
                    "very large sizes, not attempts at intrinsic width "
                    "calculation");
 #ifdef DEBUG
-  if ((aWidth != NS_UNCONSTRAINEDSIZE) && CRAZY_WIDTH(aWidth)) {
+  if ((aISize != NS_UNCONSTRAINEDSIZE) && CRAZY_SIZE(aISize)) {
     nsFrame::ListTag(stdout, mBlockReflowState->frame);
     printf(": Init: bad caller: width WAS %d(0x%x)\n",
-           aWidth, aWidth);
+           aISize, aISize);
   }
-  if ((aHeight != NS_UNCONSTRAINEDSIZE) && CRAZY_HEIGHT(aHeight)) {
+  if ((aBSize != NS_UNCONSTRAINEDSIZE) && CRAZY_SIZE(aBSize)) {
     nsFrame::ListTag(stdout, mBlockReflowState->frame);
     printf(": Init: bad caller: height WAS %d(0x%x)\n",
-           aHeight, aHeight);
+           aBSize, aBSize);
   }
 #endif
 #ifdef NOISY_REFLOW
   nsFrame::ListTag(stdout, mBlockReflowState->frame);
   printf(": BeginLineReflow: %d,%d,%d,%d impacted=%s %s\n",
-         aX, aY, aWidth, aHeight,
+         aICoord, aBCoord, aISize, aBSize,
          aImpactedByFloats?"true":"false",
          aIsTopOfPage ? "top-of-page" : "");
 #endif
 #ifdef DEBUG
   mSpansAllocated = mSpansFreed = mFramesAllocated = mFramesFreed = 0;
 #endif
 
   mFirstLetterStyleOK = false;
   mIsTopOfPage = aIsTopOfPage;
   mImpactedByFloats = aImpactedByFloats;
   mTotalPlacedFrames = 0;
   mLineIsEmpty = true;
   mLineAtStart = true;
   mLineEndsInBR = false;
   mSpanDepth = 0;
-  mMaxTopBoxHeight = mMaxBottomBoxHeight = 0;
+  mMaxStartBoxBSize = mMaxEndBoxBSize = 0;
 
   if (mGotLineBox) {
     mLineBox->ClearHasBullet();
   }
 
   PerSpanData* psd = NewPerSpanData();
   mCurrentSpan = mRootSpan = psd;
   psd->mReflowState = mBlockReflowState;
-  psd->mLeftEdge = aX;
-  psd->mX = aX;
-  psd->mRightEdge = aX + aWidth;
+  psd->mIStart = aICoord;
+  psd->mICoord = aICoord;
+  psd->mIEnd = aICoord + aISize;
+  mContainerWidth = aContainerWidth;
 
   // If we're in a constrained height frame, then we don't allow a
   // max line box width to take effect.
   if (!(LineContainerFrame()->GetStateBits() &
         NS_FRAME_IN_CONSTRAINED_HEIGHT)) {
 
     // If the available size is greater than the maximum line box width (if
     // specified), then we need to adjust the line box width to be at the max
     // possible width.
     nscoord maxLineBoxWidth =
       LineContainerFrame()->PresContext()->PresShell()->MaxLineBoxWidth();
 
     if (maxLineBoxWidth > 0 &&
-        psd->mRightEdge - psd->mLeftEdge > maxLineBoxWidth) {
-      psd->mRightEdge = psd->mLeftEdge + maxLineBoxWidth;
+        psd->mIEnd - psd->mIStart > maxLineBoxWidth) {
+      psd->mIEnd = psd->mIStart + maxLineBoxWidth;
     }
   }
 
-  mTopEdge = aY;
+  mBStartEdge = aBCoord;
 
   psd->mNoWrap =
     !mStyleText->WhiteSpaceCanWrapStyle() || LineContainerFrame()->IsSVGText();
-  psd->mDirection = aDirection;
-  psd->mChangedFrameDirection = false;
+  psd->mWritingMode = aWritingMode;
 
   // If this is the first line of a block then see if the text-indent
   // property amounts to anything.
 
   if (0 == mLineNumber && !HasPrevInFlow(mBlockReflowState->frame)) {
     const nsStyleCoord &textIndent = mStyleText->mTextIndent;
     nscoord pctBasis = 0;
     if (textIndent.HasPercent()) {
@@ -225,31 +227,26 @@ nsLineLayout::BeginLineReflow(nscoord aX
       if (mGotLineBox) {
         mLineBox->DisableResizeReflowOptimization();
       }
     }
     nscoord indent = nsRuleNode::ComputeCoordPercentCalc(textIndent, pctBasis);
 
     mTextIndent = indent;
 
-    if (NS_STYLE_DIRECTION_RTL == psd->mDirection) {
-      psd->mRightEdge -= indent;
-    }
-    else {
-      psd->mX += indent;
-    }
+    psd->mICoord += indent;
   }
 }
 
 void
 nsLineLayout::EndLineReflow()
 {
 #ifdef NOISY_REFLOW
   nsFrame::ListTag(stdout, mBlockReflowState->frame);
-  printf(": EndLineReflow: width=%d\n", mRootSpan->mX - mRootSpan->mLeftEdge);
+  printf(": EndLineReflow: width=%d\n", mRootSpan->mICoord - mRootSpan->mIStart);
 #endif
 
   FreeSpan(mRootSpan);
   mCurrentSpan = mRootSpan = nullptr;
 
   NS_ASSERTION(mSpansAllocated == mSpansFreed, "leak");
   NS_ASSERTION(mFramesAllocated == mFramesFreed, "leak");
 
@@ -263,93 +260,100 @@ nsLineLayout::EndLineReflow()
   if (mFramesAllocated > maxFramesAllocated) {
     printf("XXX: saw a line with %d frames\n", mFramesAllocated);
     maxFramesAllocated = mFramesAllocated;
   }
 #endif
 }
 
 // XXX swtich to a single mAvailLineWidth that we adjust as each frame
-// on the line is placed. Each span can still have a per-span mX that
+// on the line is placed. Each span can still have a per-span mICoord that
 // tracks where a child frame is going in its span; they don't need a
-// per-span mLeftEdge?
+// per-span mIStart?
 
 void
 nsLineLayout::UpdateBand(const nsRect& aNewAvailSpace,
                          nsIFrame* aFloatFrame)
 {
+  WritingMode lineWM = mRootSpan->mWritingMode;
+  LogicalRect availSpace(lineWM, aNewAvailSpace, mContainerWidth);
 #ifdef REALLY_NOISY_REFLOW
-  printf("nsLL::UpdateBand %d, %d, %d, %d, frame=%p\n  will set mImpacted to true\n",
+  printf("nsLL::UpdateBand %d, %d, %d, %d, (logical %d, %d, %d, %d); frame=%p\n  will set mImpacted to true\n",
          aNewAvailSpace.x, aNewAvailSpace.y,
          aNewAvailSpace.width, aNewAvailSpace.height,
+         availSpace.IStart(lineWM), availSpace.BStart(lineWM),
+         availSpace.ISize(lineWM), availSpace.BSize(lineWM),
          aFloatFrame);
 #endif
 #ifdef DEBUG
-  if ((aNewAvailSpace.width != NS_UNCONSTRAINEDSIZE) && CRAZY_WIDTH(aNewAvailSpace.width)) {
+  if ((availSpace.ISize(lineWM) != NS_UNCONSTRAINEDSIZE) &&
+      CRAZY_SIZE(availSpace.ISize(lineWM))) {
     nsFrame::ListTag(stdout, mBlockReflowState->frame);
-    printf(": UpdateBand: bad caller: width WAS %d(0x%x)\n",
-           aNewAvailSpace.width, aNewAvailSpace.width);
+    printf(": UpdateBand: bad caller: ISize WAS %d(0x%x)\n",
+           availSpace.ISize(lineWM), availSpace.ISize(lineWM));
   }
-  if ((aNewAvailSpace.height != NS_UNCONSTRAINEDSIZE) && CRAZY_HEIGHT(aNewAvailSpace.height)) {
+  if ((availSpace.BSize(lineWM) != NS_UNCONSTRAINEDSIZE) &&
+      CRAZY_SIZE(availSpace.BSize(lineWM))) {
     nsFrame::ListTag(stdout, mBlockReflowState->frame);
-    printf(": UpdateBand: bad caller: height WAS %d