Merge mozilla-central to b2g-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Fri, 02 May 2014 15:01:54 +0200
changeset 181797 e075d90d216b30cd43f76993ef43afdc55650d64
parent 181796 02222e9ab1adbf1adf1be15b1fe4e9d3e3fd8953 (current diff)
parent 181756 35c124cab3a89562af2e173ef17ed981625fa907 (diff)
child 181798 654924104aa8986b0a57ab1345feab941e3e821b
push id272
push userpvanderbeken@mozilla.com
push dateMon, 05 May 2014 16:31:18 +0000
milestone32.0a1
Merge mozilla-central to b2g-inbound
content/canvas/test/test_createPattern_broken.html
js/src/tests/ecma/LexicalConventions/7.7.3-2.js
security/manager/boot/src/PreloadedHPKPins.json
security/manager/boot/src/default-ee.der
security/manager/boot/src/genHPKPStaticPins.js
--- a/CLOBBER
+++ b/CLOBBER
@@ -1,9 +1,9 @@
-# To Trigger a clobber replace ALL of the textual description below,
+# To trigger a clobber replace ALL of the textual description below,
 # giving a bug number and a one line description of why a clobber is
 # required. Modifying this file will make configure check that a
 # clobber has been performed before the build can continue.
 #
 # MERGE NOTE: When merging two branches that require a CLOBBER, you should
 #             merge both CLOBBER descriptions, to ensure that users on
 #             both branches correctly see the clobber warning.
 #
@@ -17,9 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 1003702 - ICU is unhappy.
+Bug 1003702 - Relanding ICU changes with a clobber
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -11,16 +11,17 @@ Cu.import('resource://gre/modules/AlarmS
 Cu.import('resource://gre/modules/ActivitiesService.jsm');
 Cu.import('resource://gre/modules/PermissionPromptHelper.jsm');
 Cu.import('resource://gre/modules/NotificationDB.jsm');
 Cu.import('resource://gre/modules/Payment.jsm');
 Cu.import("resource://gre/modules/AppsUtils.jsm");
 Cu.import('resource://gre/modules/UserAgentOverrides.jsm');
 Cu.import('resource://gre/modules/Keyboard.jsm');
 Cu.import('resource://gre/modules/ErrorPage.jsm');
+Cu.import('resource://gre/modules/AlertsHelper.jsm');
 #ifdef MOZ_WIDGET_GONK
 Cu.import('resource://gre/modules/NetworkStatsService.jsm');
 #endif
 
 // Identity
 Cu.import('resource://gre/modules/SignInToWebsite.jsm');
 SignInToWebsiteController.init();
 
@@ -338,32 +339,29 @@ var shell = {
 
     window.addEventListener('MozApplicationManifest', this);
     window.addEventListener('mozfullscreenchange', this);
     window.addEventListener('MozAfterPaint', this);
     window.addEventListener('sizemodechange', this);
     window.addEventListener('unload', this);
     this.contentBrowser.addEventListener('mozbrowserloadstart', this, true);
 
-    SystemAppProxy.registerFrame(this.contentBrowser);
-
     CustomEventManager.init();
     WebappsHelper.init();
     UserAgentOverrides.init();
     IndexedDBPromptHelper.init();
     CaptivePortalLoginHelper.init();
 
     this.contentBrowser.src = homeURL;
     this.isHomeLoaded = false;
 
     ppmm.addMessageListener("content-handler", this);
     ppmm.addMessageListener("dial-handler", this);
     ppmm.addMessageListener("sms-handler", this);
     ppmm.addMessageListener("mail-handler", this);
-    ppmm.addMessageListener("app-notification-send", AlertsHelper);
     ppmm.addMessageListener("file-picker", this);
     ppmm.addMessageListener("getProfD", function(message) {
       return Services.dirsvc.get("ProfD", Ci.nsIFile).path;
     });
   },
 
   stop: function shell_stop() {
     window.removeEventListener('unload', this);
@@ -648,16 +646,18 @@ var shell = {
   notifyContentStart: function shell_notifyContentStart() {
     this.contentBrowser.removeEventListener('mozbrowserloadstart', this, true);
     this.contentBrowser.removeEventListener('mozbrowserlocationchange', this, true);
 
     let content = this.contentBrowser.contentWindow;
 
     this.reportCrash(true);
 
+    SystemAppProxy.registerFrame(shell.contentBrowser);
+
     this.sendEvent(window, 'ContentStart');
 
     Services.obs.notifyObservers(null, 'content-start', null);
 
 #ifdef MOZ_WIDGET_GONK
     Cu.import('resource://gre/modules/OperatorApps.jsm');
 #endif
 
@@ -747,21 +747,16 @@ var CustomEventManager = {
     }).bind(this), false);
   },
 
   handleEvent: function custevt_handleEvent(evt) {
     let detail = evt.detail;
     dump('XXX FIXME : Got a mozContentEvent: ' + detail.type + "\n");
 
     switch(detail.type) {
-      case 'desktop-notification-show':
-      case 'desktop-notification-click':
-      case 'desktop-notification-close':
-        AlertsHelper.handleEvent(detail);
-        break;
       case 'webapps-install-granted':
       case 'webapps-install-denied':
         WebappsHelper.handleEvent(detail);
         break;
       case 'select-choicechange':
         FormsHelper.handleEvent(detail);
         break;
       case 'system-message-listener-ready':
@@ -782,200 +777,16 @@ var CustomEventManager = {
         break;
       case 'inputmethod-update-layouts':
         KeyboardHelper.handleEvent(detail);
         break;
     }
   }
 }
 
-var AlertsHelper = {
-  _listeners: {},
-  _count: 0,
-
-  handleEvent: function alert_handleEvent(detail) {
-    if (!detail || !detail.id)
-      return;
-
-    let uid = detail.id;
-    let listener = this._listeners[uid];
-    if (!listener)
-     return;
-
-    let topic;
-    if (detail.type == "desktop-notification-click") {
-      topic = "alertclickcallback";
-    } else if (detail.type == "desktop-notification-show") {
-      topic = "alertshow";
-    } else {
-      /* desktop-notification-close */
-      topic = "alertfinished";
-    }
-
-    if (uid.startsWith("alert")) {
-      try {
-        listener.observer.observe(null, topic, listener.cookie);
-      } catch (e) { }
-    } else {
-      try {
-        listener.mm.sendAsyncMessage("app-notification-return", {
-          uid: uid,
-          topic: topic,
-          target: listener.target
-        });
-      } catch (e) {
-        // we get an exception if the app is not launched yet
-        gSystemMessenger.sendMessage("notification", {
-            clicked: (detail.type === "desktop-notification-click"),
-            title: listener.title,
-            body: listener.text,
-            imageURL: listener.imageURL,
-            lang: listener.lang,
-            dir: listener.dir,
-            id: listener.id,
-            tag: listener.tag
-          },
-          Services.io.newURI(listener.target, null, null),
-          Services.io.newURI(listener.manifestURL, null, null)
-        );
-      }
-    }
-
-    // we're done with this notification
-    if (topic === "alertfinished") {
-      delete this._listeners[uid];
-    }
-  },
-
-  registerListener: function alert_registerListener(alertId, cookie, alertListener) {
-    this._listeners[alertId] = { observer: alertListener, cookie: cookie };
-  },
-
-  registerAppListener: function alert_registerAppListener(uid, listener) {
-    this._listeners[uid] = listener;
-
-    let app = DOMApplicationRegistry.getAppByManifestURL(listener.manifestURL);
-    DOMApplicationRegistry.getManifestFor(app.manifestURL).then((manifest) => {
-      let helper = new ManifestHelper(manifest, app.origin);
-      let getNotificationURLFor = function(messages) {
-        if (!messages)
-          return null;
-
-        for (let i = 0; i < messages.length; i++) {
-          let message = messages[i];
-          if (message === "notification") {
-            return helper.fullLaunchPath();
-          } else if (typeof message == "object" && "notification" in message) {
-            return helper.resolveFromOrigin(message["notification"]);
-          }
-        }
-
-        // No message found...
-        return null;
-      }
-
-      listener.target = getNotificationURLFor(manifest.messages);
-
-      // Bug 816944 - Support notification messages for entry_points.
-    });
-  },
-
-  showNotification: function alert_showNotification(imageURL,
-                                                    title,
-                                                    text,
-                                                    textClickable,
-                                                    cookie,
-                                                    uid,
-                                                    bidi,
-                                                    lang,
-                                                    manifestURL) {
-    function send(appName, appIcon) {
-      shell.sendChromeEvent({
-        type: "desktop-notification",
-        id: uid,
-        icon: imageURL,
-        title: title,
-        text: text,
-        bidi: bidi,
-        lang: lang,
-        appName: appName,
-        appIcon: appIcon,
-        manifestURL: manifestURL
-      });
-    }
-
-    if (!manifestURL || !manifestURL.length) {
-      send(null, null);
-      return;
-    }
-
-    // If we have a manifest URL, get the icon and title from the manifest
-    // to prevent spoofing.
-    let app = DOMApplicationRegistry.getAppByManifestURL(manifestURL);
-    DOMApplicationRegistry.getManifestFor(manifestURL).then((aManifest) => {
-      let helper = new ManifestHelper(aManifest, app.origin);
-      send(helper.name, helper.iconURLForSize(128));
-    });
-  },
-
-  showAlertNotification: function alert_showAlertNotification(imageURL,
-                                                              title,
-                                                              text,
-                                                              textClickable,
-                                                              cookie,
-                                                              alertListener,
-                                                              name,
-                                                              bidi,
-                                                              lang) {
-    let currentListener = this._listeners[name];
-    if (currentListener) {
-      currentListener.observer.observe(null, "alertfinished", currentListener.cookie);
-    }
-
-    this.registerListener(name, cookie, alertListener);
-    this.showNotification(imageURL, title, text, textClickable, cookie,
-                          name, bidi, lang, null);
-  },
-
-  closeAlert: function alert_closeAlert(name) {
-    shell.sendChromeEvent({
-      type: "desktop-notification-close",
-      id: name
-    });
-  },
-
-  receiveMessage: function alert_receiveMessage(aMessage) {
-    if (!aMessage.target.assertAppHasPermission("desktop-notification")) {
-      Cu.reportError("Desktop-notification message " + aMessage.name +
-                     " from a content process with no desktop-notification privileges.");
-      return;
-    }
-
-    let data = aMessage.data;
-    let details = data.details;
-    let listener = {
-      mm: aMessage.target,
-      title: data.title,
-      text: data.text,
-      manifestURL: details.manifestURL,
-      imageURL: data.imageURL,
-      lang: details.lang || undefined,
-      id: details.id || undefined,
-      dir: details.dir || undefined,
-      tag: details.tag || undefined
-    };
-    this.registerAppListener(data.uid, listener);
-
-    this.showNotification(data.imageURL, data.title, data.text,
-                          details.textClickable, null,
-                          data.uid, details.dir,
-                          details.lang, details.manifestURL);
-  },
-}
-
 var WebappsHelper = {
   _installers: {},
   _count: 0,
 
   init: function webapps_init() {
     Services.obs.addObserver(this, "webapps-launch", false);
     Services.obs.addObserver(this, "webapps-ask-install", false);
     Services.obs.addObserver(this, "webapps-close", false);
new file mode 100644
--- /dev/null
+++ b/b2g/components/AlertsHelper.jsm
@@ -0,0 +1,291 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+this.EXPORTED_SYMBOLS = [];
+
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+const Cc = Components.classes;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/AppsUtils.jsm");
+
+XPCOMUtils.defineLazyServiceGetter(this, "gSystemMessenger",
+                                   "@mozilla.org/system-message-internal;1",
+                                   "nsISystemMessagesInternal");
+
+XPCOMUtils.defineLazyServiceGetter(this, "appsService",
+                                   "@mozilla.org/AppsService;1",
+                                   "nsIAppsService");
+
+XPCOMUtils.defineLazyModuleGetter(this, "SystemAppProxy",
+                                  "resource://gre/modules/SystemAppProxy.jsm");
+
+XPCOMUtils.defineLazyGetter(this, "ppmm", function() {
+  return Cc["@mozilla.org/parentprocessmessagemanager;1"]
+         .getService(Ci.nsIMessageListenerManager);
+});
+
+function debug(str) {
+  //dump("=*= AlertsHelper.jsm : " + str + "\n");
+}
+
+const kNotificationIconSize = 128;
+
+const kDesktopNotificationPerm = "desktop-notification";
+
+const kNotificationSystemMessageName = "notification";
+
+const kDesktopNotification      = "desktop-notification";
+const kDesktopNotificationShow  = "desktop-notification-show";
+const kDesktopNotificationClick = "desktop-notification-click";
+const kDesktopNotificationClose = "desktop-notification-close";
+
+const kTopicAlertClickCallback = "alertclickcallback";
+const kTopicAlertShow          = "alertshow";
+const kTopicAlertFinished      = "alertfinished";
+
+const kMozChromeNotificationEvent  = "mozChromeNotificationEvent";
+const kMozContentNotificationEvent = "mozContentNotificationEvent";
+
+const kMessageAppNotificationSend    = "app-notification-send";
+const kMessageAppNotificationReturn  = "app-notification-return";
+const kMessageAlertNotificationSend  = "alert-notification-send";
+const kMessageAlertNotificationClose = "alert-notification-close";
+
+const kMessages = [
+  kMessageAppNotificationSend,
+  kMessageAlertNotificationSend,
+  kMessageAlertNotificationClose
+];
+
+let AlertsHelper = {
+
+  _listeners: {},
+
+  init: function() {
+    Services.obs.addObserver(this, "xpcom-shutdown", false);
+    for (let message of kMessages) {
+      ppmm.addMessageListener(message, this);
+    }
+    SystemAppProxy.addEventListener(kMozContentNotificationEvent, this);
+  },
+
+  observe: function(aSubject, aTopic, aData) {
+    switch (aTopic) {
+      case "xpcom-shutdown":
+        Services.obs.removeObserver(this, "xpcom-shutdown");
+        for (let message of kMessages) {
+          ppmm.removeMessageListener(message, this);
+        }
+        SystemAppProxy.removeEventListener(kMozContentNotificationEvent, this);
+        break;
+    }
+  },
+
+  handleEvent: function(evt) {
+    let detail = evt.detail;
+
+    switch(detail.type) {
+      case kDesktopNotificationShow:
+      case kDesktopNotificationClick:
+      case kDesktopNotificationClose:
+        this.handleNotificationEvent(detail);
+        break;
+      default:
+        debug("FIXME: Unhandled notification event: " + detail.type);
+        break;
+    }
+  },
+
+  handleNotificationEvent: function(detail) {
+    if (!detail || !detail.id) {
+      return;
+    }
+
+    let uid = detail.id;
+    let listener = this._listeners[uid];
+    if (!listener) {
+      return;
+    }
+
+    let topic;
+    if (detail.type === kDesktopNotificationClick) {
+      topic = kTopicAlertClickCallback;
+    } else if (detail.type === kDesktopNotificationShow) {
+      topic = kTopicAlertShow;
+    } else {
+      /* kDesktopNotificationClose */
+      topic = kTopicAlertFinished;
+    }
+
+    if (listener.cookie) {
+      try {
+        listener.observer.observe(null, topic, listener.cookie);
+      } catch (e) { }
+    } else {
+      try {
+        listener.mm.sendAsyncMessage(kMessageAppNotificationReturn, {
+          uid: uid,
+          topic: topic,
+          target: listener.target
+        });
+      } catch (e) {
+        // we get an exception if the app is not launched yet
+        gSystemMessenger.sendMessage(kNotificationSystemMessageName, {
+            clicked: (detail.type === kDesktopNotificationClick),
+            title: listener.title,
+            body: listener.text,
+            imageURL: listener.imageURL,
+            lang: listener.lang,
+            dir: listener.dir,
+            id: listener.id,
+            tag: listener.tag
+          },
+          Services.io.newURI(listener.target, null, null),
+          Services.io.newURI(listener.manifestURL, null, null)
+        );
+      }
+    }
+
+    // we"re done with this notification
+    if (topic === kTopicAlertFinished) {
+      delete this._listeners[uid];
+    }
+  },
+
+  registerListener: function(alertId, cookie, alertListener) {
+    this._listeners[alertId] = { observer: alertListener, cookie: cookie };
+  },
+
+  registerAppListener: function(uid, listener) {
+    this._listeners[uid] = listener;
+
+    appsService.getManifestFor(listener.manifestURL).then((manifest) => {
+      let helper = new ManifestHelper(manifest, listener.manifestURL);
+      let getNotificationURLFor = function(messages) {
+        if (!messages) {
+          return null;
+        }
+
+        for (let i = 0; i < messages.length; i++) {
+          let message = messages[i];
+          if (message === kNotificationSystemMessageName) {
+            return helper.fullLaunchPath();
+          } else if (typeof message === "object" &&
+                     kNotificationSystemMessageName in message) {
+            return
+              helper.resolveFromOrigin(message[kNotificationSystemMessageName]);
+          }
+        }
+
+        // No message found...
+        return null;
+      }
+
+      listener.target = getNotificationURLFor(manifest.messages);
+
+      // Bug 816944 - Support notification messages for entry_points.
+    });
+  },
+
+  showNotification: function(imageURL, title, text, textClickable, cookie,
+                             uid, bidi, lang, manifestURL) {
+    function send(appName, appIcon) {
+      SystemAppProxy._sendCustomEvent(kMozChromeNotificationEvent, {
+        type: kDesktopNotification,
+        id: uid,
+        icon: imageURL,
+        title: title,
+        text: text,
+        bidi: bidi,
+        lang: lang,
+        appName: appName,
+        appIcon: appIcon,
+        manifestURL: manifestURL
+      });
+    }
+
+    if (!manifestURL || !manifestURL.length) {
+      send(null, null);
+      return;
+    }
+
+    // If we have a manifest URL, get the icon and title from the manifest
+    // to prevent spoofing.
+    appsService.getManifestFor(manifestURL).then((manifest) => {
+      let helper = new ManifestHelper(manifest, manifestURL);
+      send(helper.name, helper.iconURLForSize(kNotificationIconSize));
+    });
+  },
+
+  showAlertNotification: function(aMessage) {
+    let data = aMessage.data;
+    let currentListener = this._listeners[data.name];
+    if (currentListener && currentListener.observer) {
+      currentListener.observer.observe(null, kTopicAlertFinished, currentListener.cookie);
+    }
+
+    this.registerListener(data.name, data.cookie, data.alertListener);
+    this.showNotification(data.imageURL, data.title, data.text,
+                          data.textClickable, data.cookie, data.name, data.bidi,
+                          data.lang, null);
+  },
+
+  showAppNotification: function(aMessage) {
+    let data = aMessage.data;
+    let details = data.details;
+    let listener = {
+      mm: aMessage.target,
+      title: data.title,
+      text: data.text,
+      manifestURL: details.manifestURL,
+      imageURL: data.imageURL,
+      lang: details.lang || undefined,
+      id: details.id || undefined,
+      dir: details.dir || undefined,
+      tag: details.tag || undefined
+    };
+    this.registerAppListener(data.uid, listener);
+    this.showNotification(data.imageURL, data.title, data.text,
+                          details.textClickable, null, data.uid, details.dir,
+                          details.lang, details.manifestURL);
+  },
+
+  closeAlert: function(name) {
+    SystemAppProxy._sendCustomEvent(kMozChromeNotificationEvent, {
+      type: kDesktopNotificationClose,
+      id: name
+    });
+  },
+
+  receiveMessage: function(aMessage) {
+    if (!aMessage.target.assertAppHasPermission(kDesktopNotificationPerm)) {
+      Cu.reportError("Desktop-notification message " + aMessage.name +
+                     " from a content process with no " + kDesktopNotificationPerm +
+                     " privileges.");
+      return;
+    }
+
+    switch(aMessage.name) {
+      case kMessageAppNotificationSend:
+        this.showAppNotification(aMessage);
+        break;
+
+      case kMessageAlertNotificationSend:
+        this.showAlertNotification(aMessage);
+        break;
+
+      case kMessageAlertNotificationClose:
+        this.closeAlert(aMessage.data.name);
+        break;
+    }
+
+  },
+}
+
+AlertsHelper.init();
--- a/b2g/components/AlertsService.js
+++ b/b2g/components/AlertsService.js
@@ -30,114 +30,133 @@ XPCOMUtils.defineLazyGetter(this, "cpmm"
 function debug(str) {
   dump("=*= AlertsService.js : " + str + "\n");
 }
 
 // -----------------------------------------------------------------------
 // Alerts Service
 // -----------------------------------------------------------------------
 
+const kNotificationSystemMessageName = "notification";
+
+const kMessageAppNotificationSend    = "app-notification-send";
+const kMessageAppNotificationReturn  = "app-notification-return";
+const kMessageAlertNotificationSend  = "alert-notification-send";
+const kMessageAlertNotificationClose = "alert-notification-close";
+
+const kTopicAlertFinished      = "alertfinished";
+
 function AlertsService() {
-  cpmm.addMessageListener("app-notification-return", this);
+  Services.obs.addObserver(this, "xpcom-shutdown", false);
+  cpmm.addMessageListener(kMessageAppNotificationReturn, this);
 }
 
 AlertsService.prototype = {
   classID: Components.ID("{fe33c107-82a4-41d6-8c64-5353267e04c9}"),
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIAlertsService,
-                                         Ci.nsIAppNotificationService]),
+                                         Ci.nsIAppNotificationService,
+                                         Ci.nsIObserver]),
+
+  observe: function(aSubject, aTopic, aData) {
+    switch (aTopic) {
+      case "xpcom-shutdown":
+        Services.obs.removeObserver(this, "xpcom-shutdown");
+        cpmm.removeMessageListener(kMessageAppNotificationReturn, this);
+        break;
+    }
+  },
 
   // nsIAlertsService
-  showAlertNotification: function showAlertNotification(aImageUrl,
-                                                        aTitle,
-                                                        aText,
-                                                        aTextClickable,
-                                                        aCookie,
-                                                        aAlertListener,
-                                                        aName,
-                                                        aBidi,
-                                                        aLang) {
-    let browser = Services.wm.getMostRecentWindow("navigator:browser");
-    browser.AlertsHelper.showAlertNotification(aImageUrl, aTitle, aText,
-                                               aTextClickable, aCookie,
-                                               aAlertListener, aName, aBidi,
-                                               aLang);
+  showAlertNotification: function(aImageUrl, aTitle, aText, aTextClickable,
+                                  aCookie, aAlertListener, aName, aBidi,
+                                  aLang) {
+    cpmm.sendAsyncMessage(kMessageAlertNotificationSend, {
+      imageURL: aImageUrl,
+      title: aTitle,
+      text: aText,
+      clickable: aTextClickable,
+      cookie: aCookie,
+      listener: aAlertListener,
+      id: aName,
+      dir: aBidi,
+      lang: aLang
+    });
   },
 
   closeAlert: function(aName) {
-    let browser = Services.wm.getMostRecentWindow("navigator:browser");
-    browser.AlertsHelper.closeAlert(aName);
+    cpmm.sendAsyncMessage(kMessageAlertNotificationClose, {
+      name: aName
+    });
   },
 
   // nsIAppNotificationService
-  showAppNotification: function showAppNotification(aImageURL,
-                                                    aTitle,
-                                                    aText,
-                                                    aAlertListener,
-                                                    aDetails) {
+  showAppNotification: function(aImageURL, aTitle, aText, aAlertListener,
+                                aDetails) {
     let uid = (aDetails.id == "") ?
           "app-notif-" + uuidGenerator.generateUUID() : aDetails.id;
 
     this._listeners[uid] = {
       observer: aAlertListener,
       title: aTitle,
       text: aText,
       manifestURL: aDetails.manifestURL,
       imageURL: aImageURL,
       lang: aDetails.lang || undefined,
       id: aDetails.id || undefined,
       dbId: aDetails.dbId || undefined,
       dir: aDetails.dir || undefined,
       tag: aDetails.tag || undefined
     };
 
-    cpmm.sendAsyncMessage("app-notification-send", {
+    cpmm.sendAsyncMessage(kMessageAppNotificationSend, {
       imageURL: aImageURL,
       title: aTitle,
       text: aText,
       uid: uid,
       details: aDetails
     });
   },
 
   // AlertsService.js custom implementation
   _listeners: [],
 
-  receiveMessage: function receiveMessage(aMessage) {
+  receiveMessage: function(aMessage) {
     let data = aMessage.data;
     let listener = this._listeners[data.uid];
-    if (aMessage.name !== "app-notification-return" || !listener) {
+    if (aMessage.name !== kMessageAppNotificationReturn || !listener) {
       return;
     }
 
     let topic = data.topic;
 
     try {
       listener.observer.observe(null, topic, null);
     } catch (e) {
       // It seems like there is no callbacks anymore, forward the click on
       // notification via a system message containing the title/text/icon of
       // the notification so the app get a change to react.
       if (data.target) {
-        gSystemMessenger.sendMessage("notification", {
+        gSystemMessenger.sendMessage(kNotificationSystemMessageName, {
             title: listener.title,
             body: listener.text,
             imageURL: listener.imageURL,
             lang: listener.lang,
             dir: listener.dir,
             id: listener.id,
             tag: listener.tag,
             dbId: listener.dbId
           },
           Services.io.newURI(data.target, null, null),
-          Services.io.newURI(listener.manifestURL, null, null));
+          Services.io.newURI(listener.manifestURL, null, null)
+        );
       }
     }
 
     // we're done with this notification
-    if (topic === "alertfinished") {
+    if (topic === kTopicAlertFinished) {
       if (listener.dbId) {
         notificationStorage.delete(listener.manifestURL, listener.dbId);
       }
       delete this._listeners[data.uid];
     }
   }
 };
 
--- a/b2g/components/FxAccountsMgmtService.jsm
+++ b/b2g/components/FxAccountsMgmtService.jsm
@@ -43,30 +43,26 @@ this.FxAccountsMgmtService = {
   _onReject: function(aMsgId, aReason) {
     SystemAppProxy._sendCustomEvent("mozFxAccountsChromeEvent", {
       id: aMsgId,
       error: aReason ? aReason : null
     });
   },
 
   init: function() {
-    Services.obs.addObserver(this, "content-start", false);
     Services.obs.addObserver(this, ONLOGIN_NOTIFICATION, false);
     Services.obs.addObserver(this, ONVERIFIED_NOTIFICATION, false);
     Services.obs.addObserver(this, ONLOGOUT_NOTIFICATION, false);
+    SystemAppProxy.addEventListener("mozFxAccountsContentEvent",
+                                    FxAccountsMgmtService);
   },
 
   observe: function(aSubject, aTopic, aData) {
     log.debug("Observed " + aTopic);
     switch (aTopic) {
-      case "content-start":
-        SystemAppProxy.addEventListener("mozFxAccountsContentEvent",
-                                        FxAccountsMgmtService);
-        Services.obs.removeObserver(this, "content-start");
-        break;
       case ONLOGIN_NOTIFICATION:
       case ONVERIFIED_NOTIFICATION:
       case ONLOGOUT_NOTIFICATION:
         // FxAccounts notifications have the form of fxaccounts:*
         SystemAppProxy._sendCustomEvent("mozFxAccountsUnsolChromeEvent", {
           eventName: aTopic.substring(aTopic.indexOf(":") + 1)
         });
         break;
--- a/b2g/components/moz.build
+++ b/b2g/components/moz.build
@@ -36,16 +36,17 @@ EXTRA_PP_COMPONENTS += [
 ]
 
 if CONFIG['MOZ_UPDATER']:
     EXTRA_PP_COMPONENTS += [
         'UpdatePrompt.js',
     ]
 
 EXTRA_JS_MODULES += [
+    'AlertsHelper.jsm',
     'ErrorPage.jsm',
     'SignInToWebsite.jsm',
     'SystemAppProxy.jsm',
     'TelURIParser.jsm',
     'WebappsUpdater.jsm',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk':
--- a/browser/base/content/aboutDialog.js
+++ b/browser/base/content/aboutDialog.js
@@ -47,21 +47,22 @@ function init(aEvent)
     document.getElementById("version").textContent += " (" + buildDate + ")";
     document.getElementById("experimental").hidden = false;
     document.getElementById("communityDesc").hidden = true;
   }
 
 #ifdef MOZ_UPDATER
   gAppUpdater = new appUpdater();
 
-#if MOZ_UPDATE_CHANNEL != release
   let defaults = Services.prefs.getDefaultBranch("");
   let channelLabel = document.getElementById("currentChannel");
+  let currentChannelText = document.getElementById("currentChannelText");
   channelLabel.value = defaults.getCharPref("app.update.channel");
-#endif
+  if (channelLabel.value == "release")
+      currentChannelText.hidden = true;
 #endif
 
 #ifdef XP_MACOSX
   // it may not be sized at this point, and we need its width to calculate its position
   window.sizeToContent();
   window.moveTo((screen.availWidth / 2) - (window.outerWidth / 2), screen.availHeight / 5);
 #endif
 }
--- a/browser/base/content/aboutDialog.xul
+++ b/browser/base/content/aboutDialog.xul
@@ -103,23 +103,21 @@
               </hbox>
               <hbox id="unsupportedSystem" align="center">
                 <label>&update.unsupported.start;</label><label id="unsupportedLink" class="text-link">&update.unsupported.linkText;</label><label>&update.unsupported.end;</label>
               </hbox>
             </deck>
 #endif
           </vbox>
 
-#if MOZ_UPDATE_CHANNEL != release
 #ifdef MOZ_UPDATER
           <description class="text-blurb" id="currentChannelText">
             &channel.description.start;<label id="currentChannel"/>&channel.description.end;
           </description>
 #endif
-#endif
           <vbox id="experimental" hidden="true">
             <description class="text-blurb" id="warningDesc">
               &warningDesc.version;
 #ifdef MOZ_TELEMETRY_ON_BY_DEFAULT
               &warningDesc.telemetryDesc;
 #endif
             </description>
             <description class="text-blurb" id="communityExperimentalDesc">
--- a/configure.in
+++ b/configure.in
@@ -6347,16 +6347,17 @@ dnl build the tests by default
 dnl ========================================================
 MOZ_ARG_DISABLE_BOOL(tests,
 [  --disable-tests         Do not build test libraries & programs],
     ENABLE_TESTS=,
     ENABLE_TESTS=1 )
 
 if test -n "$ENABLE_TESTS"; then
     GTEST_HAS_RTTI=0
+    AC_DEFINE(ENABLE_TESTS)
     AC_DEFINE_UNQUOTED(GTEST_HAS_RTTI, 0)
     AC_SUBST(GTEST_HAS_RTTI)
     if test -n "$_WIN32_MSVC"; then
           AC_DEFINE_UNQUOTED(_VARIADIC_MAX, 10)
     fi
     if test "${OS_TARGET}" = "Android"; then
         AC_DEFINE(GTEST_OS_LINUX_ANDROID)
         AC_DEFINE(GTEST_USE_OWN_TR1_TUPLE)
old mode 100755
new mode 100644
--- a/content/canvas/src/CanvasRenderingContext2D.cpp
+++ b/content/canvas/src/CanvasRenderingContext2D.cpp
@@ -1485,23 +1485,17 @@ CanvasRenderingContext2D::CreatePattern(
       RefPtr<SourceSurface> srcSurf = srcCanvas->GetSurfaceSnapshot();
 
       nsRefPtr<CanvasPattern> pat =
         new CanvasPattern(this, srcSurf, repeatMode, htmlElement->NodePrincipal(), canvas->IsWriteOnly(), false);
 
       return pat.forget();
     }
   } else if (element.IsHTMLImageElement()) {
-    HTMLImageElement* img = &element.GetAsHTMLImageElement();
-    if (img->IntrinsicState().HasState(NS_EVENT_STATE_BROKEN)) {
-      error.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-      return nullptr;
-    }
-
-    htmlElement = img;
+    htmlElement = &element.GetAsHTMLImageElement();
   } else {
     htmlElement = &element.GetAsHTMLVideoElement();
   }
 
   EnsureTarget();
 
   // The canvas spec says that createPattern should use the first frame
   // of animated images
--- a/content/canvas/src/WebGLContextDraw.cpp
+++ b/content/canvas/src/WebGLContextDraw.cpp
@@ -3,16 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebGLContext.h"
 
 #include "GLContext.h"
 #include "mozilla/CheckedInt.h"
 #include "WebGLBuffer.h"
+#include "WebGLContextUtils.h"
 #include "WebGLFramebuffer.h"
 #include "WebGLProgram.h"
 #include "WebGLRenderbuffer.h"
 #include "WebGLShader.h"
 #include "WebGLTexture.h"
 #include "WebGLUniformInfo.h"
 #include "WebGLVertexArray.h"
 #include "WebGLVertexAttribData.h"
@@ -625,17 +626,17 @@ WebGLContext::BindFakeBlackTexturesHelpe
         WebGLTextureFakeBlackStatus s = boundTexturesArray[i]->ResolvedFakeBlackStatus();
         MOZ_ASSERT(s != WebGLTextureFakeBlackStatus::Unknown);
 
         if (MOZ_LIKELY(s == WebGLTextureFakeBlackStatus::NotNeeded)) {
             continue;
         }
 
         bool alpha = s == WebGLTextureFakeBlackStatus::UninitializedImageData &&
-                     FormatHasAlpha(boundTexturesArray[i]->ImageInfoBase().InternalFormat());
+                     FormatHasAlpha(boundTexturesArray[i]->ImageInfoBase().WebGLFormat());
         ScopedDeletePtr<FakeBlackTexture>&
             blackTexturePtr = alpha
                               ? transparentTextureScopedPtr
                               : opaqueTextureScopedPtr;
 
         if (!blackTexturePtr) {
             GLenum format = alpha ? LOCAL_GL_RGBA : LOCAL_GL_RGB;
             blackTexturePtr
--- a/content/canvas/src/WebGLContextGL.cpp
+++ b/content/canvas/src/WebGLContextGL.cpp
@@ -48,17 +48,16 @@
 #include "mozilla/Endian.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::gl;
 using namespace mozilla::gfx;
 
 static bool BaseTypeAndSizeFromUniformType(GLenum uType, GLenum *baseType, GLint *unitSize);
-static GLenum InternalFormatForFormatAndType(GLenum format, GLenum type, bool isGLES2);
 
 const WebGLRectangleObject*
 WebGLContext::CurValidFBRectObject() const
 {
     const WebGLRectangleObject* rect = nullptr;
 
     if (mBoundFramebuffer) {
         // We don't really need to ask the driver.
@@ -448,22 +447,23 @@ WebGLContext::CopyTexImage2D(GLenum targ
                              GLsizei height,
                              GLint border)
 {
     if (IsContextLost())
         return;
 
     // copyTexImage2D only generates textures with type = UNSIGNED_BYTE
     const WebGLTexImageFunc func = WebGLTexImageFunc::CopyTexImage;
-    GLenum type = LOCAL_GL_UNSIGNED_BYTE;
-
-    if (!ValidateTexImage(2, target, level, internalformat,
+    const GLenum format = internalformat; // WebGL/ES Format
+    const GLenum type = LOCAL_GL_UNSIGNED_BYTE; // WebGL/ES Format
+
+    if (!ValidateTexImage(2, target, level, format,
                           0, 0, 0,
                           width, height, 0,
-                          border, internalformat, type,
+                          border, format, type,
                           func))
     {
         return;
     }
 
     if (mBoundFramebuffer) {
         if (!mBoundFramebuffer->CheckAndInitializeAttachments())
             return ErrorInvalidFramebufferOperation("copyTexImage2D: incomplete framebuffer");
@@ -472,51 +472,51 @@ WebGLContext::CopyTexImage2D(GLenum targ
         if (!mBoundFramebuffer->HasCompletePlanes(readPlaneBits)) {
             return ErrorInvalidOperation("copyTexImage2D: Read source attachment doesn't have the"
                                          " correct color/depth/stencil type.");
         }
     } else {
       ClearBackbufferIfNeeded();
     }
 
-    bool texFormatRequiresAlpha = internalformat == LOCAL_GL_RGBA ||
-                                  internalformat == LOCAL_GL_ALPHA ||
-                                  internalformat == LOCAL_GL_LUMINANCE_ALPHA;
+    bool texFormatRequiresAlpha = format == LOCAL_GL_RGBA ||
+                                  format == LOCAL_GL_ALPHA ||
+                                  format == LOCAL_GL_LUMINANCE_ALPHA;
     bool fboFormatHasAlpha = mBoundFramebuffer ? mBoundFramebuffer->ColorAttachment(0).HasAlpha()
                                                : bool(gl->GetPixelFormat().alpha > 0);
     if (texFormatRequiresAlpha && !fboFormatHasAlpha)
         return ErrorInvalidOperation("copyTexImage2D: texture format requires an alpha channel "
                                      "but the framebuffer doesn't have one");
 
     // check if the memory size of this texture may change with this call
     bool sizeMayChange = true;
     WebGLTexture* tex = activeBoundTextureForTarget(target);
     if (tex->HasImageInfoAt(target, level)) {
         const WebGLTexture::ImageInfo& imageInfo = tex->ImageInfoAt(target, level);
 
         sizeMayChange = width != imageInfo.Width() ||
                         height != imageInfo.Height() ||
-                        internalformat != imageInfo.InternalFormat() ||
-                        type != imageInfo.Type();
+                        format != imageInfo.WebGLFormat() ||
+                        type != imageInfo.WebGLType();
     }
 
     if (sizeMayChange)
         GetAndFlushUnderlyingGLErrors();
 
-    CopyTexSubImage2D_base(target, level, internalformat, 0, 0, x, y, width, height, false);
+    CopyTexSubImage2D_base(target, level, format, 0, 0, x, y, width, height, false);
 
     if (sizeMayChange) {
         GLenum error = GetAndFlushUnderlyingGLErrors();
         if (error) {
             GenerateWarning("copyTexImage2D generated error %s", ErrorName(error));
             return;
         }
     }
 
-    tex->SetImageInfo(target, level, width, height, internalformat, type,
+    tex->SetImageInfo(target, level, width, height, format, type,
                       WebGLImageDataStatus::InitializedImageData);
 }
 
 void
 WebGLContext::CopyTexSubImage2D(GLenum target,
                                 GLint level,
                                 GLint xoffset,
                                 GLint yoffset,
@@ -566,51 +566,46 @@ WebGLContext::CopyTexSubImage2D(GLenum t
     GLsizei texHeight = imageInfo.Height();
 
     if (xoffset + width > texWidth || xoffset + width < 0)
       return ErrorInvalidValue("copyTexSubImage2D: xoffset+width is too large");
 
     if (yoffset + height > texHeight || yoffset + height < 0)
       return ErrorInvalidValue("copyTexSubImage2D: yoffset+height is too large");
 
-    GLenum internalFormat = imageInfo.InternalFormat();
-    if (IsGLDepthFormat(internalFormat) ||
-        IsGLDepthStencilFormat(internalFormat))
-    {
+    GLenum webGLFormat = imageInfo.WebGLFormat();
+    if (IsGLDepthFormat(webGLFormat) || IsGLDepthStencilFormat(webGLFormat))
         return ErrorInvalidOperation("copyTexSubImage2D: a base internal format of DEPTH_COMPONENT or DEPTH_STENCIL isn't supported");
-    }
 
     if (mBoundFramebuffer) {
         if (!mBoundFramebuffer->CheckAndInitializeAttachments())
             return ErrorInvalidFramebufferOperation("copyTexSubImage2D: incomplete framebuffer");
 
         GLenum readPlaneBits = LOCAL_GL_COLOR_BUFFER_BIT;
         if (!mBoundFramebuffer->HasCompletePlanes(readPlaneBits)) {
             return ErrorInvalidOperation("copyTexSubImage2D: Read source attachment doesn't have the"
                                          " correct color/depth/stencil type.");
         }
     } else {
         ClearBackbufferIfNeeded();
     }
 
-    bool texFormatRequiresAlpha = (internalFormat == LOCAL_GL_RGBA ||
-                                   internalFormat == LOCAL_GL_ALPHA ||
-                                   internalFormat == LOCAL_GL_LUMINANCE_ALPHA);
+    bool texFormatRequiresAlpha = FormatHasAlpha(webGLFormat);
     bool fboFormatHasAlpha = mBoundFramebuffer ? mBoundFramebuffer->ColorAttachment(0).HasAlpha()
                                                : bool(gl->GetPixelFormat().alpha > 0);
 
     if (texFormatRequiresAlpha && !fboFormatHasAlpha)
         return ErrorInvalidOperation("copyTexSubImage2D: texture format requires an alpha channel "
                                      "but the framebuffer doesn't have one");
 
     if (imageInfo.HasUninitializedImageData()) {
         tex->DoDeferredImageInitialization(target, level);
     }
 
-    return CopyTexSubImage2D_base(target, level, internalFormat, xoffset, yoffset, x, y, width, height, true);
+    return CopyTexSubImage2D_base(target, level, webGLFormat, xoffset, yoffset, x, y, width, height, true);
 }
 
 
 already_AddRefed<WebGLProgram>
 WebGLContext::CreateProgram()
 {
     if (IsContextLost())
         return nullptr;
@@ -895,22 +890,22 @@ WebGLContext::GenerateMipmap(GLenum targ
     if (!tex->HasImageInfoAt(imageTarget, 0))
     {
         return ErrorInvalidOperation("generateMipmap: Level zero of texture is not defined.");
     }
 
     if (!tex->IsFirstImagePowerOfTwo())
         return ErrorInvalidOperation("generateMipmap: Level zero of texture does not have power-of-two width and height.");
 
-    GLenum internalFormat = tex->ImageInfoAt(imageTarget, 0).InternalFormat();
-    if (IsTextureFormatCompressed(internalFormat))
+    GLenum webGLFormat = tex->ImageInfoAt(imageTarget, 0).WebGLFormat();
+    if (IsTextureFormatCompressed(webGLFormat))
         return ErrorInvalidOperation("generateMipmap: Texture data at level zero is compressed.");
 
     if (IsExtensionEnabled(WebGLExtensionID::WEBGL_depth_texture) &&
-        (IsGLDepthFormat(internalFormat) || IsGLDepthStencilFormat(internalFormat)))
+        (IsGLDepthFormat(webGLFormat) || IsGLDepthStencilFormat(webGLFormat)))
     {
         return ErrorInvalidOperation("generateMipmap: "
                                      "A texture that has a base internal format of "
                                      "DEPTH_COMPONENT or DEPTH_STENCIL isn't supported");
     }
 
     if (!tex->AreAllLevel0ImageInfosEqual())
         return ErrorInvalidOperation("generateMipmap: The six faces of this cube map have different dimensions, format, or type.");
@@ -1172,21 +1167,21 @@ WebGLContext::GetFramebufferAttachmentPa
         }
 
         ErrorInvalidEnumInfo("getFramebufferAttachmentParameter: pname", pname);
         return JS::NullValue();
     } else if (fba.Texture()) {
         switch (pname) {
              case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT:
                 if (IsExtensionEnabled(WebGLExtensionID::EXT_sRGB)) {
-                    const GLenum internalFormat =
-                        fba.Texture()->ImageInfoBase().InternalFormat();
-                    return (internalFormat == LOCAL_GL_SRGB_EXT ||
-                            internalFormat == LOCAL_GL_SRGB_ALPHA_EXT) ?
-                        JS::NumberValue(uint32_t(LOCAL_GL_SRGB_EXT)) :
+                    const GLenum webGLFormat =
+                        fba.Texture()->ImageInfoBase().WebGLFormat();
+                    return (webGLFormat == LOCAL_GL_SRGB ||
+                            webGLFormat == LOCAL_GL_SRGB_ALPHA) ?
+                        JS::NumberValue(uint32_t(LOCAL_GL_SRGB)) :
                         JS::NumberValue(uint32_t(LOCAL_GL_LINEAR));
                 }
                 break;
 
             case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE:
                 return JS::NumberValue(uint32_t(LOCAL_GL_TEXTURE));
 
             case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME:
@@ -1215,17 +1210,17 @@ WebGLContext::GetFramebufferAttachmentPa
                     return JS::NullValue();
                 }
 
                 if (!fba.IsComplete())
                     return JS::NumberValue(uint32_t(LOCAL_GL_NONE));
 
                 uint32_t ret = LOCAL_GL_NONE;
                 GLenum type = fba.Texture()->ImageInfoAt(fba.TexImageTarget(),
-                                                         fba.TexImageLevel()).Type();
+                                                         fba.TexImageLevel()).WebGLType();
                 switch (type) {
                 case LOCAL_GL_UNSIGNED_BYTE:
                 case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
                 case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
                 case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
                     ret = LOCAL_GL_UNSIGNED_NORMALIZED;
                     break;
                 case LOCAL_GL_FLOAT:
@@ -3518,55 +3513,56 @@ GLenum WebGLContext::CheckedTexImage2D(G
                                        GLenum internalFormat,
                                        GLsizei width,
                                        GLsizei height,
                                        GLint border,
                                        GLenum format,
                                        GLenum type,
                                        const GLvoid *data)
 {
+    MOZ_ASSERT(internalFormat == format);
     WebGLTexture *tex = activeBoundTextureForTarget(target);
     MOZ_ASSERT(tex != nullptr, "no texture bound");
 
     bool sizeMayChange = true;
 
     if (tex->HasImageInfoAt(target, level)) {
         const WebGLTexture::ImageInfo& imageInfo = tex->ImageInfoAt(target, level);
         sizeMayChange = width != imageInfo.Width() ||
                         height != imageInfo.Height() ||
-                        format != imageInfo.InternalFormat() ||
-                        type != imageInfo.Type();
+                        format != imageInfo.WebGLFormat() ||
+                        type != imageInfo.WebGLType();
     }
 
-    // convert type for half float if not on GLES2
-    GLenum realType = type;
-    if (realType == LOCAL_GL_HALF_FLOAT_OES && !gl->IsGLES()) {
-        realType = LOCAL_GL_HALF_FLOAT;
-    }
+    // Convert to format and type required by OpenGL 'driver'.
+    GLenum driverType = DriverTypeFromType(gl, type);
+    GLenum driverInternalFormat = LOCAL_GL_NONE;
+    GLenum driverFormat = LOCAL_GL_NONE;
+    DriverFormatsFromFormatAndType(gl, format, type, &driverInternalFormat, &driverFormat);
 
     if (sizeMayChange) {
         GetAndFlushUnderlyingGLErrors();
-
-        gl->fTexImage2D(target, level, internalFormat, width, height, border, format, realType, data);
-
-        GLenum error = GetAndFlushUnderlyingGLErrors();
-        return error;
     }
 
-    gl->fTexImage2D(target, level, internalFormat, width, height, border, format, realType, data);
-
-    return LOCAL_GL_NO_ERROR;
+    gl->fTexImage2D(target, level, driverInternalFormat, width, height, border, driverFormat, driverType, data);
+
+    GLenum error = LOCAL_GL_NO_ERROR;
+    if (sizeMayChange) {
+        error = GetAndFlushUnderlyingGLErrors();
+    }
+
+    return error;
 }
 
 void
 WebGLContext::TexImage2D_base(GLenum target, GLint level, GLenum internalformat,
                               GLsizei width, GLsizei height, GLsizei srcStrideOrZero,
                               GLint border,
                               GLenum format, GLenum type,
-                              void *data, uint32_t byteLength,
+                              void* data, uint32_t byteLength,
                               int jsArrayType, // a TypedArray format enum, or -1 if not relevant
                               WebGLTexelFormat srcFormat, bool srcPremultiplied)
 {
     const WebGLTexImageFunc func = WebGLTexImageFunc::TexImage;
 
     if (!ValidateTexImage(2, target, level, internalformat,
                           0, 0, 0,
                           width, height, 0,
@@ -3593,17 +3589,16 @@ WebGLContext::TexImage2D_base(GLenum tar
     WebGLTexelFormat actualSrcFormat = srcFormat == WebGLTexelFormat::Auto ? dstFormat : srcFormat;
 
     uint32_t srcTexelSize = WebGLTexelConversions::TexelBytesForFormat(actualSrcFormat);
 
     CheckedUint32 checked_neededByteLength =
         GetImageSize(height, width, srcTexelSize, mPixelStoreUnpackAlignment);
 
     CheckedUint32 checked_plainRowSize = CheckedUint32(width) * srcTexelSize;
-
     CheckedUint32 checked_alignedRowSize =
         RoundedToNextMultipleOf(checked_plainRowSize.value(), mPixelStoreUnpackAlignment);
 
     if (!checked_neededByteLength.isValid())
         return ErrorInvalidOperation("texImage2D: integer overflow computing the needed buffer size");
 
     uint32_t bytesNeeded = checked_neededByteLength.value();
 
@@ -3613,87 +3608,62 @@ WebGLContext::TexImage2D_base(GLenum tar
 
     WebGLTexture *tex = activeBoundTextureForTarget(target);
 
     if (!tex)
         return ErrorInvalidOperation("texImage2D: no texture is bound to this target");
 
     MakeContextCurrent();
 
-    // Handle ES2 and GL differences in floating point internal formats.  Note that
-    // format == internalformat, as checked above and as required by ES.
-    internalformat = InternalFormatForFormatAndType(format, type, gl->IsGLES());
-
-    // Handle ES2 and GL differences when supporting sRGB internal formats. GL ES
-    // requires that format == internalformat, but GL will fail in this case.
-    // GL requires:
-    //      format  ->  internalformat
-    //      GL_RGB      GL_SRGB_EXT
-    //      GL_RGBA     GL_SRGB_ALPHA_EXT
-    if (!gl->IsGLES()) {
-        switch (internalformat) {
-            case LOCAL_GL_SRGB_EXT:
-                format = LOCAL_GL_RGB;
-                break;
-            case LOCAL_GL_SRGB_ALPHA_EXT:
-                format = LOCAL_GL_RGBA;
-                break;
-        }
-    }
-
-    GLenum error = LOCAL_GL_NO_ERROR;
-
-    WebGLImageDataStatus imageInfoStatusIfSuccess = WebGLImageDataStatus::NoImageData;
+    nsAutoArrayPtr<uint8_t> convertedData;
+    void* pixels = nullptr;
+    WebGLImageDataStatus imageInfoStatusIfSuccess = WebGLImageDataStatus::UninitializedImageData;
 
     if (byteLength) {
-        size_t srcStride = srcStrideOrZero ? srcStrideOrZero : checked_alignedRowSize.value();
-
+        size_t   srcStride = srcStrideOrZero ? srcStrideOrZero : checked_alignedRowSize.value();
         uint32_t dstTexelSize = GetBitsPerTexel(format, type) / 8;
-        size_t dstPlainRowSize = dstTexelSize * width;
-        size_t unpackAlignment = mPixelStoreUnpackAlignment;
-        size_t dstStride = ((dstPlainRowSize + unpackAlignment-1) / unpackAlignment) * unpackAlignment;
+        size_t   dstPlainRowSize = dstTexelSize * width;
+        size_t   unpackAlignment = mPixelStoreUnpackAlignment;
+        size_t   dstStride = ((dstPlainRowSize + unpackAlignment-1) / unpackAlignment) * unpackAlignment;
 
         if (actualSrcFormat == dstFormat &&
             srcPremultiplied == mPixelStorePremultiplyAlpha &&
             srcStride == dstStride &&
             !mPixelStoreFlipY)
         {
             // no conversion, no flipping, so we avoid copying anything and just pass the source pointer
-            error = CheckedTexImage2D(target, level, internalformat,
-                                      width, height, border, format, type, data);
+            pixels = data;
         }
         else
         {
             size_t convertedDataSize = height * dstStride;
-            nsAutoArrayPtr<uint8_t> convertedData(new uint8_t[convertedDataSize]);
+            convertedData = new uint8_t[convertedDataSize];
             ConvertImage(width, height, srcStride, dstStride,
                         static_cast<uint8_t*>(data), convertedData,
                         actualSrcFormat, srcPremultiplied,
                         dstFormat, mPixelStorePremultiplyAlpha, dstTexelSize);
-            error = CheckedTexImage2D(target, level, internalformat,
-                                      width, height, border, format, type, convertedData);
+            pixels = reinterpret_cast<void*>(convertedData.get());
         }
         imageInfoStatusIfSuccess = WebGLImageDataStatus::InitializedImageData;
-    } else {
-        error = CheckedTexImage2D(target, level, internalformat,
-                                  width, height, border, format, type, nullptr);
-        imageInfoStatusIfSuccess = WebGLImageDataStatus::UninitializedImageData;
     }
 
+    GLenum error = CheckedTexImage2D(target, level, internalformat, width,
+                                     height, border, format, type, pixels);
+
     if (error) {
         GenerateWarning("texImage2D generated error %s", ErrorName(error));
         return;
     }
 
     // in all of the code paths above, we should have either initialized data,
     // or allocated data and left it uninitialized, but in any case we shouldn't
     // have NoImageData at this point.
     MOZ_ASSERT(imageInfoStatusIfSuccess != WebGLImageDataStatus::NoImageData);
 
-    tex->SetImageInfo(target, level, width, height, internalformat, type, imageInfoStatusIfSuccess);
+    tex->SetImageInfo(target, level, width, height, format, type, imageInfoStatusIfSuccess);
 }
 
 void
 WebGLContext::TexImage2D(GLenum target, GLint level,
                          GLenum internalformat, GLsizei width,
                          GLsizei height, GLint border, GLenum format,
                          GLenum type, const Nullable<ArrayBufferView> &pixels, ErrorResult& rv)
 {
@@ -3728,17 +3698,17 @@ WebGLContext::TexImage2D(GLenum target, 
 }
 
 
 void
 WebGLContext::TexSubImage2D_base(GLenum target, GLint level,
                                  GLint xoffset, GLint yoffset,
                                  GLsizei width, GLsizei height, GLsizei srcStrideOrZero,
                                  GLenum format, GLenum type,
-                                 void *pixels, uint32_t byteLength,
+                                 void* data, uint32_t byteLength,
                                  int jsArrayType,
                                  WebGLTexelFormat srcFormat, bool srcPremultiplied)
 {
     const WebGLTexImageFunc func = WebGLTexImageFunc::TexSubImage;
 
     if (!ValidateTexImage(2, target, level, format,
                           xoffset, yoffset, 0,
                           width, height, 0,
@@ -3777,52 +3747,47 @@ WebGLContext::TexSubImage2D_base(GLenum 
     WebGLTexture *tex = activeBoundTextureForTarget(target);
     const WebGLTexture::ImageInfo &imageInfo = tex->ImageInfoAt(target, level);
 
     if (imageInfo.HasUninitializedImageData())
         tex->DoDeferredImageInitialization(target, level);
 
     MakeContextCurrent();
 
-    size_t srcStride = srcStrideOrZero ? srcStrideOrZero : checked_alignedRowSize.value();
-
+    size_t   srcStride = srcStrideOrZero ? srcStrideOrZero : checked_alignedRowSize.value();
     uint32_t dstTexelSize = GetBitsPerTexel(format, type) / 8;
-    size_t dstPlainRowSize = dstTexelSize * width;
+    size_t   dstPlainRowSize = dstTexelSize * width;
     // There are checks above to ensure that this won't overflow.
-    size_t dstStride = RoundedToNextMultipleOf(dstPlainRowSize, mPixelStoreUnpackAlignment).value();
-
-    // convert type for half float if not on GLES2
-    GLenum realType = type;
-    if (realType == LOCAL_GL_HALF_FLOAT_OES) {
-        if (gl->IsSupported(gl::GLFeature::texture_half_float)) {
-            realType = LOCAL_GL_HALF_FLOAT;
-        } else {
-            MOZ_ASSERT(gl->IsExtensionSupported(gl::GLContext::OES_texture_half_float));
-        }
-    }
-
-    if (actualSrcFormat == dstFormat &&
-        srcPremultiplied == mPixelStorePremultiplyAlpha &&
-        srcStride == dstStride &&
-        !mPixelStoreFlipY)
-    {
-        // no conversion, no flipping, so we avoid copying anything and just pass the source pointer
-        gl->fTexSubImage2D(target, level, xoffset, yoffset, width, height, format, realType, pixels);
-    }
-    else
-    {
+    size_t   dstStride = RoundedToNextMultipleOf(dstPlainRowSize, mPixelStoreUnpackAlignment).value();
+
+    void* pixels = data;
+    nsAutoArrayPtr<uint8_t> convertedData;
+
+    // no conversion, no flipping, so we avoid copying anything and just pass the source pointer
+    bool noConversion = (actualSrcFormat == dstFormat &&
+                         srcPremultiplied == mPixelStorePremultiplyAlpha &&
+                         srcStride == dstStride &&
+                         !mPixelStoreFlipY);
+
+    if (!noConversion) {
         size_t convertedDataSize = height * dstStride;
-        nsAutoArrayPtr<uint8_t> convertedData(new uint8_t[convertedDataSize]);
+        convertedData = new uint8_t[convertedDataSize];
         ConvertImage(width, height, srcStride, dstStride,
-                    static_cast<const uint8_t*>(pixels), convertedData,
+                    static_cast<const uint8_t*>(data), convertedData,
                     actualSrcFormat, srcPremultiplied,
                     dstFormat, mPixelStorePremultiplyAlpha, dstTexelSize);
-
-        gl->fTexSubImage2D(target, level, xoffset, yoffset, width, height, format, realType, convertedData);
+        pixels = reinterpret_cast<void*>(convertedData.get());
     }
+
+    GLenum driverType = DriverTypeFromType(gl, type);
+    GLenum driverInternalFormat = LOCAL_GL_NONE;
+    GLenum driverFormat = LOCAL_GL_NONE;
+    DriverFormatsFromFormatAndType(gl, format, type, &driverInternalFormat, &driverFormat);
+
+    gl->fTexSubImage2D(target, level, xoffset, yoffset, width, height, driverFormat, driverType, pixels);
 }
 
 void
 WebGLContext::TexSubImage2D(GLenum target, GLint level,
                             GLint xoffset, GLint yoffset,
                             GLsizei width, GLsizei height,
                             GLenum format, GLenum type,
                             const Nullable<ArrayBufferView> &pixels,
@@ -4066,82 +4031,16 @@ WebGLTexelFormat mozilla::GetWebGLTexelF
         default:
             MOZ_ASSERT(false, "Coding mistake?! Should never reach this point.");
             return WebGLTexelFormat::BadFormat;
     }
 
     MOZ_CRASH("Invalid WebGL texture format/type?");
 }
 
-GLenum
-InternalFormatForFormatAndType(GLenum format, GLenum type, bool isGLES2)
-{
-    // ES2 requires that format == internalformat; floating-point is
-    // indicated purely by the type that's loaded.  For desktop GL, we
-    // have to specify a floating point internal format.
-    if (isGLES2)
-        return format;
-
-    if (format == LOCAL_GL_DEPTH_COMPONENT) {
-        if (type == LOCAL_GL_UNSIGNED_SHORT)
-            return LOCAL_GL_DEPTH_COMPONENT16;
-        else if (type == LOCAL_GL_UNSIGNED_INT)
-            return LOCAL_GL_DEPTH_COMPONENT32;
-    }
-
-    if (format == LOCAL_GL_DEPTH_STENCIL) {
-        if (type == LOCAL_GL_UNSIGNED_INT_24_8_EXT)
-            return LOCAL_GL_DEPTH24_STENCIL8;
-    }
-
-    switch (type) {
-    case LOCAL_GL_UNSIGNED_BYTE:
-    case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
-    case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
-    case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
-        return format;
-
-    case LOCAL_GL_FLOAT:
-        switch (format) {
-        case LOCAL_GL_RGBA:
-            return LOCAL_GL_RGBA32F_ARB;
-        case LOCAL_GL_RGB:
-            return LOCAL_GL_RGB32F_ARB;
-        case LOCAL_GL_ALPHA:
-            return LOCAL_GL_ALPHA32F_ARB;
-        case LOCAL_GL_LUMINANCE:
-            return LOCAL_GL_LUMINANCE32F_ARB;
-        case LOCAL_GL_LUMINANCE_ALPHA:
-            return LOCAL_GL_LUMINANCE_ALPHA32F_ARB;
-        }
-        break;
-
-    case LOCAL_GL_HALF_FLOAT_OES:
-        switch (format) {
-        case LOCAL_GL_RGBA:
-            return LOCAL_GL_RGBA16F;
-        case LOCAL_GL_RGB:
-            return LOCAL_GL_RGB16F;
-        case LOCAL_GL_ALPHA:
-            return LOCAL_GL_ALPHA16F_ARB;
-        case LOCAL_GL_LUMINANCE:
-            return LOCAL_GL_LUMINANCE16F_ARB;
-        case LOCAL_GL_LUMINANCE_ALPHA:
-            return LOCAL_GL_LUMINANCE_ALPHA16F_ARB;
-        }
-        break;
-
-    default:
-        break;
-    }
-
-    NS_ASSERTION(false, "Coding mistake -- bad format/type passed?");
-    return 0;
-}
-
 void
 WebGLContext::BlendColor(GLclampf r, GLclampf g, GLclampf b, GLclampf a) {
     if (IsContextLost())
         return;
     MakeContextCurrent();
     gl->fBlendColor(r, g, b, a);
 }
 
--- a/content/canvas/src/WebGLContextUtils.cpp
+++ b/content/canvas/src/WebGLContextUtils.cpp
@@ -20,29 +20,182 @@
 #include "nsIDOMDataContainerEvent.h"
 
 #include "mozilla/Preferences.h"
 
 using namespace mozilla;
 
 namespace mozilla {
 
+using namespace gl;
+
 bool
-IsGLDepthFormat(GLenum internalFormat)
+IsGLDepthFormat(GLenum webGLFormat)
 {
-    return (internalFormat == LOCAL_GL_DEPTH_COMPONENT ||
-            internalFormat == LOCAL_GL_DEPTH_COMPONENT16 ||
-            internalFormat == LOCAL_GL_DEPTH_COMPONENT32);
+    return (webGLFormat == LOCAL_GL_DEPTH_COMPONENT ||
+            webGLFormat == LOCAL_GL_DEPTH_COMPONENT16 ||
+            webGLFormat == LOCAL_GL_DEPTH_COMPONENT32);
+}
+
+bool
+IsGLDepthStencilFormat(GLenum webGLFormat)
+{
+    return (webGLFormat == LOCAL_GL_DEPTH_STENCIL ||
+            webGLFormat == LOCAL_GL_DEPTH24_STENCIL8);
 }
 
 bool
-IsGLDepthStencilFormat(GLenum internalFormat)
+FormatHasAlpha(GLenum webGLFormat)
+{
+    return webGLFormat == LOCAL_GL_RGBA ||
+           webGLFormat == LOCAL_GL_LUMINANCE_ALPHA ||
+           webGLFormat == LOCAL_GL_ALPHA ||
+           webGLFormat == LOCAL_GL_RGBA4 ||
+           webGLFormat == LOCAL_GL_RGB5_A1 ||
+           webGLFormat == LOCAL_GL_SRGB_ALPHA;
+}
+
+/**
+ * Convert WebGL/ES format and type into GL format and GL internal
+ * format valid for underlying driver.
+ */
+void
+DriverFormatsFromFormatAndType(GLContext* gl, GLenum webGLFormat, GLenum webGLType,
+                               GLenum* out_driverInternalFormat, GLenum* out_driverFormat)
 {
-    return (internalFormat == LOCAL_GL_DEPTH_STENCIL ||
-            internalFormat == LOCAL_GL_DEPTH24_STENCIL8);
+    MOZ_ASSERT(out_driverInternalFormat, "out_driverInternalFormat can't be nullptr.");
+    MOZ_ASSERT(out_driverFormat, "out_driverFormat can't be nullptr.");
+    if (!out_driverInternalFormat || !out_driverFormat)
+        return;
+
+    // ES2 requires that format == internalformat; floating-point is
+    // indicated purely by the type that's loaded.  For desktop GL, we
+    // have to specify a floating point internal format.
+    if (gl->IsGLES()) {
+        *out_driverInternalFormat = webGLFormat;
+        *out_driverFormat = webGLFormat;
+
+        return;
+    }
+
+    GLenum format = webGLFormat;
+    GLenum internalFormat = LOCAL_GL_NONE;
+
+    if (format == LOCAL_GL_DEPTH_COMPONENT) {
+        if (webGLType == LOCAL_GL_UNSIGNED_SHORT)
+            internalFormat = LOCAL_GL_DEPTH_COMPONENT16;
+        else if (webGLType == LOCAL_GL_UNSIGNED_INT)
+            internalFormat = LOCAL_GL_DEPTH_COMPONENT32;
+    } else if (format == LOCAL_GL_DEPTH_STENCIL) {
+        if (webGLType == LOCAL_GL_UNSIGNED_INT_24_8_EXT)
+            internalFormat = LOCAL_GL_DEPTH24_STENCIL8;
+    } else {
+        switch (webGLType) {
+        case LOCAL_GL_UNSIGNED_BYTE:
+        case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
+        case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
+        case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
+            internalFormat = format;
+            break;
+
+        case LOCAL_GL_FLOAT:
+            switch (format) {
+            case LOCAL_GL_RGBA:
+                internalFormat = LOCAL_GL_RGBA32F;
+                break;
+
+            case LOCAL_GL_RGB:
+                internalFormat = LOCAL_GL_RGB32F;
+                break;
+
+            case LOCAL_GL_ALPHA:
+                internalFormat = LOCAL_GL_ALPHA32F_ARB;
+                break;
+
+            case LOCAL_GL_LUMINANCE:
+                internalFormat = LOCAL_GL_LUMINANCE32F_ARB;
+                break;
+
+            case LOCAL_GL_LUMINANCE_ALPHA:
+                internalFormat = LOCAL_GL_LUMINANCE_ALPHA32F_ARB;
+                break;
+            }
+            break;
+
+        case LOCAL_GL_HALF_FLOAT_OES:
+            switch (format) {
+            case LOCAL_GL_RGBA:
+                internalFormat = LOCAL_GL_RGBA16F;
+                break;
+
+            case LOCAL_GL_RGB:
+                internalFormat = LOCAL_GL_RGB16F;
+                break;
+
+            case LOCAL_GL_ALPHA:
+                internalFormat = LOCAL_GL_ALPHA16F_ARB;
+                break;
+
+            case LOCAL_GL_LUMINANCE:
+                internalFormat = LOCAL_GL_LUMINANCE16F_ARB;
+                break;
+
+            case LOCAL_GL_LUMINANCE_ALPHA:
+                internalFormat = LOCAL_GL_LUMINANCE_ALPHA16F_ARB;
+                break;
+            }
+            break;
+
+        default:
+            break;
+        }
+
+        // Handle ES2 and GL differences when supporting sRGB internal formats. GL ES
+        // requires that format == internalformat, but GL will fail in this case.
+        // GL requires:
+        //      format  ->  internalformat
+        //      GL_RGB      GL_SRGB_EXT
+        //      GL_RGBA     GL_SRGB_ALPHA_EXT
+        switch (format) {
+        case LOCAL_GL_SRGB:
+            internalFormat = format;
+            format = LOCAL_GL_RGB;
+            break;
+
+        case LOCAL_GL_SRGB_ALPHA:
+            internalFormat = format;
+            format = LOCAL_GL_RGBA;
+            break;
+        }
+    }
+
+    MOZ_ASSERT(format != LOCAL_GL_NONE && internalFormat != LOCAL_GL_NONE,
+               "Coding mistake -- bad format/type passed?");
+
+    *out_driverInternalFormat = internalFormat;
+    *out_driverFormat = format;
+}
+
+GLenum
+DriverTypeFromType(GLContext* gl, GLenum webGLType)
+{
+    if (gl->IsGLES())
+        return webGLType;
+
+    // convert type for half float if not on GLES2
+    GLenum type = webGLType;
+    if (type == LOCAL_GL_HALF_FLOAT_OES) {
+        if (gl->IsSupported(gl::GLFeature::texture_half_float)) {
+            return LOCAL_GL_HALF_FLOAT;
+        } else {
+            MOZ_ASSERT(gl->IsExtensionSupported(gl::GLContext::OES_texture_half_float));
+        }
+    }
+
+    return webGLType;
 }
 
 } // namespace mozilla
 
 void
 WebGLContext::GenerateWarning(const char *fmt, ...)
 {
     va_list ap;
--- a/content/canvas/src/WebGLContextUtils.h
+++ b/content/canvas/src/WebGLContextUtils.h
@@ -7,18 +7,22 @@
 #define WEBGLCONTEXTUTILS_H_
 
 #include "WebGLContext.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/dom/BindingUtils.h"
 
 namespace mozilla {
 
-bool IsGLDepthFormat(GLenum internalFormat);
-bool IsGLDepthStencilFormat(GLenum internalFormat);
+bool IsGLDepthFormat(GLenum webGLFormat);
+bool IsGLDepthStencilFormat(GLenum webGLFormat);
+bool FormatHasAlpha(GLenum webGLFormat);
+void DriverFormatsFromFormatAndType(gl::GLContext* gl, GLenum webGLFormat, GLenum webGLType,
+                                    GLenum* out_driverInternalFormat, GLenum* out_driverFormat);
+GLenum DriverTypeFromType(gl::GLContext* gl, GLenum webGLType);
 
 template <typename WebGLObjectType>
 JS::Value
 WebGLContext::WebGLObjectAsJSValue(JSContext *cx, const WebGLObjectType *object, ErrorResult& rv) const
 {
     if (!object) {
         return JS::NullValue();
     }
--- a/content/canvas/src/WebGLContextValidate.cpp
+++ b/content/canvas/src/WebGLContextValidate.cpp
@@ -1384,18 +1384,18 @@ WebGLContext::ValidateTexImage(GLuint di
                                      func))
         {
             return false;
         }
 
         /* Require the format and type to match that of the existing
          * texture as created
          */
-        if (imageInfo.InternalFormat() != format ||
-            imageInfo.Type() != type)
+        if (imageInfo.WebGLFormat() != format ||
+            imageInfo.WebGLType() != type)
         {
             ErrorInvalidOperation("%s: format or type doesn't match the existing texture",
                                   info);
             return false;
         }
     }
 
     /* Additional checks for depth textures */
--- a/content/canvas/src/WebGLFramebuffer.cpp
+++ b/content/canvas/src/WebGLFramebuffer.cpp
@@ -50,27 +50,27 @@ WebGLFramebuffer::Attachment::IsDeleteRe
 
 bool
 WebGLFramebuffer::Attachment::HasAlpha() const
 {
     MOZ_ASSERT(HasImage());
 
     GLenum format = 0;
     if (Texture() && Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel))
-        format = Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel).InternalFormat();
+        format = Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel).WebGLFormat();
     else if (Renderbuffer())
         format = Renderbuffer()->InternalFormat();
     return FormatHasAlpha(format);
 }
 
 bool
 WebGLFramebuffer::Attachment::IsReadableFloat() const
 {
     if (Texture() && Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel)) {
-        GLenum type = Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel).Type();
+        GLenum type = Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel).WebGLType();
         switch (type) {
         case LOCAL_GL_FLOAT:
         case LOCAL_GL_HALF_FLOAT_OES:
             return true;
         }
         return false;
     }
 
@@ -277,29 +277,29 @@ WebGLFramebuffer::Attachment::IsComplete
     {
         return false;
     }
 
     if (Texture()) {
         MOZ_ASSERT(Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel));
         const WebGLTexture::ImageInfo& imageInfo =
             Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel);
-        GLenum internalFormat = imageInfo.InternalFormat();
+        GLenum webGLFormat = imageInfo.WebGLFormat();
 
         if (mAttachmentPoint == LOCAL_GL_DEPTH_ATTACHMENT)
-            return IsValidFBOTextureDepthFormat(internalFormat);
+            return IsValidFBOTextureDepthFormat(webGLFormat);
 
         if (mAttachmentPoint == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT)
-            return IsValidFBOTextureDepthStencilFormat(internalFormat);
+            return IsValidFBOTextureDepthStencilFormat(webGLFormat);
 
         if (mAttachmentPoint >= LOCAL_GL_COLOR_ATTACHMENT0 &&
             mAttachmentPoint < GLenum(LOCAL_GL_COLOR_ATTACHMENT0 +
                                       WebGLContext::kMaxColorAttachments))
         {
-            return IsValidFBOTextureColorFormat(internalFormat);
+            return IsValidFBOTextureColorFormat(webGLFormat);
         }
         MOZ_ASSERT(false, "Invalid WebGL attachment point?");
         return false;
     }
 
     if (Renderbuffer()) {
         GLenum internalFormat = Renderbuffer()->InternalFormat();
 
--- a/content/canvas/src/WebGLTexture.cpp
+++ b/content/canvas/src/WebGLTexture.cpp
@@ -5,16 +5,17 @@
 
 #include "WebGLTexture.h"
 
 #include "GLContext.h"
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "mozilla/Scoped.h"
 #include "ScopedGLHelpers.h"
 #include "WebGLContext.h"
+#include "WebGLContextUtils.h"
 #include "WebGLTexelConversions.h"
 
 #include <algorithm>
 
 using namespace mozilla;
 
 JSObject*
 WebGLTexture::WrapObject(JSContext *cx) {
@@ -47,17 +48,17 @@ WebGLTexture::Delete() {
     mContext->gl->fDeleteTextures(1, &mGLName);
     LinkedListElement<WebGLTexture>::removeFrom(mContext->mTextures);
 }
 
 int64_t
 WebGLTexture::ImageInfo::MemoryUsage() const {
     if (mImageDataStatus == WebGLImageDataStatus::NoImageData)
         return 0;
-    int64_t bitsPerTexel = WebGLContext::GetBitsPerTexel(mInternalFormat, mType);
+    int64_t bitsPerTexel = WebGLContext::GetBitsPerTexel(mWebGLFormat, mWebGLType);
     return int64_t(mWidth) * int64_t(mHeight) * bitsPerTexel/8;
 }
 
 int64_t
 WebGLTexture::MemoryUsage() const {
     if (IsDeleted())
         return 0;
     int64_t result = 0;
@@ -331,17 +332,17 @@ WebGLTexture::ResolvedFakeBlackStatus() 
                 mContext->GenerateWarning("%s is a cube map texture, with a minification filter not requiring a mipmap, "
                             "with some level 0 image having width or height not a power of two, and with a wrap mode "
                             "different from CLAMP_TO_EDGE.", msg_rendering_as_black);
                 mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
             }
         }
     }
 
-    if (ImageInfoBase().mType == LOCAL_GL_FLOAT &&
+    if (ImageInfoBase().mWebGLType == LOCAL_GL_FLOAT &&
         !Context()->IsExtensionEnabled(WebGLExtensionID::OES_texture_float_linear))
     {
         if (mMinFilter == LOCAL_GL_LINEAR ||
             mMinFilter == LOCAL_GL_LINEAR_MIPMAP_LINEAR ||
             mMinFilter == LOCAL_GL_LINEAR_MIPMAP_NEAREST ||
             mMinFilter == LOCAL_GL_NEAREST_MIPMAP_LINEAR)
         {
             mContext->GenerateWarning("%s is a texture with a linear minification filter, "
@@ -351,17 +352,17 @@ WebGLTexture::ResolvedFakeBlackStatus() 
         }
         else if (mMagFilter == LOCAL_GL_LINEAR)
         {
             mContext->GenerateWarning("%s is a texture with a linear magnification filter, "
                                       "which is not compatible with gl.FLOAT by default. "
                                       "Try enabling the OES_texture_float_linear extension if supported.", msg_rendering_as_black);
             mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
         }
-    } else if (ImageInfoBase().mType == LOCAL_GL_HALF_FLOAT_OES &&
+    } else if (ImageInfoBase().mWebGLType == LOCAL_GL_HALF_FLOAT_OES &&
                !Context()->IsExtensionEnabled(WebGLExtensionID::OES_texture_half_float_linear))
     {
         if (mMinFilter == LOCAL_GL_LINEAR ||
             mMinFilter == LOCAL_GL_LINEAR_MIPMAP_LINEAR ||
             mMinFilter == LOCAL_GL_LINEAR_MIPMAP_NEAREST ||
             mMinFilter == LOCAL_GL_NEAREST_MIPMAP_LINEAR)
         {
             mContext->GenerateWarning("%s is a texture with a linear minification filter, "
@@ -538,18 +539,19 @@ void
 WebGLTexture::DoDeferredImageInitialization(GLenum imageTarget, GLint level)
 {
     const ImageInfo& imageInfo = ImageInfoAt(imageTarget, level);
     MOZ_ASSERT(imageInfo.mImageDataStatus == WebGLImageDataStatus::UninitializedImageData);
 
     mContext->MakeContextCurrent();
 
     // Try to clear with glCLear.
-    WebGLTexelFormat texelformat = GetWebGLTexelFormat(imageInfo.mInternalFormat, imageInfo.mType);
-    GLenum format = WebGLTexelConversions::GLFormatForTexelFormat(texelformat);
+    GLenum format = imageInfo.mWebGLFormat;
+    GLenum type = imageInfo.mWebGLType;
+    WebGLTexelFormat texelformat = GetWebGLTexelFormat(format, type);
 
     bool cleared = ClearWithTempFB(mContext, GLName(),
                                    imageTarget, level,
                                    format, imageInfo.mHeight, imageInfo.mWidth);
     if (cleared) {
         SetImageDataStatus(imageTarget, level, WebGLImageDataStatus::InitializedImageData);
         return;
     }
@@ -563,21 +565,27 @@ WebGLTexture::DoDeferredImageInitializat
                         imageInfo.mHeight,
                         imageInfo.mWidth,
                         texelsize,
                         mContext->mPixelStoreUnpackAlignment);
     MOZ_ASSERT(checked_byteLength.isValid()); // should have been checked earlier
     ScopedFreePtr<void> zeros;
     zeros = calloc(1, checked_byteLength.value());
 
+    gl::GLContext* gl = mContext->gl;
+    GLenum driverType = DriverTypeFromType(gl, type);
+    GLenum driverInternalFormat = LOCAL_GL_NONE;
+    GLenum driverFormat = LOCAL_GL_NONE;
+    DriverFormatsFromFormatAndType(gl, format, type, &driverInternalFormat, &driverFormat);
+
     mContext->GetAndFlushUnderlyingGLErrors();
-    mContext->gl->fTexImage2D(imageTarget, level, imageInfo.mInternalFormat,
-                              imageInfo.mWidth, imageInfo.mHeight,
-                              0, format, imageInfo.mType,
-                              zeros);
+    gl->fTexImage2D(imageTarget, level, driverInternalFormat,
+                    imageInfo.mWidth, imageInfo.mHeight,
+                    0, driverFormat, driverType,
+                    zeros);
     GLenum error = mContext->GetAndFlushUnderlyingGLErrors();
     if (error) {
         // Should only be OUT_OF_MEMORY. Anyway, there's no good way to recover from this here.
         printf_stderr("Error: 0x%4x\n", error);
         MOZ_CRASH(); // errors on texture upload have been related to video memory exposure in the past.
     }
 
     SetImageDataStatus(imageTarget, level, WebGLImageDataStatus::InitializedImageData);
--- a/content/canvas/src/WebGLTexture.h
+++ b/content/canvas/src/WebGLTexture.h
@@ -18,25 +18,16 @@
 namespace mozilla {
 
 // Zero is not an integer power of two.
 inline bool is_pot_assuming_nonnegative(GLsizei x)
 {
     return x && (x & (x-1)) == 0;
 }
 
-inline bool FormatHasAlpha(GLenum format)
-{
-    return format == LOCAL_GL_RGBA ||
-           format == LOCAL_GL_LUMINANCE_ALPHA ||
-           format == LOCAL_GL_ALPHA ||
-           format == LOCAL_GL_RGBA4 ||
-           format == LOCAL_GL_RGB5_A1;
-}
-
 // NOTE: When this class is switched to new DOM bindings, update the (then-slow)
 // WrapObject calls in GetParameter and GetFramebufferAttachmentParameter.
 class WebGLTexture MOZ_FINAL
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLTexture>
     , public LinkedListElement<WebGLTexture>
     , public WebGLContextBoundObject
     , public WebGLFramebufferAttachable
@@ -77,38 +68,41 @@ protected:
 
 public:
 
     class ImageInfo
         : public WebGLRectangleObject
     {
     public:
         ImageInfo()
-            : mInternalFormat(0)
-            , mType(0)
+            : mWebGLFormat(LOCAL_GL_NONE)
+            , mWebGLType(LOCAL_GL_NONE)
             , mImageDataStatus(WebGLImageDataStatus::NoImageData)
         {}
 
-        ImageInfo(GLsizei width, GLsizei height,
-                  GLenum format, GLenum type, WebGLImageDataStatus status)
+        ImageInfo(GLsizei width,
+                  GLsizei height,
+                  GLenum webGLFormat,
+                  GLenum webGLType,
+                  WebGLImageDataStatus status)
             : WebGLRectangleObject(width, height)
-            , mInternalFormat(format)
-            , mType(type)
+            , mWebGLFormat(webGLFormat)
+            , mWebGLType(webGLType)
             , mImageDataStatus(status)
         {
             // shouldn't use this constructor to construct a null ImageInfo
             MOZ_ASSERT(status != WebGLImageDataStatus::NoImageData);
         }
 
         bool operator==(const ImageInfo& a) const {
             return mImageDataStatus == a.mImageDataStatus &&
-                   mWidth  == a.mWidth &&
+                   mWidth == a.mWidth &&
                    mHeight == a.mHeight &&
-                   mInternalFormat == a.mInternalFormat &&
-                   mType   == a.mType;
+                   mWebGLFormat == a.mWebGLFormat &&
+                   mWebGLType == a.mWebGLType;
         }
         bool operator!=(const ImageInfo& a) const {
             return !(*this == a);
         }
         bool IsSquare() const {
             return mWidth == mHeight;
         }
         bool IsPositive() const {
@@ -117,20 +111,30 @@ public:
         bool IsPowerOfTwo() const {
             return is_pot_assuming_nonnegative(mWidth) &&
                    is_pot_assuming_nonnegative(mHeight); // negative sizes should never happen (caught in texImage2D...)
         }
         bool HasUninitializedImageData() const {
             return mImageDataStatus == WebGLImageDataStatus::UninitializedImageData;
         }
         int64_t MemoryUsage() const;
-        GLenum InternalFormat() const { return mInternalFormat; }
-        GLenum Type() const { return mType; }
+        /*! This is the format passed from JS to WebGL.
+         * It can be converted to a value to be passed to driver with
+         * DriverFormatsFromFormatAndType().
+         */
+        GLenum WebGLFormat() const { return mWebGLFormat; }
+        /*! This is the type passed from JS to WebGL.
+         * It can be converted to a value to be passed to driver with
+         * DriverTypeFromType().
+         */
+        GLenum WebGLType() const { return mWebGLType; }
+
     protected:
-        GLenum mInternalFormat, mType;
+        GLenum mWebGLFormat; //!< This is the WebGL/GLES format
+        GLenum mWebGLType;   //!< This is the WebGL/GLES type
         WebGLImageDataStatus mImageDataStatus;
 
         friend class WebGLTexture;
     };
 
 private:
     static size_t FaceForTarget(GLenum target) {
         // Call this out explicitly:
--- a/content/canvas/test/mochitest.ini
+++ b/content/canvas/test/mochitest.ini
@@ -208,9 +208,8 @@ skip-if = (buildapp == 'b2g' && toolkit 
 [test_mozGetAsFile.html]
 [test_strokeText_throw.html]
 [test_toBlob.html]
 [test_toDataURL_alpha.html]
 [test_toDataURL_lowercase_ascii.html]
 [test_toDataURL_parameters.html]
 [test_windingRuleUndefined.html]
 [test_2d.fillText.gradient.html]
-[test_createPattern_broken.html]
--- a/content/canvas/test/test_canvas.html
+++ b/content/canvas/test/test_canvas.html
@@ -15088,16 +15088,37 @@ var _thrown = undefined; try {
   ctx.createPattern(img, 'repeat');
 } catch (e) { _thrown = e }; todo(_thrown && _thrown.name == "InvalidStateError" && _thrown.code == DOMException.INVALID_STATE_ERR, "should throw InvalidStateError");
 
 
 }
 </script>
 <img src="image_broken.png" id="broken_2.png" class="resource">
 
+<!-- [[[ test_2d.pattern.image.incomplete.html ]]] -->
+
+<p>Canvas test: 2d.pattern.image.incomplete</p>
+<canvas id="c466" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<script>
+
+function test_2d_pattern_image_incomplete() {
+
+var canvas = document.getElementById('c466');
+var ctx = canvas.getContext('2d');
+
+var img = new Image();
+todo(img.complete === false, "img.complete === false");
+var _thrown = undefined; try {
+  ctx.createPattern(img, 'repeat');
+} catch (e) { _thrown = e }; todo(_thrown && _thrown.name == "InvalidStateError" && _thrown.code == DOMException.INVALID_STATE_ERR, "should throw InvalidStateError");
+
+
+}
+</script>
+
 <!-- [[[ test_2d.pattern.image.null.html ]]] -->
 
 <p>Canvas test: 2d.pattern.image.null</p>
 <canvas id="c467" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
 <script>
 
 function test_2d_pattern_image_null() {
 
@@ -23764,16 +23785,21 @@ function runTests() {
   ok(false, "unexpected exception thrown in: test_2d_pattern_crosscanvas");
  }
  try {
   test_2d_pattern_image_broken();
  } catch (e) {
   ok(false, "unexpected exception thrown in: test_2d_pattern_image_broken");
  }
  try {
+  test_2d_pattern_image_incomplete();
+ } catch (e) {
+  ok(false, "unexpected exception thrown in: test_2d_pattern_image_incomplete");
+ }
+ try {
   test_2d_pattern_image_null();
  } catch (e) {
   ok(false, "unexpected exception thrown in: test_2d_pattern_image_null");
  }
  try {
   test_2d_pattern_image_string();
  } catch (e) {
   ok(false, "unexpected exception thrown in: test_2d_pattern_image_string");
deleted file mode 100644
--- a/content/canvas/test/test_createPattern_broken.html
+++ /dev/null
@@ -1,35 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <meta charset="utf-8">
-  <title>Test for createPattern with a broken image</title>
-  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="application/javascript" src="/tests/SimpleTest/WindowSnapshot.js"></script>
-  <script type="application/javascript" src="file_drawWindow_common.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-  <script type="application/javascript">
-
-  SimpleTest.waitForExplicitFinish();
-  window.addEventListener("load", function(){
-    var _thrown = undefined;
-
-    try{
-      var img = document.getElementById('broken.png');
-      var ctx = document.getElementById('c').getContext('2d');
-      var p = ctx.createPattern(img, 'repeat');
-    } catch (e) {
-      _thrown = e
-    };
-
-    ok(_thrown && _thrown.name == "InvalidStateError" && _thrown.code == DOMException.INVALID_STATE_ERR, "should throw InvalidStateError");
-
-    SimpleTest.finish();
-    });
-
-  </script>
-</head>
-<body>
-<canvas id="c" class="output" width="100" height="50"></canvas>
-<img src="/images/broken.png" id="broken.png" class="resource"/>
-</body>
-</html>
--- a/content/canvas/test/webgl-mochitest/mochitest.ini
+++ b/content/canvas/test/webgl-mochitest/mochitest.ini
@@ -6,16 +6,17 @@ support-files =
 [test_depth_readpixels.html]
 [test_draw.html]
 [test_fb_param.html]
 [test_fb_param_crash.html]
 [test_highp_fs.html]
 [test_no_arr_points.html]
 [test_noprog_draw.html]
 [test_privileged_exts.html]
+[test_texsubimage_float.html]
 [test_webgl_available.html]
 skip-if = toolkit == 'android' #bug 865443- seperate suite - the non_conf* tests pass except for one on armv6 tests
 [test_webgl_conformance.html]
 skip-if = toolkit == 'android' #bug 865443- seperate suite - the non_conf* tests pass except for one on armv6 tests
 [test_webgl_request_context.html]
 skip-if = toolkit == 'android' #bug 865443- seperate suite - the non_conf* tests pass except for one on armv6 tests
 [test_webgl_request_mismatch.html]
 skip-if = toolkit == 'android' #bug 865443- seperate suite - the non_conf* tests pass except for one on armv6 tests
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/webgl-mochitest/test_texsubimage_float.html
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML>
+<title>WebGL test: bug 1003607</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+<script src="driver-info.js"></script>
+<script src="webgl-util.js"></script>
+<body>
+<canvas id="c"></canvas>
+<script>
+
+// Give ourselves a scope to return early from:
+(function() {
+  var gl = WebGLUtil.getWebGL('c');
+  if (!gl) {
+    todo(false, 'WebGL is unavailable.');
+    return;
+  }
+
+  // Catch actual WebGLUtil errors, not GL errors.
+  function errorFunc(str) {
+    ok(false, 'Error: ' + str);
+  }
+  WebGLUtil.setErrorFunc(errorFunc);
+
+  function checkGLError(func, info, reference) {
+    var error = gl.getError();
+    var prefix = info ? ('[' + info + '] ') : '';
+    var text = 'gl.getError should be 0x' + reference.toString(16) +
+               ', was 0x' + error.toString(16) + '.';
+    func(error == reference, prefix + text);
+  }
+
+  // Begin test:
+  if (!gl.getExtension('OES_texture_float')) {
+    todo(false, 'Not having this extension is fine.');
+    return;
+  }
+  var tex = gl.createTexture();
+  gl.bindTexture(gl.TEXTURE_2D, tex);
+  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
+  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
+  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+  checkGLError(ok, 'texture parameter setup should succeed', gl.NO_ERROR);
+
+  // Generate data
+  var width = 2;
+  var height = 2;
+  var numChannels = 4;
+  var data = new Float32Array(width * height * numChannels);
+  for (var ii = 0; ii < data.length; ++ii) {
+    data[ii] = 10000;
+  }
+  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.FLOAT, data);
+  checkGLError(ok, 'floating-point texture allocation should succeed', gl.NO_ERROR);
+
+  // Try respecifying data
+  gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height, gl.RGBA, gl.FLOAT, data);
+  checkGLError(ok, 'floating-point texture sub image should succeed', gl.NO_ERROR);
+})();
+
+</script>
--- a/content/html/content/src/HTMLTableElement.cpp
+++ b/content/html/content/src/HTMLTableElement.cpp
@@ -543,27 +543,22 @@ HTMLTableElement::InsertRow(int32_t aInd
       }
 
       if (aError.Failed()) {
         return nullptr;
       }
     }
   } else {
     // the row count was 0, so 
-    // find the first row group and insert there as first child
+    // find the last row group and insert there as first child
     nsCOMPtr<nsIContent> rowGroup;
-    for (nsIContent* child = nsINode::GetFirstChild();
+    for (nsIContent* child = nsINode::GetLastChild();
          child;
-         child = child->GetNextSibling()) {
-      nsINodeInfo *childInfo = child->NodeInfo();
-      nsIAtom *localName = childInfo->NameAtom();
-      if (childInfo->NamespaceID() == kNameSpaceID_XHTML &&
-          (localName == nsGkAtoms::thead ||
-           localName == nsGkAtoms::tbody ||
-           localName == nsGkAtoms::tfoot)) {
+         child = child->GetPreviousSibling()) {
+      if (child->IsHTML(nsGkAtoms::tbody)) {
         rowGroup = child;
         break;
       }
     }
 
     if (!rowGroup) { // need to create a TBODY
       nsCOMPtr<nsINodeInfo> nodeInfo;
       nsContentUtils::NameChanged(mNodeInfo, nsGkAtoms::tbody,
--- a/content/html/content/test/mochitest.ini
+++ b/content/html/content/test/mochitest.ini
@@ -405,16 +405,17 @@ skip-if = (toolkit == 'gonk' && debug) |
 [test_bug845057.html]
 [test_bug869040.html]
 [test_bug870787.html]
 [test_bug874758.html]
 [test_bug879319.html]
 [test_bug885024.html]
 [test_bug893537.html]
 [test_bug969346.html]
+[test_bug1003539.html]
 [test_change_crossorigin.html]
 [test_checked.html]
 [test_dir_attributes_reflection.html]
 [test_dl_attributes_reflection.html]
 [test_element_prototype.html]
 [test_embed_attributes_reflection.html]
 [test_formData.html]
 [test_formSubmission.html]
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/test_bug1003539.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1003539
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1003539</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript">
+
+  /** Test for Bug 1003539 **/
+// Refering to this specification: http://www.whatwg.org/specs/web-apps/current-work/multipage/tabular-data.html#dom-table-insertrow
+var tab;
+tab = document.createElement("table");
+tab.createTHead();
+tab.insertRow();
+is(tab.innerHTML, '<thead></thead><tbody><tr></tr></tbody>', "Row should be inserted in the tbody.");
+
+tab = document.createElement("table");
+tab.createTBody();
+tab.createTBody();
+tab.insertRow();
+is(tab.innerHTML, '<tbody></tbody><tbody><tr></tr></tbody>', "Row should be inserted in the last tbody.");
+  </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1003539">Mozilla Bug 1003539</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
--- a/content/media/MediaResource.h
+++ b/content/media/MediaResource.h
@@ -417,17 +417,17 @@ public:
                   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);
+    size += mContentType.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
 
     return size;
   }
 
   virtual size_t SizeOfIncludingThis(
                   MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
   {
     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
--- a/dom/apps/src/AppsService.js
+++ b/dom/apps/src/AppsService.js
@@ -9,41 +9,52 @@ function debug(s) {
 }
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/Promise.jsm");
 
 const APPS_SERVICE_CID = Components.ID("{05072afa-92fe-45bf-ae22-39b69c117058}");
 
 function AppsService()
 {
   debug("AppsService Constructor");
-  let inParent = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime)
-                   .processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
-  debug("inParent: " + inParent);
-  Cu.import(inParent ? "resource://gre/modules/Webapps.jsm" :
-                       "resource://gre/modules/AppsServiceChild.jsm");
+  this.inParent = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime)
+                    .processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
+  debug("inParent: " + this.inParent);
+  Cu.import(this.inParent ? "resource://gre/modules/Webapps.jsm" :
+                            "resource://gre/modules/AppsServiceChild.jsm");
 }
 
 AppsService.prototype = {
 
   getCSPByLocalId: function getCSPByLocalId(localId) {
     debug("GetCSPByLocalId( " + localId + " )");
     return DOMApplicationRegistry.getCSPByLocalId(localId);
   },
 
   getAppByManifestURL: function getAppByManifestURL(aManifestURL) {
     debug("GetAppByManifestURL( " + aManifestURL + " )");
     return DOMApplicationRegistry.getAppByManifestURL(aManifestURL);
   },
 
+  getManifestFor: function getManifestFor(aManifestURL) {
+    debug("getManifestFor(" + aManifestURL + ")");
+    if (this.inParent) {
+      return DOMApplicationRegistry.getManifestFor(aManifestURL);
+    } else {
+      return Promise.reject(
+        new Error("Calling getManifestFor() from child is not supported"));
+    }
+  },
+
   getAppLocalIdByManifestURL: function getAppLocalIdByManifestURL(aManifestURL) {
     debug("getAppLocalIdByManifestURL( " + aManifestURL + " )");
     return DOMApplicationRegistry.getAppLocalIdByManifestURL(aManifestURL);
   },
 
   getAppLocalIdByStoreId: function getAppLocalIdByStoreId(aStoreId) {
     debug("getAppLocalIdByStoreId( " + aStoreId + " )");
     return DOMApplicationRegistry.getAppLocalIdByStoreId(aStoreId);
--- a/dom/apps/src/AppsUtils.jsm
+++ b/dom/apps/src/AppsUtils.jsm
@@ -121,16 +121,21 @@ this.AppsUtils = {
       if (app.manifestURL == aManifestURL) {
         return new mozIApplication(app);
       }
     }
 
     return null;
   },
 
+  getManifestFor: function getManifestFor(aManifestURL) {
+    debug("getManifestFor(" + aManifestURL + ")");
+    return DOMApplicationRegistry.getManifestFor(aManifestURL);
+  },
+
   getAppLocalIdByManifestURL: function getAppLocalIdByManifestURL(aApps, aManifestURL) {
     debug("getAppLocalIdByManifestURL " + aManifestURL);
     for (let id in aApps) {
       if (aApps[id].manifestURL == aManifestURL) {
         return aApps[id].localId;
       }
     }
 
--- a/dom/browser-element/mochitest/priority/test_HighPriorityDowngrade.html
+++ b/dom/browser-element/mochitest/priority/test_HighPriorityDowngrade.html
@@ -26,44 +26,57 @@ var childID = null;
 function runTest() {
   var iframe = document.createElement('iframe');
   iframe.setAttribute('mozbrowser', true);
 
   iframe.src = browserElementTestHelpers.emptyPage1;
 
   var highPriorityIframe = null;
   var childID = null;
+  var lock = null;
+  var p = null;
 
   expectProcessCreated().then(function(chid) {
     childID = chid;
     return expectPriorityChange(childID, 'FOREGROUND', 'CPU_NORMAL');
   }).then(function() {
     // Create a new, high-priority iframe.
     highPriorityIframe = document.createElement('iframe');
     highPriorityIframe.setAttribute('mozbrowser', true);
     highPriorityIframe.setAttribute('expecting-system-message', true);
     highPriorityIframe.setAttribute('mozapptype', 'critical');
     highPriorityIframe.setAttribute('mozapp', 'http://example.org/manifest.webapp');
     highPriorityIframe.src = browserElementTestHelpers.emptyPage2;
 
-    var p = expectPriorityChange(childID, 'FOREGROUND', 'CPU_LOW');
+    p = expectPriorityChange(childID, 'FOREGROUND', 'CPU_LOW');
 
     document.body.appendChild(highPriorityIframe);
 
     return p;
   }).then(function() {
     return expectPriorityChange(childID, 'FOREGROUND', 'CPU_NORMAL');
+  }).then(function() {
+    p = expectPriorityChange(childID, 'FOREGROUND', 'CPU_LOW');
+    lock = navigator.requestWakeLock('high-priority');
+    return p;
+  }).then(function() {
+    p = expectPriorityChange(childID, 'FOREGROUND', 'CPU_NORMAL');
+    lock.unlock();
+    return p;
   }).then(SimpleTest.finish);
 
   document.body.appendChild(iframe);
 }
 
 addEventListener('testready', function() {
-  // Cause the CPU wake lock taken on behalf of the high-priority process to
-  // time out after 1s.
   SpecialPowers.pushPrefEnv(
-    {set: [["dom.ipc.systemMessageCPULockTimeoutSec", 1]]},
+    {set: [
+      /* Cause the CPU wake lock taken on behalf of the high-priority process
+       * to time out after 1s. */
+       ["dom.ipc.systemMessageCPULockTimeoutSec", 1],
+       ["dom.wakelock.enabled", true]
+    ]},
     runTest);
 });
 
 </script>
 </body>
 </html>
--- a/dom/imptests/failures/html/html/semantics/tabular-data/the-table-element/test_table-insertRow.html.json
+++ b/dom/imptests/failures/html/html/semantics/tabular-data/the-table-element/test_table-insertRow.html.json
@@ -1,5 +1,3 @@
 {
-  "insertRow should not copy prefixes": true,
-  "insertRow should insert into a tbody, not into a thead, if table.rows is empty": true,
-  "insertRow should insert into a tbody, not into a tfoot, if table.rows is empty": true
+  "insertRow should not copy prefixes": true
 }
--- a/dom/interfaces/apps/nsIAppsService.idl
+++ b/dom/interfaces/apps/nsIAppsService.idl
@@ -12,22 +12,29 @@ interface nsIURI;
 #define APPS_SERVICE_CID { 0x05072afa, 0x92fe, 0x45bf, { 0xae, 0x22, 0x39, 0xb6, 0x9c, 0x11, 0x70, 0x58 } }
 #define APPS_SERVICE_CONTRACTID "@mozilla.org/AppsService;1"
 %}
 
 /*
  * This service allows accessing some DOMApplicationRegistry methods from
  * non-javascript code.
  */
-[scriptable, uuid(93cde78d-56f6-4322-b707-9b23eb80d90d)]
+[scriptable, uuid(2e884bbe-7a3d-4b01-ad92-fcd65a449043)]
 interface nsIAppsService : nsISupports
 {
   mozIApplication getAppByManifestURL(in DOMString manifestURL);
 
   /**
+   * Returns a Promise for the manifest for a given manifestURL.
+   * This is only supported in the parent process: the promise will be rejected
+   * in content processes.
+   */
+  jsval getManifestFor(in DOMString manifestURL);
+
+  /**
    * Returns the |localId| of the app associated with the |manifestURL| passed
    * in parameter.
    * Returns nsIScriptSecurityManager::NO_APP_ID if |manifestURL| isn't a valid
    * installed manifest URL.
    */
   unsigned long getAppLocalIdByManifestURL(in DOMString manifestURL);
 
   /**
--- a/dom/ipc/ProcessPriorityManager.cpp
+++ b/dom/ipc/ProcessPriorityManager.cpp
@@ -104,16 +104,17 @@ class ParticularProcessPriorityManager;
  *
  * ProcessPriorityManager::CurrentProcessIsForeground() and
  * ProcessPriorityManager::AnyProcessHasHighPriority() which can be called in
  * any process, are handled separately, by the ProcessPriorityManagerChild
  * class.
  */
 class ProcessPriorityManagerImpl MOZ_FINAL
   : public nsIObserver
+  , public WakeLockObserver
 {
 public:
   /**
    * If we're in the main process, get the ProcessPriorityManagerImpl
    * singleton.  If we're in a child process, return null.
    */
   static ProcessPriorityManagerImpl* GetSingleton();
 
@@ -152,38 +153,46 @@ public:
   /**
    * This must be called by a ParticularProcessPriorityManager when it changes
    * its priority.
    */
   void NotifyProcessPriorityChanged(
     ParticularProcessPriorityManager* aParticularManager,
     hal::ProcessPriority aOldPriority);
 
+  /**
+   * Implements WakeLockObserver, used to monitor wake lock changes in the
+   * main process.
+   */
+  virtual void Notify(const WakeLockInformation& aInfo) MOZ_OVERRIDE;
+
 private:
   static bool sPrefListenersRegistered;
   static bool sInitialized;
   static StaticRefPtr<ProcessPriorityManagerImpl> sSingleton;
 
   static void PrefChangedCallback(const char* aPref, void* aClosure);
 
   ProcessPriorityManagerImpl();
-  ~ProcessPriorityManagerImpl() {}
+  ~ProcessPriorityManagerImpl();
   DISALLOW_EVIL_CONSTRUCTORS(ProcessPriorityManagerImpl);
 
   void Init();
 
   already_AddRefed<ParticularProcessPriorityManager>
   GetParticularProcessPriorityManager(ContentParent* aContentParent);
 
   void ObserveContentParentCreated(nsISupports* aContentParent);
   void ObserveContentParentDestroyed(nsISupports* aSubject);
+  void ResetAllCPUPriorities();
 
   nsDataHashtable<nsUint64HashKey, nsRefPtr<ParticularProcessPriorityManager> >
     mParticularManagers;
 
+  bool mHighPriority;
   nsTHashtable<nsUint64HashKey> mHighPriorityChildIDs;
 };
 
 /**
  * This singleton class implements the parts of the process priority manager
  * that are available from all processes.
  */
 class ProcessPriorityManagerChild MOZ_FINAL
@@ -406,18 +415,25 @@ ProcessPriorityManagerImpl::GetSingleton
   if (!sSingleton) {
     StaticInit();
   }
 
   return sSingleton;
 }
 
 ProcessPriorityManagerImpl::ProcessPriorityManagerImpl()
+    : mHighPriority(false)
 {
   MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+  RegisterWakeLockObserver(this);
+}
+
+ProcessPriorityManagerImpl::~ProcessPriorityManagerImpl()
+{
+  UnregisterWakeLockObserver(this);
 }
 
 void
 ProcessPriorityManagerImpl::Init()
 {
   LOG("Starting up.  This is the master process.");
 
   // The master process's priority never changes; set it here and then forget
@@ -521,32 +537,40 @@ ProcessPriorityManagerImpl::ObserveConte
   }
 
   mParticularManagers.Remove(childID);
 
   if (mHighPriorityChildIDs.Contains(childID)) {
     mHighPriorityChildIDs.RemoveEntry(childID);
 
     // We just lost a high-priority process; reset everyone's CPU priorities.
-    nsTArray<nsRefPtr<ParticularProcessPriorityManager> > pppms;
-    mParticularManagers.EnumerateRead(
-      &EnumerateParticularProcessPriorityManagers,
-      &pppms);
+    ResetAllCPUPriorities();
+  }
+}
 
-    for (uint32_t i = 0; i < pppms.Length(); i++) {
-      pppms[i]->ResetCPUPriorityNow();
-    }
+void
+ProcessPriorityManagerImpl::ResetAllCPUPriorities( void )
+{
+  nsTArray<nsRefPtr<ParticularProcessPriorityManager> > pppms;
+  mParticularManagers.EnumerateRead(
+    &EnumerateParticularProcessPriorityManagers,
+    &pppms);
+
+  for (uint32_t i = 0; i < pppms.Length(); i++) {
+    pppms[i]->ResetCPUPriorityNow();
   }
 }
 
 bool
 ProcessPriorityManagerImpl::OtherProcessHasHighPriority(
   ParticularProcessPriorityManager* aParticularManager)
 {
-  if (mHighPriorityChildIDs.Contains(aParticularManager->ChildID())) {
+  if (mHighPriority) {
+    return true;
+  } else if (mHighPriorityChildIDs.Contains(aParticularManager->ChildID())) {
     return mHighPriorityChildIDs.Count() > 1;
   }
   return mHighPriorityChildIDs.Count() > 0;
 }
 
 bool
 ProcessPriorityManagerImpl::ChildProcessHasHighPriority( void )
 {
@@ -582,16 +606,39 @@ ProcessPriorityManagerImpl::NotifyProces
 
   for (uint32_t i = 0; i < pppms.Length(); i++) {
     if (pppms[i] != aParticularManager) {
       pppms[i]->ResetCPUPriorityNow();
     }
   }
 }
 
+/* virtual */ void
+ProcessPriorityManagerImpl::Notify(const WakeLockInformation& aInfo)
+{
+  /* The main process always has an ID of 0, if it is present in the wake-lock
+   * information then we explicitly requested a high-priority wake-lock for the
+   * main process. */
+  if (aInfo.topic().EqualsLiteral("high-priority")) {
+    if (aInfo.lockingProcesses().Contains((uint64_t)0)) {
+      mHighPriority = true;
+    } else {
+      mHighPriority = false;
+    }
+
+    /* The main process got a high-priority wakelock change; reset everyone's
+     * CPU priorities. */
+    ResetAllCPUPriorities();
+
+    LOG("Got wake lock changed event. "
+        "Now mHighPriorityParent = %d\n", mHighPriority);
+  }
+}
+
+
 NS_IMPL_ISUPPORTS(ParticularProcessPriorityManager,
                   nsIObserver,
                   nsITimerCallback,
                   nsISupportsWeakReference);
 
 ParticularProcessPriorityManager::ParticularProcessPriorityManager(
   ContentParent* aContentParent)
   : mContentParent(aContentParent)
--- a/dom/ipc/moz.build
+++ b/dom/ipc/moz.build
@@ -125,15 +125,14 @@ DEFINES['BIN_SUFFIX'] = '"%s"' % CONFIG[
 if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('android', 'gtk2', 'gonk', 'qt'):
     DEFINES['MOZ_ENABLE_FREETYPE'] = True
 
 for var in ('MOZ_PERMISSIONS', 'MOZ_CHILD_PERMISSIONS'):
     if CONFIG[var]:
         DEFINES[var] = True
 
 if CONFIG['ENABLE_TESTS']:
-    DEFINES['ENABLE_TESTS'] = 1
     LOCAL_INCLUDES += [
       'ipc/glue',
     ]
 
 JAR_MANIFESTS += ['jar.mn']
 
--- a/dom/media/tests/mochitest/pc.js
+++ b/dom/media/tests/mochitest/pc.js
@@ -1111,16 +1111,19 @@ function PeerConnectionWrapper(label, co
 
   this.constraints = [ ];
   this.offerConstraints = {};
   this.streams = [ ];
   this.mediaCheckers = [ ];
 
   this.dataChannels = [ ];
 
+  this.onAddStreamFired = false;
+  this.addStreamCallbacks = {};
+
   info("Creating " + this);
   this._pc = new mozRTCPeerConnection(this.configuration);
   is(this._pc.iceConnectionState, "new", "iceConnectionState starts at 'new'");
 
   /**
    * Setup callback handlers
    */
   var self = this;
@@ -1147,25 +1150,32 @@ function PeerConnectionWrapper(label, co
   /**
    * Callback for native peer connection 'onaddstream' events.
    *
    * @param {Object} event
    *        Event data which includes the stream to be added
    */
   this._pc.onaddstream = function (event) {
     info(self + ": 'onaddstream' event fired for " + JSON.stringify(event.stream));
+    // TODO: remove this once Bugs 998552 and 998546 are closed
+    self.onAddStreamFired = true;
 
     var type = '';
     if (event.stream.getAudioTracks().length > 0) {
       type = 'audio';
     }
     if (event.stream.getVideoTracks().length > 0) {
       type += 'video';
     }
     self.attachMedia(event.stream, type, 'remote');
+
+    Object.keys(self.addStreamCallbacks).forEach(function(name) {
+      info(this + " calling addStreamCallback " + name);
+      self.addStreamCallbacks[name]();
+    });
    };
 
   /**
    * Callback for native peer connection 'ondatachannel' events. If no custom handler
    * has been specified via 'this.ondatachannel', a failure will be raised if an
    * event of this type gets caught.
    *
    * @param {Object} event
@@ -1319,18 +1329,24 @@ PeerConnectionWrapper.prototype = {
 
           _getAllUserMedia(constraintsList, index + 1);
         }, generateErrorCallback());
       } else {
         onSuccess();
       }
     }
 
-    info("Get " + this.constraints.length + " local streams");
-    _getAllUserMedia(this.constraints, 0);
+    if (this.constraints.length === 0) {
+      info("Skipping GUM: no UserMedia requested");
+      onSuccess();
+    }
+    else {
+      info("Get " + this.constraints.length + " local streams");
+      _getAllUserMedia(this.constraints, 0);
+    }
   },
 
   /**
    * Create a new data channel instance
    *
    * @param {Object} options
    *        Options which get forwarded to nsIPeerConnection.createDataChannel
    * @param {function} [onCreation=undefined]
@@ -1557,28 +1573,160 @@ PeerConnectionWrapper.prototype = {
         myFailure();
       }
     }
 
     self.ice_connection_callbacks.waitForIceConnected = iceConnectedChanged;
   },
 
   /**
-   * Checks that we are getting the media streams we expect.
+   * Counts the amount of audio tracks in a given media constraint.
+   *
+   * @param constraints
+   *        The contraint to be examined.
+   */
+  countAudioTracksInMediaConstraint : function
+    PCW_countAudioTracksInMediaConstraint(constraints) {
+    if ((!constraints) || (constraints.length === 0)) {
+      return 0;
+    }
+    var audioTracks = 0;
+    for (var i = 0; i < constraints.length; i++) {
+      if (constraints[i].audio) {
+        audioTracks++;
+      }
+    }
+    return audioTracks;
+  },
+
+  /**
+   * Counts the amount of video tracks in a given media constraint.
+   *
+   * @param constraint
+   *        The contraint to be examined.
+   */
+  countVideoTracksInMediaConstraint : function
+    PCW_countVideoTracksInMediaConstraint(constraints) {
+    if ((!constraints) || (constraints.length === 0)) {
+      return 0;
+    }
+    var videoTracks = 0;
+    for (var i = 0; i < constraints.length; i++) {
+      if (constraints[i].video) {
+        videoTracks++;
+      }
+    }
+    return videoTracks;
+  },
+
+  /*
+   * Counts the amount of audio tracks in a given set of streams.
+   *
+   * @param streams
+   *        An array of streams (as returned by getLocalStreams()) to be
+   *        examined.
+   */
+  countAudioTracksInStreams : function PCW_countAudioTracksInStreams(streams) {
+    if (!streams || (streams.length === 0)) {
+      return 0;
+    }
+    var audioTracks = 0;
+    streams.forEach(function(st) {
+      audioTracks += st.getAudioTracks().length;
+    });
+    return audioTracks;
+  },
+
+  /*
+   * Counts the amount of video tracks in a given set of streams.
+   *
+   * @param streams
+   *        An array of streams (as returned by getLocalStreams()) to be
+   *        examined.
+   */
+  countVideoTracksInStreams: function PCW_countVideoTracksInStreams(streams) {
+    if (!streams || (streams.length === 0)) {
+      return 0;
+    }
+    var videoTracks = 0;
+    streams.forEach(function(st) {
+      videoTracks += st.getVideoTracks().length;
+    });
+    return videoTracks;
+  },
+
+  /**
+   * Checks that we are getting the media tracks we expect.
    *
    * @param {object} constraintsRemote
-   *        The media constraints of the remote peer connection object
+   *        The media constraints of the local and remote peer connection object
    */
-  checkMediaStreams : function PCW_checkMediaStreams(constraintsRemote) {
-    is(this._pc.getLocalStreams().length, this.constraints.length,
-       this + ' has ' + this.constraints.length + ' local streams');
+  checkMediaTracks : function PCW_checkMediaTracks(constraintsRemote, onSuccess) {
+    var self = this;
+    var addStreamTimeout = null;
+
+    function _checkMediaTracks(constraintsRemote, onSuccess) {
+      if (self.addStreamTimeout === null) {
+        clearTimeout(self.addStreamTimeout);
+      }
+
+      var localConstraintAudioTracks =
+        self.countAudioTracksInMediaConstraint(self.constraints);
+      var localStreams = self._pc.getLocalStreams();
+      var localAudioTracks = self.countAudioTracksInStreams(localStreams, false);
+      is(localAudioTracks, localConstraintAudioTracks, self + ' has ' +
+        localAudioTracks + ' local audio tracks');
+
+      var localConstraintVideoTracks =
+        self.countVideoTracksInMediaConstraint(self.constraints);
+      var localVideoTracks = self.countVideoTracksInStreams(localStreams, false);
+      is(localVideoTracks, localConstraintVideoTracks, self + ' has ' +
+        localVideoTracks + ' local video tracks');
+
+      var remoteConstraintAudioTracks =
+        self.countAudioTracksInMediaConstraint(constraintsRemote);
+      var remoteStreams = self._pc.getRemoteStreams();
+      var remoteAudioTracks = self.countAudioTracksInStreams(remoteStreams, false);
+      is(remoteAudioTracks, remoteConstraintAudioTracks, self + ' has ' +
+        remoteAudioTracks + ' remote audio tracks');
 
-    // TODO: change this when multiple incoming streams are supported (bug 834835)
-    is(this._pc.getRemoteStreams().length, 1,
-       this + ' has ' + 1 + ' remote streams');
+      var remoteConstraintVideoTracks =
+        self.countVideoTracksInMediaConstraint(constraintsRemote);
+      var remoteVideoTracks = self.countVideoTracksInStreams(remoteStreams, false);
+      is(remoteVideoTracks, remoteConstraintVideoTracks, self + ' has ' +
+        remoteVideoTracks + ' remote video tracks');
+
+      onSuccess();
+    }
+
+    function __checkMediaTracksTimeout(onSuccess) {
+      ok(false, self + " checkMediaTracks() timed out waiting for onaddstream event to fire");
+      onSuccess();
+    }
+
+    // we have to do this check as the onaddstream never fires if the remote
+    // stream has no track at all!
+    var expectedRemoteTracks =
+      self.countAudioTracksInMediaConstraint(constraintsRemote) +
+      self.countVideoTracksInMediaConstraint(constraintsRemote);
+
+    // TODO: remove this once Bugs 998552 and 998546 are closed
+    if ((self.onAddStreamFired) || (expectedRemoteTracks == 0)) {
+      _checkMediaTracks(constraintsRemote, onSuccess);
+    } else {
+      info(self + " checkMediaTracks() got called before onAddStream fired");
+      // we rely on the outer mochitest timeout to catch the case where
+      // onaddstream never fires
+      self.addStreamCallbacks.checkMediaTracks = function() {
+        _checkMediaTracks(constraintsRemote, onSuccess);
+      };
+      addStreamTimeout = setTimeout(function () {
+        self._checkMediaTracksTimeout(onSuccess);
+      }, 60000);
+    }
   },
 
   /**
    * Check that media flow is present on all media elements involved in this
    * test by waiting for confirmation that media flow is present.
    *
    * @param {Function} onSuccess the success callback when media flow
    *                             is confirmed on all media elements
--- a/dom/media/tests/mochitest/templates.js
+++ b/dom/media/tests/mochitest/templates.js
@@ -198,27 +198,29 @@ var commandsPeerConnection = [
         dump("ERROR: pc_remote: SDP offer: " + myTest._local_offer.sdp.replace(/[\r]/g, ''));
         dump("ERROR: pc_remote: SDP answer: " + myTest._remote_answer.sdp.replace(/[\r]/g, ''));
         ok(false, "pc_remote: ICE is already in bad state: " + myPc.iceConnectionState);
         myTest.next();
       }
     }
   ],
   [
-    'PC_LOCAL_CHECK_MEDIA_STREAMS',
+    'PC_LOCAL_CHECK_MEDIA_TRACKS',
     function (test) {
-      test.pcLocal.checkMediaStreams(test._remote_constraints);
-      test.next();
+      test.pcLocal.checkMediaTracks(test._remote_constraints, function () {
+        test.next();
+      });
     }
   ],
   [
-    'PC_REMOTE_CHECK_MEDIA_STREAMS',
+    'PC_REMOTE_CHECK_MEDIA_TRACKS',
     function (test) {
-      test.pcRemote.checkMediaStreams(test._local_constraints);
-      test.next();
+      test.pcRemote.checkMediaTracks(test._local_constraints, function () {
+        test.next();
+      });
     }
   ],
   [
     'PC_LOCAL_CHECK_MEDIA_FLOW_PRESENT',
     function (test) {
       test.pcLocal.checkMediaFlowPresent(function () {
         test.next();
       });
@@ -369,27 +371,29 @@ var commandsDataChannel = [
           is(test.pcRemote.signalingState, STABLE,
              "signalingState after remote setLocalDescription is 'stable'");
           test.next();
         }
       );
     }
   ],
   [
-    'PC_LOCAL_CHECK_MEDIA_STREAMS',
+    'PC_LOCAL_CHECK_MEDIA_TRACKS',
     function (test) {
-      test.pcLocal.checkMediaStreams(test.pcRemote.constraints);
-      test.next();
+      test.pcLocal.checkMediaTracks(test.pcRemote.constraints, function () {
+        test.next();
+      });
     }
   ],
   [
-    'PC_REMOTE_CHECK_MEDIA_STREAMS',
+    'PC_REMOTE_CHECK_MEDIA_TRACKS',
     function (test) {
-      test.pcRemote.checkMediaStreams(test.pcLocal.constraints);
-      test.next();
+      test.pcRemote.checkMediaTracks(test.pcLocal.constraints, function () {
+        test.next();
+      });
     }
   ],
   [
     'PC_LOCAL_CHECK_MEDIA_FLOW_PRESENT',
     function (test) {
       test.pcLocal.checkMediaFlowPresent(function () {
         test.next();
       });
--- a/dom/media/tests/mochitest/test_dataChannel_basicDataOnly.html
+++ b/dom/media/tests/mochitest/test_dataChannel_basicDataOnly.html
@@ -13,20 +13,15 @@
   createHTML({
     bug: "796894",
     title: "Basic datachannel only connection"
   });
 
   var test;
   runTest(function () {
     test = new DataChannelTest();
-
-    // TODO: Add back once bug 873049 has been fixed
-    test.chain.remove("PC_LOCAL_CHECK_MEDIA_STREAMS");
-    test.chain.remove("PC_REMOTE_CHECK_MEDIA_STREAMS");
-
     test.run();
   }, true);
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/webidl/WebGLRenderingContext.webidl
+++ b/dom/webidl/WebGLRenderingContext.webidl
@@ -793,20 +793,20 @@ interface WebGLExtensionCompressedTextur
 interface WebGLExtensionCompressedTextureETC1
 {
     const GLenum COMPRESSED_RGB_ETC1_WEBGL = 0x8D64;
 };
 
 [NoInterfaceObject]
 interface WebGLExtensionCompressedTexturePVRTC
 {
-    const GLenum COMPRESSED_RGB_PVRTC_4BPPV1  = 0x8C00;
-    const GLenum COMPRESSED_RGB_PVRTC_2BPPV1  = 0x8C01;
-    const GLenum COMPRESSED_RGBA_PVRTC_4BPPV1 = 0x8C02;
-    const GLenum COMPRESSED_RGBA_PVRTC_2BPPV1 = 0x8C03;
+    const GLenum COMPRESSED_RGB_PVRTC_4BPPV1_IMG  = 0x8C00;
+    const GLenum COMPRESSED_RGB_PVRTC_2BPPV1_IMG  = 0x8C01;
+    const GLenum COMPRESSED_RGBA_PVRTC_4BPPV1_IMG = 0x8C02;
+    const GLenum COMPRESSED_RGBA_PVRTC_2BPPV1_IMG = 0x8C03;
 };
 
 [NoInterfaceObject]
 interface WebGLExtensionDebugRendererInfo
 {
     const GLenum UNMASKED_VENDOR_WEBGL        = 0x9245;
     const GLenum UNMASKED_RENDERER_WEBGL      = 0x9246;
 };
--- a/dom/workers/WorkerRunnable.cpp
+++ b/dom/workers/WorkerRunnable.cpp
@@ -394,19 +394,22 @@ StopSyncLoopRunnable::StopSyncLoopRunnab
   mWorkerPrivate->AssertValidSyncLoop(mSyncLoopTarget);
 #endif
 }
 
 NS_IMETHODIMP
 StopSyncLoopRunnable::Cancel()
 {
   nsresult rv = Run();
-  NS_ENSURE_SUCCESS(rv, rv);
+  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Run() failed");
 
-  return NS_OK;
+  nsresult rv2 = WorkerSyncRunnable::Cancel();
+  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv2), "Cancel() failed");
+
+  return NS_FAILED(rv) ? rv : rv2;
 }
 
 bool
 StopSyncLoopRunnable::WorkerRun(JSContext* aCx,
                                 WorkerPrivate* aWorkerPrivate)
 {
   aWorkerPrivate->AssertIsOnWorkerThread();
   MOZ_ASSERT(mSyncLoopTarget);
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -660,16 +660,20 @@ ClientTiledLayerBuffer::PaintThebes(cons
           GetContentType());
 
       mSinglePaintDrawTarget =
         gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
           gfx::IntSize(ceilf(bounds.width * mResolution),
                        ceilf(bounds.height * mResolution)),
           gfx::ImageFormatToSurfaceFormat(format));
 
+      if (!mSinglePaintDrawTarget) {
+        return;
+      }
+
       ctxt = new gfxContext(mSinglePaintDrawTarget);
 
       mSinglePaintBufferOffset = nsIntPoint(bounds.x, bounds.y);
     }
     ctxt->NewPath();
     ctxt->Scale(mResolution, mResolution);
     ctxt->Translate(gfxPoint(-bounds.x, -bounds.y));
 #ifdef GFX_TILEDLAYER_PREF_WARNINGS
--- a/gfx/thebes/gfxASurface.cpp
+++ b/gfx/thebes/gfxASurface.cpp
@@ -890,17 +890,17 @@ gfxASurface::WriteAsPNG_internal(FILE* a
          printf_stderr("IMG: %.140s\n", cStr);
          if (len <= 140)
            break;
          len -= 140;
          cStr += 140;
        }
      }
 #endif
-    fprintf(aFile, "%s", string.BeginReading());
+    fprintf_stderr(aFile, "%s", string.BeginReading());
   } else {
     nsCOMPtr<nsIClipboardHelper> clipboard(do_GetService("@mozilla.org/widget/clipboardhelper;1", &rv));
     if (clipboard) {
       clipboard->CopyString(NS_ConvertASCIItoUTF16(string), nullptr);
     }
   }
 
   return;
new file mode 100644
--- /dev/null
+++ b/intl/icu-patches/suppress-warnings.diff
@@ -0,0 +1,80 @@
+diff --git a/intl/icu/source/acinclude.m4 b/intl/icu/source/acinclude.m4
+--- a/intl/icu/source/acinclude.m4
++++ b/intl/icu/source/acinclude.m4
+@@ -466,30 +466,36 @@ AC_DEFUN([AC_CHECK_STRICT_COMPILE],
+             *)
+                 # Do not use -ansi. It limits us to C90, and it breaks some platforms.
+                 # We use -std=c99 to disable the gnu99 defaults and its associated warnings
+                 CFLAGS="$CFLAGS -std=c99"
+                 ;;
+             esac
+             
+             CFLAGS="$CFLAGS -Wall -pedantic -Wshadow -Wpointer-arith -Wmissing-prototypes -Wwrite-strings"
++
++            # Suppress clang C warnings:
++            CFLAGS="$CFLAGS -Wno-sign-compare -Wno-unused"
+         else
+             case "${host}" in
+             *-*-cygwin)
+                 if test "`$CC /help 2>&1 | head -c9`" = "Microsoft"
+                 then
+                     CFLAGS="$CFLAGS /W4"
+                 fi ;;
+             *-*-mingw32|*-*-mingw64)
+                 CFLAGS="$CFLAGS -W4" ;;
+             esac
+         fi
+         if test "$GXX" = yes
+         then
+             CXXFLAGS="$CXXFLAGS -W -Wall -pedantic -Wpointer-arith -Wwrite-strings -Wno-long-long"
++
++            # Suppress clang C++ warnings:
++            CXXFLAGS="$CXXFLAGS -Wno-unused -Wno-unused-parameter"
+         else
+             case "${host}" in
+             *-*-cygwin)
+                 if test "`$CXX /help 2>&1 | head -c9`" = "Microsoft"
+                 then
+                     CXXFLAGS="$CXXFLAGS /W4"
+                 fi ;;
+             *-*-mingw32|*-*-mingw64)
+diff --git a/intl/icu/source/configure b/intl/icu/source/configure
+--- a/intl/icu/source/configure
++++ b/intl/icu/source/configure
+@@ -4089,30 +4089,36 @@ fi
+             *)
+                 # Do not use -ansi. It limits us to C90, and it breaks some platforms.
+                 # We use -std=c99 to disable the gnu99 defaults and its associated warnings
+                 CFLAGS="$CFLAGS -std=c99"
+                 ;;
+             esac
+ 
+             CFLAGS="$CFLAGS -Wall -pedantic -Wshadow -Wpointer-arith -Wmissing-prototypes -Wwrite-strings"
++
++            # Suppress clang C warnings:
++            CFLAGS="$CFLAGS -Wno-sign-compare -Wno-unused"
+         else
+             case "${host}" in
+             *-*-cygwin)
+                 if test "`$CC /help 2>&1 | head -c9`" = "Microsoft"
+                 then
+                     CFLAGS="$CFLAGS /W4"
+                 fi ;;
+             *-*-mingw32|*-*-mingw64)
+                 CFLAGS="$CFLAGS -W4" ;;
+             esac
+         fi
+         if test "$GXX" = yes
+         then
+             CXXFLAGS="$CXXFLAGS -W -Wall -pedantic -Wpointer-arith -Wwrite-strings -Wno-long-long"
++
++            # Suppress clang C++ warnings:
++            CXXFLAGS="$CXXFLAGS -Wno-unused -Wno-unused-parameter"
+         else
+             case "${host}" in
+             *-*-cygwin)
+                 if test "`$CXX /help 2>&1 | head -c9`" = "Microsoft"
+                 then
+                     CXXFLAGS="$CXXFLAGS /W4"
+                 fi ;;
+             *-*-mingw32|*-*-mingw64)
--- a/intl/icu/source/acinclude.m4
+++ b/intl/icu/source/acinclude.m4
@@ -466,30 +466,36 @@ AC_DEFUN([AC_CHECK_STRICT_COMPILE],
             *)
                 # Do not use -ansi. It limits us to C90, and it breaks some platforms.
                 # We use -std=c99 to disable the gnu99 defaults and its associated warnings
                 CFLAGS="$CFLAGS -std=c99"
                 ;;
             esac
             
             CFLAGS="$CFLAGS -Wall -pedantic -Wshadow -Wpointer-arith -Wmissing-prototypes -Wwrite-strings"
+
+            # Suppress clang C warnings:
+            CFLAGS="$CFLAGS -Wno-sign-compare -Wno-unused"
         else
             case "${host}" in
             *-*-cygwin)
                 if test "`$CC /help 2>&1 | head -c9`" = "Microsoft"
                 then
                     CFLAGS="$CFLAGS /W4"
                 fi ;;
             *-*-mingw32|*-*-mingw64)
                 CFLAGS="$CFLAGS -W4" ;;
             esac
         fi
         if test "$GXX" = yes
         then
             CXXFLAGS="$CXXFLAGS -W -Wall -pedantic -Wpointer-arith -Wwrite-strings -Wno-long-long"
+
+            # Suppress clang C++ warnings:
+            CXXFLAGS="$CXXFLAGS -Wno-unused -Wno-unused-parameter"
         else
             case "${host}" in
             *-*-cygwin)
                 if test "`$CXX /help 2>&1 | head -c9`" = "Microsoft"
                 then
                     CXXFLAGS="$CXXFLAGS /W4"
                 fi ;;
             *-*-mingw32|*-*-mingw64)
--- a/intl/icu/source/configure
+++ b/intl/icu/source/configure
@@ -4089,30 +4089,36 @@ fi
             *)
                 # Do not use -ansi. It limits us to C90, and it breaks some platforms.
                 # We use -std=c99 to disable the gnu99 defaults and its associated warnings
                 CFLAGS="$CFLAGS -std=c99"
                 ;;
             esac
 
             CFLAGS="$CFLAGS -Wall -pedantic -Wshadow -Wpointer-arith -Wmissing-prototypes -Wwrite-strings"
+
+            # Suppress clang C warnings:
+            CFLAGS="$CFLAGS -Wno-sign-compare -Wno-unused"
         else
             case "${host}" in
             *-*-cygwin)
                 if test "`$CC /help 2>&1 | head -c9`" = "Microsoft"
                 then
                     CFLAGS="$CFLAGS /W4"
                 fi ;;
             *-*-mingw32|*-*-mingw64)
                 CFLAGS="$CFLAGS -W4" ;;
             esac
         fi
         if test "$GXX" = yes
         then
             CXXFLAGS="$CXXFLAGS -W -Wall -pedantic -Wpointer-arith -Wwrite-strings -Wno-long-long"
+
+            # Suppress clang C++ warnings:
+            CXXFLAGS="$CXXFLAGS -Wno-unused -Wno-unused-parameter"
         else
             case "${host}" in
             *-*-cygwin)
                 if test "`$CXX /help 2>&1 | head -c9`" = "Microsoft"
                 then
                     CXXFLAGS="$CXXFLAGS /W4"
                 fi ;;
             *-*-mingw32|*-*-mingw64)
--- a/intl/update-icu.sh
+++ b/intl/update-icu.sh
@@ -50,10 +50,11 @@ rm ${icu_dir}/source/data/translit/*
 # the tree.)
 svn info $1 | grep -v '^Revision: [[:digit:]]\+$' > ${icu_dir}/SVN-INFO
 
 patch -d ${icu_dir}/../../ -p1 < ${icu_dir}/../icu-patches/bug-724533
 patch -d ${icu_dir}/../../ -p1 < ${icu_dir}/../icu-patches/bug-899722-4
 patch -d ${icu_dir}/../../ -p1 < ${icu_dir}/../icu-patches/bug-915735
 patch -d ${icu_dir}/../../ -p1 < ${icu_dir}/../icu-patches/genrb-omitCollationRules.diff
 patch -d ${icu_dir}/../../ -p1 < ${icu_dir}/../icu-patches/qualify-uinitonce-windows.diff
+patch -d ${icu_dir}/../../ -p1 < ${icu_dir}/../icu-patches/suppress-warnings.diff
 
 hg addremove ${icu_dir}
--- a/js/public/HeapAPI.h
+++ b/js/public/HeapAPI.h
@@ -36,20 +36,20 @@ const size_t ChunkShift = 20;
 const size_t ChunkSize = size_t(1) << ChunkShift;
 const size_t ChunkMask = ChunkSize - 1;
 
 const size_t CellShift = 3;
 const size_t CellSize = size_t(1) << CellShift;
 const size_t CellMask = CellSize - 1;
 
 /* These are magic constants derived from actual offsets in gc/Heap.h. */
-const size_t ChunkMarkBitmapOffset = 1032352;
+const size_t ChunkMarkBitmapOffset = 1032360;
 const size_t ChunkMarkBitmapBits = 129024;
 const size_t ChunkRuntimeOffset = ChunkSize - sizeof(void*);
-const size_t ChunkLocationOffset = ChunkSize - 2 * sizeof(void*) - sizeof(uint64_t);
+const size_t ChunkLocationOffset = ChunkSize - sizeof(void*) - sizeof(uintptr_t);
 
 /*
  * Live objects are marked black. How many other additional colors are available
  * depends on the size of the GCThing. Objects marked gray are eligible for
  * cycle collection.
  */
 static const uint32_t BLACK = 0;
 static const uint32_t GRAY = 1;
--- a/js/src/gc/Barrier.cpp
+++ b/js/src/gc/Barrier.cpp
@@ -37,22 +37,22 @@ bool
 HeapSlot::preconditionForSet(Zone *zone, JSObject *owner, Kind kind, uint32_t slot)
 {
     bool ok = kind == Slot
             ? &owner->getSlotRef(slot) == this
             : &owner->getDenseElement(slot) == (const Value *)this;
     return ok && owner->zone() == zone;
 }
 
-bool
-HeapSlot::preconditionForWriteBarrierPost(JSObject *obj, Kind kind, uint32_t slot, Value target) const
+void
+HeapSlot::preconditionForWriteBarrierPost(JSObject *obj, Kind kind, uint32_t slot, Value target)
 {
-    return kind == Slot
-         ? obj->getSlotAddressUnchecked(slot)->get() == target
-         : static_cast<HeapSlot *>(obj->getDenseElements() + slot)->get() == target;
+    JS_ASSERT_IF(kind == Slot, obj->getSlotAddressUnchecked(slot)->get() == target);
+    JS_ASSERT_IF(kind == Element,
+                 static_cast<HeapSlot *>(obj->getDenseElements() + slot)->get() == target);
 }
 
 bool
 RuntimeFromMainThreadIsHeapMajorCollecting(JS::shadow::Zone *shadowZone)
 {
     return shadowZone->runtimeFromMainThread()->isHeapMajorCollecting();
 }
 #endif // DEBUG
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -228,19 +228,19 @@ class BarrieredCell : public gc::Cell
             MOZ_ASSERT(!RuntimeFromMainThreadIsHeapMajorCollecting(shadowZone));
             T *tmp = thing;
             js::gc::MarkUnbarriered<T>(shadowZone->barrierTracer(), &tmp, "write barrier");
             JS_ASSERT(tmp == thing);
         }
 #endif
     }
 
-    static void writeBarrierPost(T *thing, void *cellp) {}
-    static void writeBarrierPostRelocate(T *thing, void *cellp) {}
-    static void writeBarrierPostRemove(T *thing, void *cellp) {}
+    static void writeBarrierPost(T *thing, void *addr) {}
+    static void writeBarrierPostRelocate(T *thing, void *addr) {}
+    static void writeBarrierPostRemove(T *thing, void *addr) {}
 };
 
 } // namespace gc
 
 // Note: the following Zone-getting functions must be equivalent to the zone()
 // and shadowZone() functions implemented by the subclasses of BarrieredCell.
 
 JS::Zone *
@@ -334,58 +334,47 @@ struct InternalGCMethods<Value>
     static bool isMarkable(Value v) { return v.isMarkable(); }
 
     static void preBarrier(Value v) {
 #ifdef JSGC_INCREMENTAL
         if (v.isMarkable() && shadowRuntimeFromAnyThread(v)->needsBarrier())
             preBarrier(ZoneOfValueFromAnyThread(v), v);
 #endif
     }
-
     static void preBarrier(Zone *zone, Value v) {
 #ifdef JSGC_INCREMENTAL
         if (v.isString() && StringIsPermanentAtom(v.toString()))
             return;
         JS::shadow::Zone *shadowZone = JS::shadow::Zone::asShadowZone(zone);
         if (shadowZone->needsBarrier()) {
             JS_ASSERT_IF(v.isMarkable(), shadowRuntimeFromMainThread(v)->needsBarrier());
             Value tmp(v);
             js::gc::MarkValueUnbarriered(shadowZone->barrierTracer(), &tmp, "write barrier");
             JS_ASSERT(tmp == v);
         }
 #endif
     }
-
     static void postBarrier(Value *vp) {
 #ifdef JSGC_GENERATIONAL
-        if (vp->isObject()) {
-            gc::StoreBuffer *sb = reinterpret_cast<gc::Cell *>(&vp->toObject())->storeBuffer();
-            if (sb)
-                sb->putValueFromAnyThread(vp);
-        }
+        if (vp->isObject())
+            shadowRuntimeFromAnyThread(*vp)->gcStoreBufferPtr()->putValue(vp);
 #endif
     }
 
     static void postBarrierRelocate(Value *vp) {
 #ifdef JSGC_GENERATIONAL
-        if (vp->isObject()) {
-            gc::StoreBuffer *sb = reinterpret_cast<gc::Cell *>(&vp->toObject())->storeBuffer();
-            if (sb)
-                sb->putRelocatableValueFromAnyThread(vp);
-        }
+        shadowRuntimeFromAnyThread(*vp)->gcStoreBufferPtr()->putRelocatableValue(vp);
 #endif
     }
 
     static void postBarrierRemove(Value *vp) {
 #ifdef JSGC_GENERATIONAL
-        JS_ASSERT(vp);
-        JS_ASSERT(vp->isMarkable());
         JSRuntime *rt = static_cast<js::gc::Cell *>(vp->toGCThing())->runtimeFromAnyThread();
         JS::shadow::Runtime *shadowRuntime = JS::shadow::Runtime::asShadowRuntime(rt);
-        shadowRuntime->gcStoreBufferPtr()->removeRelocatableValueFromAnyThread(vp);
+        shadowRuntime->gcStoreBufferPtr()->removeRelocatableValue(vp);
 #endif
     }
 
     static void readBarrier(const Value &v) { ValueReadBarrier(v); }
 };
 
 template <>
 struct InternalGCMethods<jsid>
@@ -857,53 +846,74 @@ class HeapSlot : public BarrieredBase<Va
         pre();
     }
 
     void init(JSObject *owner, Kind kind, uint32_t slot, const Value &v) {
         value = v;
         post(owner, kind, slot, v);
     }
 
+    void init(JSRuntime *rt, JSObject *owner, Kind kind, uint32_t slot, const Value &v) {
+        value = v;
+        post(rt, owner, kind, slot, v);
+    }
+
 #ifdef DEBUG
     bool preconditionForSet(JSObject *owner, Kind kind, uint32_t slot);
     bool preconditionForSet(Zone *zone, JSObject *owner, Kind kind, uint32_t slot);
-    bool preconditionForWriteBarrierPost(JSObject *obj, Kind kind, uint32_t slot, Value target) const;
+    static void preconditionForWriteBarrierPost(JSObject *obj, Kind kind, uint32_t slot,
+                                                Value target);
 #endif
 
     void set(JSObject *owner, Kind kind, uint32_t slot, const Value &v) {
         JS_ASSERT(preconditionForSet(owner, kind, slot));
+        pre();
         JS_ASSERT(!IsPoisonedValue(v));
-        pre();
         value = v;
         post(owner, kind, slot, v);
     }
 
     void set(Zone *zone, JSObject *owner, Kind kind, uint32_t slot, const Value &v) {
         JS_ASSERT(preconditionForSet(zone, owner, kind, slot));
-        JS_ASSERT(!IsPoisonedValue(v));
+        JS::shadow::Zone *shadowZone = JS::shadow::Zone::asShadowZone(zone);
         pre(zone);
+        JS_ASSERT(!IsPoisonedValue(v));
         value = v;
-        post(owner, kind, slot, v);
+        post(shadowZone->runtimeFromAnyThread(), owner, kind, slot, v);
     }
 
-    /* For users who need to manually barrier the raw types. */
-    static void writeBarrierPost(JSObject *owner, Kind kind, uint32_t slot, const Value &target) {
-        reinterpret_cast<HeapSlot *>(const_cast<Value *>(&target))->post(owner, kind, slot, target);
+    static void writeBarrierPost(JSObject *obj, Kind kind, uint32_t slot, const Value &target)
+    {
+#ifdef JSGC_GENERATIONAL
+        js::gc::Cell *cell = reinterpret_cast<js::gc::Cell*>(obj);
+        writeBarrierPost(cell->runtimeFromAnyThread(), obj, kind, slot, target);
+#endif
+    }
+
+    static void writeBarrierPost(JSRuntime *rt, JSObject *obj, Kind kind, uint32_t slot,
+                                 const Value &target)
+    {
+#ifdef DEBUG
+        preconditionForWriteBarrierPost(obj, kind, slot, target);
+#endif
+#ifdef JSGC_GENERATIONAL
+        if (target.isObject()) {
+            JS::shadow::Runtime *shadowRuntime = JS::shadow::Runtime::asShadowRuntime(rt);
+            shadowRuntime->gcStoreBufferPtr()->putSlot(obj, kind, slot, 1);
+        }
+#endif
     }
 
   private:
-    void post(JSObject *owner, Kind kind, uint32_t slot, const Value &target) {
-        JS_ASSERT(preconditionForWriteBarrierPost(owner, kind, slot, target));
-#ifdef JSGC_GENERATIONAL
-        if (this->value.isObject()) {
-            gc::Cell *cell = reinterpret_cast<gc::Cell *>(&this->value.toObject());
-            if (cell->storeBuffer())
-                cell->storeBuffer()->putSlotFromAnyThread(owner, kind, slot, 1);
-        }
-#endif
+    void post(JSObject *owner, Kind kind, uint32_t slot, Value target) {
+        HeapSlot::writeBarrierPost(owner, kind, slot, target);
+    }
+
+    void post(JSRuntime *rt, JSObject *owner, Kind kind, uint32_t slot, Value target) {
+        HeapSlot::writeBarrierPost(rt, owner, kind, slot, target);
     }
 };
 
 static inline const Value *
 Valueify(const BarrieredBase<Value> *array)
 {
     JS_STATIC_ASSERT(sizeof(HeapValue) == sizeof(Value));
     JS_STATIC_ASSERT(sizeof(HeapSlot) == sizeof(Value));
--- a/js/src/gc/Heap.h
+++ b/js/src/gc/Heap.h
@@ -106,18 +106,16 @@ struct Cell
     inline JS::Zone *tenuredZoneFromAnyThread() const;
     inline bool tenuredIsInsideZone(JS::Zone *zone) const;
 
     // Note: Unrestricted access to the runtime of a GC thing from an arbitrary
     // thread can easily lead to races. Use this method very carefully.
     inline JSRuntime *runtimeFromAnyThread() const;
     inline JS::shadow::Runtime *shadowRuntimeFromAnyThread() const;
 
-    inline StoreBuffer *storeBuffer() const;
-
 #ifdef DEBUG
     inline bool isAligned() const;
     inline bool isTenured() const;
 #endif
 
   protected:
     inline uintptr_t address() const;
     inline Chunk *chunk() const;
@@ -605,26 +603,25 @@ ArenaHeader::getThingSize() const
  * The tail of the chunk info is shared between all chunks in the system, both
  * nursery and tenured. This structure is locatable from any GC pointer by
  * aligning to 1MiB.
  */
 struct ChunkTrailer
 {
     /* The index the chunk in the nursery, or LocationTenuredHeap. */
     uint32_t        location;
+
+#if JS_BITS_PER_WORD == 64
     uint32_t        padding;
-
-    /* The store buffer for writes to things in this chunk or nullptr. */
-    StoreBuffer     *storeBuffer;
+#endif
 
     JSRuntime       *runtime;
 };
 
-static_assert(sizeof(ChunkTrailer) == 2 * sizeof(uintptr_t) + sizeof(uint64_t),
-              "ChunkTrailer size is incorrect.");
+static_assert(sizeof(ChunkTrailer) == 2 * sizeof(uintptr_t), "ChunkTrailer size is incorrect.");
 
 /* The chunk header (located at the end of the chunk to preserve arena alignment). */
 struct ChunkInfo
 {
     Chunk           *next;
     Chunk           **prevp;
 
     /* Free arenas are linked together with aheader.next. */
@@ -863,23 +860,19 @@ struct Chunk
     inline void addArenaToFreeList(JSRuntime *rt, ArenaHeader *aheader);
 };
 
 static_assert(sizeof(Chunk) == ChunkSize,
               "Ensure the hardcoded chunk size definition actually matches the struct.");
 static_assert(js::gc::ChunkMarkBitmapOffset == offsetof(Chunk, bitmap),
               "The hardcoded API bitmap offset must match the actual offset.");
 static_assert(js::gc::ChunkRuntimeOffset == offsetof(Chunk, info) +
-                                            offsetof(ChunkInfo, trailer) +
-                                            offsetof(ChunkTrailer, runtime),
+                                               offsetof(ChunkInfo, trailer) +
+                                               offsetof(ChunkTrailer, runtime),
               "The hardcoded API runtime offset must match the actual offset.");
-static_assert(js::gc::ChunkLocationOffset == offsetof(Chunk, info) +
-                                             offsetof(ChunkInfo, trailer) +
-                                             offsetof(ChunkTrailer, location),
-              "The hardcoded API location offset must match the actual offset.");
 
 inline uintptr_t
 ArenaHeader::address() const
 {
     uintptr_t addr = reinterpret_cast<uintptr_t>(this);
     JS_ASSERT(!(addr & ArenaMask));
     JS_ASSERT(Chunk::withinArenasRange(addr));
     return addr;
@@ -1099,22 +1092,16 @@ Chunk *
 Cell::chunk() const
 {
     uintptr_t addr = uintptr_t(this);
     JS_ASSERT(addr % CellSize == 0);
     addr &= ~(ChunkSize - 1);
     return reinterpret_cast<Chunk *>(addr);
 }
 
-inline StoreBuffer *
-Cell::storeBuffer() const
-{
-    return chunk()->info.trailer.storeBuffer;
-}
-
 inline bool
 InFreeList(ArenaHeader *aheader, void *thing)
 {
     if (!aheader->hasFreeThings())
         return false;
 
     FreeSpan firstSpan(aheader->getFirstFreeSpan());
     uintptr_t addr = reinterpret_cast<uintptr_t>(thing);
--- a/js/src/gc/Nursery.h
+++ b/js/src/gc/Nursery.h
@@ -198,17 +198,16 @@ class Nursery
     NurseryChunkLayout &chunk(int index) const {
         JS_ASSERT(index < NumNurseryChunks);
         JS_ASSERT(start());
         return reinterpret_cast<NurseryChunkLayout *>(start())[index];
     }
 
     MOZ_ALWAYS_INLINE void initChunk(int chunkno) {
         NurseryChunkLayout &c = chunk(chunkno);
-        c.trailer.storeBuffer = JS::shadow::Runtime::asShadowRuntime(runtime())->gcStoreBufferPtr();
         c.trailer.location = gc::ChunkLocationNursery;
         c.trailer.runtime = runtime();
     }
 
     MOZ_ALWAYS_INLINE void setCurrentChunk(int chunkno) {
         JS_ASSERT(chunkno < NumNurseryChunks);
         JS_ASSERT(chunkno < numActiveChunks_);
         currentChunk_ = chunkno;
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -397,20 +397,16 @@ AutoGCRooter::trace(JSTracer *trc)
             MarkValueRoot(trc, &desc.pd_, "PropDesc::pd_");
             MarkValueRoot(trc, &desc.value_, "PropDesc::value_");
             MarkValueRoot(trc, &desc.get_, "PropDesc::get_");
             MarkValueRoot(trc, &desc.set_, "PropDesc::set_");
         }
         return;
       }
 
-      case ID:
-        MarkIdRoot(trc, &static_cast<AutoIdRooter *>(this)->id_, "JS::AutoIdRooter.id_");
-        return;
-
       case VALVECTOR: {
         AutoValueVector::VectorImpl &vector = static_cast<AutoValueVector *>(this)->vector;
         MarkValueRootRange(trc, vector.length(), vector.begin(), "js::AutoValueVector.vector");
         return;
       }
 
       case IDVECTOR: {
         AutoIdVector::VectorImpl &vector = static_cast<AutoIdVector *>(this)->vector;
--- a/js/src/gc/StoreBuffer.cpp
+++ b/js/src/gc/StoreBuffer.cpp
@@ -330,55 +330,49 @@ StoreBuffer::addSizeOfExcludingThis(mozi
     sizes->storeBufferRelocVals  += bufferRelocVal.sizeOfExcludingThis(mallocSizeOf);
     sizes->storeBufferRelocCells += bufferRelocCell.sizeOfExcludingThis(mallocSizeOf);
     sizes->storeBufferGenerics   += bufferGeneric.sizeOfExcludingThis(mallocSizeOf);
 }
 
 JS_PUBLIC_API(void)
 JS::HeapCellPostBarrier(js::gc::Cell **cellp)
 {
-    JS_ASSERT(cellp);
     JS_ASSERT(*cellp);
-    StoreBuffer *storeBuffer = (*cellp)->storeBuffer();
-    if (storeBuffer)
-        storeBuffer->putRelocatableCellFromAnyThread(cellp);
+    JSRuntime *runtime = (*cellp)->runtimeFromMainThread();
+    runtime->gc.storeBuffer.putRelocatableCell(cellp);
 }
 
 JS_PUBLIC_API(void)
 JS::HeapCellRelocate(js::gc::Cell **cellp)
 {
-    /* Called with old contents of *cellp before overwriting. */
-    JS_ASSERT(cellp);
+    /* Called with old contents of *pp before overwriting. */
     JS_ASSERT(*cellp);
     JSRuntime *runtime = (*cellp)->runtimeFromMainThread();
-    runtime->gc.storeBuffer.removeRelocatableCellFromAnyThread(cellp);
+    runtime->gc.storeBuffer.removeRelocatableCell(cellp);
 }
 
 JS_PUBLIC_API(void)
 JS::HeapValuePostBarrier(JS::Value *valuep)
 {
-    JS_ASSERT(valuep);
     JS_ASSERT(valuep->isMarkable());
-    if (valuep->isObject()) {
-        StoreBuffer *storeBuffer = valuep->toObject().storeBuffer();
-        if (storeBuffer)
-            storeBuffer->putRelocatableValueFromAnyThread(valuep);
-    }
+    if (valuep->isString() && StringIsPermanentAtom(valuep->toString()))
+        return;
+    JSRuntime *runtime = static_cast<js::gc::Cell *>(valuep->toGCThing())->runtimeFromMainThread();
+    runtime->gc.storeBuffer.putRelocatableValue(valuep);
 }
 
 JS_PUBLIC_API(void)
 JS::HeapValueRelocate(JS::Value *valuep)
 {
     /* Called with old contents of *valuep before overwriting. */
-    JS_ASSERT(valuep);
     JS_ASSERT(valuep->isMarkable());
-    if (valuep->isString() && valuep->toString()->isPermanentAtom())
+    if (valuep->isString() && StringIsPermanentAtom(valuep->toString()))
         return;
     JSRuntime *runtime = static_cast<js::gc::Cell *>(valuep->toGCThing())->runtimeFromMainThread();
-    runtime->gc.storeBuffer.removeRelocatableValueFromAnyThread(valuep);
+    runtime->gc.storeBuffer.removeRelocatableValue(valuep);
 }
 
 template class StoreBuffer::MonoTypeBuffer<StoreBuffer::ValueEdge>;
 template class StoreBuffer::MonoTypeBuffer<StoreBuffer::CellPtrEdge>;
 template class StoreBuffer::MonoTypeBuffer<StoreBuffer::SlotsEdge>;
 template class StoreBuffer::MonoTypeBuffer<StoreBuffer::WholeCellEdges>;
 template class StoreBuffer::RelocatableMonoTypeBuffer<StoreBuffer::ValueEdge>;
 template class StoreBuffer::RelocatableMonoTypeBuffer<StoreBuffer::CellPtrEdge>;
--- a/js/src/gc/StoreBuffer.h
+++ b/js/src/gc/StoreBuffer.h
@@ -239,18 +239,17 @@ class StoreBuffer
     {
         Cell **edge;
 
         explicit CellPtrEdge(Cell **v) : edge(v) {}
         bool operator==(const CellPtrEdge &other) const { return edge == other.edge; }
         bool operator!=(const CellPtrEdge &other) const { return edge != other.edge; }
 
         bool maybeInRememberedSet(const Nursery &nursery) const {
-            JS_ASSERT(nursery.isInside(*edge));
-            return !nursery.isInside(edge);
+            return !nursery.isInside(edge) && nursery.isInside(*edge);
         }
 
         void mark(JSTracer *trc);
 
         CellPtrEdge tagged() const { return CellPtrEdge((Cell **)(uintptr_t(edge) | 1)); }
         CellPtrEdge untagged() const { return CellPtrEdge((Cell **)(uintptr_t(edge) & ~1)); }
         bool isTagged() const { return bool(uintptr_t(edge) & 1); }
 
@@ -263,18 +262,17 @@ class StoreBuffer
 
         explicit ValueEdge(JS::Value *v) : edge(v) {}
         bool operator==(const ValueEdge &other) const { return edge == other.edge; }
         bool operator!=(const ValueEdge &other) const { return edge != other.edge; }
 
         void *deref() const { return edge->isGCThing() ? edge->toGCThing() : nullptr; }
 
         bool maybeInRememberedSet(const Nursery &nursery) const {
-            JS_ASSERT(nursery.isInside(deref()));
-            return !nursery.isInside(edge);
+            return !nursery.isInside(edge) && nursery.isInside(deref());
         }
 
         void mark(JSTracer *trc);
 
         ValueEdge tagged() const { return ValueEdge((JS::Value *)(uintptr_t(edge) | 1)); }
         ValueEdge untagged() const { return ValueEdge((JS::Value *)(uintptr_t(edge) & ~1)); }
         bool isTagged() const { return bool(uintptr_t(edge) & 1); }
 
@@ -359,17 +357,18 @@ class StoreBuffer
         }
 
       private:
         MarkCallback callback;
         Key *key;
         void *data;
     };
 
-    bool isOkayToUseBuffer() const {
+    template <typename Edge>
+    bool isOkayToUseBuffer(const Edge &edge) const {
         /*
          * Disabled store buffers may not have a valid state; e.g. when stored
          * inline in the ChunkTrailer.
          */
         if (!isEnabled())
             return false;
 
         /*
@@ -378,27 +377,27 @@ class StoreBuffer
          */
         if (!CurrentThreadCanAccessRuntime(runtime_))
             return false;
 
         return true;
     }
 
     template <typename Buffer, typename Edge>
-    void putFromAnyThread(Buffer &buffer, const Edge &edge) {
-        if (!isOkayToUseBuffer())
+    void put(Buffer &buffer, const Edge &edge) {
+        if (!isOkayToUseBuffer(edge))
             return;
         mozilla::ReentrancyGuard g(*this);
         if (edge.maybeInRememberedSet(nursery_))
             buffer.put(this, edge);
     }
 
     template <typename Buffer, typename Edge>
-    void unputFromAnyThread(Buffer &buffer, const Edge &edge) {
-        if (!isOkayToUseBuffer())
+    void unput(Buffer &buffer, const Edge &edge) {
+        if (!isOkayToUseBuffer(edge))
             return;
         mozilla::ReentrancyGuard g(*this);
         buffer.unput(this, edge);
     }
 
     MonoTypeBuffer<ValueEdge> bufferVal;
     MonoTypeBuffer<CellPtrEdge> bufferCell;
     MonoTypeBuffer<SlotsEdge> bufferSlot;
@@ -428,48 +427,40 @@ class StoreBuffer
     bool isEnabled() const { return enabled_; }
 
     bool clear();
 
     /* Get the overflowed status. */
     bool isAboutToOverflow() const { return aboutToOverflow_; }
 
     /* Insert a single edge into the buffer/remembered set. */
-    void putValueFromAnyThread(JS::Value *valuep) { putFromAnyThread(bufferVal, ValueEdge(valuep)); }
-    void putCellFromAnyThread(Cell **cellp) { putFromAnyThread(bufferCell, CellPtrEdge(cellp)); }
-    void putSlotFromAnyThread(JSObject *obj, int kind, int32_t start, int32_t count) {
-        putFromAnyThread(bufferSlot, SlotsEdge(obj, kind, start, count));
+    void putValue(JS::Value *valuep) { put(bufferVal, ValueEdge(valuep)); }
+    void putCell(Cell **cellp) { put(bufferCell, CellPtrEdge(cellp)); }
+    void putSlot(JSObject *obj, int kind, int32_t start, int32_t count) {
+        put(bufferSlot, SlotsEdge(obj, kind, start, count));
     }
     void putWholeCell(Cell *cell) {
         JS_ASSERT(cell->isTenured());
-        putFromAnyThread(bufferWholeCell, WholeCellEdges(cell));
+        put(bufferWholeCell, WholeCellEdges(cell));
     }
 
     /* Insert or update a single edge in the Relocatable buffer. */
-    void putRelocatableValueFromAnyThread(JS::Value *valuep) {
-        putFromAnyThread(bufferRelocVal, ValueEdge(valuep));
-    }
-    void removeRelocatableValueFromAnyThread(JS::Value *valuep) {
-        unputFromAnyThread(bufferRelocVal, ValueEdge(valuep));
-    }
-    void putRelocatableCellFromAnyThread(Cell **cellp) {
-        putFromAnyThread(bufferRelocCell, CellPtrEdge(cellp));
-    }
-    void removeRelocatableCellFromAnyThread(Cell **cellp) {
-        unputFromAnyThread(bufferRelocCell, CellPtrEdge(cellp));
-    }
+    void putRelocatableValue(JS::Value *valuep) { put(bufferRelocVal, ValueEdge(valuep)); }
+    void putRelocatableCell(Cell **cellp) { put(bufferRelocCell, CellPtrEdge(cellp)); }
+    void removeRelocatableValue(JS::Value *valuep) { unput(bufferRelocVal, ValueEdge(valuep)); }
+    void removeRelocatableCell(Cell **cellp) { unput(bufferRelocCell, CellPtrEdge(cellp)); }
 
     /* Insert an entry into the generic buffer. */
     template <typename T>
-    void putGeneric(const T &t) { putFromAnyThread(bufferGeneric, t);}
+    void putGeneric(const T &t) { put(bufferGeneric, t);}
 
     /* Insert or update a callback entry. */
     template <typename Key>
     void putCallback(void (*callback)(JSTracer *trc, Key *key, void *data), Key *key, void *data) {
-        putFromAnyThread(bufferGeneric, CallbackRef<Key>(callback, key, data));
+        put(bufferGeneric, CallbackRef<Key>(callback, key, data));
     }
 
     /* Methods to mark the source of all edges in the store buffer. */
     void markAll(JSTracer *trc);
     void markValues(JSTracer *trc)            { bufferVal.mark(this, trc); }
     void markCells(JSTracer *trc)             { bufferCell.mark(this, trc); }
     void markSlots(JSTracer *trc)             { bufferSlot.mark(this, trc); }
     void markWholeCells(JSTracer *trc)        { bufferWholeCell.mark(this, trc); }
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/bug1004447.js
@@ -0,0 +1,23 @@
+// Tests that earlier try notes don't interfere with later exception handling.
+
+var g = newGlobal();
+g.debuggeeGlobal = this;
+g.eval("(" + function () {
+  dbg = new Debugger(debuggeeGlobal);
+} + ")();");
+var myObj = { p1: 'a', }
+try {
+  with(myObj) {
+    do {
+      throw value;
+    } while(false);
+  }
+} catch(e) {
+  // The above is expected to throw.
+}
+
+try {
+  if(!(p1 === 1)) { }
+} catch (e) {
+  // The above is expected to throw.
+}
--- a/js/src/jit/IonFrames.cpp
+++ b/js/src/jit/IonFrames.cpp
@@ -459,17 +459,17 @@ HandleExceptionIon(JSContext *cx, const 
           default:
             MOZ_ASSUME_UNREACHABLE("Unexpected try note");
         }
     }
 }
 
 static void
 HandleExceptionBaseline(JSContext *cx, const JitFrameIterator &frame, ResumeFromException *rfe,
-                        bool *calledDebugEpilogue)
+                        jsbytecode **unwoundScopeToPc, bool *calledDebugEpilogue)
 {
     JS_ASSERT(frame.isBaselineJS());
     JS_ASSERT(!*calledDebugEpilogue);
 
     RootedScript script(cx);
     jsbytecode *pc;
     frame.baselineScriptAndPc(script.address(), &pc);
 
@@ -523,27 +523,18 @@ HandleExceptionBaseline(JSContext *cx, c
         // See the big comment in TryNoteIter::settle for more info.
         JS_ASSERT(frame.baselineFrame()->numValueSlots() >= script->nfixed());
         size_t stackDepth = frame.baselineFrame()->numValueSlots() - script->nfixed();
         if (tn->stackDepth > stackDepth)
             continue;
 
         // Unwind scope chain (pop block objects).
         if (cx->isExceptionPending()) {
-            jsbytecode *unwindPc = script->main() + tn->start;
-            UnwindScope(cx, si, unwindPc);
-
-            // If we still need to call DebugEpilogue, we must remember the pc
-            // we unwound the scope chain to, as it will be out of sync with
-            // the frame's actual pc.
-            if (tn->kind != JSTRY_CATCH && tn->kind != JSTRY_FINALLY &&
-                cx->compartment()->debugMode() && !*calledDebugEpilogue)
-            {
-                frame.baselineFrame()->setUnwoundScopeOverridePc(unwindPc);
-            }
+            *unwoundScopeToPc = script->main() + tn->start;
+            UnwindScope(cx, si, *unwoundScopeToPc);
         }
 
         // Compute base pointer and stack pointer.
         rfe->framePointer = frame.fp() - BaselineFrame::FramePointerOffset;
         rfe->stackPointer = rfe->framePointer - BaselineFrame::Size() -
             (script->nfixed() + tn->stackDepth) * sizeof(Value);
 
         switch (tn->kind) {
@@ -665,17 +656,20 @@ HandleException(ResumeFromException *rfe
 
             if (invalidated)
                 ionScript->decref(cx->runtime()->defaultFreeOp());
 
         } else if (iter.isBaselineJS()) {
             // It's invalid to call DebugEpilogue twice for the same frame.
             bool calledDebugEpilogue = false;
 
-            HandleExceptionBaseline(cx, iter, rfe, &calledDebugEpilogue);
+            // Remember the pc we unwound the scope to.
+            jsbytecode *unwoundScopeToPc = nullptr;
+
+            HandleExceptionBaseline(cx, iter, rfe, &unwoundScopeToPc, &calledDebugEpilogue);
 
             // If we are propagating an exception through a frame with
             // on-stack recompile info, we should free the allocated
             // RecompileInfo struct before we leave this block, as we will not
             // be returning to the recompile handler.
             //
             // We cannot delete it immediately because of the call to
             // iter.baselineScriptAndPc below.
@@ -689,16 +683,22 @@ HandleException(ResumeFromException *rfe
             probes::ExitScript(cx, script, script->functionNonDelazifying(),
                                iter.baselineFrame()->hasPushedSPSFrame());
             // After this point, any pushed SPS frame would have been popped if it needed
             // to be.  Unset the flag here so that if we call DebugEpilogue below,
             // it doesn't try to pop the SPS frame again.
             iter.baselineFrame()->unsetPushedSPSFrame();
 
             if (cx->compartment()->debugMode() && !calledDebugEpilogue) {
+                // If we still need to call the DebugEpilogue, we must
+                // remember the pc we unwound the scope chain to, as it will
+                // be out of sync with the frame's actual pc.
+                if (unwoundScopeToPc)
+                    iter.baselineFrame()->setUnwoundScopeOverridePc(unwoundScopeToPc);
+
                 // If DebugEpilogue returns |true|, we have to perform a forced
                 // return, e.g. return frame->returnValue() to the caller.
                 BaselineFrame *frame = iter.baselineFrame();
                 RootedScript script(cx);
                 jsbytecode *pc;
                 iter.baselineScriptAndPc(script.address(), &pc);
                 if (jit::DebugEpilogue(cx, frame, pc, false)) {
                     JS_ASSERT(frame->hasReturnValue());
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -102,17 +102,16 @@ class JS_PUBLIC_API(AutoGCRooter) {
     ptrdiff_t tag_;
 
     enum {
         VALARRAY =     -2, /* js::AutoValueArray */
         PARSER =       -3, /* js::frontend::Parser */
         SHAPEVECTOR =  -4, /* js::AutoShapeVector */
         IDARRAY =      -6, /* js::AutoIdArray */
         DESCRIPTORS =  -7, /* js::AutoPropDescArrayRooter */
-        ID =           -9, /* js::AutoIdRooter */
         VALVECTOR =   -10, /* js::AutoValueVector */
         IDVECTOR =    -13, /* js::AutoIdVector */
         OBJVECTOR =   -14, /* js::AutoObjectVector */
         STRINGVECTOR =-15, /* js::AutoStringVector */
         SCRIPTVECTOR =-16, /* js::AutoScriptVector */
         NAMEVECTOR =  -17, /* js::AutoNameVector */
         HASHABLEVALUE=-18, /* js::HashableValue */
         IONMASM =     -19, /* js::jit::MacroAssembler */
@@ -834,41 +833,16 @@ JS_StringHasBeenInterned(JSContext *cx, 
  * N.B. if a jsid is backed by a string which has not been interned, that
  * string must be appropriately rooted to avoid being collected by the GC.
  */
 JS_PUBLIC_API(jsid)
 INTERNED_STRING_TO_JSID(JSContext *cx, JSString *str);
 
 namespace JS {
 
-class AutoIdRooter : private AutoGCRooter
-{
-  public:
-    explicit AutoIdRooter(JSContext *cx, jsid aId = INT_TO_JSID(0)
-                          MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-      : AutoGCRooter(cx, ID), id_(aId)
-    {
-        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-    }
-
-    jsid id() {
-        return id_;
-    }
-
-    jsid * addr() {
-        return &id_;
-    }
-
-    friend void AutoGCRooter::trace(JSTracer *trc);
-
-  private:
-    jsid id_;
-    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
-};
-
 // Container class for passing in script source buffers to the JS engine.  This
 // not only groups the buffer and length values, it also provides a way to
 // optionally pass ownership of the buffer to the JS engine without copying.
 // Rules for use:
 //
 //  1) The data array must be allocated with js_malloc() or js_realloc() if
 //     ownership is being granted to the SourceBufferHolder.
 //  2) If ownership is not given to the SourceBufferHolder, then the memory
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -784,17 +784,16 @@ Chunk::init(JSRuntime *rt)
     /*
      * Decommit the arenas. We do this after poisoning so that if the OS does
      * not have to recycle the pages, we still get the benefit of poisoning.
      */
     decommitAllArenas(rt);
 
     /* Initialize the chunk info. */
     info.age = 0;
-    info.trailer.storeBuffer = nullptr;
     info.trailer.location = ChunkLocationTenuredHeap;
     info.trailer.runtime = rt;
 
     /* The rest of info fields are initialized in PickChunk. */
 }
 
 static inline Chunk **
 GetAvailableChunkList(Zone *zone)
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -2288,18 +2288,18 @@ JSObject::TradeGuts(JSContext *cx, JSObj
         js_memcpy(b, tmp, size);
 
 #ifdef JSGC_GENERATIONAL
         /*
          * Trigger post barriers for fixed slots. JSObject bits are barriered
          * below, in common with the other case.
          */
         for (size_t i = 0; i < a->numFixedSlots(); ++i) {
-            HeapSlot::writeBarrierPost(a, HeapSlot::Slot, i, a->getSlot(i));
-            HeapSlot::writeBarrierPost(b, HeapSlot::Slot, i, b->getSlot(i));
+            HeapSlot::writeBarrierPost(cx->runtime(), a, HeapSlot::Slot, i, a->getSlot(i));
+            HeapSlot::writeBarrierPost(cx->runtime(), b, HeapSlot::Slot, i, b->getSlot(i));
         }
 #endif
     } else {
         /*
          * If the objects are of differing sizes, use the space we reserved
          * earlier to save the slots from each object and then copy them into
          * the new layout for the other object.
          */
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -182,17 +182,17 @@ class StrictArgumentsObject;
  * single step.
  */
 inline void
 DenseRangeWriteBarrierPost(JSRuntime *rt, JSObject *obj, uint32_t start, uint32_t count)
 {
 #ifdef JSGC_GENERATIONAL
     if (count > 0) {
         JS::shadow::Runtime *shadowRuntime = JS::shadow::Runtime::asShadowRuntime(rt);
-        shadowRuntime->gcStoreBufferPtr()->putSlotFromAnyThread(obj, HeapSlot::Element, start, count);
+        shadowRuntime->gcStoreBufferPtr()->putSlot(obj, HeapSlot::Element, start, count);
     }
 #endif
 }
 
 }  /* namespace js */
 
 /*
  * The public interface for an object.
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -218,23 +218,22 @@ JSObject::ensureDenseInitializedLengthNo
      * Ensure that the array's contents have been initialized up to index, and
      * mark the elements through 'index + extra' as initialized in preparation
      * for a write.
      */
     JS_ASSERT(index + extra <= getDenseCapacity());
     uint32_t &initlen = getElementsHeader()->initializedLength;
 
     if (initlen < index + extra) {
+        JSRuntime *rt = runtimeFromAnyThread();
         size_t offset = initlen;
         for (js::HeapSlot *sp = elements + initlen;
              sp != elements + (index + extra);
              sp++, offset++)
-        {
-            sp->init(this, js::HeapSlot::Element, offset, js::MagicValue(JS_ELEMENTS_HOLE));
-        }
+            sp->init(rt, this, js::HeapSlot::Element, offset, js::MagicValue(JS_ELEMENTS_HOLE));
         initlen = index + extra;
     }
 }
 
 inline void
 JSObject::ensureDenseInitializedLength(js::ExclusiveContext *cx, uint32_t index, uint32_t extra)
 {
     if (writeToIndexWouldMarkNotPacked(index))
--- a/js/src/vm/ObjectImpl.cpp
+++ b/js/src/vm/ObjectImpl.cpp
@@ -259,32 +259,34 @@ js::ObjectImpl::initializeSlotRange(uint
 {
     /*
      * No bounds check, as this is used when the object's shape does not
      * reflect its allocated slots (updateSlotsForSpan).
      */
     HeapSlot *fixedStart, *fixedEnd, *slotsStart, *slotsEnd;
     getSlotRangeUnchecked(start, length, &fixedStart, &fixedEnd, &slotsStart, &slotsEnd);
 
+    JSRuntime *rt = runtimeFromAnyThread();
     uint32_t offset = start;
     for (HeapSlot *sp = fixedStart; sp < fixedEnd; sp++)
-        sp->init(this->asObjectPtr(), HeapSlot::Slot, offset++, UndefinedValue());
+        sp->init(rt, this->asObjectPtr(), HeapSlot::Slot, offset++, UndefinedValue());
     for (HeapSlot *sp = slotsStart; sp < slotsEnd; sp++)
-        sp->init(this->asObjectPtr(), HeapSlot::Slot, offset++, UndefinedValue());
+        sp->init(rt, this->asObjectPtr(), HeapSlot::Slot, offset++, UndefinedValue());
 }
 
 void
 js::ObjectImpl::initSlotRange(uint32_t start, const Value *vector, uint32_t length)
 {
+    JSRuntime *rt = runtimeFromAnyThread();
     HeapSlot *fixedStart, *fixedEnd, *slotsStart, *slotsEnd;
     getSlotRange(start, length, &fixedStart, &fixedEnd, &slotsStart, &slotsEnd);
     for (HeapSlot *sp = fixedStart; sp < fixedEnd; sp++)
-        sp->init(this->asObjectPtr(), HeapSlot::Slot, start++, *vector++);
+        sp->init(rt, this->asObjectPtr(), HeapSlot::Slot, start++, *vector++);
     for (HeapSlot *sp = slotsStart; sp < slotsEnd; sp++)
-        sp->init(this->asObjectPtr(), HeapSlot::Slot, start++, *vector++);
+        sp->init(rt, this->asObjectPtr(), HeapSlot::Slot, start++, *vector++);
 }
 
 void
 js::ObjectImpl::copySlotRange(uint32_t start, const Value *vector, uint32_t length)
 {
     JS::Zone *zone = this->zone();
     HeapSlot *fixedStart, *fixedEnd, *slotsStart, *slotsEnd;
     getSlotRange(start, length, &fixedStart, &fixedEnd, &slotsStart, &slotsEnd);
--- a/js/src/vm/ObjectImpl.h
+++ b/js/src/vm/ObjectImpl.h
@@ -835,22 +835,17 @@ class ObjectImpl : public gc::BarrieredC
 
     /* GC support. */
     static ThingRootKind rootKind() { return THING_ROOT_OBJECT; }
 
     inline void privateWriteBarrierPre(void **oldval);
 
     void privateWriteBarrierPost(void **pprivate) {
 #ifdef JSGC_GENERATIONAL
-        js::gc::Cell **cellp = reinterpret_cast<js::gc::Cell **>(pprivate);
-        JS_ASSERT(cellp);
-        JS_ASSERT(*cellp);
-        js::gc::StoreBuffer *storeBuffer = (*cellp)->storeBuffer();
-        if (storeBuffer)
-            storeBuffer->putCellFromAnyThread(cellp);
+        shadowRuntimeFromAnyThread()->gcStoreBufferPtr()->putCell(reinterpret_cast<js::gc::Cell **>(pprivate));
 #endif
     }
 
     void markChildren(JSTracer *trc);
 
     /* Private data accessors. */
 
     inline void *&privateRef(uint32_t nfixed) const { /* XXX should be private, not protected! */
@@ -944,53 +939,40 @@ template<>
 /* static */ inline bool
 BarrieredCell<ObjectImpl>::isNullLike(ObjectImpl *obj)
 {
     return IsNullTaggedPointer(obj);
 }
 
 template<>
 /* static */ inline void
-BarrieredCell<ObjectImpl>::writeBarrierPost(ObjectImpl *obj, void *cellp)
+BarrieredCell<ObjectImpl>::writeBarrierPost(ObjectImpl *obj, void *addr)
 {
-    JS_ASSERT(cellp);
 #ifdef JSGC_GENERATIONAL
     if (IsNullTaggedPointer(obj))
         return;
-    JS_ASSERT(obj == *static_cast<ObjectImpl **>(cellp));
-    gc::StoreBuffer *storeBuffer = obj->storeBuffer();
-    if (storeBuffer)
-        storeBuffer->putCellFromAnyThread(static_cast<Cell **>(cellp));
+    obj->shadowRuntimeFromAnyThread()->gcStoreBufferPtr()->putCell((Cell **)addr);
 #endif
 }
 
 template<>
 /* static */ inline void
-BarrieredCell<ObjectImpl>::writeBarrierPostRelocate(ObjectImpl *obj, void *cellp)
+BarrieredCell<ObjectImpl>::writeBarrierPostRelocate(ObjectImpl *obj, void *addr)
 {
-    JS_ASSERT(cellp);
-    JS_ASSERT(obj);
-    JS_ASSERT(obj == *static_cast<ObjectImpl **>(cellp));
 #ifdef JSGC_GENERATIONAL
-    gc::StoreBuffer *storeBuffer = obj->storeBuffer();
-    if (storeBuffer)
-        storeBuffer->putRelocatableCellFromAnyThread(static_cast<Cell **>(cellp));
+    obj->shadowRuntimeFromAnyThread()->gcStoreBufferPtr()->putRelocatableCell((Cell **)addr);
 #endif
 }
 
 template<>
 /* static */ inline void
-BarrieredCell<ObjectImpl>::writeBarrierPostRemove(ObjectImpl *obj, void *cellp)
+BarrieredCell<ObjectImpl>::writeBarrierPostRemove(ObjectImpl *obj, void *addr)
 {
-    JS_ASSERT(cellp);
-    JS_ASSERT(obj);
-    JS_ASSERT(obj == *static_cast<ObjectImpl **>(cellp));
 #ifdef JSGC_GENERATIONAL
-    obj->shadowRuntimeFromAnyThread()->gcStoreBufferPtr()->removeRelocatableCellFromAnyThread(
-        static_cast<Cell **>(cellp));
+    obj->shadowRuntimeFromAnyThread()->gcStoreBufferPtr()->removeRelocatableCell((Cell **)addr);
 #endif
 }
 
 } // namespace gc
 
 inline void
 ObjectImpl::privateWriteBarrierPre(void **oldval)
 {
--- a/js/src/vm/Shape.h
+++ b/js/src/vm/Shape.h
@@ -520,31 +520,27 @@ struct StackBaseShape;
 namespace gc {
 void MergeCompartments(JSCompartment *source, JSCompartment *target);
 }
 
 static inline void
 GetterSetterWriteBarrierPost(JSRuntime *rt, JSObject **objp)
 {
 #ifdef JSGC_GENERATIONAL
-    JS_ASSERT(objp);
-    JS_ASSERT(*objp);
-    gc::Cell **cellp = reinterpret_cast<gc::Cell **>(objp);
-    gc::StoreBuffer *storeBuffer = (*cellp)->storeBuffer();
-    if (storeBuffer)
-        storeBuffer->putRelocatableCellFromAnyThread(cellp);
+    JS::shadow::Runtime *shadowRuntime = JS::shadow::Runtime::asShadowRuntime(rt);
+    shadowRuntime->gcStoreBufferPtr()->putRelocatableCell(reinterpret_cast<gc::Cell **>(objp));
 #endif
 }
 
 static inline void
 GetterSetterWriteBarrierPostRemove(JSRuntime *rt, JSObject **objp)
 {
 #ifdef JSGC_GENERATIONAL
     JS::shadow::Runtime *shadowRuntime = JS::shadow::Runtime::asShadowRuntime(rt);
-    shadowRuntime->gcStoreBufferPtr()->removeRelocatableCellFromAnyThread(reinterpret_cast<gc::Cell **>(objp));
+    shadowRuntime->gcStoreBufferPtr()->removeRelocatableCell(reinterpret_cast<gc::Cell **>(objp));
 #endif
 }
 
 class BaseShape : public gc::BarrieredCell<BaseShape>
 {
   public:
     friend class Shape;
     friend struct StackBaseShape;
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -134,25 +134,25 @@ template
 void InterpreterFrame::copyFrameAndValues<InterpreterFrame::DoPostBarrier>(
                                     JSContext *, Value *, InterpreterFrame *, const Value *, Value *);
 
 void
 InterpreterFrame::writeBarrierPost()
 {
     /* This needs to follow the same rules as in InterpreterFrame::mark. */
     if (scopeChain_)
-        JSObject::writeBarrierPost(scopeChain_, &scopeChain_);
+        JSObject::writeBarrierPost(scopeChain_, (void *)&scopeChain_);
     if (flags_ & HAS_ARGS_OBJ)
-        JSObject::writeBarrierPost(argsObj_, &argsObj_);
+        JSObject::writeBarrierPost(argsObj_, (void *)&argsObj_);
     if (isFunctionFrame()) {
-        JSFunction::writeBarrierPost(exec.fun, &exec.fun);
+        JSFunction::writeBarrierPost(exec.fun, (void *)&exec.fun);
         if (isEvalFrame())
-            JSScript::writeBarrierPost(u.evalScript, &u.evalScript);
+            JSScript::writeBarrierPost(u.evalScript, (void *)&u.evalScript);
     } else {
-        JSScript::writeBarrierPost(exec.script, &exec.script);
+        JSScript::writeBarrierPost(exec.script, (void *)&exec.script);
     }
     if (hasReturnValue())
         HeapValue::writeBarrierPost(rval_, &rval_);
 }
 
 bool
 InterpreterFrame::copyRawFrameSlots(AutoValueVector *vec)
 {
--- a/js/xpconnect/loader/mozJSComponentLoader.cpp
+++ b/js/xpconnect/loader/mozJSComponentLoader.cpp
@@ -1367,16 +1367,19 @@ NS_IMETHODIMP
 mozJSComponentLoader::Unload(const nsACString & aLocation)
 {
     nsresult rv;
 
     if (!mInitialized) {
         return NS_OK;
     }
 
+    MOZ_RELEASE_ASSERT(!mReuseLoaderGlobal, "Module unloading not supported when "
+                                            "compartment sharing is enabled");
+
     nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Get the URI.
     nsCOMPtr<nsIURI> resURI;
     rv = ioService->NewURI(aLocation, nullptr, nullptr, getter_AddRefs(resURI));
     NS_ENSURE_SUCCESS(rv, rv);
 
--- a/js/xpconnect/src/XPCJSID.cpp
+++ b/js/xpconnect/src/XPCJSID.cpp
@@ -488,55 +488,34 @@ nsresult
 xpc::HasInstance(JSContext *cx, HandleObject objArg, const nsID *iid, bool *bp)
 {
     *bp = false;
 
     RootedObject obj(cx, FindObjectForHasInstance(cx, objArg));
     if (!obj)
         return NS_OK;
 
-    if (IsDOMObject(obj)) {
-        // Not all DOM objects implement nsISupports. But if they don't,
-        // there's nothing to do in this HasInstance hook.
-        nsISupports *identity = UnwrapDOMObjectToISupports(obj);
-        if (!identity)
-            return NS_OK;;
-        nsCOMPtr<nsISupports> supp;
-        identity->QueryInterface(*iid, getter_AddRefs(supp));
-        *bp = supp;
-        return NS_OK;
-    }
-
     if (mozilla::jsipc::JavaScriptParent::IsCPOW(obj))
         return mozilla::jsipc::JavaScriptParent::InstanceOf(obj, iid, bp);
 
-    MOZ_ASSERT(IS_WN_REFLECTOR(obj));
-    XPCWrappedNative* other_wrapper = XPCWrappedNative::Get(obj);
-    if (!other_wrapper)
+    nsISupports *identity = UnwrapReflectorToISupports(obj);
+    if (!identity)
         return NS_OK;
 
-    // We'll trust the interface set of the wrapper if this is known
-    // to be an interface that the objects *expects* to be able to
-    // handle.
-    if (other_wrapper->HasInterfaceNoQI(*iid)) {
-        *bp = true;
-        return NS_OK;
-    }
+    nsCOMPtr<nsISupports> supp;
+    identity->QueryInterface(*iid, getter_AddRefs(supp));
+    *bp = supp;
 
-    // Otherwise, we'll end up Querying the native object to be sure.
-    XPCCallContext ccx(JS_CALLER, cx);
-
-    AutoMarkingNativeInterfacePtr iface(ccx);
-    iface = XPCNativeInterface::GetNewOrUsed(iid);
-
-    nsresult findResult = NS_OK;
-    if (iface && other_wrapper->FindTearOff(iface, false, &findResult))
-        *bp = true;
-    if (NS_FAILED(findResult) && findResult != NS_ERROR_NO_INTERFACE)
-        return findResult;
+    // Our old HasInstance implementation operated by invoking FindTearOff on
+    // XPCWrappedNatives, and various bits of chrome JS came to depend on
+    // |instanceof| doing an implicit QI if it succeeds. Do a drive-by QI to
+    // preserve that behavior. This is just a compatibility hack, so we don't
+    // really care if it fails.
+    if (IS_WN_REFLECTOR(obj))
+        (void) XPCWrappedNative::Get(obj)->FindTearOff(*iid);
 
     return NS_OK;
 }
 
 /* bool hasInstance (in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj, in jsval val, out bool bp); */
 NS_IMETHODIMP
 nsJSIID::HasInstance(nsIXPConnectWrappedNative *wrapper,
                      JSContext *cx, JSObject * /* unused */,
--- a/js/xpconnect/src/XPCWrappedNative.cpp
+++ b/js/xpconnect/src/XPCWrappedNative.cpp
@@ -1426,16 +1426,24 @@ XPCWrappedNative::FindTearOff(XPCNativeI
             to = nullptr;
     }
 
     if (pError)
         *pError = rv;
     return to;
 }
 
+XPCWrappedNativeTearOff*
+XPCWrappedNative::FindTearOff(const nsIID& iid) {
+    AutoJSContext cx;
+    AutoMarkingNativeInterfacePtr iface(cx);
+    iface = XPCNativeInterface::GetNewOrUsed(&iid);
+    return iface ? FindTearOff(iface) : nullptr;
+}
+
 nsresult
 XPCWrappedNative::InitTearOff(XPCWrappedNativeTearOff* aTearOff,
                               XPCNativeInterface* aInterface,
                               bool needJSObject)
 {
     AutoJSContext cx;
 
     // Determine if the object really does this interface...
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -623,38 +623,44 @@ nsXPConnect::GetWrappedNativeOfJSObject(
         return NS_ERROR_FAILURE;
     }
 
     nsRefPtr<XPCWrappedNative> temp = XPCWrappedNative::Get(aJSObj);
     temp.forget(_retval);
     return NS_OK;
 }
 
+nsISupports*
+xpc::UnwrapReflectorToISupports(JSObject *reflector)
+{
+    // Unwrap security wrappers, if allowed.
+    reflector = js::CheckedUnwrap(reflector, /* stopAtOuter = */ false);
+    if (!reflector)
+        return nullptr;
+
+    // Try XPCWrappedNatives.
+    if (IS_WN_REFLECTOR(reflector)) {
+        XPCWrappedNative *wn = XPCWrappedNative::Get(reflector);
+        if (!wn)
+            return nullptr;
+        return wn->Native();
+    }
+
+    // Try DOM objects.
+    nsCOMPtr<nsISupports> canonical =
+        do_QueryInterface(mozilla::dom::UnwrapDOMObjectToISupports(reflector));
+    return canonical;
+}
+
 /* nsISupports getNativeOfWrapper(in JSContextPtr aJSContext, in JSObjectPtr  aJSObj); */
 NS_IMETHODIMP_(nsISupports*)
 nsXPConnect::GetNativeOfWrapper(JSContext *aJSContext,
                                 JSObject *aJSObj)
 {
-    MOZ_ASSERT(aJSContext, "bad param");
-    MOZ_ASSERT(aJSObj, "bad param");
-
-    aJSObj = js::CheckedUnwrap(aJSObj, /* stopAtOuter = */ false);
-    if (!aJSObj) {
-        JS_ReportError(aJSContext, "Permission denied to get native of security wrapper");
-        return nullptr;
-    }
-    if (IS_WN_REFLECTOR(aJSObj)) {
-        if (XPCWrappedNative *wn = XPCWrappedNative::Get(aJSObj))
-            return wn->Native();
-        return nullptr;
-    }
-
-    nsCOMPtr<nsISupports> canonical =
-        do_QueryInterface(mozilla::dom::UnwrapDOMObjectToISupports(aJSObj));
-    return canonical;
+    return UnwrapReflectorToISupports(aJSObj);
 }
 
 /* nsIXPConnectWrappedNative getWrappedNativeOfNativeObject (in JSContextPtr aJSContext, in JSObjectPtr aScope, in nsISupports aCOMObj, in nsIIDRef aIID); */
 NS_IMETHODIMP
 nsXPConnect::GetWrappedNativeOfNativeObject(JSContext * aJSContext,
                                             JSObject * aScopeArg,
                                             nsISupports *aCOMObj,
                                             const nsIID & aIID,
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -2088,16 +2088,18 @@ public:
         {return CallMethod(ccx, CALL_SETTER);}
 
     inline bool HasInterfaceNoQI(const nsIID& iid);
 
     XPCWrappedNativeTearOff* LocateTearOff(XPCNativeInterface* aInterface);
     XPCWrappedNativeTearOff* FindTearOff(XPCNativeInterface* aInterface,
                                          bool needJSObject = false,
                                          nsresult* pError = nullptr);
+    XPCWrappedNativeTearOff* FindTearOff(const nsIID& iid);
+
     void Mark() const
     {
         mSet->Mark();
         if (mScriptableInfo) mScriptableInfo->Mark();
         if (HasProto()) GetProto()->Mark();
     }
 
     // Yes, we *do* need to mark the mScriptableInfo in both cases.
@@ -2535,16 +2537,19 @@ public:
 
     static bool GetNativeInterfaceFromJSObject(void** dest, JSObject* src,
                                                const nsID* iid,
                                                nsresult* pErr);
     static bool JSObject2NativeInterface(void** dest, JS::HandleObject src,
                                          const nsID* iid,
                                          nsISupports* aOuter,
                                          nsresult* pErr);
+
+    // Note - This return the XPCWrappedNative, rather than the native itself,
+    // for the WN case. You probably want UnwrapReflectorToISupports.
     static bool GetISupportsFromJSObject(JSObject* obj, nsISupports** iface);
 
     /**
      * Convert a native array into a jsval.
      *
      * @param d [out] the resulting jsval
      * @param s the native array we're working with
      * @param type the type of objects in the array
--- a/js/xpconnect/src/xpcpublic.h
+++ b/js/xpconnect/src/xpcpublic.h
@@ -409,16 +409,23 @@ Throw(JSContext *cx, nsresult rv);
 
 /**
  * Every global should hold a native that implements the nsIGlobalObject interface.
  */
 nsIGlobalObject *
 GetNativeForGlobal(JSObject *global);
 
 /**
+ * Returns the nsISupports native behind a given reflector (either DOM or
+ * XPCWN).
+ */
+nsISupports *
+UnwrapReflectorToISupports(JSObject *reflector);
+
+/**
  * In some cases a native object does not really belong to any compartment (XBL,
  * document created from by XHR of a worker, etc.). But when for some reason we
  * have to wrap these natives (because of an event for example) instead of just
  * wrapping them into some random compartment we find on the context stack (like
  * we did previously) a default compartment is used. This function returns that
  * compartment's global. It is a singleton on the runtime.
  * If you find yourself wanting to use this compartment, you're probably doing
  * something wrong. Callers MUST consult with the XPConnect module owner before
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -4641,36 +4641,36 @@ nsDisplayTransform::ShouldPrerenderTrans
       message.AppendLiteral("Performance warning: Async animation disabled because frame was not marked active for transform animation");
       CommonElementAnimationData::LogAsyncAnimationFailure(message,
                                                            aFrame->GetContent());
     }
     return false;
   }
 
   nsSize refSize = aBuilder->RootReferenceFrame()->GetSize();
-  // Only prerender if the transformed frame's size is <= the
-  // reference frame size (~viewport), allowing a 1/8th fuzz factor
-  // for shadows, borders, etc.
+  // Only prerender if the transformed frame's size (in the reference
+  // frames coordinate space) is <= the reference frame size (~viewport),
+  // allowing a 1/8th fuzz factor for shadows, borders, etc.
   refSize += nsSize(refSize.width / 8, refSize.height / 8);
-  nsSize frameSize = aFrame->GetVisualOverflowRectRelativeToSelf().Size();
-  if (frameSize <= refSize) {
-    // Bug 717521 - pre-render max 4096 x 4096 device pixels.
-    nscoord max = aFrame->PresContext()->DevPixelsToAppUnits(4096);
-    nsRect visual = aFrame->GetVisualOverflowRect();
-    if (visual.width <= max && visual.height <= max) {
-      return true;
-    }
+  nsRect frameRect = aFrame->GetVisualOverflowRectRelativeToSelf();
+
+  frameRect =
+    nsLayoutUtils::TransformFrameRectToAncestor(aFrame, frameRect,
+                                                aBuilder->RootReferenceFrame());
+
+  if (frameRect.Size() <= refSize) {
+    return true;
   }
 
   if (aLogAnimations) {
     nsCString message;
     message.AppendLiteral("Performance warning: Async animation disabled because frame size (");
-    message.AppendInt(nsPresContext::AppUnitsToIntCSSPixels(frameSize.width));
+    message.AppendInt(nsPresContext::AppUnitsToIntCSSPixels(frameRect.width));
     message.AppendLiteral(", ");
-    message.AppendInt(nsPresContext::AppUnitsToIntCSSPixels(frameSize.height));
+    message.AppendInt(nsPresContext::AppUnitsToIntCSSPixels(frameRect.height));
     message.AppendLiteral(") is bigger than the viewport (");
     message.AppendInt(nsPresContext::AppUnitsToIntCSSPixels(refSize.width));
     message.AppendLiteral(", ");
     message.AppendInt(nsPresContext::AppUnitsToIntCSSPixels(refSize.height));
     message.AppendLiteral(")");
     CommonElementAnimationData::LogAsyncAnimationFailure(message,
                                                          aFrame->GetContent());
   }
--- a/media/webrtc/signaling/src/media-conduit/AudioConduit.cpp
+++ b/media/webrtc/signaling/src/media-conduit/AudioConduit.cpp
@@ -38,20 +38,17 @@ static const char* logTag ="WebrtcAudioS
 const unsigned int WebrtcAudioConduit::CODEC_PLNAME_SIZE = 32;
 
 /**
  * Factory Method for AudioConduit
  */
 mozilla::RefPtr<AudioSessionConduit> AudioSessionConduit::Create(AudioSessionConduit *aOther)
 {
   CSFLogDebug(logTag,  "%s ", __FUNCTION__);
-#ifdef MOZILLA_INTERNAL_API
-  // unit tests create their own "main thread"
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
-#endif
 
   WebrtcAudioConduit* obj = new WebrtcAudioConduit();
   if(obj->Init(static_cast<WebrtcAudioConduit*>(aOther)) != kMediaConduitNoError)
   {
     CSFLogError(logTag,  "%s AudioConduit Init Failed ", __FUNCTION__);
     delete obj;
     return nullptr;
   }
@@ -59,20 +56,17 @@ mozilla::RefPtr<AudioSessionConduit> Aud
   return obj;
 }
 
 /**
  * Destruction defines for our super-classes
  */
 WebrtcAudioConduit::~WebrtcAudioConduit()
 {
-#ifdef MOZILLA_INTERNAL_API
-  // unit tests create their own "main thread"
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
-#endif
 
   CSFLogDebug(logTag,  "%s ", __FUNCTION__);
   for(std::vector<AudioCodecConfig*>::size_type i=0;i < mRecvCodecList.size();i++)
   {
     delete mRecvCodecList[i];
   }
   delete mCurSendCodecConfig;
 
--- a/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
+++ b/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
@@ -34,39 +34,33 @@ static const char* logTag ="WebrtcVideoS
 // 32 bytes is what WebRTC CodecInst expects
 const unsigned int WebrtcVideoConduit::CODEC_PLNAME_SIZE = 32;
 
 /**
  * Factory Method for VideoConduit
  */
 mozilla::RefPtr<VideoSessionConduit> VideoSessionConduit::Create(VideoSessionConduit *aOther)
 {
-#ifdef MOZILLA_INTERNAL_API
-  // unit tests create their own "main thread"
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
-#endif
   CSFLogDebug(logTag,  "%s ", __FUNCTION__);
 
   WebrtcVideoConduit* obj = new WebrtcVideoConduit();
   if(obj->Init(static_cast<WebrtcVideoConduit*>(aOther)) != kMediaConduitNoError)
   {
     CSFLogError(logTag,  "%s VideoConduit Init Failed ", __FUNCTION__);
     delete obj;
     return nullptr;
   }
   CSFLogDebug(logTag,  "%s Successfully created VideoConduit ", __FUNCTION__);
   return obj;
 }
 
 WebrtcVideoConduit::~WebrtcVideoConduit()
 {
-#ifdef MOZILLA_INTERNAL_API
-  // unit tests create their own "main thread"
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
-#endif
   CSFLogDebug(logTag,  "%s ", __FUNCTION__);
 
   for(std::vector<VideoCodecConfig*>::size_type i=0;i < mRecvCodecList.size();i++)
   {
     delete mRecvCodecList[i];
   }
 
   delete mCurSendCodecConfig;
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp
@@ -188,33 +188,24 @@ static void join_waiter() {
 
 nsresult PeerConnectionCtx::InitializeGlobal(nsIThread *mainThread,
   nsIEventTarget* stsThread) {
   if (!gMainThread) {
     gMainThread = mainThread;
     CSF::VcmSIPCCBinding::setMainThread(gMainThread);
     init_thread_monitor(&thread_ended_dispatcher, &join_waiter);
   } else {
-#ifdef MOZILLA_INTERNAL_API
     MOZ_ASSERT(gMainThread == mainThread);
-#endif
   }
 
   CSF::VcmSIPCCBinding::setSTSThread(stsThread);
 
   nsresult res;
 
-#ifdef MOZILLA_INTERNAL_API
-  // This check fails on the unit tests because they do not
-  // have the right thread behavior.
-  bool on;
-  res = gMainThread->IsOnCurrentThread(&on);
-  NS_ENSURE_SUCCESS(res, res);
-  MOZ_ASSERT(on);
-#endif
+  MOZ_ASSERT(NS_IsMainThread());
 
   if (!gInstance) {
     CSFLogDebug(logTag, "Creating PeerConnectionCtx");
     PeerConnectionCtx *ctx = new PeerConnectionCtx();
 
     res = ctx->Initialize();
     PR_ASSERT(NS_SUCCEEDED(res));
     if (!NS_SUCCEEDED(res))
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -188,19 +188,17 @@ public:
 // Since this is for JS-calls, these can only be dispatched to the main thread.
 
 class WrappableJSErrorResult {
 public:
   WrappableJSErrorResult() : isCopy(false) {}
   WrappableJSErrorResult(WrappableJSErrorResult &other) : mRv(), isCopy(true) {}
   ~WrappableJSErrorResult() {
     if (isCopy) {
-#ifdef MOZILLA_INTERNAL_API
       MOZ_ASSERT(NS_IsMainThread());
-#endif
     }
   }
   operator JSErrorResult &() { return mRv; }
 private:
   JSErrorResult mRv;
   bool isCopy;
 };
 }
@@ -702,19 +700,17 @@ PeerConnectionImpl::Initialize(PeerConne
                                const IceConfiguration* aConfiguration,
                                const RTCConfiguration* aRTCConfiguration,
                                nsISupports* aThread)
 {
   nsresult res;
 
   // Invariant: we receive configuration one way or the other but not both (XOR)
   MOZ_ASSERT(!aConfiguration != !aRTCConfiguration);
-#ifdef MOZILLA_INTERNAL_API
   MOZ_ASSERT(NS_IsMainThread());
-#endif
   MOZ_ASSERT(aThread);
   mThread = do_QueryInterface(aThread);
 
   mPCObserver = do_GetWeakReference(&aObserver);
 
   // Find the STS thread
 
   mSTSThread = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &res);
--- a/media/webrtc/signaling/test/signaling_unittests.cpp
+++ b/media/webrtc/signaling/test/signaling_unittests.cpp
@@ -957,28 +957,28 @@ class SignalingAgent {
   }
 
 
   ~SignalingAgent() {
     mozilla::SyncRunnable::DispatchToThread(gMainThread,
       WrapRunnable(this, &SignalingAgent::Close));
   }
 
-  void Init_m(nsCOMPtr<nsIThread> thread)
+  void Init_m()
   {
     pObserver = new TestObserver(pc->pcImpl(), name);
     ASSERT_TRUE(pObserver);
 
-    ASSERT_EQ(pc->Initialize(pObserver, nullptr, cfg_, thread), NS_OK);
+    ASSERT_EQ(pc->Initialize(pObserver, nullptr, cfg_, gMainThread), NS_OK);
   }
 
-  void Init(nsCOMPtr<nsIThread> thread)
+  void Init()
   {
-    mozilla::SyncRunnable::DispatchToThread(thread,
-      WrapRunnable(this, &SignalingAgent::Init_m, thread));
+    mozilla::SyncRunnable::DispatchToThread(gMainThread,
+      WrapRunnable(this, &SignalingAgent::Init_m));
 
     ASSERT_TRUE_WAIT(sipcc_state() == PCImplSipccState::Started,
                      kDefaultTimeout);
   }
 
   void WaitForGather() {
     ASSERT_TRUE_WAIT(ice_gathering_state() == PCImplIceGatheringState::Complete,
                      5000);
@@ -1550,17 +1550,17 @@ class SignalingAgentTest : public ::test
     return CreateAgent(g_stun_server_address, g_stun_server_port);
   }
 
   bool CreateAgent(const std::string stun_addr, uint16_t stun_port,
                    bool wait_for_gather = true) {
     ScopedDeletePtr<SignalingAgent> agent(
         new SignalingAgent("agent", stun_addr, stun_port));
 
-    agent->Init(gMainThread);
+    agent->Init();
 
     if (wait_for_gather) {
       if (!agent->WaitForGatherAllowFail())
         return false;
     }
 
     agents_.push_back(agent.forget());
 
@@ -1604,18 +1604,18 @@ public:
   void EnsureInit() {
 
     if (init_)
       return;
 
     a1_ = new SignalingAgent(callerName, stun_addr_, stun_port_);
     a2_ = new SignalingAgent(calleeName, stun_addr_, stun_port_);
 
-    a1_->Init(gMainThread);
-    a2_->Init(gMainThread);
+    a1_->Init();
+    a2_->Init();
 
     if (wait_for_gather_) {
       WaitForGather();
     }
   }
 
   void WaitForGather() {
     a1_->WaitForGather();
--- a/mfbt/RefPtr.h
+++ b/mfbt/RefPtr.h
@@ -174,30 +174,37 @@ class RefCounted : public detail::RefCou
 {
   public:
     ~RefCounted() {
       static_assert(IsBaseOf<RefCounted, T>::value,
                     "T must derive from RefCounted<T>");
     }
 };
 
+namespace external {
+
 /**
  * AtomicRefCounted<T> is like RefCounted<T>, with an atomically updated
  * reference counter.
+ *
+ * NOTE: Please do not use this class, use NS_INLINE_DECL_THREADSAFE_REFCOUNTING
+ * instead.
  */
 template<typename T>
-class AtomicRefCounted : public detail::RefCounted<T, detail::AtomicRefCount>
+class AtomicRefCounted : public mozilla::detail::RefCounted<T, mozilla::detail::AtomicRefCount>
 {
   public:
     ~AtomicRefCounted() {
       static_assert(IsBaseOf<AtomicRefCounted, T>::value,
                     "T must derive from AtomicRefCounted<T>");
     }
 };
 
+}
+
 /**
  * RefPtr points to a refcounted thing that has AddRef and Release
  * methods to increase/decrease the refcount, respectively.  After a
  * RefPtr<T> is assigned a T*, the T* can be used through the RefPtr
  * as if it were a T*.
  *
  * A RefPtr can forget its underlying T*, which results in the T*
  * being wrapped in a temporary object until the T* is either
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -13,16 +13,18 @@ import java.util.Vector;
 
 import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
 import org.mozilla.gecko.AndroidGamepadManager;
 import org.mozilla.gecko.DynamicToolbar.PinReason;
 import org.mozilla.gecko.DynamicToolbar.VisibilityTransition;
 import org.mozilla.gecko.GeckoProfileDirectories.NoMozillaDirectoryException;
+import org.mozilla.gecko.Telemetry;
+import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.animation.PropertyAnimator;
 import org.mozilla.gecko.animation.ViewHelper;
 import org.mozilla.gecko.db.BrowserContract.Combined;
 import org.mozilla.gecko.db.BrowserContract.ReadingListItems;
 import org.mozilla.gecko.db.BrowserDB;
 import org.mozilla.gecko.favicons.Favicons;
 import org.mozilla.gecko.favicons.LoadFaviconTask;
 import org.mozilla.gecko.favicons.OnFaviconLoadedListener;
@@ -480,16 +482,18 @@ abstract public class BrowserApp extends
         mActionBar = (ActionModeCompatView) findViewById(R.id.actionbar);
 
         mBrowserToolbar = (BrowserToolbar) findViewById(R.id.browser_toolbar);
         mProgressView = (ToolbarProgressView) findViewById(R.id.progress);
         mBrowserToolbar.setProgressBar(mProgressView);
         if (Intent.ACTION_VIEW.equals(intent.getAction())) {
             // Show the target URL immediately in the toolbar.
             mBrowserToolbar.setTitle(intent.getDataString());
+
+            Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.INTENT);
         }
 
         ((GeckoApp.MainLayout) mMainLayout).setTouchEventInterceptor(new HideTabsTouchListener());
         ((GeckoApp.MainLayout) mMainLayout).setMotionEventInterceptor(new MotionEventInterceptor() {
             @Override
             public boolean onInterceptMotionEvent(View view, MotionEvent event) {
                 // If we get a gamepad panning MotionEvent while the focus is not on the layerview,
                 // put the focus on the layerview and carry on
@@ -638,16 +642,22 @@ abstract public class BrowserApp extends
         });
 
         mBrowserToolbar.setOnFocusChangeListener(new View.OnFocusChangeListener() {
             @Override
             public void onFocusChange(View v, boolean hasFocus) {
                 if (isHomePagerVisible()) {
                     mHomePager.onToolbarFocusChange(hasFocus);
                 }
+
+                if (hasFocus) {
+                    Telemetry.startUISession(TelemetryContract.Session.URLBAR_FOCUSED);
+                } else {
+                    Telemetry.stopUISession(TelemetryContract.Session.URLBAR_FOCUSED);
+                }
             }
         });
 
         mBrowserToolbar.setOnStartEditingListener(new BrowserToolbar.OnStartEditingListener() {
             public void onStartEditing() {
                 // Temporarily disable doorhanger notifications.
                 mDoorHangerPopup.disable();
             }
@@ -746,16 +756,17 @@ abstract public class BrowserApp extends
 
     @Override
     public boolean onContextItemSelected(MenuItem item) {
         final int itemId = item.getItemId();
         if (itemId == R.id.pasteandgo) {
             String text = Clipboard.getText();
             if (!TextUtils.isEmpty(text)) {
                 Tabs.getInstance().loadUrl(text);
+                Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.CONTEXT_MENU);
             }
             return true;
         }
 
         if (itemId == R.id.site_settings) {
             GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Permissions:Get", null));
             return true;
         }
@@ -937,16 +948,19 @@ abstract public class BrowserApp extends
         }
 
         if (AboutPages.isAboutReader(url)) {
             url = ReaderModeUtils.getUrlFromAboutReader(url);
         }
 
         GeckoAppShell.openUriExternal(url, "text/plain", "", "",
                                       Intent.ACTION_SEND, tab.getDisplayTitle());
+
+        // Context: Sharing via chrome list (no explicit session is active)
+        Telemetry.sendUIEvent(TelemetryContract.Event.SHARE, TelemetryContract.Method.LIST);
     }
 
     @Override
     protected void loadStartupTab(String url) {
         // We aren't showing about:home, so cancel the telemetry timer
         if (url != null || mShouldRestore) {
             mAboutHomeStartupTimer.cancel();
         }
@@ -1608,16 +1622,18 @@ abstract public class BrowserApp extends
         // Don't do anything if the user entered an empty URL.
         if (TextUtils.isEmpty(url)) {
             return;
         }
 
         // If the URL doesn't look like a search query, just load it.
         if (!StringUtils.isSearchQuery(url, true)) {
             Tabs.getInstance().loadUrl(url, Tabs.LOADURL_USER_ENTERED);
+
+            Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL);
             return;
         }
 
         // Otherwise, check for a bookmark keyword.
         ThreadUtils.postToBackgroundThread(new Runnable() {
             @Override
             public void run() {
                 final String keyword;
@@ -1633,24 +1649,26 @@ abstract public class BrowserApp extends
                 }
 
                 final String keywordUrl = BrowserDB.getUrlForKeyword(getContentResolver(), keyword);
 
                 // If there isn't a bookmark keyword, load the url. This may result in a query
                 // using the default search engine.
                 if (TextUtils.isEmpty(keywordUrl)) {
                     Tabs.getInstance().loadUrl(url, Tabs.LOADURL_USER_ENTERED);
+                    Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL);
                     return;
                 }
 
                 recordSearch(null, "barkeyword");
 
                 // Otherwise, construct a search query from the bookmark keyword.
                 final String searchUrl = keywordUrl.replace("%s", URLEncoder.encode(keywordSearch));
                 Tabs.getInstance().loadUrl(searchUrl, Tabs.LOADURL_USER_ENTERED);
+                Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, "", "keyword");
             }
         });
     }
 
     /**
      * Record in Health Report that a search has occurred.
      *
      * @param engine
@@ -2533,19 +2551,21 @@ abstract public class BrowserApp extends
             String uri = intent.getDataString();
             GeckoAppShell.sendEventToGecko(GeckoEvent.createURILoadEvent(uri));
         }
 
         if (!mInitialized) {
             return;
         }
 
-        // Dismiss editing mode if the user is loading a URL from an external app.
         if (Intent.ACTION_VIEW.equals(action)) {
+            // Dismiss editing mode if the user is loading a URL from an external app.
             mBrowserToolbar.cancelEdit();
+
+            Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.INTENT);
             return;
         }
 
         // Only solicit feedback when the app has been launched from the icon shortcut.
         if (!Intent.ACTION_MAIN.equals(action)) {
             return;
         }
 
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -602,16 +602,19 @@ public abstract class GeckoApp
                 final String url = message.getString("url");
                 final String origin = message.getString("origin");
                 final String title = message.getString("title");
                 final String type = message.getString("shortcutType");
                 GeckoAppShell.removeShortcut(title, url, origin, type);
             } else if (event.equals("Share:Text")) {
                 String text = message.getString("text");
                 GeckoAppShell.openUriExternal(text, "text/plain", "", "", Intent.ACTION_SEND, "");
+
+                // Context: Sharing via chrome list (no explicit session is active)
+                Telemetry.sendUIEvent(TelemetryContract.Event.SHARE, TelemetryContract.Method.LIST);
             } else if (event.equals("Image:SetAs")) {
                 String src = message.getString("url");
                 setImageAs(src);
             } else if (event.equals("Sanitize:ClearHistory")) {
                 handleClearHistory();
             } else if (event.equals("Update:Check")) {
                 startService(new Intent(UpdateServiceHelper.ACTION_CHECK_FOR_UPDATE, null, this, UpdateService.class));
             } else if (event.equals("Update:Download")) {
--- a/mobile/android/base/TelemetryContract.java
+++ b/mobile/android/base/TelemetryContract.java
@@ -25,40 +25,85 @@ public interface TelemetryContract {
         public static final String TOP_SITES_UNPIN = "unpin.1";
 
         // Top site edited.
         public static final String TOP_SITES_EDIT = "edit.1";
 
         // Set default panel.
         public static final String PANEL_SET_DEFAULT = "setdefault.1";
 
+        // Sharing content.
+        public static final String SHARE = "share.1";
+
         // Sanitizing private data.
         public static final String SANITIZE = "sanitize.1";
+
+        // Saving a resource (reader, bookmark, etc) for viewing later.
+        // Note: Only used in JavaScript for now, but here for completeness.
+        public static final String SAVE = "save.1";
+
+        // Stop holding a resource (reader, bookmark, etc) for viewing later.
+        // Note: Only used in JavaScript for now, but here for completeness.
+        public static final String UNSAVE = "unsave.1";
+
+        // Loading a URL.
+        public static final String LOAD_URL = "loadurl.1";
     }
 
     /**
      * Holds event methods. Intended for use in
      * Telemetry.sendUIEvent() as the "method" parameter.
      */
     public interface Method {
+        // Action triggered from a list.
+        public static final String LIST = "list";
+
+        // Action triggered from a button.
+        public static final String BUTTON = "button";
+
         // Action triggered from a dialog.
         public static final String DIALOG = "dialog";
+
+        // Action occurred via an intent.
+        public static final String INTENT = "intent";
+
+        // Action occurred via a context menu.
+        public static final String CONTEXT_MENU = "contextmenu";
+
+        // Action triggered from a view grid item, like a thumbnail.
+        public static final String GRID_ITEM = "griditem";
+
+        // Action triggered from a view list item, like a row of a list.
+        public static final String LIST_ITEM = "listitem";
+
+        // Action triggered from a suggestion provided to the user.
+        public static final String SUGGESTION = "suggestion";
     }
 
     /**
      * Holds session names. Intended for use with
      * Telemetry.startUISession() as the "sessionName" parameter.
      */
     public interface Session {
         // Started when a user enters about:home.
         public static final String HOME = "home.1";
 
         // Started when a user enters a given home panel.
         // Session name is dynamic, encoded as "homepanel.1:<panel_id>"
         public static final String HOME_PANEL = "homepanel.1:";
+
+        // Started when a Reader viewer becomes active in the foreground.
+        // Note: Only used in JavaScript for now, but here for completeness.
+        public static final String READER = "reader.1";
+
+        // URL bar focused.
+        public static final String URLBAR_FOCUSED = "urlbar.1:";
+
+        // Awesomescreen frecency search is active.
+        public static final String FRECENCY = "frecency.1:";
     }
 
     /**
      * Holds reasons for stopping a session. Intended for use in
      * Telemetry.stopUISession() as the "reason" parameter.
      */
     public interface Reason {}
 }
--- a/mobile/android/base/home/BookmarksListView.java
+++ b/mobile/android/base/home/BookmarksListView.java
@@ -3,16 +3,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.home;
 
 import java.util.EnumSet;
 
 import org.mozilla.gecko.R;
+import org.mozilla.gecko.Telemetry;
+import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.db.BrowserContract.Bookmarks;
 import org.mozilla.gecko.db.BrowserDB.URLColumns;
 import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
 
 import android.content.Context;
 import android.database.Cursor;
 import android.util.AttributeSet;
 import android.view.KeyEvent;
@@ -86,16 +88,18 @@ public class BookmarksListView extends H
             // If we're clicking on a folder, update adapter to move to that folder
             final int folderId = cursor.getInt(cursor.getColumnIndexOrThrow(Bookmarks._ID));
             final String folderTitle = adapter.getFolderTitle(parent.getContext(), cursor);
             adapter.moveToChildFolder(folderId, folderTitle);
         } else {
             // Otherwise, just open the URL
             final String url = cursor.getString(cursor.getColumnIndexOrThrow(URLColumns.URL));
 
+            Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL);
+
             // This item is a TwoLinePageRow, so we allow switch-to-tab.
             getOnUrlOpenListener().onUrlOpen(url, EnumSet.of(OnUrlOpenListener.Flags.ALLOW_SWITCH_TO_TAB));
         }
     }
 
     @Override
     public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
         // Adjust the item position to account for the parent folder row that is inserted
--- a/mobile/android/base/home/BrowserSearch.java
+++ b/mobile/android/base/home/BrowserSearch.java
@@ -6,16 +6,18 @@
 package org.mozilla.gecko.home;
 
 import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.GeckoEvent;
 import org.mozilla.gecko.PrefsHelper;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.Tab;
 import org.mozilla.gecko.Tabs;
+import org.mozilla.gecko.Telemetry;
+import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.db.BrowserDB.URLColumns;
 import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
 import org.mozilla.gecko.home.SearchEngine;
 import org.mozilla.gecko.home.SearchLoader.SearchCursorLoader;
 import org.mozilla.gecko.mozglue.RobocopTarget;
 import org.mozilla.gecko.toolbar.AutocompleteHandler;
 import org.mozilla.gecko.util.GeckoEventListener;
 import org.mozilla.gecko.util.StringUtils;
@@ -216,16 +218,30 @@ public class BrowserSearch extends HomeF
     @Override
     public void onStop() {
         super.onStop();
 
         getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
     }
 
     @Override
+    public void onResume() {
+        super.onResume();
+
+        Telemetry.startUISession(TelemetryContract.Session.FRECENCY);
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+
+        Telemetry.stopUISession(TelemetryContract.Session.FRECENCY);
+    }
+
+    @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
         // All list views are styled to look the same with a global activity theme.
         // If the style of the list changes, inflate it from an XML.
         mView = (LinearLayout) inflater.inflate(R.layout.browser_search, container, false);
         mList = (HomeListView) mView.findViewById(R.id.home_list_view);
 
         return mView;
     }
@@ -259,16 +275,21 @@ public class BrowserSearch extends HomeF
                     return;
                 }
 
                 // Account for the search engine rows.
                 position -= getSuggestEngineCount();
                 final Cursor c = mAdapter.getCursor(position);
                 final String url = c.getString(c.getColumnIndexOrThrow(URLColumns.URL));
 
+                // The "urlbar" and "frecency" sessions can be open at the same time. Use the LIST_ITEM
+                // method to set this LOAD_URL event apart from the case where the user commits what's in
+                // the url bar.
+                Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.LIST_ITEM);
+
                 // This item is a TwoLinePageRow, so we allow switch-to-tab.
                 mUrlOpenListener.onUrlOpen(url, EnumSet.of(OnUrlOpenListener.Flags.ALLOW_SWITCH_TO_TAB));
             }
         });
 
         mList.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
             @Override
             public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
--- a/mobile/android/base/home/HomeFragment.java
+++ b/mobile/android/base/home/HomeFragment.java
@@ -7,16 +7,18 @@ package org.mozilla.gecko.home;
 
 import org.mozilla.gecko.EditBookmarkDialog;
 import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.GeckoEvent;
 import org.mozilla.gecko.GeckoProfile;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.ReaderModeUtils;
 import org.mozilla.gecko.Tabs;
+import org.mozilla.gecko.Telemetry;
+import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.db.BrowserContract.Combined;
 import org.mozilla.gecko.db.BrowserDB;
 import org.mozilla.gecko.favicons.Favicons;
 import org.mozilla.gecko.util.ThreadUtils;
 import org.mozilla.gecko.util.UiAsyncTask;
 
 import android.content.ContentResolver;
 import android.content.Context;
@@ -127,16 +129,19 @@ abstract class HomeFragment extends Frag
         final int itemId = item.getItemId();
         if (itemId == R.id.home_share) {
             if (info.url == null) {
                 Log.e(LOGTAG, "Can't share because URL is null");
                 return false;
             } else {
                 GeckoAppShell.openUriExternal(info.url, SHARE_MIME_TYPE, "", "",
                                               Intent.ACTION_SEND, info.getDisplayTitle());
+
+                // Context: Sharing via chrome homepage contextmenu list (home session should be active)
+                Telemetry.sendUIEvent(TelemetryContract.Event.SHARE, TelemetryContract.Method.LIST);
                 return true;
             }
         }
 
         if (itemId == R.id.home_add_to_launcher) {
             if (info.url == null) {
                 Log.e(LOGTAG, "Can't add to home screen because URL is null");
                 return false;
@@ -152,16 +157,18 @@ abstract class HomeFragment extends Frag
                 Log.e(LOGTAG, "Can't open in new tab because URL is null");
                 return false;
             }
 
             int flags = Tabs.LOADURL_NEW_TAB | Tabs.LOADURL_BACKGROUND;
             if (item.getItemId() == R.id.home_open_private_tab)
                 flags |= Tabs.LOADURL_PRIVATE;
 
+            Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.CONTEXT_MENU);
+
             final String url = (info.isInReadingList() ? ReaderModeUtils.getAboutReaderForUrl(info.url) : info.url);
 
             // Some pinned site items have "user-entered" urls. URLs entered in the PinSiteDialog are wrapped in
             // a special URI until we can get a valid URL. If the url is a user-entered url, decode the URL before loading it.
             Tabs.getInstance().loadUrl(decodeUserEnteredUrl(url), flags);
             Toast.makeText(context, R.string.new_tab_opened, Toast.LENGTH_SHORT).show();
             return true;
         }
--- a/mobile/android/base/home/LastTabsPanel.java
+++ b/mobile/android/base/home/LastTabsPanel.java
@@ -4,16 +4,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.home;
 
 import org.mozilla.gecko.AboutPages;
 import org.mozilla.gecko.GeckoProfile;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.SessionParser;
+import org.mozilla.gecko.Telemetry;
+import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.db.BrowserContract.Combined;
 import org.mozilla.gecko.home.HomePager.OnNewTabsListener;
 
 import android.app.Activity;
 import android.content.Context;
 import android.database.Cursor;
 import android.database.MatrixCursor;
 import android.database.MatrixCursor.RowBuilder;
@@ -105,16 +107,18 @@ public class LastTabsPanel extends HomeF
         mList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
             @Override
             public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                 final Cursor c = mAdapter.getCursor();
                 if (c == null || !c.moveToPosition(position)) {
                     return;
                 }
 
+                Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL);
+
                 final String url = c.getString(c.getColumnIndexOrThrow(Combined.URL));
                 mNewTabsListener.onNewTabs(new String[] { url });
             }
         });
 
         mList.setContextMenuInfoFactory(new HomeContextMenuInfo.Factory() {
             @Override
             public HomeContextMenuInfo makeInfoForCursor(View view, int position, long id, Cursor cursor) {
@@ -201,16 +205,18 @@ public class LastTabsPanel extends HomeF
         }
 
         final String[] urls = new String[c.getCount()];
 
         do {
             urls[c.getPosition()] = c.getString(c.getColumnIndexOrThrow(Combined.URL));
         } while (c.moveToNext());
 
+        Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.BUTTON);
+
         mNewTabsListener.onNewTabs(urls);
     }
 
     private static class LastTabsCursorLoader extends SimpleCursorLoader {
         public LastTabsCursorLoader(Context context) {
             super(context);
         }
 
--- a/mobile/android/base/home/MostRecentPanel.java
+++ b/mobile/android/base/home/MostRecentPanel.java
@@ -4,16 +4,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.home;
 
 import java.util.Date;
 import java.util.EnumSet;
 
 import org.mozilla.gecko.R;
+import org.mozilla.gecko.Telemetry;
+import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.db.BrowserContract.Combined;
 import org.mozilla.gecko.db.BrowserDB;
 import org.mozilla.gecko.db.BrowserDB.URLColumns;
 import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
 
 import android.app.Activity;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -93,16 +95,18 @@ public class MostRecentPanel extends Hom
 
         mList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
             @Override
             public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                 position -= mAdapter.getMostRecentSectionsCountBefore(position);
                 final Cursor c = mAdapter.getCursor(position);
                 final String url = c.getString(c.getColumnIndexOrThrow(URLColumns.URL));
 
+                Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL);
+
                 // This item is a TwoLinePageRow, so we allow switch-to-tab.
                 mUrlOpenListener.onUrlOpen(url, EnumSet.of(OnUrlOpenListener.Flags.ALLOW_SWITCH_TO_TAB));
             }
         });
 
         mList.setContextMenuInfoFactory(new HomeContextMenuInfo.Factory() {
             @Override
             public HomeContextMenuInfo makeInfoForCursor(View view, int position, long id, Cursor cursor) {
--- a/mobile/android/base/home/ReadingListPanel.java
+++ b/mobile/android/base/home/ReadingListPanel.java
@@ -4,16 +4,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.home;
 
 import java.util.EnumSet;
 
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.ReaderModeUtils;
+import org.mozilla.gecko.Telemetry;
+import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.db.BrowserContract.ReadingListItems;
 import org.mozilla.gecko.db.BrowserDB;
 import org.mozilla.gecko.db.BrowserDB.URLColumns;
 import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
 
 import android.app.Activity;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -100,16 +102,18 @@ public class ReadingListPanel extends Ho
                 final Cursor c = mAdapter.getCursor();
                 if (c == null || !c.moveToPosition(position)) {
                     return;
                 }
 
                 String url = c.getString(c.getColumnIndexOrThrow(URLColumns.URL));
                 url = ReaderModeUtils.getAboutReaderForUrl(url);
 
+                Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL);
+
                 // This item is a TwoLinePageRow, so we allow switch-to-tab.
                 mUrlOpenListener.onUrlOpen(url, EnumSet.of(OnUrlOpenListener.Flags.ALLOW_SWITCH_TO_TAB));
             }
         });
 
         mList.setContextMenuInfoFactory(new HomeContextMenuInfo.Factory() {
             @Override
             public HomeContextMenuInfo makeInfoForCursor(View view, int position, long id, Cursor cursor) {
--- a/mobile/android/base/home/SearchEngineRow.java
+++ b/mobile/android/base/home/SearchEngineRow.java
@@ -1,16 +1,18 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.home;
 
 import org.mozilla.gecko.R;
+import org.mozilla.gecko.Telemetry;
+import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.home.BrowserSearch.OnEditSuggestionListener;
 import org.mozilla.gecko.home.BrowserSearch.OnSearchListener;
 import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
 import org.mozilla.gecko.util.StringUtils;
 import org.mozilla.gecko.widget.AnimatedHeightLayout;
 import org.mozilla.gecko.widget.FaviconView;
 import org.mozilla.gecko.widget.FlowLayout;
 
@@ -74,19 +76,26 @@ class SearchEngineRow extends AnimatedHe
             public void onClick(View v) {
                 final String suggestion = getSuggestionTextFromView(v);
 
                 // If we're not clicking the user-entered view (the first suggestion item)
                 // and the search matches a URL pattern, go to that URL. Otherwise, do a
                 // search for the term.
                 if (v != mUserEnteredView && !StringUtils.isSearchQuery(suggestion, false)) {
                     if (mUrlOpenListener != null) {
+                        Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.SUGGESTION, "url");
+
                         mUrlOpenListener.onUrlOpen(suggestion, EnumSet.noneOf(OnUrlOpenListener.Flags.class));
                     }
                 } else if (mSearchListener != null) {
+                    if (v == mUserEnteredView) {
+                        Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.SUGGESTION, "user");
+                    } else {
+                        Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.SUGGESTION, "engine");
+                    }
                     mSearchListener.onSearch(mSearchEngine, suggestion);
                 }
             }
         };
 
         mLongClickListener = new OnLongClickListener() {
             @Override
             public boolean onLongClick(View v) {
@@ -130,16 +139,17 @@ class SearchEngineRow extends AnimatedHe
     }
 
     /**
      * Perform a search for the user-entered term.
      */
     public void performUserEnteredSearch() {
         String searchTerm = getSuggestionTextFromView(mUserEnteredView);
         if (mSearchListener != null) {
+            Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.SUGGESTION, "user");
             mSearchListener.onSearch(mSearchEngine, searchTerm);
         }
     }
 
     public void setSearchTerm(String searchTerm) {
         mUserEnteredTextView.setText(searchTerm);
 
         // mSearchEngine is not set in the first call to this method; the content description
--- a/mobile/android/base/home/TopSitesGridView.java
+++ b/mobile/android/base/home/TopSitesGridView.java
@@ -3,16 +3,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.home;
 
 import java.util.EnumSet;
 
 import org.mozilla.gecko.R;
+import org.mozilla.gecko.Telemetry;
+import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.ThumbnailHelper;
 import org.mozilla.gecko.db.BrowserDB.URLColumns;
 import org.mozilla.gecko.db.TopSitesCursorWrapper;
 import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
 
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.database.Cursor;
@@ -105,16 +107,18 @@ public class TopSitesGridView extends Gr
 
                 // Decode "user-entered" URLs before loading them.
                 String url = HomeFragment.decodeUserEnteredUrl(row.getUrl());
 
                 // If the url is empty, the user can pin a site.
                 // If not, navigate to the page given by the url.
                 if (!TextUtils.isEmpty(url)) {
                     if (mUrlOpenListener != null) {
+                        Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.GRID_ITEM);
+
                         mUrlOpenListener.onUrlOpen(url, EnumSet.noneOf(OnUrlOpenListener.Flags.class));
                     }
                 } else {
                     if (mEditPinnedSiteListener != null) {
                         mEditPinnedSiteListener.onEditPinnedSite(position, "");
                     }
                 }
             }
--- a/mobile/android/base/home/TopSitesPanel.java
+++ b/mobile/android/base/home/TopSitesPanel.java
@@ -176,16 +176,18 @@ public class TopSitesPanel extends HomeF
 
                 final Cursor c = mListAdapter.getCursor();
                 if (c == null || !c.moveToPosition(position)) {
                     return;
                 }
 
                 final String url = c.getString(c.getColumnIndexOrThrow(URLColumns.URL));
 
+                Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.LIST_ITEM);
+
                 // This item is a TwoLinePageRow, so we allow switch-to-tab.
                 mUrlOpenListener.onUrlOpen(url, EnumSet.of(OnUrlOpenListener.Flags.ALLOW_SWITCH_TO_TAB));
             }
         });
 
         mList.setContextMenuInfoFactory(new HomeContextMenuInfo.Factory() {
             @Override
             public HomeContextMenuInfo makeInfoForCursor(View view, int position, long id, Cursor cursor) {
--- a/mobile/android/base/prompts/PromptListAdapter.java
+++ b/mobile/android/base/prompts/PromptListAdapter.java
@@ -1,14 +1,16 @@
 package org.mozilla.gecko.prompts;
 
-import org.mozilla.gecko.menu.MenuItemActionView;
+import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.R;
-import org.mozilla.gecko.GeckoAppShell;
+import org.mozilla.gecko.Telemetry;
+import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.gfx.BitmapUtils;
+import org.mozilla.gecko.menu.MenuItemActionView;
 import org.mozilla.gecko.widget.GeckoActionProvider;
 
 import org.json.JSONArray;
 import org.json.JSONObject;
 import org.json.JSONException;
 
 import android.content.Context;
 import android.content.Intent;
@@ -192,16 +194,19 @@ public class PromptListAdapter extends A
                 }
 
                 final GeckoActionProvider provider = GeckoActionProvider.getForType(item.getIntent().getType(), getContext());
                 IntentChooserPrompt prompt = new IntentChooserPrompt(getContext(), provider);
                 prompt.show(item.label, getContext(), new IntentHandler() {
                     @Override
                     public void onIntentSelected(final Intent intent, final int p) {
                         provider.chooseActivity(p);
+
+                        // Context: Sharing via content contextmenu list (no explicit session is active)
+                        Telemetry.sendUIEvent(TelemetryContract.Event.SHARE, TelemetryContract.Method.LIST);
                     }
 
                     @Override
                     public void onCancelled() {
                         // do nothing
                     }
                 });
             }
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..f3e70cc10e805d716be2bc4dd1057fae29448ec9
GIT binary patch
literal 408
zc%17D@N?(olHy`uVBq!ia0vp^Dj>|k1|%Oc%$NbB7>k44ofy`glX(f`uqAoByD<C*
z!3BGlPX>x`7I;J!Gca%qgD@k*tT_@uE!v(gjv*0;-(K7AEtVj1^rOF+E}xUfB>P|D
zQ86(x#vLB*%vNlsY=OLjf{I?=j}n^e9k#@D6qINzJb71_BRWS^isfKdr8v7X=l$=Z
z^WIm?e>m^?oYPM)Wtd3yCdXL3R6O~9b5>NS`Zw7Z=Iwfa&*%G{-oN8sIiIQHbH5As
z+kUAn`+aDkO8Lh2>3@Z8y1z*<Ke<_@d9T=(9^aV5*)_gKw~ZY`zb;ypRv?>SC@Me6
z=H*qMx9XMsovW%ktbcTxd%pXsv`JYf!TO|^`aVmUB;Au4+gW=P<m^94WQTs`TyM~^
zblp68Pq{?d@D06%caEjk92Yod(At|fM}GB>rrJC2ugnX->X@z?b8Kzjf%v<(u1v0o
z3hnmXm#B5pVA}@<^Hr~8^|o1WznEciZi4;UDBrhQllR5|!;-<%)z4*}Q$iB}-m|86
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..4bd3b344bc1f657144963a6c75f8f245a519fbff
GIT binary patch
literal 454
zc$@*o0XhDOP)<h;3K|Lk000e1NJLTq001Na001Ni1^@s6;Q*MJ00001b5ch_0Itp)
z=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00045Nkl<ZSi|ks
zJ!_XS6o&DiwhEmD5k+GC3W9Oz;;3R25vPt7?I0A3br2B|@n!J^t%Kkyj#3Iia8Y#X
zkV&^f(IK4+4uWWPc@~F)()Q_p$Tglo&V9~(5)O?<qtW<_ZEGFGTz_v?(vrfOdAAQ*
zhlbZv&HwT*T_4`4i#DpTfwQa$TF(p}!_k50P|WoVmT|KSoy4cDv}_Ka@g?SZ4IgkA
zPq#|YUA)D9+{YqLl@u;x605xmDk<D3DICW^Ji}hRj=9d16kcGa?-^Q&xxT_^Nnr}7
z@eL1Rt`m5IH+@?63*%UbxjyXdJBuHf?W~-_&%U1hKAgiNT){|5;Zn@?7VhCrN#Q(p
z^s;`$T&M9IyRm=+NO*^@*oA;G%=fW=JjO??;6fL>UQ+npeF-~pq7%5<hh=aCS4#@F
wW3EdWi@EN>dtAh!lEO<2wLQ1dXf!sFKYRUM!EfPUEdT%j07*qoM6N<$f|7j1V*mgE
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..097895726fa7ed147c0c0a48e5f3e2b69afc4b97
GIT binary patch
literal 532
zc$@(g0_**WP)<h;3K|Lk000e1NJLTq000;O000;W1^@s6;CDUv00001b5ch_0Itp)
z=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_0004}Nkl<ZNXPAz
zy=zoa5XFCI_vsQ0+E^H{u~AUeLJ+|;+65wt+_AS%5Ud16w6he_h+rdDR^C?z!N0&F
zRoavi`~a)0Yh)FV&A#R3nPPp}Ac6w}cLwg9Idkq@_-}vPR1YFzwwc?34RX#ui+aX(
zUz$#R+dW{|*+NI!m{RvcNizV&Hpm-gSsrLxIU)>v1NH!Pc2VDhyA8k?Af;5ro07gy
zsrHAl24z_i5k26d?UNO#By!F>LMdwEK9tmv^fsk*xGc-{k!Wo}zHZS#(wyy^z%r0i
zO4n0L-JCNK(FWj0Q{-EcP5_INW+c6k^f)4VEf}m;KSHNAqSv;UY_9?Hz;oaZa4)6Q
zZNZ{wj%ZWTHDC?60o(>M@JQ0}KV)$lIMm$aE5Iq>DX;_#Y(J264tNI4r<CR-y`KP}
z3k;hYJOPeMx&W*I_azO0H^BLbI40@xgbeomrU7sXcm*r~cWoa57J+5k*uDz999^t@
zZ_SHQZa>ho{joWs1z;9f2R;L5ZNHP$mvqMVr=0VbMyK0teZM-4e~3w6NXvcr$L$vg
Wzl<9Q_>S)Y0000<MNUMnLSTaL{^5E6
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..a32e037f3f3bbd9ca5d937a87d1d07d83c021f93
GIT binary patch
literal 566
zc$@(?0?GY}P)<h;3K|Lk000e1NJLTq000;O000;W1^@s6;CDUv00001b5ch_0Itp)
z=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_0005WNkl<ZNXPA!
zK}b|l6o$Wlm{rKoRwstaM04p2mu+hIq!w)iH??RHxUrT}5QMpP<*snkCJ3P!H-+FP
zR}V#q$b}Kk;7U=Fhys5XZ)SQ<&wPX9&I6CjIp^}uIsgCPd*R%&SWu~*$$y8+Rg@?)
z0z`@Y7SNqolkxvqH?RjBL}Bq)K9P9e$?gq<z?a-v!7jJ}kWa~#Y)jIgL~I}EYT37U
zv}F$<jR4P+;j{vIU==8|*RlWxS_}LGkxfTo%LSEcgOxtRoW!<J*I%wqSs)585tM5M
zM2}!R3X8jz?*N<F0z0E>QoRNoeYTf?-Jnu^4O#(u5pNpco-~b)@p5&4L(+up1<+mW
z8t@2s2uv9c0QqFdzNRS#$8CO-bRTn7;wdl#DgiHm5-_ILMze5GALtr*9(xfOG>ihX
zz!P9p@C?|oW0|FXu>`NDP7v!}p~XwVE3BgNgrt7VW6MiI!^m}{Qc0Gio%90cqT#p1
z4dA216tD<PB9?`2+WsVIEgQ*hMj9kvmiT~u4|rhvCukb@46FgSC9Su06UD-4Li7Xt
z9qAVER^lCSrM_t%=miQ<SX8_30YN$YW79nK=hjI(1XWL+(5edbEdT%j07*qoM6N<$
Eg4+A=nE(I)
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..c12d50ab8d509c81a74360295aa3102ed9348967
GIT binary patch
literal 648
zc$@)<0(bq1P)<h;3K|Lk000e1NJLTq001xm001xu1^@s6R|5Hm00001b5ch_0Itp)
z=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_0006TNkl<ZXx{Ca
zO^A+B6vuzB@%<5+8Vf}z3le34m5*Y<f`$G*h7=27VKHTGWr3wxn50RK#-GxV6-^ln
zg=VERWtvGEnNm_ydU?Gz=dIh+)X2yCJh^xCoO{l__53>jb3dM^Qm@x3bDKfQTL#Dg
z86X2>fd3m{=2QVmh1#NRSF(gkC84R(C+T;kFzvC^k#Uw#2FL&zAOrk0fXBc@X#s5S
z0hR*0O9dclc>?jx_LH(5CEiP#0A7TQdTo!CO+pKRoxmkvSkfa&Tgn130{lq&6<{y$
zTv9i%q|gAiS4Z(40B!@-1mpxT2y860glvDY-DkTUSP$F*8b{kzz%yWbA&Xd256}-h
zv^{S7EpS*;S7c-vun6djCH7^}GH?{Q0gOqyC23`3r61S|49B>|z&)U~s5@~Ba2yzt
zbS@x9fnC5@j9CG677gGO@BsK8{W-ujNmrx)4mbkTqJIe3QKVJqrtST}I^by%>X39k
z>;d3<61&(efXd`QG?H3ve+s(==mw5OxC(3n-T-aDC@?Q#8-UlqX5iJIe$ij~F7!cC
zr=*3pYayvdCT0Vd!yX5El6md%90Wc#vj%1XXMur$r~+q_n%D=d3A;C$*EqG-CrR(0
zODJ`8**+cqyTHNlU$A{e(p=zk6nYJqAHapVk)4#(7WVzbzb$n66&OmE-L*|G;(RC0
i02v?yWPl7%ruGxes;h;z*R)Fj0000<MNUMnLSTa4rx4`;
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..11881bee959e181ad9e932002c1656b2b4027c6c
GIT binary patch
literal 666
zc$@*60%iS)P)<h;3K|Lk000e1NJLTq001xm001xu1^@s6R|5Hm0000PbVXQnQ*UN;
zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!JV``BRCwC#nN3JlK@`W|wGYD-6TwA6
zQ9)ovE>x^l-z{uWi=YpaKo^oOTnK_~Dg%RRS&JefB%>&Zf^=aD)}l?g4~?S4s8xhv
zsP*~{{2v@1JusB}=HeXqF=x(j;r^L(&dhx;N~hDQBwIl$;T8l00YN|z5Ckl50V{JY
z)DBE}bws~ySRaKHO0s|`ZgQo}@+smR7FS_Mp$3A0ARq`x4Z+VOx6#9}3ic#w2e`y-
zFw;4BY35g8#`hGOM8q`X2z0|{vvwYK!m#f(uom_~FT95rP_MNKrPqZRPv-v;@B#j0
z9qSG72JXQ+t@pqgnDt5kx4<n7WpE50m=|<H&cYZ}E9Tal^SKiRIlg{=Fg$}ssDel4
zC0$LtHr=UTwc<?|>W3EG3X}6E;UIiftV9CqG`|XizU;thxC2wr2b&akU1^t!+F2<)
zfpYCmD@^-z60d+Jcn9r@eT4lI=WHX~l>b>fk#_+ueV)U-=7n$ru511YPQh=@PrzRJ
zmzU_1a#3CAE*!<(4x?tN6FTI-f?H<oqEAI!E_WObz<smqQlwJ;C3CHC9chRB8r8(2
zSm^Ik|4N}RJnZtiSZUH?c4D>3^KWXQ8Ee!yX1Y~gYQWt<+mt57I>^qMnm7Vm<v%pf
z9Wo#Oqj3p{vj61E+Pd4+L#yJC8ICL7Mf@7EHTd6E=x(=)h|l`cMx8bDl9>bTsfq7a
z!T(a5&9vQZaTjqE{$>pVf`A|(2nYg_Y<~q90B4AtKUs*Y^Z)<=07*qoM6N<$f>c8&
AGXMYp
--- a/mobile/android/base/tabspanel/RemoteTabsList.java
+++ b/mobile/android/base/tabspanel/RemoteTabsList.java
@@ -14,16 +14,18 @@ import android.widget.SimpleExpandableLi
 
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.Tabs;
 import org.mozilla.gecko.TabsAccessor;
+import org.mozilla.gecko.Telemetry;
+import org.mozilla.gecko.TelemetryContract;
 
 /**
  * The actual list of synced tabs. This serves as the only child view of {@link RemoteTabsContainer}
  * so it can be refreshed using a swipe-to-refresh gesture.
  */
 class RemoteTabsList extends ExpandableListView
                      implements ExpandableListView.OnGroupClickListener,
                                 ExpandableListView.OnChildClickListener,
@@ -63,16 +65,18 @@ class RemoteTabsList extends ExpandableL
     @Override
     public boolean onChildClick(ExpandableListView parent, View view, int groupPosition, int childPosition, long id) {
         HashMap <String, String> tab = tabsList.get(groupPosition).get(childPosition);
         if (tab == null) {
             autoHidePanel();
             return true;
         }
 
+        Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, "", "remote");
+
         Tabs.getInstance().loadUrl(tab.get("url"), Tabs.LOADURL_NEW_TAB);
         autoHidePanel();
         return true;
     }
 
     @Override
     public void onQueryTabsComplete(List<TabsAccessor.RemoteTab> remoteTabsList) {
         ArrayList<TabsAccessor.RemoteTab> remoteTabs = new ArrayList<TabsAccessor.RemoteTab> (remoteTabsList);
--- a/mobile/android/base/tests/testVideoDiscovery.js
+++ b/mobile/android/base/tests/testVideoDiscovery.js
@@ -43,17 +43,23 @@ add_test(function setup_browser() {
 });
 
 let videoDiscoveryTests = [
   { id: "simple-mp4", source: "http://mochi.test:8888/simple.mp4", poster: "http://mochi.test:8888/simple.png", text: "simple video with mp4 src" },
   { id: "simple-fail", pass: false, text: "simple video with no mp4 src" },
   { id: "with-sources-mp4", source: "http://mochi.test:8888/simple.mp4", text: "video with mp4 extension source child" },
   { id: "with-sources-fail", pass: false, text: "video with no mp4 extension source child" },
   { id: "with-sources-mimetype", source: "http://mochi.test:8888/simple-video-mp4", text: "video with mp4 mimetype source child" },
-  { id: "video-overlay", source: "http://mochi.test:8888/simple.mp4", text: "div overlay covering a simple video with mp4 src" }
+  { id: "video-overlay", source: "http://mochi.test:8888/simple.mp4", text: "div overlay covering a simple video with mp4 src" },
+
+  { id: "opt-in-simple-mp4", source: "http://mochi.test:8888/simple.mp4", poster: "http://mochi.test:8888/simple.png", text: "simple video with mp4 src" },
+  { id: "opt-out-simple-mp4", pass: false, text: "simple video with mp4 src but opt-out" },
+  { id: "opt-in-simple-fail", pass: false, text: "simple video with opt-in but no mp4 src" },
+  { id: "opt-in-with-sources-mp4", source: "http://mochi.test:8888/simple.mp4", text: "opt-in video with mp4 extension source child" },
+  { id: "opt-out-with-sources-mp4", pass: false, text: "video with mp4 extension source child but opt-out" }
 ];
 
 function execute_video_test(test) {
   let element = browser.contentDocument.getElementById(test.id);
   if (element) {
     let [x, y] = middle(element);
     let video = chromeWin.CastingApps.getVideo(element, x, y);
     if (video) {
--- a/mobile/android/base/tests/video_discovery.html
+++ b/mobile/android/base/tests/video_discovery.html
@@ -51,10 +51,31 @@
 
     <!-- PASS: div overlay covers a video with mp4 src -->
     <div id="video-box">
       <div id="video-overlay"></div>
       <div>
         <video id="video-player" src="/simple.mp4"></video>
       </div>
     </div>
+
+    <!-- PASS: opt-in and src uses a mp4 extension -->
+    <video id="opt-in-simple-mp4" poster="/simple.png" src="/simple.mp4" x-webkit-airplay="allow"></video>
+
+    <!-- FAIL: opt-out and src uses a mp4 extension -->
+    <video id="opt-out-simple-mp4" poster="/simple.png" src="/simple.mp4" x-webkit-airplay="deny"></video>
+
+    <!-- FAIL: opt-in and src uses a ogg extension -->
+    <video id="opt-in-simple-fail" src="/simple.ogg" x-webkit-airplay="allow"></video>
+
+    <!-- PASS: video with opt-in and source list uses a mp4 extension -->
+    <video id="opt-in-with-sources-mp4" x-webkit-airplay="allow">
+      <source src="/simple.ogg">
+      <source src="/simple.mp4">
+    </video>
+
+    <!-- FAIL: video with opt-out and source list uses a mp4 extension -->
+    <video id="opt-out-with-sources-mp4" x-webkit-airplay="deny">
+      <source src="/simple.ogg">
+      <source src="/simple.mp4">
+    </video>
   </body>
 </html>
--- a/mobile/android/base/widget/GeckoActionProvider.java
+++ b/mobile/android/base/widget/GeckoActionProvider.java
@@ -1,15 +1,17 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.widget;
 
+import org.mozilla.gecko.Telemetry;
+import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.menu.MenuItemActionView;
 
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.graphics.drawable.Drawable;
 import android.view.MenuItem;
@@ -189,18 +191,24 @@ public class GeckoActionProvider {
             if (mOnTargetListener != null) {
                 mOnTargetListener.onTargetSelected();
             }
         }
 
         @Override
         public boolean onMenuItemClick(MenuItem item) {
             chooseActivity(item.getItemId());
+
+            // Context: Sharing via chrome mainmenu list (no explicit session is active)
+            Telemetry.sendUIEvent(TelemetryContract.Event.SHARE, TelemetryContract.Method.LIST);
             return true;
         }
 
         @Override
         public void onClick(View view) {
             Integer index = (Integer) view.getTag();
             chooseActivity(index);
+
+            // Context: Sharing via chrome mainmenu and content contextmenu quickshare (no explicit session is active)
+            Telemetry.sendUIEvent(TelemetryContract.Event.SHARE, TelemetryContract.Method.BUTTON);
         }
     }
 }
--- a/mobile/android/chrome/content/CastingApps.js
+++ b/mobile/android/chrome/content/CastingApps.js
@@ -24,19 +24,29 @@ var CastingApps = {
       Strings.browser.GetStringFromName("contextmenu.castToScreen"),
       this.filterCast,
       this.openExternal.bind(this)
     );
 
     Services.obs.addObserver(this, "Casting:Play", false);
     Services.obs.addObserver(this, "Casting:Pause", false);
     Services.obs.addObserver(this, "Casting:Stop", false);
+
+    BrowserApp.deck.addEventListener("TabSelect", this, true);
+    BrowserApp.deck.addEventListener("pageshow", this, true);
+    BrowserApp.deck.addEventListener("playing", this, true);
+    BrowserApp.deck.addEventListener("ended", this, true);
   },
 
   uninit: function ca_uninit() {
+    BrowserApp.deck.removeEventListener("TabSelect", this, true);
+    BrowserApp.deck.removeEventListener("pageshow", this, true);
+    BrowserApp.deck.removeEventListener("playing", this, true);
+    BrowserApp.deck.removeEventListener("ended", this, true);
+
     Services.obs.removeObserver(this, "Casting:Play");
     Services.obs.removeObserver(this, "Casting:Pause");
     Services.obs.removeObserver(this, "Casting:Stop");
 
     NativeWindow.contextmenus.remove(this._castMenuId);
   },
 
   isEnabled: function isEnabled() {
@@ -58,16 +68,39 @@ var CastingApps = {
       case "Casting:Stop":
         if (this.session) {
           this.closeExternal();
         }
         break;
     }
   },
 
+  handleEvent: function(aEvent) {
+    switch (aEvent.type) {
+      case "TabSelect": {
+        let tab = BrowserApp.getTabForBrowser(aEvent.target);
+        this._updatePageActionForTab(tab, aEvent);
+        break;
+      }
+      case "pageshow": {
+        let tab = BrowserApp.getTabForWindow(aEvent.originalTarget.defaultView);
+        this._updatePageActionForTab(tab, aEvent);
+        break;
+      }
+      case "playing":
+      case "ended": {
+        let video = aEvent.target;
+        if (video instanceof HTMLVideoElement) {
+          this._updatePageActionForVideo(video);
+        }
+        break;
+      }
+    }
+  },
+
   _sendEventToVideo: function _sendEventToVideo(aElement, aData) {
     let event = aElement.ownerDocument.createEvent("CustomEvent");
     event.initCustomEvent("media-videoCasting", false, true, JSON.stringify(aData));
     aElement.dispatchEvent(event);
   },
 
   handleVideoBindingAttached: function handleVideoBindingAttached(aTab, aEvent) {
     // Let's figure out if we have everything needed to cast a video. The binding
@@ -133,21 +166,27 @@ var CastingApps = {
       }
     } catch(e) {}
 
     // Could be null
     return video;
   },
 
   _getVideo: function(aElement) {
-    // Given the hardware support for H264, let's only look for 'mp4' sources
     if (!aElement instanceof HTMLVideoElement) {
       return null;
     }
 
+    // Allow websites to opt-out using the Apple airplay attribute
+    // https://developer.apple.com/library/safari/documentation/AudioVideo/Conceptual/AirPlayGuide/OptingInorOutofAirPlay/OptingInorOutofAirPlay.html
+    if (aElement.getAttribute("x-webkit-airplay") === "deny") {
+      return null;
+    }
+
+    // Given the hardware support for H264, let's only look for 'mp4' sources
     function allowableExtension(aURI) {
       if (aURI && aURI instanceof Ci.nsIURL) {
         return (aURI.fileExtension == "mp4");
       }
       return false;
     }
 
     // Grab the poster attribute from the <video>
@@ -192,16 +231,115 @@ var CastingApps = {
       let video = CastingApps.getVideo(aElement, aX, aY);
       if (CastingApps.session) {
         return (video && CastingApps.session.data.source != video.source);
       }
       return (video != null);
     }
   },
 
+  pageAction: {
+    click: function() {
+      // Since this is a pageaction, we use the selected browser
+      let browser = BrowserApp.selectedBrowser;
+      if (!browser) {
+        return;
+      }
+
+      // Look for a castable <video> that is playing, and start casting it
+      let videos = browser.contentDocument.querySelectorAll("video");
+      for (let video of videos) {
+        let unwrappedVideo = XPCNativeWrapper.unwrap(video);
+        if (!video.paused && unwrappedVideo.mozAllowCasting) {
+          CastingApps.openExternal(video, 0, 0);
+          return;
+        }
+      }
+    }
+  },
+
+  _findCastableVideo: function _findCastableVideo(aBrowser) {
+      // Scan for a <video> being actively cast. Also look for a castable <video>
+      // on the page.
+      let castableVideo = null;
+      let videos = aBrowser.contentDocument.querySelectorAll("video");
+      for (let video of videos) {
+        let unwrappedVideo = XPCNativeWrapper.unwrap(video);
+        if (unwrappedVideo.mozIsCasting) {
+          // This <video> is cast-active. Break out of loop.
+          return video;
+        }
+
+        if (!video.paused && unwrappedVideo.mozAllowCasting) {
+          // This <video> is cast-ready. Keep looking so cast-active could be found.
+          castableVideo = video;
+        }
+      }
+
+      // Could be null
+      return castableVideo;
+  },
+
+  _updatePageActionForTab: function _updatePageActionForTab(aTab, aEvent) {
+    // We only care about events on the selected tab
+    if (aTab != BrowserApp.selectedTab) {
+      return;
+    }
+
+    // Update the page action, scanning for a castable <video>
+    this._updatePageAction();
+  },
+
+  _updatePageActionForVideo: function _updatePageActionForVideo(aVideo) {
+    // If playing, send the <video>, but if ended we send nothing to shutdown the pageaction
+    this._updatePageAction(aEvent.type == "playing" ? video : null);
+  },
+
+  _updatePageAction: function _updatePageAction(aVideo) {
+    // Remove any exising pageaction first, in case state changes or we don't have
+    // a castable video
+    if (this.pageAction.id) {
+      NativeWindow.pageactions.remove(this.pageAction.id);
+      delete this.pageAction.id;
+    }
+
+    if (!aVideo) {
+      aVideo = this._findCastableVideo(BrowserApp.selectedBrowser);
+      if (!aVideo) {
+        return;
+      }
+    }
+
+    // We only show pageactions if the <video> is from the selected tab
+    if (BrowserApp.selectedTab != BrowserApp.getTabForWindow(aVideo.ownerDocument.defaultView.top)) {
+      return;
+    }
+
+    // We check for two state here:
+    // 1. The video is actively being cast
+    // 2. The video is allowed to be cast and is currently playing
+    // Both states have the same action: Show the cast page action
+    let unwrappedVideo = XPCNativeWrapper.unwrap(aVideo);
+    if (unwrappedVideo.mozIsCasting) {
+      this.pageAction.id = NativeWindow.pageactions.add({
+        title: Strings.browser.GetStringFromName("contextmenu.castToScreen"),
+        icon: "drawable://casting_active",
+        clickCallback: this.pageAction.click,
+        important: true
+      });
+    } else if (unwrappedVideo.mozAllowCasting) {
+      this.pageAction.id = NativeWindow.pageactions.add({
+        title: Strings.browser.GetStringFromName("contextmenu.castToScreen"),
+        icon: "drawable://casting",
+        clickCallback: this.pageAction.click,
+        important: true
+      });
+    }
+  },
+
   prompt: function(aCallback) {
     let items = [];
     SimpleServiceDiscovery.services.forEach(function(aService) {
       let item = {
         label: aService.friendlyName,
         selected: false
       };
       items.push(item);
@@ -277,16 +415,17 @@ var CastingApps = {
     }
 
     this.session.remoteMedia.shutdown();
     this.session.app.stop();
 
     let video = this.session.videoRef.get();
     if (video) {
       this._sendEventToVideo(video, { active: false });
+      this._updatePageAction();
     }
 
     delete this.session;
   },
 
   // RemoteMedia callback API methods
   onRemoteMediaStart: function(aRemoteMedia) {
     if (!this.session) {
@@ -294,16 +433,17 @@ var CastingApps = {
     }
 
     aRemoteMedia.load(this.session.data);
     sendMessageToJava({ type: "Casting:Started", device: this.session.service.friendlyName });
 
     let video = this.session.videoRef.get();
     if (video) {
       this._sendEventToVideo(video, { active: true });
+      this._updatePageAction(video);
     }
   },
 
   onRemoteMediaStop: function(aRemoteMedia) {
     sendMessageToJava({ type: "Casting:Stopped" });
   },
 
   onRemoteMediaStatus: function(aRemoteMedia) {
--- a/mobile/android/chrome/content/aboutReader.js
+++ b/mobile/android/chrome/content/aboutReader.js
@@ -2,16 +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/. */
 
 let Ci = Components.interfaces, Cc = Components.classes, Cu = Components.utils;
 
 Cu.import("resource://gre/modules/Services.jsm")
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "UITelemetry",
+                                  "resource://gre/modules/UITelemetry.jsm");
+
 XPCOMUtils.defineLazyGetter(window, "gChromeWin", function ()
   window.QueryInterface(Ci.nsIInterfaceRequestor)
     .getInterface(Ci.nsIWebNavigation)
     .QueryInterface(Ci.nsIDocShellTreeItem)
     .rootTreeItem
     .QueryInterface(Ci.nsIInterfaceRequestor)
     .getInterface(Ci.nsIDOMWindow)
     .QueryInterface(Ci.nsIDOMChromeWindow));
@@ -281,22 +284,28 @@ AboutReader.prototype = {
 
   _onReaderToggle: function Reader_onToggle() {
     if (!this._article)
       return;
 
     this._isReadingListItem = (this._isReadingListItem == 1) ? 0 : 1;
     this._updateToggleButton();
 
+    // Create a relative timestamp for telemetry
+    let uptime = Date.now() - Services.startup.getStartupInfo().linkerInitialized;
+
     if (this._isReadingListItem == 1) {
       gChromeWin.Reader.storeArticleInCache(this._article, function(success) {
         dump("Reader:Add (in reader) success=" + success);
 
-        let result = (success ? gChromeWin.Reader.READER_ADD_SUCCESS :
-            gChromeWin.Reader.READER_ADD_FAILED);
+        let result = gChromeWin.Reader.READER_ADD_FAILED;
+        if (success) {
+          result = gChromeWin.Reader.READER_ADD_SUCCESS;
+          UITelemetry.addEvent("save.1", "button", uptime, "reader");
+        }
 
         let json = JSON.stringify({ fromAboutReader: true, url: this._article.url });
         Services.obs.notifyObservers(null, "Reader:Add", json);
 
         gChromeWin.sendMessageToJava({
           type: "Reader:Added",
           result: result,
           title: this._article.title,
@@ -305,28 +314,34 @@ AboutReader.prototype = {
           excerpt: this._article.excerpt
         });
       }.bind(this));
     } else {
       // In addition to removing the article from the cache (handled in
       // browser.js), sending this message will cause the toggle button to be
       // updated (handled in this file).
       Services.obs.notifyObservers(null, "Reader:Remove", this._article.url);
+
+      UITelemetry.addEvent("unsave.1", "button", uptime, "reader");
     }
   },
 
   _onShare: function Reader_onShare() {
     if (!this._article)
       return;
 
     gChromeWin.sendMessageToJava({
       type: "Reader:Share",
       url: this._article.url,
       title: this._article.title
     });
+
+    // Create a relative timestamp for telemetry
+    let uptime = Date.now() - Services.startup.getStartupInfo().linkerInitialized;
+    UITelemetry.addEvent("share.1", "list", uptime);
   },
 
   _setFontSize: function Reader_setFontSize(newFontSize) {
     let bodyClasses = this._doc.body.classList;
 
     if (this._fontSize > 0)
       bodyClasses.remove("font-size" + this._fontSize);
 
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -7478,33 +7478,50 @@ let Reader = {
         type: "Reader:Click",
       });
     },
 
     readerModeActiveCallback: function(){
       sendMessageToJava({
         type: "Reader:LongClick",
       });
+
+      // Create a relative timestamp for telemetry
+      let uptime = Date.now() - Services.startup.getStartupInfo().linkerInitialized;
+      UITelemetry.addEvent("save.1", "pageaction", uptime, "reader");
     },
   },
 
   updatePageAction: function(tab) {
-    if(this.pageAction.id) {
+    if (this.pageAction.id) {
       NativeWindow.pageactions.remove(this.pageAction.id);
       delete this.pageAction.id;
     }
 
+    // Create a relative timestamp for telemetry
+    let uptime = Date.now() - Services.startup.getStartupInfo().linkerInitialized;
+
     if (tab.readerActive) {
       this.pageAction.id = NativeWindow.pageactions.add({
         title: Strings.browser.GetStringFromName("readerMode.exit"),
         icon: "drawable://reader_active",
         clickCallback: this.pageAction.readerModeCallback,
         important: true
       });
-    } else if (tab.readerEnabled) {
+
+      // Only start a reader session if the viewer is in the foreground. We do
+      // not track background reader viewers.
+      UITelemetry.startSession("reader.1", uptime);
+      return;
+    }
+
+    // Only stop a reader session if the foreground viewer is not visible.
+    UITelemetry.stopSession("reader.1", "", uptime);
+
+    if (tab.readerEnabled) {
       this.pageAction.id = NativeWindow.pageactions.add({
         title: Strings.browser.GetStringFromName("readerMode.enter"),
         icon: "drawable://reader",
         clickCallback:this.pageAction.readerModeCallback,
         longClickCallback: this.pageAction.readerModeActiveCallback,
         important: true
       });
     }
--- a/mozglue/linker/ElfLoader.h
+++ b/mozglue/linker/ElfLoader.h
@@ -83,17 +83,17 @@ template <> inline RefCounted<LibHandle,
 
 } /* namespace detail */
 } /* namespace mozilla */
 
 /**
  * Abstract class for loaded libraries. Libraries may be loaded through the
  * system linker or this linker, both cases will be derived from this class.
  */
-class LibHandle: public mozilla::AtomicRefCounted<LibHandle>
+class LibHandle: public mozilla::external::AtomicRefCounted<LibHandle>
 {
 public:
   MOZ_DECLARE_REFCOUNTED_TYPENAME(LibHandle)
   /**
    * Constructor. Takes the path of the loaded library and will store a copy
    * of the leaf name.
    */
   LibHandle(const char *path)
@@ -135,32 +135,32 @@ public:
    * Library handles can be referenced from other library handles or
    * externally (when dlopen()ing using this linker). We need to be
    * able to distinguish between the two kind of referencing for better
    * bookkeeping.
    */
   void AddDirectRef()
   {
     ++directRefCnt;
-    mozilla::AtomicRefCounted<LibHandle>::AddRef();
+    mozilla::external::AtomicRefCounted<LibHandle>::AddRef();
   }
 
   /**
    * Releases a direct reference, and returns whether there are any direct
    * references left.
    */
   bool ReleaseDirectRef()
   {
     bool ret = false;
     if (directRefCnt) {
       MOZ_ASSERT(directRefCnt <=
-                 mozilla::AtomicRefCounted<LibHandle>::refCount());
+                 mozilla::external::AtomicRefCounted<LibHandle>::refCount());
       if (--directRefCnt)
         ret = true;
-      mozilla::AtomicRefCounted<LibHandle>::Release();
+      mozilla::external::AtomicRefCounted<LibHandle>::Release();
     }
     return ret;
   }
 
   /**
    * Returns the number of direct references
    */
   MozRefCountType DirectRefCount()
--- a/mozglue/linker/Zip.h
+++ b/mozglue/linker/Zip.h
@@ -21,17 +21,17 @@ class ZipCollection;
  * Class to handle access to Zip archive streams. The Zip archive is mapped
  * in memory, and streams are direct references to that mapped memory.
  * Zip files are assumed to be correctly formed. No boundary checks are
  * performed, which means hand-crafted malicious Zip archives can make the
  * code fail in bad ways. However, since the only intended use is to load
  * libraries from Zip archives, there is no interest in making this code
  * safe, since the libraries could contain malicious code anyways.
  */
-class Zip: public mozilla::AtomicRefCounted<Zip>
+class Zip: public mozilla::external::AtomicRefCounted<Zip>
 {
 public:
   MOZ_DECLARE_REFCOUNTED_TYPENAME(Zip)
   /**
    * Create a Zip instance for the given file name. Returns nullptr in case
    * of failure.
    */
   static mozilla::TemporaryRef<Zip> Create(const char *filename);
--- a/netwerk/ipc/NeckoCommon.h
+++ b/netwerk/ipc/NeckoCommon.h
@@ -12,17 +12,17 @@
 #include "prenv.h"
 #include "nsPrintfCString.h"
 #include "mozilla/Preferences.h"
 
 namespace mozilla { namespace dom {
 class TabChild;
 }}
 
-#if defined(DEBUG) || defined(ENABLE_TESTS)
+#if defined(DEBUG)
 # define NECKO_ERRORS_ARE_FATAL_DEFAULT true
 #else
 # define NECKO_ERRORS_ARE_FATAL_DEFAULT false
 #endif 
 
 // TODO: Eventually remove NECKO_MAYBE_ABORT and DROP_DEAD (bug 575494).
 // Still useful for catching listener interfaces we don't yet support across
 // processes, etc.
--- a/netwerk/test/TestUDPSocket.cpp
+++ b/netwerk/test/TestUDPSocket.cpp
@@ -325,17 +325,20 @@ main(int32_t argc, char *argv[])
   nsCOMPtr<MulticastTimerCallback> timerCb = new MulticastTimerCallback();
 
   // The following multicast tests using multiple sockets require a firewall
   // exception on Windows XP before they pass.  For now, we'll skip them here.
   // Later versions of Windows don't seem to have this issue.
 #ifdef XP_WIN
   OSVERSIONINFO OsVersion;
   OsVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+#pragma warning(push)
+#pragma warning(disable:4996) // 'GetVersionExA': was declared deprecated
   GetVersionEx(&OsVersion);
+#pragma warning(pop)
   if (OsVersion.dwMajorVersion == 5 && OsVersion.dwMinorVersion == 1) {
     goto close;
   }
 #endif
 
   // Join multicast group
   printf("Joining multicast group\n");
   phase = TEST_MULTICAST;
--- a/netwerk/test/unit/test_ping_aboutnetworking.js
+++ b/netwerk/test/unit/test_ping_aboutnetworking.js
@@ -1,74 +1,74 @@
 /* -*- Mode: Javasript; indent-tab-mode: nil; js-indent-level: 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/. */
 
 const gDashboard = Cc['@mozilla.org/network/dashboard;1']
   .getService(Ci.nsIDashboard);
-const serverSocket = Components.classes["@mozilla.org/network/server-socket;1"]
-    .createInstance(Ci.nsIServerSocket);
+
 function connectionFailed(status) {
   let status_ok = [
                     "NS_NET_STATUS_RESOLVING_HOST"
                     ,"NS_NET_STATUS_RESOLVED_HOST"
                     ,"NS_NET_STATUS_CONNECTING_TO"
                     ,"NS_NET_STATUS_CONNECTED_TO"
                   ];
   for (let i = 0; i < status_ok.length; i++) {
     if (status == status_ok[i]) {
       return false;
     }
   }
 
   return true;
 }
 
-function test_sockets() {
+function test_sockets(serverSocket) {
   do_test_pending();
   gDashboard.requestSockets(function(data) {
     let index = -1;
+    do_print("requestSockets: " + JSON.stringify(data.sockets));
     for (let i = 0; i < data.sockets.length; i++) {
       if (data.sockets[i].host == "127.0.0.1") {
         index = i;
         break;
       }
     }
     do_check_neq(index, -1);
     do_check_eq(data.sockets[index].port, serverSocket.port);
     do_check_eq(data.sockets[index].tcp, 1);
 
-    serverSocket.close();
-
     do_test_finished();
   });
 }
 
 function run_test() {
+  let serverSocket = Components.classes["@mozilla.org/network/server-socket;1"]
+    .createInstance(Ci.nsIServerSocket);
   serverSocket.init(-1, true, -1);
 
   do_test_pending();
   gDashboard.requestConnection("localhost", serverSocket.port,
                                "tcp", 15, function(connInfo) {
     if (connInfo.status == "NS_NET_STATUS_CONNECTED_TO") {
       do_test_pending();
       gDashboard.requestDNSInfo(function(data) {
         let found = false;
+        do_print("requestDNSInfo: " + JSON.stringify(data.entries));
         for (let i = 0; i < data.entries.length; i++) {
           if (data.entries[i].hostname == "localhost") {
             found = true;
             break;
           }
         }
         do_check_eq(found, true);
 
         do_test_finished();
-
-        test_sockets();
+        test_sockets(serverSocket);
       });
 
       do_test_finished();
     }
     if (connectionFailed(connInfo.status)) {
       do_throw(connInfo.status);
     }
   });
--- a/security/apps/AppSignatureVerification.cpp
+++ b/security/apps/AppSignatureVerification.cpp
@@ -598,18 +598,19 @@ VerifySignature(AppTrustedRoot trustedRo
     return NS_ERROR_CMS_VERIFY_ERROR_PROCESSING;
   }
 
   // Verify certificate.
   AppTrustDomain trustDomain(nullptr); // TODO: null pinArg
   if (trustDomain.SetTrustedRoot(trustedRoot) != SECSuccess) {
     return MapSECStatus(SECFailure);
   }
-  if (BuildCertChain(trustDomain, signerCert, PR_Now(), MustBeEndEntity,
-                     KU_DIGITAL_SIGNATURE, SEC_OID_EXT_KEY_USAGE_CODE_SIGN,
+  if (BuildCertChain(trustDomain, signerCert, PR_Now(),
+                     EndEntityOrCA::MustBeEndEntity, KU_DIGITAL_SIGNATURE,
+                     SEC_OID_EXT_KEY_USAGE_CODE_SIGN,
                      SEC_OID_X509_ANY_POLICY, nullptr, builtChain)
         != SECSuccess) {
     return MapSECStatus(SECFailure);
   }
 
   // See NSS_CMSContentInfo_GetContentTypeOID, which isn't exported from NSS.
   SECOidData* contentTypeOidData =
     SECOID_FindOID(&signedData->contentInfo.contentType);
--- a/security/apps/AppTrustDomain.cpp
+++ b/security/apps/AppTrustDomain.cpp
@@ -125,47 +125,47 @@ AppTrustDomain::GetCertTrust(EndEntityOr
   if (CERT_GetCertTrust(candidateCert, &trust) == SECSuccess) {
     PRUint32 flags = SEC_GET_TRUST_FLAGS(&trust, trustObjectSigning);
 
     // For DISTRUST, we use the CERTDB_TRUSTED or CERTDB_TRUSTED_CA bit,
     // because we can have active distrust for either type of cert. Note that
     // CERTDB_TERMINAL_RECORD means "stop trying to inherit trust" so if the
     // relevant trust bit isn't set then that means the cert must be considered
     // distrusted.
-    PRUint32 relevantTrustBit = endEntityOrCA == MustBeCA
+    PRUint32 relevantTrustBit = endEntityOrCA == EndEntityOrCA::MustBeCA
                               ? CERTDB_TRUSTED_CA
                               : CERTDB_TRUSTED;
     if (((flags & (relevantTrustBit | CERTDB_TERMINAL_RECORD)))
             == CERTDB_TERMINAL_RECORD) {
-      *trustLevel = ActivelyDistrusted;
+      *trustLevel = TrustLevel::ActivelyDistrusted;
       return SECSuccess;
     }
 
 #ifdef MOZ_B2G_CERTDATA
     // XXX(Bug 972201): We have to allow the old way of supporting additional
     // roots until we fix bug 889744. Remove this along with the rest of the
     // MOZ_B2G_CERTDATA stuff.
 
     // For TRUST, we only use the CERTDB_TRUSTED_CA bit, because Gecko hasn't
     // needed to consider end-entity certs to be their own trust anchors since
     // Gecko implemented nsICertOverrideService.
     if (flags & CERTDB_TRUSTED_CA) {
-      *trustLevel = TrustAnchor;
+      *trustLevel = TrustLevel::TrustAnchor;
       return SECSuccess;
     }
 #endif
   }
 
   // mTrustedRoot is the only trust anchor for this validation.
   if (CERT_CompareCerts(mTrustedRoot.get(), candidateCert)) {
-    *trustLevel = TrustAnchor;
+    *trustLevel = TrustLevel::TrustAnchor;
     return SECSuccess;
   }
 
-  *trustLevel = InheritsTrust;
+  *trustLevel = TrustLevel::InheritsTrust;
   return SECSuccess;
 }
 
 SECStatus
 AppTrustDomain::VerifySignedData(const CERTSignedData* signedData,
                                   const CERTCertificate* cert)
 {
   return ::mozilla::pkix::VerifySignedData(signedData, cert, mPinArg);
--- a/security/apps/AppTrustDomain.h
+++ b/security/apps/AppTrustDomain.h
@@ -18,17 +18,17 @@ class AppTrustDomain MOZ_FINAL : public 
 public:
   AppTrustDomain(void* pinArg);
 
   SECStatus SetTrustedRoot(AppTrustedRoot trustedRoot);
 
   SECStatus GetCertTrust(mozilla::pkix::EndEntityOrCA endEntityOrCA,
                          SECOidTag policy,
                          const CERTCertificate* candidateCert,
-                 /*out*/ TrustLevel* trustLevel) MOZ_OVERRIDE;
+                 /*out*/ mozilla::pkix::TrustLevel* trustLevel) MOZ_OVERRIDE;
   SECStatus FindPotentialIssuers(const SECItem* encodedIssuerName,
                                  PRTime time,
                          /*out*/ mozilla::pkix::ScopedCERTCertList& results)
                                  MOZ_OVERRIDE;
   SECStatus VerifySignedData(const CERTSignedData* signedData,
                              const CERTCertificate* cert) MOZ_OVERRIDE;
   SECStatus CheckRevocation(mozilla::pkix::EndEntityOrCA endEntityOrCA,
                             const CERTCertificate* cert,
--- a/security/certverifier/CertVerifier.cpp
+++ b/security/certverifier/CertVerifier.cpp
@@ -303,29 +303,32 @@ BuildCertChainForOneKeyUsage(TrustDomain
                              KeyUsages ku3, SECOidTag eku,
                              SECOidTag requiredPolicy,
                              const SECItem* stapledOCSPResponse,
                              ScopedCERTCertList& builtChain)
 {
   PR_ASSERT(ku1);
   PR_ASSERT(ku2);
 
-  SECStatus rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity,
-                                ku1, eku, requiredPolicy, stapledOCSPResponse,
-                                builtChain);
+  SECStatus rv = BuildCertChain(trustDomain, cert, time,
+                                EndEntityOrCA::MustBeEndEntity, ku1,
+                                eku, requiredPolicy,
+                                stapledOCSPResponse, builtChain);
   if (rv != SECSuccess && ku2 &&
       PR_GetError() == SEC_ERROR_INADEQUATE_KEY_USAGE) {
-    rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity,
-                        ku2, eku, requiredPolicy, stapledOCSPResponse,
-                        builtChain);
+    rv = BuildCertChain(trustDomain, cert, time,
+                        EndEntityOrCA::MustBeEndEntity, ku2,
+                        eku, requiredPolicy,
+                        stapledOCSPResponse, builtChain);
     if (rv != SECSuccess && ku3 &&
         PR_GetError() == SEC_ERROR_INADEQUATE_KEY_USAGE) {
-      rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity,
-                          ku3, eku, requiredPolicy, stapledOCSPResponse,
-                          builtChain);
+      rv = BuildCertChain(trustDomain, cert, time,
+                          EndEntityOrCA::MustBeEndEntity, ku3,
+                          eku, requiredPolicy,
+                          stapledOCSPResponse, builtChain);
       if (rv != SECSuccess) {
         PR_SetError(SEC_ERROR_INADEQUATE_KEY_USAGE, 0);
       }
     }
   }
   return rv;
 }
 
@@ -381,18 +384,18 @@ CertVerifier::MozillaPKIXVerifyCert(
 
   mozilla::pkix::ScopedCERTCertList builtChain;
   switch (usage) {
     case certificateUsageSSLClient: {
       // XXX: We don't really have a trust bit for SSL client authentication so
       // just use trustEmail as it is the closest alternative.
       NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache,
                                        pinArg);
-      rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity,
-                          KU_DIGITAL_SIGNATURE,
+      rv = BuildCertChain(trustDomain, cert, time,
+                          EndEntityOrCA::MustBeEndEntity, KU_DIGITAL_SIGNATURE,
                           SEC_OID_EXT_KEY_USAGE_CLIENT_AUTH,
                           SEC_OID_X509_ANY_POLICY,
                           stapledOCSPResponse, builtChain);
       break;
     }
 
     case certificateUsageSSLServer: {
       // TODO: When verifying a certificate in an SSL handshake, we should
@@ -444,29 +447,29 @@ CertVerifier::MozillaPKIXVerifyCert(
                                         SEC_OID_X509_ANY_POLICY,
                                         stapledOCSPResponse, builtChain);
       break;
     }
 
     case certificateUsageSSLCA: {
       NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching, mOCSPCache,
                                        pinArg);
-      rv = BuildCertChain(trustDomain, cert, time, MustBeCA,
+      rv = BuildCertChain(trustDomain, cert, time, EndEntityOrCA::MustBeCA,
                           KU_KEY_CERT_SIGN,
                           SEC_OID_EXT_KEY_USAGE_SERVER_AUTH,
                           SEC_OID_X509_ANY_POLICY,
                           stapledOCSPResponse, builtChain);
       break;
     }
 
     case certificateUsageEmailSigner: {
       NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache,
                                        pinArg);
-      rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity,
-                          KU_DIGITAL_SIGNATURE,
+      rv = BuildCertChain(trustDomain, cert, time,
+                          EndEntityOrCA::MustBeEndEntity, KU_DIGITAL_SIGNATURE,
                           SEC_OID_EXT_KEY_USAGE_EMAIL_PROTECT,
                           SEC_OID_X509_ANY_POLICY,
                           stapledOCSPResponse, builtChain);
       break;
     }
 
     case certificateUsageEmailRecipient: {
       // TODO: The higher level S/MIME processing should pass in which key
@@ -482,39 +485,39 @@ CertVerifier::MozillaPKIXVerifyCert(
                                         SEC_OID_X509_ANY_POLICY,
                                         stapledOCSPResponse, builtChain);
       break;
     }
 
     case certificateUsageObjectSigner: {
       NSSCertDBTrustDomain trustDomain(trustObjectSigning, ocspFetching,
                                        mOCSPCache, pinArg);
-      rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity,
-                          KU_DIGITAL_SIGNATURE,
+      rv = BuildCertChain(trustDomain, cert, time,
+                          EndEntityOrCA::MustBeEndEntity, KU_DIGITAL_SIGNATURE,
                           SEC_OID_EXT_KEY_USAGE_CODE_SIGN,
                           SEC_OID_X509_ANY_POLICY,
                           stapledOCSPResponse, builtChain);
       break;
     }
 
     case certificateUsageVerifyCA:
     case certificateUsageStatusResponder: {
       // XXX This is a pretty useless way to verify a certificate. It is used
       // by the implementation of window.crypto.importCertificates and in the
       // certificate viewer UI. Because we don't know what trust bit is
       // interesting, we just try them all.
       mozilla::pkix::EndEntityOrCA endEntityOrCA;
       mozilla::pkix::KeyUsages keyUsage;
       SECOidTag eku;
       if (usage == certificateUsageVerifyCA) {
-        endEntityOrCA = MustBeCA;
+        endEntityOrCA = EndEntityOrCA::MustBeCA;
         keyUsage = KU_KEY_CERT_SIGN;
         eku = SEC_OID_UNKNOWN;
       } else {
-        endEntityOrCA = MustBeEndEntity;
+        endEntityOrCA = EndEntityOrCA::MustBeEndEntity;
         keyUsage = KU_DIGITAL_SIGNATURE;
         eku = SEC_OID_OCSP_RESPONDER;
       }
 
       NSSCertDBTrustDomain sslTrust(trustSSL, ocspFetching, mOCSPCache,
                                     pinArg);
       rv = BuildCertChain(sslTrust, cert, time, endEntityOrCA,
                           keyUsage, eku, SEC_OID_X509_ANY_POLICY,
--- a/security/certverifier/NSSCertDBTrustDomain.cpp
+++ b/security/certverifier/NSSCertDBTrustDomain.cpp
@@ -4,22 +4,23 @@
  * 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 "NSSCertDBTrustDomain.h"
 
 #include <stdint.h>
 
 #include "ExtendedValidation.h"
+#include "OCSPRequestor.h"
 #include "certdb.h"
-#include "pkix/pkix.h"
 #include "mozilla/Telemetry.h"
 #include "nss.h"
 #include "ocsp.h"
 #include "pk11pub.h"
+#include "pkix/pkix.h"
 #include "prerror.h"
 #include "prmem.h"
 #include "prprf.h"
 #include "secerr.h"
 #include "secmod.h"
 
 using namespace mozilla::pkix;
 
@@ -95,52 +96,73 @@ NSSCertDBTrustDomain::GetCertTrust(EndEn
   if (CERT_GetCertTrust(candidateCert, &trust) == SECSuccess) {
     PRUint32 flags = SEC_GET_TRUST_FLAGS(&trust, mCertDBTrustType);
 
     // For DISTRUST, we use the CERTDB_TRUSTED or CERTDB_TRUSTED_CA bit,
     // because we can have active distrust for either type of cert. Note that
     // CERTDB_TERMINAL_RECORD means "stop trying to inherit trust" so if the
     // relevant trust bit isn't set then that means the cert must be considered
     // distrusted.
-    PRUint32 relevantTrustBit = endEntityOrCA == MustBeCA ? CERTDB_TRUSTED_CA
-                                                          : CERTDB_TRUSTED;
+    PRUint32 relevantTrustBit =
+      endEntityOrCA == EndEntityOrCA::MustBeCA ? CERTDB_TRUSTED_CA
+                                               : CERTDB_TRUSTED;
     if (((flags & (relevantTrustBit|CERTDB_TERMINAL_RECORD)))
             == CERTDB_TERMINAL_RECORD) {
-      *trustLevel = ActivelyDistrusted;
+      *trustLevel = TrustLevel::ActivelyDistrusted;
       return SECSuccess;
     }
 
     // For TRUST, we only use the CERTDB_TRUSTED_CA bit, because Gecko hasn't
     // needed to consider end-entity certs to be their own trust anchors since
     // Gecko implemented nsICertOverrideService.
     if (flags & CERTDB_TRUSTED_CA) {
       if (policy == SEC_OID_X509_ANY_POLICY) {
-        *trustLevel = TrustAnchor;
+        *trustLevel = TrustLevel::TrustAnchor;
         return SECSuccess;
       }
 #ifndef MOZ_NO_EV_CERTS
       if (CertIsAuthoritativeForEVPolicy(candidateCert, policy)) {
-        *trustLevel = TrustAnchor;
+        *trustLevel = TrustLevel::TrustAnchor;
         return SECSuccess;
       }
 #endif
     }
   }
 
-  *trustLevel = InheritsTrust;
+  *trustLevel = TrustLevel::InheritsTrust;
   return SECSuccess;
 }
 
 SECStatus
 NSSCertDBTrustDomain::VerifySignedData(const CERTSignedData* signedData,
                                        const CERTCertificate* cert)
 {
   return ::mozilla::pkix::VerifySignedData(signedData, cert, mPinArg);
 }
 
+static PRIntervalTime
+OCSPFetchingTypeToTimeoutTime(NSSCertDBTrustDomain::OCSPFetching ocspFetching)
+{
+  switch (ocspFetching) {
+    case NSSCertDBTrustDomain::FetchOCSPForDVSoftFail:
+      return PR_SecondsToInterval(2);
+    case NSSCertDBTrustDomain::FetchOCSPForEV:
+    case NSSCertDBTrustDomain::FetchOCSPForDVHardFail:
+      return PR_SecondsToInterval(10);
+    // The rest of these are error cases. Assert in debug builds, but return
+    // the default value corresponding to 2 seconds in release builds.
+    case NSSCertDBTrustDomain::NeverFetchOCSP:
+    case NSSCertDBTrustDomain::LocalOnlyOCSPForEV:
+      PR_NOT_REACHED("we should never see this OCSPFetching type here");
+    default:
+      PR_NOT_REACHED("we're not handling every OCSPFetching type");
+  }
+  return PR_SecondsToInterval(2);
+}
+
 SECStatus
 NSSCertDBTrustDomain::CheckRevocation(
   mozilla::pkix::EndEntityOrCA endEntityOrCA,
   const CERTCertificate* cert,
   /*const*/ CERTCertificate* issuerCert,
   PRTime time,
   /*optional*/ const SECItem* stapledOCSPResponse)
 {
@@ -160,17 +182,17 @@ NSSCertDBTrustDomain::CheckRevocation(
     return SECFailure;
   }
 
   // If we have a stapled OCSP response then the verification of that response
   // determines the result unless the OCSP response is expired. We make an
   // exception for expired responses because some servers, nginx in particular,
   // are known to serve expired responses due to bugs.
   if (stapledOCSPResponse) {
-    PR_ASSERT(endEntityOrCA == MustBeEndEntity);
+    PR_ASSERT(endEntityOrCA == EndEntityOrCA::MustBeEndEntity);
     SECStatus rv = VerifyAndMaybeCacheEncodedOCSPResponse(cert, issuerCert,
                                                           time,
                                                           stapledOCSPResponse,
                                                           ResponseWasStapled);
     if (rv == SECSuccess) {
       // stapled OCSP response present and good
       Telemetry::Accumulate(Telemetry::SSL_OCSP_STAPLING, 1);
       PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
@@ -245,18 +267,19 @@ NSSCertDBTrustDomain::CheckRevocation(
             (cachedResponsePresent && cachedResponseErrorCode != 0));
 
   // TODO: We still need to handle the fallback for expired responses. But,
   // if/when we disable OCSP fetching by default, it would be ambiguous whether
   // security.OCSP.enable==0 means "I want the default" or "I really never want
   // you to ever fetch OCSP."
 
   if ((mOCSPFetching == NeverFetchOCSP) ||
-      (endEntityOrCA == MustBeCA && (mOCSPFetching == FetchOCSPForDVHardFail ||
-                                     mOCSPFetching == FetchOCSPForDVSoftFail))) {
+      (endEntityOrCA == EndEntityOrCA::MustBeCA &&
+       (mOCSPFetching == FetchOCSPForDVHardFail ||
+        mOCSPFetching == FetchOCSPForDVSoftFail))) {
     // We're not going to be doing any fetching, so if there was a cached
     // "unknown" response, say so.
     if (cachedResponseErrorCode == SEC_ERROR_OCSP_UNKNOWN_CERT) {
       PR_SetError(SEC_ERROR_OCSP_UNKNOWN_CERT, 0);
       return SECFailure;
     }
     // If we're doing hard-fail, we want to know if we have a cached response
     // that has expired.
@@ -309,46 +332,47 @@ NSSCertDBTrustDomain::CheckRevocation(
       cachedResponseErrorCode == SEC_ERROR_OCSP_UNKNOWN_CERT ||
       cachedResponseErrorCode == SEC_ERROR_OCSP_OLD_RESPONSE) {
     const SECItem* request(CreateEncodedOCSPRequest(arena.get(), cert,
                                                     issuerCert));
     if (!request) {
       return SECFailure;
     }
 
-    response = CERT_PostOCSPRequest(arena.get(), url.get(), request);
+    response = DoOCSPRequest(arena.get(), url.get(), request,
+                             OCSPFetchingTypeToTimeoutTime(mOCSPFetching));
   }
 
   if (!response) {
     PRErrorCode error = PR_GetError();
     if (error == 0) {
       error = cachedResponseErrorCode;
     }
     PRTime timeout = time + ServerFailureDelay;
     if (mOCSPCache.Put(cert, issuerCert, error, time, timeout) != SECSuccess) {
       return SECFailure;
     }
     PR_SetError(error, 0);
     if (mOCSPFetching != FetchOCSPForDVSoftFail) {
       PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
              ("NSSCertDBTrustDomain: returning SECFailure after "
-              "CERT_PostOCSPRequest failure"));
+              "OCSP request failure"));
       return SECFailure;
     }
     if (cachedResponseErrorCode == SEC_ERROR_OCSP_UNKNOWN_CERT) {
       PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
              ("NSSCertDBTrustDomain: returning SECFailure from cached "
-              "response after CERT_PostOCSPRequest failure"));
+              "response after OCSP request failure"));
       PR_SetError(cachedResponseErrorCode, 0);
       return SECFailure;
     }
 
     PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
            ("NSSCertDBTrustDomain: returning SECSuccess after "
-            "CERT_PostOCSPRequest failure"));
+            "OCSP request failure"));
     return SECSuccess; // Soft fail -> success :(
   }
 
   SECStatus rv = VerifyAndMaybeCacheEncodedOCSPResponse(cert, issuerCert, time,
                                                         response,
                                                         ResponseIsFromNetwork);
   if (rv == SECSuccess || mOCSPFetching != FetchOCSPForDVSoftFail) {
     PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
--- a/security/certverifier/NSSCertDBTrustDomain.h
+++ b/security/certverifier/NSSCertDBTrustDomain.h
@@ -63,17 +63,17 @@ public:
   virtual SECStatus FindPotentialIssuers(
                         const SECItem* encodedIssuerName,
                         PRTime time,
                 /*out*/ mozilla::pkix::ScopedCERTCertList& results);
 
   virtual SECStatus GetCertTrust(mozilla::pkix::EndEntityOrCA endEntityOrCA,
                                  SECOidTag policy,
                                  const CERTCertificate* candidateCert,
-                         /*out*/ TrustLevel* trustLevel);
+                         /*out*/ mozilla::pkix::TrustLevel* trustLevel);
 
   virtual SECStatus VerifySignedData(const CERTSignedData* signedData,
                                      const CERTCertificate* cert);
 
   virtual SECStatus CheckRevocation(mozilla::pkix::EndEntityOrCA endEntityOrCA,
                                     const CERTCertificate* cert,
                           /*const*/ CERTCertificate* issuerCert,
                                     PRTime time,
new file mode 100644
--- /dev/null
+++ b/security/certverifier/OCSPRequestor.cpp
@@ -0,0 +1,130 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "OCSPRequestor.h"
+
+#include "nsIURLParser.h"
+#include "nsNSSCallbacks.h"
+#include "nsNetCID.h"
+#include "nsServiceManagerUtils.h"
+#include "pkix/ScopedPtr.h"
+#include "secerr.h"
+
+namespace mozilla { namespace psm {
+
+using mozilla::pkix::ScopedPtr;
+
+void
+ReleaseHttpServerSession(nsNSSHttpServerSession* httpServerSession)
+{
+  delete httpServerSession;
+}
+typedef ScopedPtr<nsNSSHttpServerSession, ReleaseHttpServerSession>
+  ScopedHTTPServerSession;
+
+void
+ReleaseHttpRequestSession(nsNSSHttpRequestSession* httpRequestSession)
+{
+  httpRequestSession->Release();
+}
+typedef ScopedPtr<nsNSSHttpRequestSession, ReleaseHttpRequestSession>
+  ScopedHTTPRequestSession;
+
+SECItem* DoOCSPRequest(PLArenaPool* arena, const char* url,
+                       const SECItem* encodedRequest, PRIntervalTime timeout)
+{
+  nsCOMPtr<nsIURLParser> urlParser = do_GetService(NS_STDURLPARSER_CONTRACTID);
+  if (!urlParser) {
+    PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0);
+    return nullptr;
+  }
+
+  uint32_t schemePos;
+  int32_t schemeLen;
+  uint32_t authorityPos;
+  int32_t authorityLen;
+  uint32_t pathPos;
+  int32_t pathLen;
+  nsresult rv = urlParser->ParseURL(url, PL_strlen(url),
+                                    &schemePos, &schemeLen,
+                                    &authorityPos, &authorityLen,
+                                    &pathPos, &pathLen);
+  if (NS_FAILED(rv)) {
+    PR_SetError(SEC_ERROR_CERT_BAD_ACCESS_LOCATION, 0);
+    return nullptr;
+  }
+  uint32_t hostnamePos;
+  int32_t hostnameLen;
+  int32_t port;
+  rv = urlParser->ParseAuthority(url + authorityPos, authorityLen,
+                                 nullptr, nullptr, nullptr, nullptr,
+                                 &hostnamePos, &hostnameLen, &port);
+  if (NS_FAILED(rv)) {
+    PR_SetError(SEC_ERROR_CERT_BAD_ACCESS_LOCATION, 0);
+    return nullptr;
+  }
+  if (port == -1) {
+    port = 80;
+  }
+
+  nsAutoCString hostname(url + authorityPos + hostnamePos, hostnameLen);
+  SEC_HTTP_SERVER_SESSION serverSessionPtr = nullptr;
+  if (nsNSSHttpInterface::createSessionFcn(hostname.BeginReading(), port,
+                                           &serverSessionPtr) != SECSuccess) {
+    PR_SetError(SEC_ERROR_NO_MEMORY, 0);
+    return nullptr;
+  }
+
+  ScopedHTTPServerSession serverSession(
+    reinterpret_cast<nsNSSHttpServerSession*>(serverSessionPtr));
+  nsAutoCString path(url + pathPos, pathLen);
+  SEC_HTTP_REQUEST_SESSION requestSessionPtr;
+  if (nsNSSHttpInterface::createFcn(serverSession.get(), "http",
+                                    path.BeginReading(), "POST",
+                                    timeout, &requestSessionPtr)
+        != SECSuccess) {
+    PR_SetError(SEC_ERROR_NO_MEMORY, 0);
+    return nullptr;
+  }
+
+  ScopedHTTPRequestSession requestSession(
+    reinterpret_cast<nsNSSHttpRequestSession*>(requestSessionPtr));
+  if (nsNSSHttpInterface::setPostDataFcn(requestSession.get(),
+        reinterpret_cast<char*>(encodedRequest->data), encodedRequest->len,
+        "application/ocsp-request") != SECSuccess) {
+    PR_SetError(SEC_ERROR_NO_MEMORY, 0);
+    return nullptr;
+  }
+
+  uint16_t httpResponseCode;
+  const char* httpResponseData;
+  uint32_t httpResponseDataLen = 0; // 0 means any response size is acceptable
+  if (nsNSSHttpInterface::trySendAndReceiveFcn(requestSession.get(), nullptr,
+                                               &httpResponseCode, nullptr,
+                                               nullptr, &httpResponseData,
+                                               &httpResponseDataLen)
+        != SECSuccess) {
+    PR_SetError(SEC_ERROR_OCSP_SERVER_ERROR, 0);
+    return nullptr;
+  }
+
+  if (httpResponseCode != 200) {
+    PR_SetError(SEC_ERROR_OCSP_SERVER_ERROR, 0);
+    return nullptr;
+  }
+
+  SECItem* encodedResponse = SECITEM_AllocItem(arena, nullptr,
+                                               httpResponseDataLen);
+  if (!encodedResponse) {
+    PR_SetError(SEC_ERROR_NO_MEMORY, 0);
+    return nullptr;
+  }
+
+  memcpy(encodedResponse->data, httpResponseData, httpResponseDataLen);
+  return encodedResponse;
+}
+
+} } // namespace mozilla::psm
new file mode 100644
--- /dev/null
+++ b/security/certverifier/OCSPRequestor.h
@@ -0,0 +1,20 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_psm_OCSPRequestor_h
+#define mozilla_psm_OCSPRequestor_h
+
+#include "secmodt.h"
+
+namespace mozilla { namespace psm {
+
+// The memory returned is owned by the given arena.
+SECItem* DoOCSPRequest(PLArenaPool* arena, const char* url,
+                       const SECItem* encodedRequest, PRIntervalTime timeout);
+
+} } // namespace mozilla::psm
+
+#endif // mozilla_psm_OCSPRequestor_h
--- a/security/certverifier/moz.build
+++ b/security/certverifier/moz.build
@@ -3,25 +3,27 @@
 # 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/.
 
 UNIFIED_SOURCES += [
     'CertVerifier.cpp',
     'NSSCertDBTrustDomain.cpp',
     'OCSPCache.cpp',
+    'OCSPRequestor.cpp',
 ]
 
 if not CONFIG['NSS_NO_EV_CERTS']:
     UNIFIED_SOURCES += [
         'ExtendedValidation.cpp',
     ]
 
 LOCAL_INCLUDES += [
     '../manager/boot/src',
+    '../manager/ssl/src',
     '../pkix/include',
 ]
 
 DIRS += [
     '../pkix',
 ]
 
 FAIL_ON_WARNINGS = True
--- a/security/manager/boot/src/StaticHPKPins.h
+++ b/security/manager/boot/src/StaticHPKPins.h
@@ -1,15 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /*****************************************************************************/
 /* This is an automatically generated file. If you're not                    */
-/* PublicKeyPinningSerice.cpp, you shouldn't be #including it.               */
+/* PublicKeyPinningService.cpp, you shouldn't be #including it.              */
 /*****************************************************************************/
 #include <stdint.h>
 /* Baltimore CyberTrust Root */
 static const char kBaltimore_CyberTrust_RootFingerprint[]=
   "Y9mvm0exBk1JoQ57f9Vm28jKo5lFm/woKcVxrYxu80o=";
 
 /* DigiCert Assured ID Root CA */
 static const char kDigiCert_Assured_ID_Root_CAFingerprint[]=
@@ -197,9 +197,9 @@ static const TransportSecurityPreload kP
   { "cdn.mozilla.org",	true,	&kPinSet_mozilla_cdn },
   { "exclude-subdomains.pinning.example.com",	false,	&kPinSet_mozilla_test },
   { "include-subdomains.pinning.example.com",	true,	&kPinSet_mozilla_test },
   { "media.mozilla.com",	true,	&kPinSet_mozilla_cdn },
 };
 
 static const int kPublicKeyPinningPreloadListLength = 7;
 
-const PRTime kPreloadPKPinsExpirationTime = INT64_C(1409782406553000);
+const PRTime kPreloadPKPinsExpirationTime = INT64_C(1409867186821000);
deleted file mode 100644
index 6874346226edccf7133f7f249ead3b1c39c318be..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ocsp_timeout.js
@@ -0,0 +1,58 @@
+// -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+"use strict";
+
+function run_test() {
+  do_get_profile();
+
+  add_tls_server_setup("OCSPStaplingServer");
+
+  let socket = Cc["@mozilla.org/network/server-socket;1"]
+                 .createInstance(Ci.nsIServerSocket);
+  socket.init(8080, true, -1);
+
+
+  add_tests_in_mode(true, true);
+  add_tests_in_mode(false, true);
+  add_tests_in_mode(true, false);
+  add_tests_in_mode(false, false);
+
+  add_test(function() { socket.close(); run_next_test(); });
+  run_next_test();
+}
+
+function add_tests_in_mode(useMozillaPKIX, useHardFail) {
+  let startTime;
+  add_test(function () {
+    Services.prefs.setBoolPref("security.use_mozillapkix_verification",
+                               useMozillaPKIX);
+    Services.prefs.setBoolPref("security.OCSP.require", useHardFail);
+    startTime = new Date();
+    run_next_test();
+  });
+
+  add_connection_test("ocsp-stapling-none.example.com", useHardFail
+                      ? getXPCOMStatusFromNSS(SEC_ERROR_OCSP_SERVER_ERROR)
+                      : Cr.NS_OK, clearSessionCache);
+
+  // Reset state
+  add_test(function() {
+    let endTime = new Date();
+    // With OCSP hard-fail on, we timeout after 10 seconds.
+    // With OCSP soft-fail, we timeout after 2 seconds.
+    if (useHardFail) {
+      do_check_true((endTime - startTime) > 10000);
+    } else {
+      do_check_true((endTime - startTime) > 2000);
+    }
+    // Make sure we didn't wait too long.
+    // (Unfortunately, we probably can't have a tight upper bound on
+    // how long is too long for this test, because we might be running
+    // on slow hardware.)
+    do_check_true((endTime - startTime) < 60000);
+    clearOCSPCache();
+    run_next_test();
+  });
+}
--- a/security/manager/ssl/tests/unit/xpcshell.ini
+++ b/security/manager/ssl/tests/unit/xpcshell.ini
@@ -42,16 +42,20 @@ fail-if = os == "android" || buildapp ==
 [test_ocsp_caching.js]
 run-sequentially = hardcoded ports
 # Bug 676972: test fails consistently on Android and B2G
 fail-if = os == "android" || buildapp == "b2g"
 [test_ocsp_required.js]
 run-sequentially = hardcoded ports
 # Bug 676972: test fails consistently on Android and B2G
 fail-if = os == "android" || buildapp == "b2g"
+[test_ocsp_timeout.js]
+run-sequentially = hardcoded ports
+# Bug 676972: test fails consistently on Android and B2G
+fail-if = os == "android" || buildapp == "b2g"
 [test_cert_signatures.js]
 [test_ev_certs.js]
 # Bug 676972: test fails consistently on Android and B2G
 fail-if = os == "android" || buildapp == "b2g"
 [test_getchain.js]
 [test_cert_overrides.js]
 run-sequentially = hardcoded ports
 # Bug 676972: test fails consistently on Android and B2G
rename from security/manager/boot/src/PreloadedHPKPins.json
rename to security/manager/tools/PreloadedHPKPins.json
rename from security/manager/boot/src/genHPKPStaticPins.js
rename to security/manager/tools/genHPKPStaticPins.js
--- a/security/manager/boot/src/genHPKPStaticPins.js
+++ b/security/manager/tools/genHPKPStaticPins.js
@@ -1,80 +1,76 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // How to run this file:
 // 1. [obtain firefox source code]
 // 2. [build/obtain firefox binaries]
 // 3. run `[path to]/run-mozilla.sh [path to]/xpcshell \
-//                                  [path to]/genHPKPStaticpins.js
-// Files PreloadedHPKPins.json and default-ee.der must be in the current
-// working directory.
+//                                  [path to]/genHPKPStaticpins.js \
+//                                  [absolute path to]/PreloadedHPKPins.json \
+//                                  [absolute path to]/default-ee.der \
+//                                  [absolute path to]/StaticHPKPins.h
+
+if (arguments.length != 3) {
+  throw "Usage: genHPKPins.js <absolute path to PreloadedHPKPins.json> " +
+        "<absolute path to default-ee.der> " +
+        "<absolute path to StaticHPKPins.h>";
+}
 
 const { 'classes': Cc, 'interfaces': Ci, 'utils': Cu, 'results': Cr } = Components;
 
 let { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {});
 let { FileUtils } = Cu.import("resource://gre/modules/FileUtils.jsm", {});
 let { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
 
 const certdb2 = Cc["@mozilla.org/security/x509certdb;1"]
                    .getService(Ci.nsIX509CertDB2);
 
-const OUTPUT = "StaticHPKPins.h";
-const MOZINPUT = "PreloadedHPKPins.json";
+// Pins expire in 18 weeks
 const PINNING_MINIMUM_REQUIRED_MAX_AGE = 60 * 60 * 24 * 7 * 18;
 const FILE_HEADER = "/* This Source Code Form is subject to the terms of the Mozilla Public\n" +
 " * License, v. 2.0. If a copy of the MPL was not distributed with this\n" +
 " * file, You can obtain one at http://mozilla.org/MPL/2.0/. */\n" +
 "\n" +
 "/*****************************************************************************/\n" +
 "/* This is an automatically generated file. If you're not                    */\n" +
-"/* PublicKeyPinningSerice.cpp, you shouldn't be #including it.               */\n" +
+"/* PublicKeyPinningService.cpp, you shouldn't be #including it.              */\n" +
 "/*****************************************************************************/\n" +
 "#include <stdint.h>" +
 "\n";
 const DOMAINHEADER ="/*Domainlist*/\n" +
 "typedef struct {\n"+
 "  const char *mHost;\n"+
 "  const bool mIncludeSubdomains;\n"+
 "  const StaticPinset *pinset;\n"+
 "} TransportSecurityPreload;\n"+
 "\nstatic const TransportSecurityPreload kPublicKeyPinningPreloadList[] = {\n";
 const PINSETDEF ="/*Now the pinsets, each is an ordered list by the actual value of the FP*/\n"+
 "typedef struct {\n"+
 "  const size_t size;\n"+
 "  const char* const* data;\n"+
 "} StaticPinset;\n";
 
+// Command-line arguments
+var gStaticPins = parseJson(arguments[0]);
+var gTestCertFile = arguments[1];
+var gOutputFile = arguments[2];
+
 function writeTo(string, fos) {
   fos.write(string, string.length);
 }
 
 function readFileToString(filename) {
-  let path = filename;
-
-  let lf = Components.classes["@mozilla.org/file/directory_service;1"]
-             .getService(Components.interfaces.nsIProperties)
-             .get("CurWorkD", Components.interfaces.nsILocalFile);
-
-  let bits = path.split("/");
-  for (let i = 0; i < bits.length; i++) {
-    if (bits[i]) {
-      if (bits[i] == "..") {
-        lf = lf.parent;
-      }
-      else {
-        lf.append(bits[i]);
-      }
-    }
-  }
+  let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
+  file.initWithPath(filename);
   let stream = Cc["@mozilla.org/network/file-input-stream;1"]
                  .createInstance(Ci.nsIFileInputStream);
-  stream.init(lf, -1, 0, 0);
+  stream.init(file, -1, 0, 0);
   let buf = NetUtil.readInputStreamToString(stream, stream.available());
   return buf;
 }
 
 function stripComments(buf) {
   var lines = buf.split("\n");
   let entryRegex = /^\s*\/\//;
   let data = "";
@@ -100,17 +96,17 @@ function isCertBuiltIn(cert) {
   if (tokenNames.some(isBuiltinToken)) {
     return true;
   }
   return false;
 }
 
 // Returns a pair of maps [certNameToSKD, certSDKToName] between cert
 // nicknames and digests of the SPKInfo for the mozilla trust store
-function loadNSSCertinfo() {
+function loadNSSCertinfo(derTestFile) {
   let allCerts = certdb2.getCerts();
   let enumerator = allCerts.getEnumerator();
   let certNameToSKD = {};
   let certSDKToName = {};
   const BUILT_IN_NICK_PREFIX = "Builtin Object Token:";
   while (enumerator.hasMoreElements()) {
     let cert = enumerator.getNext().QueryInterface(Ci.nsIX509Cert);
     if (!isCertBuiltIn(cert)) {
@@ -118,35 +114,34 @@ function loadNSSCertinfo() {
     }
     let name = cert.nickname.substr(BUILT_IN_NICK_PREFIX.length);
     let SDK  = cert.sha256SubjectPublicKeyInfoDigest;
     certNameToSKD[name] = SDK;
     certSDKToName[SDK] = name;
   }
   {
     // A certificate for *.example.com.
-    let der = readFileToString("default-ee.der");
+    let der = readFileToString(derTestFile);
     // XPCOM is too dumb to automatically query the parent interface of
     // nsIX509CertDB2 without a hint.
     let certdb = certdb2.QueryInterface(Ci.nsIX509CertDB);
     let testCert = certdb.constructX509(der, der.length);
     // We can't include this cert in the previous loop, because it skips
     // non-builtin certs and the nickname is not built-in to the cert.
     let name = "End Entity Test Cert";
     let SDK  = testCert.sha256SubjectPublicKeyInfoDigest;
     certNameToSKD[name] = SDK;
     certSDKToName[SDK] = name;
   }
   return [certNameToSKD, certSDKToName];
 }
 
-function parseMozFile() {
-  mozFile = stripComments(readFileToString(MOZINPUT));
-  mozJSON = JSON.parse(mozFile);
-  return mozJSON;
+function parseJson(filename) {
+  let json = stripComments(readFileToString(filename));
+  return JSON.parse(json);
 }
 
 function nameToAlias(certName) {
   // change the name to a string valid as a c identifier
   // remove  non-ascii characters
   certName = certName.replace( /[^[:ascii:]]/g, "_");
   // replace non word characters
   certName = certName.replace(/[^A-Za-z0-9]/g ,"_");
@@ -164,17 +159,18 @@ function genExpirationTime() {
   let expirationMillis = nowMillis + (PINNING_MINIMUM_REQUIRED_MAX_AGE * 1000);
   let expirationMicros = expirationMillis * 1000;
   return "const PRTime kPreloadPKPinsExpirationTime = INT64_C(" +
          expirationMicros +");\n";
 }
 
 function writeFile(certNameToSDK, certSDKToName, jsonPins) {
   try {
-    let file = FileUtils.getFile("CurWorkD", [OUTPUT]);
+    let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
+    file.initWithPath(gOutputFile);
     let fos = FileUtils.openSafeFileOutputStream(file);
 
     writeTo(FILE_HEADER, fos);
 
     // compute used keys
     let usedFingerPrints = {};
     let pinset = jsonPins["pinsets"];
     for (let pinsetEntry of pinset) {
@@ -228,18 +224,16 @@ function writeFile(certNameToSDK, certSD
                        jsonPins["entries"].length + ";\n";
     writeTo(domainFooter, fos);
     writeTo("\n", fos);
     writeTo(genExpirationTime(), fos);
 
     FileUtils.closeSafeFileOutputStream(fos);
 
   } catch (e) {
-    dump("ERROR: problem writing output to '" + OUTPUT + "': " + e + "\n");
+    dump("ERROR: problem writing output to '" + gOutputFile + "': " + e + "\n");
   }
 }
 
 // ****************************************************************************
 // This is where the action happens:
-
-let [certNameToSKD, certSDKToName] = loadNSSCertinfo();
-let mozJSON = parseMozFile();
-writeFile(certNameToSKD, certSDKToName, mozJSON);
+let [ certNameToSKD, certSDKToName ] = loadNSSCertinfo(gTestCertFile);
+writeFile(certNameToSKD, certSDKToName, gStaticPins);
new file mode 100644
--- /dev/null
+++ b/security/pkix/include/pkix/enumclass.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* Copyright 2013 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Work around missing std::bind, std::ref, std::cref in older compilers. This
+// implementation isn't intended to be complete; rather, it is the minimal
+// implementation needed to make our use of std::bind work.
+
+#ifndef mozilla_pkix__enumclass_h
+#define mozilla_pkix__enumclass_h
+
+#if defined(_MSC_VER) && (_MSC_VER < 1700)
+// Microsoft added support for "enum class" in Visual C++ 2012. Before that,
+// Visual C++ has supported typed enums for longer than that, but using typed
+// enums results in C4480: nonstandard extension used: specifying underlying
+// type for enum.
+#define MOZILLA_PKIX_ENUM_CLASS  __pragma(warning(disable: 4480)) enum
+#else
+#define MOZILLA_PKIX_ENUM_CLASS enum class
+#endif
+
+#endif // mozilla_pkix__enumclass_h
--- a/security/pkix/include/pkix/pkixtypes.h
+++ b/security/pkix/include/pkix/pkixtypes.h
@@ -13,16 +13,17 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #ifndef mozilla_pkix__pkixtypes_h
 #define mozilla_pkix__pkixtypes_h
 
+#include "pkix/enumclass.h"
 #include "pkix/ScopedPtr.h"
 #include "plarena.h"
 #include "cert.h"
 #include "keyhi.h"
 
 namespace mozilla { namespace pkix {
 
 typedef ScopedPtr<PLArenaPool, PL_FreeArenaPool> ScopedPLArenaPool;
@@ -30,34 +31,34 @@ typedef ScopedPtr<PLArenaPool, PL_FreeAr
 typedef ScopedPtr<CERTCertificate, CERT_DestroyCertificate>
         ScopedCERTCertificate;
 typedef ScopedPtr<CERTCertList, CERT_DestroyCertList> ScopedCERTCertList;
 typedef ScopedPtr<SECKEYPublicKey, SECKEY_DestroyPublicKey>
         ScopedSECKEYPublicKey;
 
 typedef unsigned int KeyUsages;
 
-enum EndEntityOrCA { MustBeEndEntity, MustBeCA };
+MOZILLA_PKIX_ENUM_CLASS EndEntityOrCA { MustBeEndEntity = 0, MustBeCA = 1 };
+
+MOZILLA_PKIX_ENUM_CLASS TrustLevel {
+  TrustAnchor = 1,        // certificate is a trusted root CA certificate or
+                          // equivalent *for the given policy*.
+  ActivelyDistrusted = 2, // certificate is known to be bad
+  InheritsTrust = 3       // certificate must chain to a trust anchor
+};
 
 // Applications control the behavior of path building and verification by
 // implementing the TrustDomain interface. The TrustDomain is used for all
 // cryptography and for determining which certificates are trusted or
 // distrusted.
 class TrustDomain
 {
 public:
   virtual ~TrustDomain() { }
 
-  enum TrustLevel {
-    TrustAnchor = 1,        // certificate is a trusted root CA certificate or
-                            // equivalent *for the given policy*.
-    ActivelyDistrusted = 2, // certificate is known to be bad
-    InheritsTrust = 3       // certificate must chain to a trust anchor
-  };
-
   // Determine the level of trust in the given certificate for the given role.
   // This will be called for every certificate encountered during path
   // building.
   //
   // When policy == SEC_OID_X509_ANY_POLICY, then no policy-related checking
   // should be done. When policy != SEC_OID_X509_ANY_POLICY, then GetCertTrust
   // MUST NOT return with *trustLevel == TrustAnchor unless the given cert is
   // considered a trust anchor *for that policy*. In particular, if the user
--- a/security/pkix/lib/pkixbuild.cpp
+++ b/security/pkix/lib/pkixbuild.cpp
@@ -123,17 +123,17 @@ BuildForwardInner(TrustDomain& trustDoma
                   SECOidTag requiredPolicy,
                   CERTCertificate* potentialIssuerCertToDup,
                   unsigned int subCACount,
                   ScopedCERTCertList& results)
 {
   PORT_Assert(potentialIssuerCertToDup);
 
   BackCert potentialIssuer(potentialIssuerCertToDup, &subject,
-                           BackCert::ExcludeCN);
+                           BackCert::IncludeCN::No);
   Result rv = potentialIssuer.Init();
   if (rv != Success) {
     return rv;
   }
 
   // RFC5280 4.2.1.1. Authority Key Identifier
   // RFC5280 4.2.1.2. Subject Key Identifier
 
@@ -152,22 +152,22 @@ BuildForwardInner(TrustDomain& trustDoma
   }
 
   rv = CheckNameConstraints(potentialIssuer);
   if (rv != Success) {
     return rv;
   }
 
   unsigned int newSubCACount = subCACount;
-  if (endEntityOrCA == MustBeCA) {
+  if (endEntityOrCA == EndEntityOrCA::MustBeCA) {
     newSubCACount = subCACount + 1;
   } else {
     PR_ASSERT(newSubCACount == 0);
   }
-  rv = BuildForward(trustDomain, potentialIssuer, time, MustBeCA,
+  rv = BuildForward(trustDomain, potentialIssuer, time, EndEntityOrCA::MustBeCA,
                     KU_KEY_CERT_SIGN, requiredEKUIfPresent, requiredPolicy,
                     nullptr, newSubCACount, results);
   if (rv != Success) {
     return rv;
   }
 
   if (trustDomain.VerifySignedData(&subject.GetNSSCert()->signatureWrap,
                                    potentialIssuer.GetNSSCert()) != SECSuccess) {
@@ -199,36 +199,36 @@ BuildForward(TrustDomain& trustDomain,
   // XXX: 6 is not enough for chains.sh anypolicywithlevel.cfg tests
   static const size_t MAX_DEPTH = 8;
   if (subCACount >= MAX_DEPTH - 1) {
     return RecoverableError;
   }
 
   Result rv;
 
-  TrustDomain::TrustLevel trustLevel;
+  TrustLevel trustLevel;
   // If this is an end-entity and not a trust anchor, we defer reporting
   // any error found here until after attempting to find a valid chain.
   // See the explanation of error prioritization in pkix.h.
   rv = CheckIssuerIndependentProperties(trustDomain, subject, time,
                                         endEntityOrCA,
                                         requiredKeyUsagesIfPresent,
                                         requiredEKUIfPresent, requiredPolicy,
                                         subCACount, &trustLevel);
   PRErrorCode deferredEndEntityError = 0;
   if (rv != Success) {
-    if (endEntityOrCA == MustBeEndEntity &&
-        trustLevel != TrustDomain::TrustAnchor) {
+    if (endEntityOrCA == EndEntityOrCA::MustBeEndEntity &&
+        trustLevel != TrustLevel::TrustAnchor) {
       deferredEndEntityError = PR_GetError();
     } else {
       return rv;
     }
   }
 
-  if (trustLevel == TrustDomain::TrustAnchor) {
+  if (trustLevel == TrustLevel::TrustAnchor) {
     ScopedCERTCertList certChain(CERT_NewCertList());
     if (!certChain) {
       PR_SetError(SEC_ERROR_NO_MEMORY, 0);
       return MapSECStatus(SECFailure);
     }
 
     rv = subject.PrependNSSCertToList(certChain.get());
     if (rv != Success) {
@@ -341,23 +341,23 @@ BuildCertChain(TrustDomain& trustDomain,
     return SECFailure;
   }
 
   // The only non-const operation on the cert we are allowed to do is
   // CERT_DupCertificate.
 
   // XXX: Support the legacy use of the subject CN field for indicating the
   // domain name the certificate is valid for.
-  BackCert::ConstrainedNameOptions cnOptions
-    = endEntityOrCA == MustBeEndEntity &&
+  BackCert::IncludeCN includeCN
+    = endEntityOrCA == EndEntityOrCA::MustBeEndEntity &&
       requiredEKUIfPresent == SEC_OID_EXT_KEY_USAGE_SERVER_AUTH
-    ? BackCert::IncludeCN
-    : BackCert::ExcludeCN;
+    ? BackCert::IncludeCN::Yes
+    : BackCert::IncludeCN::No;
 
-  BackCert cert(certToDup, nullptr, cnOptions);
+  BackCert cert(certToDup, nullptr, includeCN);
   Result rv = cert.Init();
   if (rv != Success) {
     return SECFailure;
   }
 
   rv = BuildForward(trustDomain, cert, time, endEntityOrCA,
                     requiredKeyUsagesIfPresent, requiredEKUIfPresent,
                     requiredPolicy, stapledOCSPResponse, 0, results);
--- a/security/pkix/lib/pkixcheck.cpp
+++ b/security/pkix/lib/pkixcheck.cpp
@@ -46,17 +46,17 @@ CheckKeyUsage(EndEntityOrCA endEntityOrC
               KeyUsages requiredKeyUsagesIfPresent,
               PLArenaPool* arena)
 {
   if (!encodedKeyUsage) {
     // TODO(bug 970196): Reject certificates that are being used to verify
     // certificate signatures unless the certificate is a trust anchor, to
     // reduce the chances of an end-entity certificate being abused as a CA
     // certificate.
-    // if (endEntityOrCA == MustBeCA && !isTrustAnchor) {
+    // if (endEntityOrCA == EndEntityOrCA::MustBeCA && !isTrustAnchor) {
     //   return Fail(RecoverableError, SEC_ERROR_INADEQUATE_KEY_USAGE);
     // }
     //
     // TODO: Users may configure arbitrary certificates as trust anchors, not
     // just roots. We should only allow a certificate without a key usage to be
     // used as a CA when it is self-issued and self-signed.
     return Success;
   }
@@ -72,17 +72,17 @@ CheckKeyUsage(EndEntityOrCA endEntityOrC
   // TODO XXX: Why is tmpItem.len > 1?
 
   KeyUsages allowedKeyUsages = tmpItem.data[0];
   if ((allowedKeyUsages & requiredKeyUsagesIfPresent)
         != requiredKeyUsagesIfPresent) {
     return Fail(RecoverableError, SEC_ERROR_INADEQUATE_KEY_USAGE);
   }
 
-  if (endEntityOrCA == MustBeCA) {
+  if (endEntityOrCA == EndEntityOrCA::MustBeCA) {
    // "If the keyUsage extension is present, then the subject public key
    //  MUST NOT be used to verify signatures on certificates or CRLs unless
    //  the corresponding keyCertSign or cRLSign bit is set."
    if ((allowedKeyUsages & KU_KEY_CERT_SIGN) == 0) {
       return Fail(RecoverableError, SEC_ERROR_INADEQUATE_KEY_USAGE);
     }
   } else {
     // "The keyCertSign bit is asserted when the subject public key is
@@ -124,17 +124,17 @@ CheckCertificatePolicies(BackCert& cert,
   if (cert.encodedInhibitAnyPolicy) {
     return Fail(RecoverableError, SEC_ERROR_POLICY_VALIDATION_FAILED);
   }
 
   // The root CA certificate may omit the policies that it has been
   // trusted for, so we cannot require the policies to be present in those
   // certificates. Instead, the determination of which roots are trusted for
   // which policies is made by the TrustDomain's GetCertTrust method.
-  if (isTrustAnchor && endEntityOrCA == MustBeCA) {
+  if (isTrustAnchor && endEntityOrCA == EndEntityOrCA::MustBeCA) {
     return Success;
   }
 
   if (!cert.encodedCertificatePolicies) {
     return Fail(RecoverableError, SEC_ERROR_POLICY_VALIDATION_FAILED);
   }
 
   ScopedPtr<CERTCertificatePolicies, CERT_DestroyCertificatePoliciesExtension>
@@ -145,17 +145,17 @@ CheckCertificatePolicies(BackCert& cert,
   }
 
   for (const CERTPolicyInfo* const* policyInfos = policies->policyInfos;
        *policyInfos; ++policyInfos) {
     if ((*policyInfos)->oid == requiredPolicy) {
       return Success;
     }
     // Intermediate certs are allowed to have the anyPolicy OID
-    if (endEntityOrCA == MustBeCA &&
+    if (endEntityOrCA == EndEntityOrCA::MustBeCA &&
         (*policyInfos)->oid == SEC_OID_X509_ANY_POLICY) {
       return Success;
     }
   }
 
   return Fail(RecoverableError, SEC_ERROR_POLICY_VALIDATION_FAILED);
 }
 
@@ -243,29 +243,29 @@ CheckBasicConstraints(const BackCert& ce
     //  certificate, or the extension is present but the cA boolean is not
     //  asserted, then the certified public key MUST NOT be used to verify
     //  certificate signatures."
     //
     // For compatibility, we must accept v1 trust anchors without basic
     // constraints as CAs.
     //
     // TODO: add check for self-signedness?
-    if (endEntityOrCA == MustBeCA && isTrustAnchor) {
+    if (endEntityOrCA == EndEntityOrCA::MustBeCA && isTrustAnchor) {
       const CERTCertificate* nssCert = cert.GetNSSCert();
       // We only allow trust anchor CA certs to omit the
       // basicConstraints extension if they are v1. v1 is encoded
       // implicitly.
       if (!nssCert->version.data && !nssCert->version.len) {
         basicConstraints.isCA = true;
         basicConstraints.pathLenConstraint = CERT_UNLIMITED_PATH_CONSTRAINT;
       }
     }
   }
 
-  if (endEntityOrCA == MustBeEndEntity) {
+  if (endEntityOrCA == EndEntityOrCA::MustBeEndEntity) {
     // CA certificates are not trusted as EE certs.
 
     if (basicConstraints.isCA) {
       // XXX: We use SEC_ERROR_CA_CERT_INVALID here so we can distinguish
       // this error from other errors, given that NSS does not have a "CA cert
       // used as end-entity" error code since it doesn't have such a
       // prohibition. We should add such an error code and stop abusing
       // SEC_ERROR_CA_CERT_INVALID this way.
@@ -275,17 +275,17 @@ CheckBasicConstraints(const BackCert& ce
       // validating when we check it from pkixocsp.cpp, which is a good thing.
       //
       return Fail(RecoverableError, SEC_ERROR_CA_CERT_INVALID);
     }
 
     return Success;
   }
 
-  PORT_Assert(endEntityOrCA == MustBeCA);
+  PORT_Assert(endEntityOrCA == EndEntityOrCA::MustBeCA);
 
   // End-entity certificates are not allowed to act as CA certs.
   if (!basicConstraints.isCA) {
     return Fail(RecoverableError, SEC_ERROR_CA_CERT_INVALID);
   }
 
   if (basicConstraints.pathLenConstraint >= 0) {
     if (subCACount >
@@ -302,17 +302,17 @@ BackCert::GetConstrainedNames(/*out*/ co
 {
   if (!constrainedNames) {
     if (!GetArena()) {
       return FatalError;
     }
 
     constrainedNames =
       CERT_GetConstrainedCertificateNames(nssCert, arena.get(),
-                                          cnOptions == IncludeCN);
+                                          includeCN == IncludeCN::Yes);
     if (!constrainedNames) {
       return MapSECStatus(SECFailure);
     }
   }
 
   *result = constrainedNames;
   return Success;
 }
@@ -392,17 +392,17 @@ CheckExtendedKeyUsage(EndEntityOrCA endE
       SECOidTag oidTag = SECOID_FindOIDTag(*oids);
       if (requiredEKU != SEC_OID_UNKNOWN && oidTag == requiredEKU) {
         found = true;
       } else {
         // Treat CA certs with step-up OID as also having SSL server type.
         // COMODO has issued certificates that require this behavior
         // that don't expire until June 2020!
         // TODO 982932: Limit this expection to old certificates
-        if (endEntityOrCA == MustBeCA &&
+        if (endEntityOrCA == EndEntityOrCA::MustBeCA &&
             requiredEKU == SEC_OID_EXT_KEY_USAGE_SERVER_AUTH &&
             oidTag == SEC_OID_NS_KEY_USAGE_GOVT_APPROVED) {
           found = true;
         }
       }
       if (oidTag == SEC_OID_OCSP_RESPONDER) {
         foundOCSPSigning = true;
       }
@@ -412,17 +412,17 @@ CheckExtendedKeyUsage(EndEntityOrCA endE
     // list.
     if (!found) {
       return Fail(RecoverableError, SEC_ERROR_INADEQUATE_CERT_TYPE);
     }
   }
 
   // pkixocsp.cpp depends on the following additional checks.
 
-  if (endEntityOrCA == MustBeEndEntity) {
+  if (endEntityOrCA == EndEntityOrCA::MustBeEndEntity) {
     // When validating anything other than an delegated OCSP signing cert,
     // reject any cert that also claims to be an OCSP responder, because such
     // a cert does not make sense. For example, if an SSL certificate were to
     // assert id-kp-OCSPSigning then it could sign OCSP responses for itself,
     // if not for this check.
     // That said, we accept CA certificates with id-kp-OCSPSigning because
     // some CAs in Mozilla's CA program have issued such intermediate
     // certificates, and because some CAs have reported some Microsoft server
@@ -456,43 +456,43 @@ Result
 CheckIssuerIndependentProperties(TrustDomain& trustDomain,
                                  BackCert& cert,
                                  PRTime time,
                                  EndEntityOrCA endEntityOrCA,
                                  KeyUsages requiredKeyUsagesIfPresent,
                                  SECOidTag requiredEKUIfPresent,
                                  SECOidTag requiredPolicy,
                                  unsigned int subCACount,
-                /*optional out*/ TrustDomain::TrustLevel* trustLevelOut)
+                /*optional out*/ TrustLevel* trustLevelOut)
 {
   Result rv;
 
-  TrustDomain::TrustLevel trustLevel;
+  TrustLevel trustLevel;
   rv = MapSECStatus(trustDomain.GetCertTrust(endEntityOrCA,
                                              requiredPolicy,
                                              cert.GetNSSCert(),
                                              &trustLevel));
   if (rv != Success) {
     return rv;
   }
-  if (trustLevel == TrustDomain::ActivelyDistrusted) {
+  if (trustLevel == TrustLevel::ActivelyDistrusted) {
     return Fail(RecoverableError, SEC_ERROR_UNTRUSTED_CERT);
   }
-  if (trustLevel != TrustDomain::TrustAnchor &&
-      trustLevel != TrustDomain::InheritsTrust) {
+  if (trustLevel != TrustLevel::TrustAnchor &&
+      trustLevel != TrustLevel::InheritsTrust) {
     // The TrustDomain returned a trust level that we weren't expecting.
     PORT_SetError(PR_INVALID_STATE_ERROR);
     return FatalError;
   }
   if (trustLevelOut) {
     *trustLevelOut = trustLevel;
   }
 
-  bool isTrustAnchor = endEntityOrCA == MustBeCA &&
-                       trustLevel == TrustDomain::TrustAnchor;
+  bool isTrustAnchor = endEntityOrCA == EndEntityOrCA::MustBeCA &&
+                       trustLevel == TrustLevel::TrustAnchor;
 
   PLArenaPool* arena = cert.GetArena();
   if (!arena) {
     return FatalError;
   }
 
   // 4.2.1.1. Authority Key Identifier is ignored (see bug 965136).
 
--- a/security/pkix/lib/pkixcheck.h
+++ b/security/pkix/lib/pkixcheck.h
@@ -27,15 +27,15 @@ Result CheckIssuerIndependentProperties(
           TrustDomain& trustDomain,
           BackCert& cert,
           PRTime time,
           EndEntityOrCA endEntityOrCA,
           KeyUsages requiredKeyUsagesIfPresent,
           SECOidTag requiredEKUIfPresent,
           SECOidTag requiredPolicy,
           unsigned int subCACount,
-          /*optional out*/ TrustDomain::TrustLevel* trustLevel = nullptr);
+          /*optional out*/ TrustLevel* trustLevel = nullptr);
 
 Result CheckNameConstraints(BackCert& cert);
 
 } } // namespace mozilla::pkix
 
 #endif // mozilla_pkix__pkixcheck_h
--- a/security/pkix/lib/pkixder.h
+++ b/security/pkix/lib/pkixder.h
@@ -13,16 +13,17 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #ifndef mozilla_pkix__pkixder_h
 #define mozilla_pkix__pkixder_h
 
+#include "pkix/enumclass.h"
 #include "pkix/nullptr.h"
 
 #include "prerror.h"
 #include "prlog.h"
 #include "secder.h"
 #include "secerr.h"
 #include "secoidt.h"
 #include "stdint.h"
@@ -56,17 +57,17 @@ enum Tag
 };
 
 enum Result
 {
   Failure = -1,
   Success = 0
 };
 
-enum EmptyAllowed { MayBeEmpty = 0, MustNotBeEmpty = 1 };
+MOZILLA_PKIX_ENUM_CLASS EmptyAllowed { No = 0, Yes = 1 };
 
 Result Fail(PRErrorCode errorCode);
 
 class Input
 {
 public:
   Input()
     : input(nullptr)
@@ -319,17 +320,17 @@ NestedOf(Input& input, uint8_t outerTag,
   }
 
   Input inner;
   if (input.Skip(responsesLength, inner) != Success) {
     return Failure;
   }
 
   if (inner.AtEnd()) {
-    if (mayBeEmpty != MayBeEmpty) {
+    if (mayBeEmpty != EmptyAllowed::Yes) {
       return Fail(SEC_ERROR_BAD_DER);
     }
     return Success;
   }
 
   do {
     if (Nested(inner, innerTag, decoder) != Success) {
       return Failure;
@@ -412,17 +413,17 @@ Enumerated(Input& input, uint8_t& value)
 inline Result
 GeneralizedTime(Input& input, PRTime& time)
 {
   uint16_t length;
   SECItem encoded;
   if (ExpectTagAndGetLength(input, GENERALIZED_TIME, length) != Success) {
     return Failure;
   }
-  if (input.Skip(length, encoded)) {
+  if (input.Skip(length, encoded) != Success) {
     return Failure;
   }
   if (DER_GeneralizedTimeToTime(&time, &encoded) != SECSuccess) {
     return Failure;
   }
 
   return Success;
 }
@@ -459,17 +460,17 @@ Integer(Input& input, /*out*/ SECItem& v
 }
 
 inline Result
 Null(Input& input)
 {
   return ExpectTagAndLength(input, NULLTag, 0);
 }
 
-template <uint16_t Len>
+template <uint8_t Len>
 Result
 OID(Input& input, const uint8_t (&expectedOid)[Len])
 {
   if (ExpectTagAndLength(input, OIDTag, Len) != Success) {
     return Failure;
   }
 
   return input.Expect(expectedOid, Len);
--- a/security/pkix/lib/pkixocsp.cpp
+++ b/security/pkix/lib/pkixocsp.cpp
@@ -21,34 +21,27 @@
 #include "pkix/pkix.h"
 #include "pkixcheck.h"
 #include "pkixder.h"
 
 #include "hasht.h"
 #include "pk11pub.h"
 #include "secder.h"
 
-#ifdef _MSC_VER
-// C4480: nonstandard extension used: specifying underlying type for enum
-#define ENUM_CLASS  __pragma(warning(disable: 4480)) enum
-#else
-#define ENUM_CLASS enum class
-#endif
-
 // TODO: use typed/qualified typedefs everywhere?
 // TODO: When should we return SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE?
 
 namespace mozilla { namespace pkix {
 
 static const PRTime ONE_DAY
   = INT64_C(24) * INT64_C(60) * INT64_C(60) * PR_USEC_PER_SEC;
 static const PRTime SLOP = ONE_DAY;
 
 // These values correspond to the tag values in the ASN.1 CertStatus
-ENUM_CLASS CertStatus : uint8_t {
+MOZILLA_PKIX_ENUM_CLASS CertStatus : uint8_t {
   Good = der::CONTEXT_SPECIFIC | 0,
   Revoked = der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 1,
   Unknown = der::CONTEXT_SPECIFIC | 2
 };
 
 class Context
 {
 public:
@@ -91,17 +84,17 @@ private:
 // according to RFC 6960 section 4.2.2.2.
 static Result
 CheckOCSPResponseSignerCert(TrustDomain& trustDomain,
                             CERTCertificate& potentialSigner,
                             const CERTCertificate& issuerCert, PRTime time)
 {
   Result rv;
 
-  BackCert cert(&potentialSigner, nullptr, BackCert::ExcludeCN);
+  BackCert cert(&potentialSigner, nullptr, BackCert::IncludeCN::No);
   rv = cert.Init();
   if (rv != Success) {
     return rv;
   }
 
   // We don't need to do a complete verification of the signer (i.e. we don't
   // have to call BuildCertChain to verify the entire chain) because we
   // already know that the issuerCert is valid, since revocation checking is
@@ -121,17 +114,17 @@ CheckOCSPResponseSignerCert(TrustDomain&
   // Note that CheckIssuerIndependentProperties processes
   // SEC_OID_OCSP_RESPONDER in the way that the OCSP specification requires us
   // to--in particular, it doesn't allow SEC_OID_OCSP_RESPONDER to be implied
   // by a missing EKU extension, unlike other EKUs.
   //
   // TODO(bug 926261): If we're validating for a policy then the policy OID we
   // are validating for should be passed to CheckIssuerIndependentProperties.
   rv = CheckIssuerIndependentProperties(trustDomain, cert, time,
-                                        MustBeEndEntity, 0,
+                                        EndEntityOrCA::MustBeEndEntity, 0,
                                         SEC_OID_OCSP_RESPONDER,
                                         SEC_OID_X509_ANY_POLICY, 0);
   if (rv != Success) {
     return rv;
   }
 
   // It is possible that there exists a certificate with the same key as the
   // issuer but with a different name, so we need to compare names
@@ -152,22 +145,17 @@ CheckOCSPResponseSignerCert(TrustDomain&
 
   // TODO: check for revocation of the OCSP responder certificate unless no-check
   // or the caller forcing no-check. To properly support the no-check policy, we'd
   // need to enforce policy constraints from the issuerChain.
 
   return Success;
 }
 
-//typedef enum {
-//    ocspResponderID_byName = 1,
-//    ocspResponderID_byKey = 2
-//} ResponderIDType;
-
-ENUM_CLASS ResponderIDType : uint8_t
+MOZILLA_PKIX_ENUM_CLASS ResponderIDType : uint8_t
 {
   byName = der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 1,
   byKey = der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 2
 };
 
 static inline der::Result OCSPResponse(der::Input&, Context&);
 static inline der::Result ResponseBytes(der::Input&, Context&);
 static inline der::Result BasicResponse(der::Input&, Context&);
@@ -575,17 +563,17 @@ ResponseData(der::Input& input, Context&
   if (der::GeneralizedTime(input, producedAt) != der::Success) {
     return der::Failure;
   }
 
   // We don't accept an empty sequence of responses. In practice, a legit OCSP
   // responder will never return an empty response, and handling the case of an
   // empty response makes things unnecessarily complicated.
   if (der::NestedOf(input, der::SEQUENCE, der::SEQUENCE,
-                    der::MustNotBeEmpty,
+                    der::EmptyAllowed::No,
                     bind(SingleResponse, _1, ref(context))) != der::Success) {
     return der::Failure;
   }
 
   if (!input.AtEnd()) {
     if (der::Nested(input, der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 1,
                     CheckExtensionsForCriticality) != der::Success) {
       return der::Failure;
@@ -861,17 +849,17 @@ CheckExtensionForCriticality(der::Input&
 // Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
 static der::Result
 CheckExtensionsForCriticality(der::Input& input)
 {
   // TODO(bug 997994): some responders include an empty SEQUENCE OF
   // Extension, which is invalid (der::MayBeEmpty should really be
   // der::MustNotBeEmpty).
   return der::NestedOf(input, der::SEQUENCE, der::SEQUENCE,
-                       der::MayBeEmpty, CheckExtensionForCriticality);
+                       der::EmptyAllowed::Yes, CheckExtensionForCriticality);
 }
 
 //   1. The certificate identified in a received response corresponds to
 //      the certificate that was identified in the corresponding request;
 //   2. The signature on the response is valid;
 //   3. The identity of the signer matches the intended recipient of the
 //      request;
 //   4. The signer is currently authorized to provide a response for the
--- a/security/pkix/lib/pkixutil.h
+++ b/security/pkix/lib/pkixutil.h
@@ -13,16 +13,17 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #ifndef mozilla_pkix__pkixutil_h
 #define mozilla_pkix__pkixutil_h
 
+#include "pkix/enumclass.h"
 #include "pkix/pkixtypes.h"
 #include "prerror.h"
 #include "seccomon.h"
 #include "secerr.h"
 
 namespace mozilla { namespace pkix {
 
 enum Result
@@ -76,34 +77,33 @@ MapSECStatus(SECStatus srv)
 // such as name constraints.
 //
 // Each BackCert contains pointers to all the given certificate's extensions
 // so that we can parse the extension block once and then process the
 // extensions in an order that may be different than they appear in the cert.
 class BackCert
 {
 public:
-  // ExcludeCN means that GetConstrainedNames won't include the subject CN in
-  // its results. IncludeCN means that GetConstrainedNames will include the
-  // subject CN in its results.
-  enum ConstrainedNameOptions { ExcludeCN = 0, IncludeCN = 1 };
+  // IncludeCN::No means that GetConstrainedNames won't include the subject CN
+  // in its results. IncludeCN::Yes means that GetConstrainedNames will include
+  // the subject CN in its results.
+  MOZILLA_PKIX_ENUM_CLASS IncludeCN { No = 0, Yes = 1 };
 
   // nssCert and childCert must be valid for the lifetime of BackCert
-  BackCert(CERTCertificate* nssCert, BackCert* childCert,
-           ConstrainedNameOptions cnOptions)
+  BackCert(CERTCertificate* nssCert, BackCert* childCert, IncludeCN includeCN)
     : encodedBasicConstraints(nullptr)
     , encodedCertificatePolicies(nullptr)
     , encodedExtendedKeyUsage(nullptr)
     , encodedKeyUsage(nullptr)
     , encodedNameConstraints(nullptr)
     , encodedInhibitAnyPolicy(nullptr)
     , childCert(childCert)
     , nssCert(nssCert)
     , constrainedNames(nullptr)
-    , cnOptions(cnOptions)
+    , includeCN(includeCN)
   {
   }
 
   Result Init();
 
   const SECItem* encodedBasicConstraints;
   const SECItem* encodedCertificatePolicies;
   const SECItem* encodedExtendedKeyUsage;
@@ -132,17 +132,17 @@ public:
 
   PLArenaPool* GetArena();
 
 private:
   CERTCertificate* nssCert;
 
   ScopedPLArenaPool arena;
   CERTGeneralName* constrainedNames;
-  ConstrainedNameOptions cnOptions;
+  IncludeCN includeCN;
 
   BackCert(const BackCert&) /* = delete */;
   void operator=(const BackCert&); /* = delete */;
 };
 
 } } // namespace mozilla::pkix
 
 #endif // mozilla_pkix__pkixutil_h
--- a/security/pkix/test/gtest/pkixder_input_tests.cpp
+++ b/security/pkix/test/gtest/pkixder_input_tests.cpp
@@ -545,17 +545,17 @@ Result NestedOfHelper(Input& input, std:
 TEST_F(pkixder_input_tests, NestedOf)
 {
   Input input;
   ASSERT_EQ(Success,
             input.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8));
 
   std::vector<uint8_t> readValues;
   ASSERT_EQ(Success,
-    NestedOf(input, SEQUENCE, INTEGER, MustNotBeEmpty,
+    NestedOf(input, SEQUENCE, INTEGER, EmptyAllowed::No,
              mozilla::pkix::bind(NestedOfHelper, mozilla::pkix::_1,
                                  mozilla::pkix::ref(readValues))));
   ASSERT_EQ((size_t) 3, readValues.size());
   ASSERT_EQ(0x01, readValues[0]);
   ASSERT_EQ(0x02, readValues[1]);
   ASSERT_EQ(0x03, readValues[2]);
   ASSERT_EQ(Success, End(input));
 }
@@ -563,15 +563,15 @@ TEST_F(pkixder_input_tests, NestedOf)
 TEST_F(pkixder_input_tests, NestedOfWithTruncatedData)
 {
   Input input;
   ASSERT_EQ(Success, input.Init(DER_TRUNCATED_SEQUENCE_OF_INT8,
                                 sizeof DER_TRUNCATED_SEQUENCE_OF_INT8));
 
   std::vector<uint8_t> readValues;
   ASSERT_EQ(Failure,
-    NestedOf(input, SEQUENCE, INTEGER, MustNotBeEmpty,
+    NestedOf(input, SEQUENCE, INTEGER, EmptyAllowed::No,
              mozilla::pkix::bind(NestedOfHelper, mozilla::pkix::_1,
                                  mozilla::pkix::ref(readValues))));
   ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
   ASSERT_EQ((size_t) 0, readValues.size());
 }
 } // unnamed namespace
new file mode 100644
--- /dev/null
+++ b/testing/gtest/MozGtestFriend.h
@@ -0,0 +1,15 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GTEST_MOZGTESTFRIEND_H
+#define GTEST_MOZGTESTFRIEND_H
+
+#ifdef ENABLE_TESTS
+#include "gtest_prod.h"
+#else
+#define FRIEND_TEST(a, b)
+#endif
+
+#endif // GTEST_MOZGTESTFRIEND_H
--- a/testing/gtest/moz.build
+++ b/testing/gtest/moz.build
@@ -1,75 +1,80 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 EXPORTS.gtest += [
-    'gtest/include/gtest/gtest-death-test.h',
-    'gtest/include/gtest/gtest-message.h',
-    'gtest/include/gtest/gtest-param-test.h',
-    'gtest/include/gtest/gtest-printers.h',
-    'gtest/include/gtest/gtest-spi.h',
-    'gtest/include/gtest/gtest-test-part.h',
-    'gtest/include/gtest/gtest-typed-test.h',
-    'gtest/include/gtest/gtest.h',
-    'gtest/include/gtest/gtest_pred_impl.h',
-    'gtest/include/gtest/gtest_prod.h',
-]
-
-# GTest internal are exposed in gtest.h. See comment in gtest.h
-EXPORTS.gtest.internal += [
-    'gtest/include/gtest/internal/gtest-death-test-internal.h',
-    'gtest/include/gtest/internal/gtest-filepath.h',
-    'gtest/include/gtest/internal/gtest-internal.h',
-    'gtest/include/gtest/internal/gtest-linked_ptr.h',
-    'gtest/include/gtest/internal/gtest-param-util-generated.h',
-    'gtest/include/gtest/internal/gtest-param-util.h',
-    'gtest/include/gtest/internal/gtest-port.h',
-    'gtest/include/gtest/internal/gtest-string.h',
-    'gtest/include/gtest/internal/gtest-tuple.h',
-    'gtest/include/gtest/internal/gtest-type-util.h',
+    'MozGtestFriend.h',
 ]
 
-EXPORTS.gmock += [
-    'gmock/include/gmock/gmock-actions.h',
-    'gmock/include/gmock/gmock-cardinalities.h',
-    'gmock/include/gmock/gmock-generated-actions.h',
-    'gmock/include/gmock/gmock-generated-function-mockers.h',
-    'gmock/include/gmock/gmock-generated-matchers.h',
-    'gmock/include/gmock/gmock-generated-nice-strict.h',
-    'gmock/include/gmock/gmock-matchers.h',
-    'gmock/include/gmock/gmock-more-actions.h',
-    'gmock/include/gmock/gmock-spec-builders.h',
-    'gmock/include/gmock/gmock.h',
-]
+if CONFIG['ENABLE_TESTS']:
+    EXPORTS.gtest += [
+        'gtest/include/gtest/gtest-death-test.h',
+        'gtest/include/gtest/gtest-message.h',
+        'gtest/include/gtest/gtest-param-test.h',
+        'gtest/include/gtest/gtest-printers.h',
+        'gtest/include/gtest/gtest-spi.h',
+        'gtest/include/gtest/gtest-test-part.h',
+        'gtest/include/gtest/gtest-typed-test.h',
+        'gtest/include/gtest/gtest.h',
+        'gtest/include/gtest/gtest_pred_impl.h',
+        'gtest/include/gtest/gtest_prod.h',
+    ]
 
-# gmock also includes internal interfaces in it's public header
-EXPORTS.gmock.internal += [
-    'gmock/include/gmock/internal/gmock-generated-internal-utils.h',
-    'gmock/include/gmock/internal/gmock-internal-utils.h',
-    'gmock/include/gmock/internal/gmock-port.h',
-]
+    # GTest internal are exposed in gtest.h. See comment in gtest.h
+    EXPORTS.gtest.internal += [
+        'gtest/include/gtest/internal/gtest-death-test-internal.h',
+        'gtest/include/gtest/internal/gtest-filepath.h',
+        'gtest/include/gtest/internal/gtest-internal.h',
+        'gtest/include/gtest/internal/gtest-linked_ptr.h',
+        'gtest/include/gtest/internal/gtest-param-util-generated.h',
+        'gtest/include/gtest/internal/gtest-param-util.h',
+        'gtest/include/gtest/internal/gtest-port.h',
+        'gtest/include/gtest/internal/gtest-string.h',
+        'gtest/include/gtest/internal/gtest-tuple.h',
+        'gtest/include/gtest/internal/gtest-type-util.h',
+    ]
 
-SOURCES += [
-    'gmock/src/gmock-all.cc',
-    'gtest/src/gtest-all.cc',
-    'mozilla/GTestRunner.cpp',
-]
+    EXPORTS.gmock += [
+        'gmock/include/gmock/gmock-actions.h',
+        'gmock/include/gmock/gmock-cardinalities.h',
+        'gmock/include/gmock/gmock-generated-actions.h',
+        'gmock/include/gmock/gmock-generated-function-mockers.h',
+        'gmock/include/gmock/gmock-generated-matchers.h',
+        'gmock/include/gmock/gmock-generated-nice-strict.h',
+        'gmock/include/gmock/gmock-matchers.h',
+        'gmock/include/gmock/gmock-more-actions.h',
+        'gmock/include/gmock/gmock-spec-builders.h',
+        'gmock/include/gmock/gmock.h',
+    ]
 
-LIBRARY_NAME = 'gtest'
+    # gmock also includes internal interfaces in it's public header
+    EXPORTS.gmock.internal += [
+        'gmock/include/gmock/internal/gmock-generated-internal-utils.h',
+        'gmock/include/gmock/internal/gmock-internal-utils.h',
+        'gmock/include/gmock/internal/gmock-port.h',
+    ]
 
-SOURCES += [
-    'mozilla/SanityTest.cpp',
-]
+    SOURCES += [
+        'gmock/src/gmock-all.cc',
+        'gtest/src/gtest-all.cc',
+        'mozilla/GTestRunner.cpp',
+    ]
 
-EXPORT_LIBRARY = True
+    LIBRARY_NAME = 'gtest'
 
-LOCAL_INCLUDES += [
-    'gmock',
-    'gmock/include',
-    'gtest',
-    'gtest/include',
-]
+    SOURCES += [
+        'mozilla/SanityTest.cpp',
+    ]
+
+    EXPORT_LIBRARY = True
 
-FINAL_LIBRARY = 'xul-gtest'
+    LOCAL_INCLUDES += [
+        'gmock',
+        'gmock/include',
+        'gtest',
+        'gtest/include',
+    ]
+
+    FINAL_LIBRARY = 'xul-gtest'
new file mode 100644
--- /dev/null
+++ b/testing/marionette/client/marionette/tests/unit/test_click_scrolling.py
@@ -0,0 +1,86 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from by import By
+from errors import MoveTargetOutOfBoundsException
+from marionette_test import MarionetteTestCase, skip
+
+
+class TestClickScrolling(MarionetteTestCase):
+
+    def test_clicking_on_anchor_scrolls_page(self):
+        scrollScript = """
+            var pageY;
+            if (typeof(window.pageYOffset) == 'number') {
+                pageY = window.pageYOffset;
+            } else {
+                pageY = document.documentElement.scrollTop;
+            }
+            return pageY;"""
+
+        test_html = self.marionette.absolute_url("macbeth.html")
+        self.marionette.navigate(test_html)
+
+        self.marionette.find_element(By.PARTIAL_LINK_TEXT, "last speech").click()
+        y_offset = self.marionette.execute_script(scrollScript)
+
+        # Focusing on to click, but not actually following,
+        # the link will scroll it in to view, which is a few
+        # pixels further than 0
+
+        self.assertTrue(y_offset > 300)
+
+    def test_should_scroll_to_click_on_an_element_hidden_by_overflow(self):
+        test_html = self.marionette.absolute_url("click_out_of_bounds_overflow.html")
+        self.marionette.navigate(test_html)
+
+        link = self.marionette.find_element(By.ID, "link")
+        try:
+            link.click()
+        except MoveTargetOutOfBoundsException:
+            self.fail("Should not be out of bounds")
+
+    @skip("Bug 1003682")
+    def test_should_be_able_to_click_on_an_element_hidden_by_overflow(self):
+        test_html = self.marionette.absolute_url("scroll.html")
+        self.marionette.navigate(test_html)
+
+        link = self.marionette.find_element(By.ID, "line8")
+        link.click()
+        self.assertEqual("line8", self.marionette.find_element(By.ID, "clicked").text)
+
+    def test_should_not_scroll_overflow_elements_which_are_visible(self):
+        test_html = self.marionette.absolute_url("scroll2.html")
+        self.marionette.navigate(test_html)
+
+        list_el = self.marionette.find_element(By.TAG_NAME, "ul")
+        item = list_el.find_element(By.ID, "desired")
+        item.click()
+        y_offset = self.marionette.execute_script("return arguments[0].scrollTop;", script_args=[list_el])
+        self.assertEqual(0, y_offset)
+
+    @skip("Bug 1003688")
+    def test_should_not_scroll_if_already_scrolled_and_element_is_in_view(self):
+        test_html = self.marionette.absolute_url("scroll3.html")
+        self.marionette.navigate(test_html)
+
+        self.marionette.find_element(By.ID, "button1").click()
+        scroll_top = self.marionette.execute_script("return document.body.scrollTop;")
+        self.marionette.find_element(By.ID, "button2").click()
+        self.assertEqual(scroll_top, self.marionette.execute_script("return document.body.scrollTop;"))
+
+    def test_should_be_able_to_click_radio_button_scrolled_into_view(self):
+        test_html = self.marionette.absolute_url("scroll4.html")
+        self.marionette.navigate(test_html)
+
+        # If we dont throw we are good
+        self.marionette.find_element(By.ID, "radio").click()
+
+    @skip("Bug 1003687")
+    def test_should_scroll_overflow_elements_if_click_point_is_out_of_view_but_element_is_in_view(self):
+        test_html = self.marionette.absolute_url("scroll5.html")
+        self.marionette.navigate(test_html)
+
+        self.marionette.find_element(By.ID, "inner").click()
+        self.assertEqual("clicked", self.marionette.find_element(By.ID, "clicked").text)
--- a/testing/marionette/client/marionette/tests/unit/unit-tests.ini
+++ b/testing/marionette/client/marionette/tests/unit/unit-tests.ini
@@ -109,10 +109,11 @@ b2g = false
 disabled = "Bug 925688"
 [test_submit.py]
 [test_chrome_async_finish.js]
 [test_screen_orientation.py]
 browser = false
 [test_errors.py]
 
 [test_execute_isolate.py]
+[test_click_scrolling.py]
 
-[include:oop/manifest.ini]
\ No newline at end of file
+[include:oop/manifest.ini]
new file mode 100644
--- /dev/null
+++ b/testing/marionette/client/marionette/www/click_out_of_bounds_overflow.html
@@ -0,0 +1,90 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!DOCTYPE html>
+<html><head>
+  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<body>
+<div style="height: 100px; overflow: auto;">
+  <table>
+    <tbody>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td>data</td></tr>
+      <tr><td><a href="#clicked" id="link">click me</a></td></tr>
+    </tbody>
+  </table>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/marionette/client/marionette/www/macbeth.html
@@ -0,0 +1,5254 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
+ <html>
+ <head>
+ <title>Macbeth: Entire Play
+ </title>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ </HEAD>
+ <body bgcolor="#ffffff" text="#000000">
+
+ <!-- Originally from http://shakespeare.mit.edu/macbeth/full.html -->
+
+<a href="#5.8.86">Quick link to last speech</a>
+
+<H3>ACT I</h3>
+<h3>SCENE I. A desert place.</h3>
+<p><blockquote>
+<i>Thunder and lightning. Enter three Witches</i>
+</blockquote>
+
+<A NAME=speech1><b>First Witch</b></a>
+<blockquote>
+<A NAME=1.1.1>When shall we three meet again</A><br>
+<A NAME=1.1.2>In thunder, lightning, or in rain?</A><br>
+</blockquote>
+
+<A NAME=speech2><b>Second Witch</b></a>
+<blockquote>
+<A NAME=1.1.3>When the hurlyburly's done,</A><br>
+<A NAME=1.1.4>When the battle's lost and won.</A><br>
+</blockquote>
+
+<A NAME=speech3><b>Third Witch</b></a>
+<blockquote>
+<A NAME=1.1.5>That will be ere the set of sun.</A><br>
+</blockquote>
+
+<A NAME=speech4><b>First Witch</b></a>
+<blockquote>
+<A NAME=1.1.6>Where the place?</A><br>
+</blockquote>
+
+<A NAME=speech5><b>Second Witch</b></a>
+<blockquote>
+<A NAME=1.1.7>                  Upon the heath.</A><br>
+</blockquote>
+
+<A NAME=speech6><b>Third Witch</b></a>
+<blockquote>
+<A NAME=1.1.8>There to meet with Macbeth.</A><br>
+</blockquote>
+
+<A NAME=speech7><b>First Witch</b></a>
+<blockquote>
+<A NAME=1.1.9>I come, Graymalkin!</A><br>
+</blockquote>
+
+<A NAME=speech8><b>Second Witch</b></a>
+<blockquote>
+<A NAME=1.1.10>Paddock calls.</A><br>
+</blockquote>
+
+<A NAME=speech9><b>Third Witch</b></a>
+<blockquote>
+<A NAME=1.1.11>Anon.</A><br>
+</blockquote>
+
+<A NAME=speech10><b>ALL</b></a>
+<blockquote>
+<A NAME=1.1.12>Fair is foul, and foul is fair:</A><br>
+<A NAME=1.1.13>Hover through the fog and filthy air.</A><br>
+<p><i>Exeunt</i></p>
+</blockquote>
+<h3>SCENE II. A camp near Forres.</h3>
+<p><blockquote>
+<i>Alarum within. Enter DUNCAN, MALCOLM, DONALBAIN, LENNOX, with Attendants, meeting a bleeding Sergeant</i>
+</blockquote>
+
+<A NAME=speech1><b>DUNCAN</b></a>
+<blockquote>
+<A NAME=1.2.1>What bloody man is that? He can report,</A><br>
+<A NAME=1.2.2>As seemeth by his plight, of the revolt</A><br>
+<A NAME=1.2.3>The newest state.</A><br>
+</blockquote>
+
+<A NAME=speech2><b>MALCOLM</b></a>
+<blockquote>
+<A NAME=1.2.4>                  This is the sergeant</A><br>
+<A NAME=1.2.5>Who like a good and hardy soldier fought</A><br>
+<A NAME=1.2.6>'Gainst my captivity. Hail, brave friend!</A><br>
+<A NAME=1.2.7>Say to the king the knowledge of the broil</A><br>
+<A NAME=1.2.8>As thou didst leave it.</A><br>
+</blockquote>
+
+<A NAME=speech3><b>Sergeant</b></a>
+<blockquote>
+<A NAME=1.2.9>Doubtful it stood;</A><br>
+<A NAME=1.2.10>As two spent swimmers, that do cling together</A><br>
+<A NAME=1.2.11>And choke their art. The merciless Macdonwald--</A><br>
+<A NAME=1.2.12>Worthy to be a rebel, for to that</A><br>
+<A NAME=1.2.13>The multiplying villanies of nature</A><br>
+<A NAME=1.2.14>Do swarm upon him--from the western isles</A><br>
+<A NAME=1.2.15>Of kerns and gallowglasses is supplied;</A><br>
+<A NAME=1.2.16>And fortune, on his damned quarrel smiling,</A><br>
+<A NAME=1.2.17>Show'd like a rebel's whore: but all's too weak:</A><br>
+<A NAME=1.2.18>For brave Macbeth--well he deserves that name--</A><br>
+<A NAME=1.2.19>Disdaining fortune, with his brandish'd steel,</A><br>
+<A NAME=1.2.20>Which smoked with bloody execution,</A><br>
+<A NAME=1.2.21>Like valour's minion carved out his passage</A><br>
+<A NAME=1.2.22>Till he faced the slave;</A><br>
+<A NAME=1.2.23>Which ne'er shook hands, nor bade farewell to him,</A><br>
+<A NAME=1.2.24>Till he unseam'd him from the nave to the chaps,</A><br>
+<A NAME=1.2.25>And fix'd his head upon our battlements.</A><br>
+</blockquote>
+
+<A NAME=speech4><b>DUNCAN</b></a>
+<blockquote>
+<A NAME=1.2.26>O valiant cousin! worthy gentleman!</A><br>
+</blockquote>
+
+<A NAME=speech5><b>Sergeant</b></a>
+<blockquote>
+<A NAME=1.2.27>As whence the sun 'gins his reflection</A><br>
+<A NAME=1.2.28>Shipwrecking storms and direful thunders break,</A><br>
+<A NAME=1.2.29>So from that spring whence comfort seem'd to come</A><br>
+<A NAME=1.2.30>Discomfort swells. Mark, king of Scotland, mark:</A><br>
+<A NAME=1.2.31>No sooner justice had with valour arm'd</A><br>
+<A NAME=1.2.32>Compell'd these skipping kerns to trust their heels,</A><br>
+<A NAME=1.2.33>But the Norweyan lord surveying vantage,</A><br>
+<A NAME=1.2.34>With furbish'd arms and new supplies of men</A><br>
+<A NAME=1.2.35>Began a fresh assault.</A><br>
+</blockquote>
+
+<A NAME=speech6><b>DUNCAN</b></a>
+<blockquote>
+<A NAME=1.2.36>Dismay'd not this</A><br>
+<A NAME=1.2.37>Our captains, Macbeth and Banquo?</A><br>
+</blockquote>
+
+<A NAME=speech7><b>Sergeant</b></a>
+<blockquote>
+<A NAME=1.2.38>Yes;</A><br>
+<A NAME=1.2.39>As sparrows eagles, or the hare the lion.</A><br>
+<A NAME=1.2.40>If I say sooth, I must report they were</A><br>
+<A NAME=1.2.41>As cannons overcharged with double cracks, so they</A><br>
+<A NAME=1.2.42>Doubly redoubled strokes upon the foe:</A><br>
+<A NAME=1.2.43>Except they meant to bathe in reeking wounds,</A><br>
+<A NAME=1.2.44>Or memorise another Golgotha,</A><br>
+<A NAME=1.2.45>I cannot tell.</A><br>
+<A NAME=1.2.46>But I am faint, my gashes cry for help.</A><br>
+</blockquote>
+
+<A NAME=speech8><b>DUNCAN</b></a>
+<blockquote>
+<A NAME=1.2.47>So well thy words become thee as thy wounds;</A><br>
+<A NAME=1.2.48>They smack of honour both. Go get him surgeons.</A><br>
+<p><i>Exit Sergeant, attended</i></p>
+<A NAME=1.2.49>Who comes here?</A><br>
+<p><i>Enter ROSS</i></p>
+</blockquote>
+
+<A NAME=speech9><b>MALCOLM</b></a>
+<blockquote>
+<A NAME=1.2.50>                  The worthy thane of Ross.</A><br>
+</blockquote>
+
+<A NAME=speech10><b>LENNOX</b></a>
+<blockquote>
+<A NAME=1.2.51>What a haste looks through his eyes! So should he look</A><br>
+<A NAME=1.2.52>That seems to speak things strange.</A><br>
+</blockquote>
+
+<A NAME=speech11><b>ROSS</b></a>
+<blockquote>
+<A NAME=1.2.53>God save the king!</A><br>
+</blockquote>
+
+<A NAME=speech12><b>DUNCAN</b></a>
+<blockquote>
+<A NAME=1.2.54>Whence camest thou, worthy thane?</A><br>
+</blockquote>
+
+<A NAME=speech13><b>ROSS</b></a>
+<blockquote>
+<A NAME=1.2.55>From Fife, great king;</A><br>
+<A NAME=1.2.56>Where the Norweyan banners flout the sky</A><br>
+<A NAME=1.2.57>And fan our people cold. Norway himself,</A><br>
+<A NAME=1.2.58>With terrible numbers,</A><br>
+<A NAME=1.2.59>Assisted by that most disloyal traitor</A><br>
+<A NAME=1.2.60>The thane of Cawdor, began a dismal conflict;</A><br>
+<A NAME=1.2.61>Till that Bellona's bridegroom, lapp'd in proof,</A><br>
+<A NAME=1.2.62>Confronted him with self-comparisons,</A><br>
+<A NAME=1.2.63>Point against point rebellious, arm 'gainst arm.</A><br>
+<A NAME=1.2.64>Curbing his lavish spirit: and, to conclude,</A><br>
+<A NAME=1.2.65>The victory fell on us.</A><br>
+</blockquote>
+
+<A NAME=speech14><b>DUNCAN</b></a>
+<blockquote>
+<A NAME=1.2.66>Great happiness!</A><br>
+</blockquote>
+
+<A NAME=speech15><b>ROSS</b></a>
+<blockquote>
+<A NAME=1.2.67>That now</A><br>
+<A NAME=1.2.68>Sweno, the Norways' king, craves composition:</A><br>
+<A NAME=1.2.69>Nor would we deign him burial of his men</A><br>
+<A NAME=1.2.70>Till he disbursed at Saint Colme's inch</A><br>
+<A NAME=1.2.71>Ten thousand dollars to our general use.</A><br>
+</blockquote>
+
+<A NAME=speech16><b>DUNCAN</b></a>
+<blockquote>
+<A NAME=1.2.72>No more that thane of Cawdor shall deceive</A><br>
+<A NAME=1.2.73>Our bosom interest: go pronounce his present death,</A><br>
+<A NAME=1.2.74>And with his former title greet Macbeth.</A><br>
+</blockquote>
+
+<A NAME=speech17><b>ROSS</b></a>
+<blockquote>
+<A NAME=1.2.75>I'll see it done.</A><br>
+</blockquote>
+
+<A NAME=speech18><b>DUNCAN</b></a>
+<blockquote>
+<A NAME=1.2.76>What he hath lost noble Macbeth hath won.</A><br>
+<p><i>Exeunt</i></p>
+</blockquote>
+<h3>SCENE III. A heath near Forres.</h3>
+<p><blockquote>
+<i>Thunder. Enter the three Witches</i>
+</blockquote>
+
+<A NAME=speech1><b>First Witch</b></a>
+<blockquote>
+<A NAME=1.3.1>Where hast thou been, sister?</A><br>
+</blockquote>
+
+<A NAME=speech2><b>Second Witch</b></a>
+<blockquote>
+<A NAME=1.3.2>Killing swine.</A><br>
+</blockquote>
+
+<A NAME=speech3><b>Third Witch</b></a>
+<blockquote>
+<A NAME=1.3.3>Sister, where thou?</A><br>
+</blockquote>
+
+<A NAME=speech4><b>First Witch</b></a>
+<blockquote>
+<A NAME=1.3.4>A sailor's wife had chestnuts in her lap,</A><br>
+<A NAME=1.3.5>And munch'd, and munch'd, and munch'd:--</A><br>
+<A NAME=1.3.6>'Give me,' quoth I:</A><br>
+<A NAME=1.3.7>'Aroint thee, witch!' the rump-fed ronyon cries.</A><br>
+<A NAME=1.3.8>Her husband's to Aleppo gone, master o' the Tiger:</A><br>
+<A NAME=1.3.9>But in a sieve I'll thither sail,</A><br>
+<A NAME=1.3.10>And, like a rat without a tail,</A><br>
+<A NAME=1.3.11>I'll do, I'll do, and I'll do.</A><br>
+</blockquote>
+
+<A NAME=speech5><b>Second Witch</b></a>
+<blockquote>
+<A NAME=1.3.12>I'll give thee a wind.</A><br>
+</blockquote>
+
+<A NAME=speech6><b>First Witch</b></a>
+<blockquote>
+<A NAME=1.3.13>Thou'rt kind.</A><br>
+</blockquote>
+
+<A NAME=speech7><b>Third Witch</b></a>
+<blockquote>
+<A NAME=1.3.14>And I another.</A><br>
+</blockquote>
+
+<A NAME=speech8><b>First Witch</b></a>
+<blockquote>
+<A NAME=1.3.15>I myself have all the other,</A><br>
+<A NAME=1.3.16>And the very ports they blow,</A><br>
+<A NAME=1.3.17>All the quarters that they know</A><br>
+<A NAME=1.3.18>I' the shipman's card.</A><br>
+<A NAME=1.3.19>I will drain him dry as hay:</A><br>
+<A NAME=1.3.20>Sleep shall neither night nor day</A><br>
+<A NAME=1.3.21>Hang upon his pent-house lid;</A><br>
+<A NAME=1.3.22>He shall live a man forbid:</A><br>
+<A NAME=1.3.23>Weary se'nnights nine times nine</A><br>
+<A NAME=1.3.24>Shall he dwindle, peak and pine:</A><br>
+<A NAME=1.3.25>Though his bark cannot be lost,</A><br>
+<A NAME=1.3.26>Yet it shall be tempest-tost.</A><br>
+<A NAME=1.3.27>Look what I have.</A><br>
+</blockquote>
+
+<A NAME=speech9><b>Second Witch</b></a>
+<blockquote>
+<A NAME=1.3.28>Show me, show me.</A><br>
+</blockquote>
+
+<A NAME=speech10><b>First Witch</b></a>
+<blockquote>
+<A NAME=1.3.29>Here I have a pilot's thumb,</A><br>
+<A NAME=1.3.30>Wreck'd as homeward he did come.</A><br>
+<p><i>Drum within</i></p>
+</blockquote>
+
+<A NAME=speech11><b>Third Witch</b></a>
+<blockquote>
+<A NAME=1.3.31>A drum, a drum!</A><br>
+<A NAME=1.3.32>Macbeth doth come.</A><br>
+</blockquote>
+
+<A NAME=speech12><b>ALL</b></a>
+<blockquote>
+<A NAME=1.3.33>The weird sisters, hand in hand,</A><br>
+<A NAME=1.3.34>Posters of the sea and land,</A><br>
+<A NAME=1.3.35>Thus do go about, about:</A><br>
+<A NAME=1.3.36>Thrice to thine and thrice to mine</A><br>
+<A NAME=1.3.37>And thrice again, to make up nine.</A><br>
+<A NAME=1.3.38>Peace! the charm's wound up.</A><br>
+<p><i>Enter MACBETH and BANQUO</i></p>
+</blockquote>
+
+<A NAME=speech13><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.3.39>So foul and fair a day I have not seen.</A><br>
+</blockquote>
+
+<A NAME=speech14><b>BANQUO</b></a>
+<blockquote>
+<A NAME=1.3.40>How far is't call'd to Forres? What are these</A><br>
+<A NAME=1.3.41>So wither'd and so wild in their attire,</A><br>
+<A NAME=1.3.42>That look not like the inhabitants o' the earth,</A><br>
+<A NAME=1.3.43>And yet are on't? Live you? or are you aught</A><br>
+<A NAME=1.3.44>That man may question? You seem to understand me,</A><br>
+<A NAME=1.3.45>By each at once her chappy finger laying</A><br>
+<A NAME=1.3.46>Upon her skinny lips: you should be women,</A><br>
+<A NAME=1.3.47>And yet your beards forbid me to interpret</A><br>
+<A NAME=1.3.48>That you are so.</A><br>
+</blockquote>
+
+<A NAME=speech15><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.3.49>                  Speak, if you can: what are you?</A><br>
+</blockquote>
+
+<A NAME=speech16><b>First Witch</b></a>
+<blockquote>
+<A NAME=1.3.50>All hail, Macbeth! hail to thee, thane of Glamis!</A><br>
+</blockquote>
+
+<A NAME=speech17><b>Second Witch</b></a>
+<blockquote>
+<A NAME=1.3.51>All hail, Macbeth, hail to thee, thane of Cawdor!</A><br>
+</blockquote>
+
+<A NAME=speech18><b>Third Witch</b></a>
+<blockquote>
+<A NAME=1.3.52>All hail, Macbeth, thou shalt be king hereafter!</A><br>
+</blockquote>
+
+<A NAME=speech19><b>BANQUO</b></a>
+<blockquote>
+<A NAME=1.3.53>Good sir, why do you start; and seem to fear</A><br>
+<A NAME=1.3.54>Things that do sound so fair? I' the name of truth,</A><br>
+<A NAME=1.3.55>Are ye fantastical, or that indeed</A><br>
+<A NAME=1.3.56>Which outwardly ye show? My noble partner</A><br>
+<A NAME=1.3.57>You greet with present grace and great prediction</A><br>
+<A NAME=1.3.58>Of noble having and of royal hope,</A><br>
+<A NAME=1.3.59>That he seems rapt withal: to me you speak not.</A><br>
+<A NAME=1.3.60>If you can look into the seeds of time,</A><br>
+<A NAME=1.3.61>And say which grain will grow and which will not,</A><br>
+<A NAME=1.3.62>Speak then to me, who neither beg nor fear</A><br>
+<A NAME=1.3.63>Your favours nor your hate.</A><br>
+</blockquote>
+
+<A NAME=speech20><b>First Witch</b></a>
+<blockquote>
+<A NAME=1.3.64>Hail!</A><br>
+</blockquote>
+
+<A NAME=speech21><b>Second Witch</b></a>
+<blockquote>
+<A NAME=1.3.65>Hail!</A><br>
+</blockquote>
+
+<A NAME=speech22><b>Third Witch</b></a>
+<blockquote>
+<A NAME=1.3.66>Hail!</A><br>
+</blockquote>
+
+<A NAME=speech23><b>First Witch</b></a>
+<blockquote>
+<A NAME=1.3.67>Lesser than Macbeth, and greater.</A><br>
+</blockquote>
+
+<A NAME=speech24><b>Second Witch</b></a>
+<blockquote>
+<A NAME=1.3.68>Not so happy, yet much happier.</A><br>
+</blockquote>
+
+<A NAME=speech25><b>Third Witch</b></a>
+<blockquote>
+<A NAME=1.3.69>Thou shalt get kings, though thou be none:</A><br>
+<A NAME=1.3.70>So all hail, Macbeth and Banquo!</A><br>
+</blockquote>
+
+<A NAME=speech26><b>First Witch</b></a>
+<blockquote>
+<A NAME=1.3.71>Banquo and Macbeth, all hail!</A><br>
+</blockquote>
+
+<A NAME=speech27><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.3.72>Stay, you imperfect speakers, tell me more:</A><br>
+<A NAME=1.3.73>By Sinel's death I know I am thane of Glamis;</A><br>
+<A NAME=1.3.74>But how of Cawdor? the thane of Cawdor lives,</A><br>
+<A NAME=1.3.75>A prosperous gentleman; and to be king</A><br>
+<A NAME=1.3.76>Stands not within the prospect of belief,</A><br>
+<A NAME=1.3.77>No more than to be Cawdor. Say from whence</A><br>
+<A NAME=1.3.78>You owe this strange intelligence? or why</A><br>
+<A NAME=1.3.79>Upon this blasted heath you stop our way</A><br>
+<A NAME=1.3.80>With such prophetic greeting? Speak, I charge you.</A><br>
+<p><i>Witches vanish</i></p>
+</blockquote>
+
+<A NAME=speech28><b>BANQUO</b></a>
+<blockquote>
+<A NAME=1.3.81>The earth hath bubbles, as the water has,</A><br>
+<A NAME=1.3.82>And these are of them. Whither are they vanish'd?</A><br>
+</blockquote>
+
+<A NAME=speech29><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.3.83>Into the air; and what seem'd corporal melted</A><br>
+<A NAME=1.3.84>As breath into the wind. Would they had stay'd!</A><br>
+</blockquote>
+
+<A NAME=speech30><b>BANQUO</b></a>
+<blockquote>
+<A NAME=1.3.85>Were such things here as we do speak about?</A><br>
+<A NAME=1.3.86>Or have we eaten on the insane root</A><br>
+<A NAME=1.3.87>That takes the reason prisoner?</A><br>
+</blockquote>
+
+<A NAME=speech31><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.3.88>Your children shall be kings.</A><br>
+</blockquote>
+
+<A NAME=speech32><b>BANQUO</b></a>
+<blockquote>
+<A NAME=1.3.89>You shall be king.</A><br>
+</blockquote>
+
+<A NAME=speech33><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.3.90>And thane of Cawdor too: went it not so?</A><br>
+</blockquote>
+
+<A NAME=speech34><b>BANQUO</b></a>
+<blockquote>
+<A NAME=1.3.91>To the selfsame tune and words. Who's here?</A><br>
+<p><i>Enter ROSS and ANGUS</i></p>
+</blockquote>
+
+<A NAME=speech35><b>ROSS</b></a>
+<blockquote>
+<A NAME=1.3.92>The king hath happily received, Macbeth,</A><br>
+<A NAME=1.3.93>The news of thy success; and when he reads</A><br>
+<A NAME=1.3.94>Thy personal venture in the rebels' fight,</A><br>
+<A NAME=1.3.95>His wonders and his praises do contend</A><br>
+<A NAME=1.3.96>Which should be thine or his: silenced with that,</A><br>
+<A NAME=1.3.97>In viewing o'er the rest o' the selfsame day,</A><br>
+<A NAME=1.3.98>He finds thee in the stout Norweyan ranks,</A><br>
+<A NAME=1.3.99>Nothing afeard of what thyself didst make,</A><br>
+<A NAME=1.3.100>Strange images of death. As thick as hail</A><br>
+<A NAME=1.3.101>Came post with post; and every one did bear</A><br>
+<A NAME=1.3.102>Thy praises in his kingdom's great defence,</A><br>
+<A NAME=1.3.103>And pour'd them down before him.</A><br>
+</blockquote>
+
+<A NAME=speech36><b>ANGUS</b></a>
+<blockquote>
+<A NAME=1.3.104>We are sent</A><br>
+<A NAME=1.3.105>To give thee from our royal master thanks;</A><br>
+<A NAME=1.3.106>Only to herald thee into his sight,</A><br>
+<A NAME=1.3.107>Not pay thee.</A><br>
+</blockquote>
+
+<A NAME=speech37><b>ROSS</b></a>
+<blockquote>
+<A NAME=1.3.108>And, for an earnest of a greater honour,</A><br>
+<A NAME=1.3.109>He bade me, from him, call thee thane of Cawdor:</A><br>
+<A NAME=1.3.110>In which addition, hail, most worthy thane!</A><br>
+<A NAME=1.3.111>For it is thine.</A><br>
+</blockquote>
+
+<A NAME=speech38><b>BANQUO</b></a>
+<blockquote>
+<A NAME=1.3.112>                  What, can the devil speak true?</A><br>
+</blockquote>
+
+<A NAME=speech39><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.3.113>The thane of Cawdor lives: why do you dress me</A><br>
+<A NAME=1.3.114>In borrow'd robes?</A><br>
+</blockquote>
+
+<A NAME=speech40><b>ANGUS</b></a>
+<blockquote>
+<A NAME=1.3.115>                  Who was the thane lives yet;</A><br>
+<A NAME=1.3.116>But under heavy judgment bears that life</A><br>
+<A NAME=1.3.117>Which he deserves to lose. Whether he was combined</A><br>
+<A NAME=1.3.118>With those of Norway, or did line the rebel</A><br>
+<A NAME=1.3.119>With hidden help and vantage, or that with both</A><br>
+<A NAME=1.3.120>He labour'd in his country's wreck, I know not;</A><br>
+<A NAME=1.3.121>But treasons capital, confess'd and proved,</A><br>
+<A NAME=1.3.122>Have overthrown him.</A><br>
+</blockquote>
+
+<A NAME=speech41><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.3.123>[Aside]  Glamis, and thane of Cawdor!</A><br>
+<A NAME=1.3.124>The greatest is behind.</A><br>
+<p><i>To ROSS and ANGUS</i></p>
+<A NAME=1.3.125>Thanks for your pains.</A><br>
+<p><i>To BANQUO</i></p>
+<A NAME=1.3.126>Do you not hope your children shall be kings,</A><br>
+<A NAME=1.3.127>When those that gave the thane of Cawdor to me</A><br>
+<A NAME=1.3.128>Promised no less to them?</A><br>
+</blockquote>
+
+<A NAME=speech42><b>BANQUO</b></a>
+<blockquote>
+<A NAME=1.3.129>That trusted home</A><br>
+<A NAME=1.3.130>Might yet enkindle you unto the crown,</A><br>
+<A NAME=1.3.131>Besides the thane of Cawdor. But 'tis strange:</A><br>
+<A NAME=1.3.132>And oftentimes, to win us to our harm,</A><br>
+<A NAME=1.3.133>The instruments of darkness tell us truths,</A><br>
+<A NAME=1.3.134>Win us with honest trifles, to betray's</A><br>
+<A NAME=1.3.135>In deepest consequence.</A><br>
+<A NAME=1.3.136>Cousins, a word, I pray you.</A><br>
+</blockquote>
+
+<A NAME=speech43><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.3.137>[Aside] Two truths are told,</A><br>
+<A NAME=1.3.138>As happy prologues to the swelling act</A><br>
+<A NAME=1.3.139>Of the imperial theme.--I thank you, gentlemen.</A><br>
+<p><i>Aside</i></p>
+<A NAME=1.3.140>Cannot be ill, cannot be good: if ill,</A><br>
+<A NAME=1.3.141>Why hath it given me earnest of success,</A><br>
+<A NAME=1.3.142>Commencing in a truth? I am thane of Cawdor:</A><br>
+<A NAME=1.3.143>If good, why do I yield to that suggestion</A><br>
+<A NAME=1.3.144>Whose horrid image doth unfix my hair</A><br>
+<A NAME=1.3.145>And make my seated heart knock at my ribs,</A><br>
+<A NAME=1.3.146>Against the use of nature? Present fears</A><br>
+<A NAME=1.3.147>Are less than horrible imaginings:</A><br>
+<A NAME=1.3.148>My thought, whose murder yet is but fantastical,</A><br>
+<A NAME=1.3.149>Shakes so my single state of man that function</A><br>
+<A NAME=1.3.150>Is smother'd in surmise, and nothing is</A><br>
+<A NAME=1.3.151>But what is not.</A><br>
+</blockquote>
+
+<A NAME=speech44><b>BANQUO</b></a>
+<blockquote>
+<A NAME=1.3.152>                  Look, how our partner's rapt.</A><br>
+</blockquote>
+
+<A NAME=speech45><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.3.153>[Aside]  If chance will have me king, why, chance may crown me,</A><br>
+<A NAME=1.3.154>Without my stir.</A><br>
+</blockquote>
+
+<A NAME=speech46><b>BANQUO</b></a>
+<blockquote>
+<A NAME=1.3.155>                  New horrors come upon him,</A><br>
+<A NAME=1.3.156>Like our strange garments, cleave not to their mould</A><br>
+<A NAME=1.3.157>But with the aid of use.</A><br>
+</blockquote>
+
+<A NAME=speech47><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.3.158>[Aside]                Come what come may,</A><br>
+<A NAME=1.3.159>Time and the hour runs through the roughest day.</A><br>
+</blockquote>
+
+<A NAME=speech48><b>BANQUO</b></a>
+<blockquote>
+<A NAME=1.3.160>Worthy Macbeth, we stay upon your leisure.</A><br>
+</blockquote>
+
+<A NAME=speech49><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.3.161>Give me your favour: my dull brain was wrought</A><br>
+<A NAME=1.3.162>With things forgotten. Kind gentlemen, your pains</A><br>
+<A NAME=1.3.163>Are register'd where every day I turn</A><br>
+<A NAME=1.3.164>The leaf to read them. Let us toward the king.</A><br>
+<A NAME=1.3.165>Think upon what hath chanced, and, at more time,</A><br>
+<A NAME=1.3.166>The interim having weigh'd it, let us speak</A><br>
+<A NAME=1.3.167>Our free hearts each to other.</A><br>
+</blockquote>
+
+<A NAME=speech50><b>BANQUO</b></a>
+<blockquote>
+<A NAME=1.3.168>Very gladly.</A><br>
+</blockquote>
+
+<A NAME=speech51><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.3.169>Till then, enough. Come, friends.</A><br>
+<p><i>Exeunt</i></p>
+</blockquote>
+<h3>SCENE IV. Forres. The palace.</h3>
+<p><blockquote>
+<i>Flourish. Enter DUNCAN, MALCOLM, DONALBAIN, LENNOX, and Attendants</i>
+</blockquote>
+
+<A NAME=speech1><b>DUNCAN</b></a>
+<blockquote>
+<A NAME=1.4.1>Is execution done on Cawdor? Are not</A><br>
+<A NAME=1.4.2>Those in commission yet return'd?</A><br>
+</blockquote>
+
+<A NAME=speech2><b>MALCOLM</b></a>
+<blockquote>
+<A NAME=1.4.3>My liege,</A><br>
+<A NAME=1.4.4>They are not yet come back. But I have spoke</A><br>
+<A NAME=1.4.5>With one that saw him die: who did report</A><br>
+<A NAME=1.4.6>That very frankly he confess'd his treasons,</A><br>
+<A NAME=1.4.7>Implored your highness' pardon and set forth</A><br>
+<A NAME=1.4.8>A deep repentance: nothing in his life</A><br>
+<A NAME=1.4.9>Became him like the leaving it; he died</A><br>
+<A NAME=1.4.10>As one that had been studied in his death</A><br>
+<A NAME=1.4.11>To throw away the dearest thing he owed,</A><br>
+<A NAME=1.4.12>As 'twere a careless trifle.</A><br>
+</blockquote>
+
+<A NAME=speech3><b>DUNCAN</b></a>
+<blockquote>
+<A NAME=1.4.13>There's no art</A><br>
+<A NAME=1.4.14>To find the mind's construction in the face:</A><br>
+<A NAME=1.4.15>He was a gentleman on whom I built</A><br>
+<A NAME=1.4.16>An absolute trust.</A><br>
+<p><i>Enter MACBETH, BANQUO, ROSS, and ANGUS</i></p>
+<A NAME=1.4.17>O worthiest cousin!</A><br>
+<A NAME=1.4.18>The sin of my ingratitude even now</A><br>
+<A NAME=1.4.19>Was heavy on me: thou art so far before</A><br>
+<A NAME=1.4.20>That swiftest wing of recompense is slow</A><br>
+<A NAME=1.4.21>To overtake thee. Would thou hadst less deserved,</A><br>
+<A NAME=1.4.22>That the proportion both of thanks and payment</A><br>
+<A NAME=1.4.23>Might have been mine! only I have left to say,</A><br>
+<A NAME=1.4.24>More is thy due than more than all can pay.</A><br>
+</blockquote>
+
+<A NAME=speech4><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.4.25>The service and the loyalty I owe,</A><br>
+<A NAME=1.4.26>In doing it, pays itself. Your highness' part</A><br>
+<A NAME=1.4.27>Is to receive our duties; and our duties</A><br>
+<A NAME=1.4.28>Are to your throne and state children and servants,</A><br>
+<A NAME=1.4.29>Which do but what they should, by doing every thing</A><br>
+<A NAME=1.4.30>Safe toward your love and honour.</A><br>
+</blockquote>
+
+<A NAME=speech5><b>DUNCAN</b></a>
+<blockquote>
+<A NAME=1.4.31>Welcome hither:</A><br>
+<A NAME=1.4.32>I have begun to plant thee, and will labour</A><br>
+<A NAME=1.4.33>To make thee full of growing. Noble Banquo,</A><br>
+<A NAME=1.4.34>That hast no less deserved, nor must be known</A><br>
+<A NAME=1.4.35>No less to have done so, let me enfold thee</A><br>
+<A NAME=1.4.36>And hold thee to my heart.</A><br>
+</blockquote>
+
+<A NAME=speech6><b>BANQUO</b></a>
+<blockquote>
+<A NAME=1.4.37>There if I grow,</A><br>
+<A NAME=1.4.38>The harvest is your own.</A><br>
+</blockquote>
+
+<A NAME=speech7><b>DUNCAN</b></a>
+<blockquote>
+<A NAME=1.4.39>My plenteous joys,</A><br>
+<A NAME=1.4.40>Wanton in fulness, seek to hide themselves</A><br>
+<A NAME=1.4.41>In drops of sorrow. Sons, kinsmen, thanes,</A><br>
+<A NAME=1.4.42>And you whose places are the nearest, know</A><br>
+<A NAME=1.4.43>We will establish our estate upon</A><br>
+<A NAME=1.4.44>Our eldest, Malcolm, whom we name hereafter</A><br>
+<A NAME=1.4.45>The Prince of Cumberland; which honour must</A><br>
+<A NAME=1.4.46>Not unaccompanied invest him only,</A><br>
+<A NAME=1.4.47>But signs of nobleness, like stars, shall shine</A><br>
+<A NAME=1.4.48>On all deservers. From hence to Inverness,</A><br>
+<A NAME=1.4.49>And bind us further to you.</A><br>
+</blockquote>
+
+<A NAME=speech8><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.4.50>The rest is labour, which is not used for you:</A><br>
+<A NAME=1.4.51>I'll be myself the harbinger and make joyful</A><br>
+<A NAME=1.4.52>The hearing of my wife with your approach;</A><br>
+<A NAME=1.4.53>So humbly take my leave.</A><br>
+</blockquote>
+
+<A NAME=speech9><b>DUNCAN</b></a>
+<blockquote>
+<A NAME=1.4.54>My worthy Cawdor!</A><br>
+</blockquote>
+
+<A NAME=speech10><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.4.55>[Aside]  The Prince of Cumberland! that is a step</A><br>
+<A NAME=1.4.56>On which I must fall down, or else o'erleap,</A><br>
+<A NAME=1.4.57>For in my way it lies. Stars, hide your fires;</A><br>
+<A NAME=1.4.58>Let not light see my black and deep desires:</A><br>
+<A NAME=1.4.59>The eye wink at the hand; yet let that be,</A><br>
+<A NAME=1.4.60>Which the eye fears, when it is done, to see.</A><br>
+<p><i>Exit</i></p>
+</blockquote>
+
+<A NAME=speech11><b>DUNCAN</b></a>
+<blockquote>
+<A NAME=1.4.61>True, worthy Banquo; he is full so valiant,</A><br>
+<A NAME=1.4.62>And in his commendations I am fed;</A><br>
+<A NAME=1.4.63>It is a banquet to me. Let's after him,</A><br>
+<A NAME=1.4.64>Whose care is gone before to bid us welcome:</A><br>
+<A NAME=1.4.65>It is a peerless kinsman.</A><br>
+<p><i>Flourish. Exeunt</i></p>
+</blockquote>
+<h3>SCENE V. Inverness. Macbeth's castle.</h3>
+<p><blockquote>
+<i>Enter LADY MACBETH, reading a letter</i>
+</blockquote>
+
+<A NAME=speech1><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=1.5.1>'They met me in the day of success: and I have</A><br>
+<A NAME=1.5.2>learned by the perfectest report, they have more in</A><br>
+<A NAME=1.5.3>them than mortal knowledge. When I burned in desire</A><br>
+<A NAME=1.5.4>to question them further, they made themselves air,</A><br>
+<A NAME=1.5.5>into which they vanished. Whiles I stood rapt in</A><br>
+<A NAME=1.5.6>the wonder of it, came missives from the king, who</A><br>
+<A NAME=1.5.7>all-hailed me 'Thane of Cawdor;' by which title,</A><br>
+<A NAME=1.5.8>before, these weird sisters saluted me, and referred</A><br>
+<A NAME=1.5.9>me to the coming on of time, with 'Hail, king that</A><br>
+<A NAME=1.5.10>shalt be!' This have I thought good to deliver</A><br>
+<A NAME=1.5.11>thee, my dearest partner of greatness, that thou</A><br>
+<A NAME=1.5.12>mightst not lose the dues of rejoicing, by being</A><br>
+<A NAME=1.5.13>ignorant of what greatness is promised thee. Lay it</A><br>
+<A NAME=1.5.14>to thy heart, and farewell.'</A><br>
+<A NAME=1.5.15>Glamis thou art, and Cawdor; and shalt be</A><br>
+<A NAME=1.5.16>What thou art promised: yet do I fear thy nature;</A><br>
+<A NAME=1.5.17>It is too full o' the milk of human kindness</A><br>
+<A NAME=1.5.18>To catch the nearest way: thou wouldst be great;</A><br>
+<A NAME=1.5.19>Art not without ambition, but without</A><br>
+<A NAME=1.5.20>The illness should attend it: what thou wouldst highly,</A><br>
+<A NAME=1.5.21>That wouldst thou holily; wouldst not play false,</A><br>
+<A NAME=1.5.22>And yet wouldst wrongly win: thou'ldst have, great Glamis,</A><br>
+<A NAME=1.5.23>That which cries 'Thus thou must do, if thou have it;</A><br>
+<A NAME=1.5.24>And that which rather thou dost fear to do</A><br>
+<A NAME=1.5.25>Than wishest should be undone.' Hie thee hither,</A><br>
+<A NAME=1.5.26>That I may pour my spirits in thine ear;</A><br>
+<A NAME=1.5.27>And chastise with the valour of my tongue</A><br>
+<A NAME=1.5.28>All that impedes thee from the golden round,</A><br>
+<A NAME=1.5.29>Which fate and metaphysical aid doth seem</A><br>
+<A NAME=1.5.30>To have thee crown'd withal.</A><br>
+<p><i>Enter a Messenger</i></p>
+<A NAME=1.5.31>What is your tidings?</A><br>
+</blockquote>
+
+<A NAME=speech2><b>Messenger</b></a>
+<blockquote>
+<A NAME=1.5.32>The king comes here to-night.</A><br>
+</blockquote>
+
+<A NAME=speech3><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=1.5.33>Thou'rt mad to say it:</A><br>
+<A NAME=1.5.34>Is not thy master with him? who, were't so,</A><br>
+<A NAME=1.5.35>Would have inform'd for preparation.</A><br>
+</blockquote>
+
+<A NAME=speech4><b>Messenger</b></a>
+<blockquote>
+<A NAME=1.5.36>So please you, it is true: our thane is coming:</A><br>
+<A NAME=1.5.37>One of my fellows had the speed of him,</A><br>
+<A NAME=1.5.38>Who, almost dead for breath, had scarcely more</A><br>
+<A NAME=1.5.39>Than would make up his message.</A><br>
+</blockquote>
+
+<A NAME=speech5><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=1.5.40>Give him tending;</A><br>
+<A NAME=1.5.41>He brings great news.</A><br>
+<p><i>Exit Messenger</i></p>
+<A NAME=1.5.42>The raven himself is hoarse</A><br>
+<A NAME=1.5.43>That croaks the fatal entrance of Duncan</A><br>
+<A NAME=1.5.44>Under my battlements. Come, you spirits</A><br>
+<A NAME=1.5.45>That tend on mortal thoughts, unsex me here,</A><br>
+<A NAME=1.5.46>And fill me from the crown to the toe top-full</A><br>
+<A NAME=1.5.47>Of direst cruelty! make thick my blood;</A><br>
+<A NAME=1.5.48>Stop up the access and passage to remorse,</A><br>
+<A NAME=1.5.49>That no compunctious visitings of nature</A><br>
+<A NAME=1.5.50>Shake my fell purpose, nor keep peace between</A><br>
+<A NAME=1.5.51>The effect and it! Come to my woman's breasts,</A><br>
+<A NAME=1.5.52>And take my milk for gall, you murdering ministers,</A><br>
+<A NAME=1.5.53>Wherever in your sightless substances</A><br>
+<A NAME=1.5.54>You wait on nature's mischief! Come, thick night,</A><br>
+<A NAME=1.5.55>And pall thee in the dunnest smoke of hell,</A><br>
+<A NAME=1.5.56>That my keen knife see not the wound it makes,</A><br>
+<A NAME=1.5.57>Nor heaven peep through the blanket of the dark,</A><br>
+<A NAME=1.5.58>To cry 'Hold, hold!'</A><br>
+<p><i>Enter MACBETH</i></p>
+<A NAME=1.5.59>Great Glamis! worthy Cawdor!</A><br>
+<A NAME=1.5.60>Greater than both, by the all-hail hereafter!</A><br>
+<A NAME=1.5.61>Thy letters have transported me beyond</A><br>
+<A NAME=1.5.62>This ignorant present, and I feel now</A><br>
+<A NAME=1.5.63>The future in the instant.</A><br>
+</blockquote>
+
+<A NAME=speech6><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.5.64>My dearest love,</A><br>
+<A NAME=1.5.65>Duncan comes here to-night.</A><br>
+</blockquote>
+
+<A NAME=speech7><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=1.5.66>And when goes hence?</A><br>
+</blockquote>
+
+<A NAME=speech8><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.5.67>To-morrow, as he purposes.</A><br>
+</blockquote>
+
+<A NAME=speech9><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=1.5.68>O, never</A><br>
+<A NAME=1.5.69>Shall sun that morrow see!</A><br>
+<A NAME=1.5.70>Your face, my thane, is as a book where men</A><br>
+<A NAME=1.5.71>May read strange matters. To beguile the time,</A><br>
+<A NAME=1.5.72>Look like the time; bear welcome in your eye,</A><br>
+<A NAME=1.5.73>Your hand, your tongue: look like the innocent flower,</A><br>
+<A NAME=1.5.74>But be the serpent under't. He that's coming</A><br>
+<A NAME=1.5.75>Must be provided for: and you shall put</A><br>
+<A NAME=1.5.76>This night's great business into my dispatch;</A><br>
+<A NAME=1.5.77>Which shall to all our nights and days to come</A><br>
+<A NAME=1.5.78>Give solely sovereign sway and masterdom.</A><br>
+</blockquote>
+
+<A NAME=speech10><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.5.79>We will speak further.</A><br>
+</blockquote>
+
+<A NAME=speech11><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=1.5.80>Only look up clear;</A><br>
+<A NAME=1.5.81>To alter favour ever is to fear:</A><br>
+<A NAME=1.5.82>Leave all the rest to me.</A><br>
+<p><i>Exeunt</i></p>
+</blockquote>
+<h3>SCENE VI. Before Macbeth's castle.</h3>
+<p><blockquote>
+<i>Hautboys and torches. Enter DUNCAN, MALCOLM,  DONALBAIN, BANQUO, LENNOX, MACDUFF, ROSS, ANGUS, and Attendants</i>
+</blockquote>
+
+<A NAME=speech1><b>DUNCAN</b></a>
+<blockquote>
+<A NAME=1.6.1>This castle hath a pleasant seat; the air</A><br>
+<A NAME=1.6.2>Nimbly and sweetly recommends itself</A><br>
+<A NAME=1.6.3>Unto our gentle senses.</A><br>
+</blockquote>
+
+<A NAME=speech2><b>BANQUO</b></a>
+<blockquote>
+<A NAME=1.6.4>This guest of summer,</A><br>
+<A NAME=1.6.5>The temple-haunting martlet, does approve,</A><br>
+<A NAME=1.6.6>By his loved mansionry, that the heaven's breath</A><br>
+<A NAME=1.6.7>Smells wooingly here: no jutty, frieze,</A><br>
+<A NAME=1.6.8>Buttress, nor coign of vantage, but this bird</A><br>
+<A NAME=1.6.9>Hath made his pendent bed and procreant cradle:</A><br>
+<A NAME=1.6.10>Where they most breed and haunt, I have observed,</A><br>
+<A NAME=1.6.11>The air is delicate.</A><br>
+<p><i>Enter LADY MACBETH</i></p>
+</blockquote>
+
+<A NAME=speech3><b>DUNCAN</b></a>
+<blockquote>
+<A NAME=1.6.12>See, see, our honour'd hostess!</A><br>
+<A NAME=1.6.13>The love that follows us sometime is our trouble,</A><br>
+<A NAME=1.6.14>Which still we thank as love. Herein I teach you</A><br>
+<A NAME=1.6.15>How you shall bid God 'ild us for your pains,</A><br>
+<A NAME=1.6.16>And thank us for your trouble.</A><br>
+</blockquote>
+
+<A NAME=speech4><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=1.6.17>All our service</A><br>
+<A NAME=1.6.18>In every point twice done and then done double</A><br>
+<A NAME=1.6.19>Were poor and single business to contend</A><br>
+<A NAME=1.6.20>Against those honours deep and broad wherewith</A><br>
+<A NAME=1.6.21>Your majesty loads our house: for those of old,</A><br>
+<A NAME=1.6.22>And the late dignities heap'd up to them,</A><br>
+<A NAME=1.6.23>We rest your hermits.</A><br>
+</blockquote>
+
+<A NAME=speech5><b>DUNCAN</b></a>
+<blockquote>
+<A NAME=1.6.24>Where's the thane of Cawdor?</A><br>
+<A NAME=1.6.25>We coursed him at the heels, and had a purpose</A><br>
+<A NAME=1.6.26>To be his purveyor: but he rides well;</A><br>
+<A NAME=1.6.27>And his great love, sharp as his spur, hath holp him</A><br>
+<A NAME=1.6.28>To his home before us. Fair and noble hostess,</A><br>
+<A NAME=1.6.29>We are your guest to-night.</A><br>
+</blockquote>
+
+<A NAME=speech6><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=1.6.30>Your servants ever</A><br>
+<A NAME=1.6.31>Have theirs, themselves and what is theirs, in compt,</A><br>
+<A NAME=1.6.32>To make their audit at your highness' pleasure,</A><br>
+<A NAME=1.6.33>Still to return your own.</A><br>
+</blockquote>
+
+<A NAME=speech7><b>DUNCAN</b></a>
+<blockquote>
+<A NAME=1.6.34>Give me your hand;</A><br>
+<A NAME=1.6.35>Conduct me to mine host: we love him highly,</A><br>
+<A NAME=1.6.36>And shall continue our graces towards him.</A><br>
+<A NAME=1.6.37>By your leave, hostess.</A><br>
+<p><i>Exeunt</i></p>
+</blockquote>
+<h3>SCENE VII. Macbeth's castle.</h3>
+<p><blockquote>
+<i>Hautboys and torches. Enter a Sewer, and divers  Servants with dishes and service, and pass over the stage. Then enter MACBETH</i>
+</blockquote>
+
+<A NAME=speech1><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.7.1>If it were done when 'tis done, then 'twere well</A><br>
+<A NAME=1.7.2>It were done quickly: if the assassination</A><br>
+<A NAME=1.7.3>Could trammel up the consequence, and catch</A><br>
+<A NAME=1.7.4>With his surcease success; that but this blow</A><br>
+<A NAME=1.7.5>Might be the be-all and the end-all here,</A><br>
+<A NAME=1.7.6>But here, upon this bank and shoal of time,</A><br>
+<A NAME=1.7.7>We'ld jump the life to come. But in these cases</A><br>
+<A NAME=1.7.8>We still have judgment here; that we but teach</A><br>
+<A NAME=1.7.9>Bloody instructions, which, being taught, return</A><br>
+<A NAME=1.7.10>To plague the inventor: this even-handed justice</A><br>
+<A NAME=1.7.11>Commends the ingredients of our poison'd chalice</A><br>
+<A NAME=1.7.12>To our own lips. He's here in double trust;</A><br>
+<A NAME=1.7.13>First, as I am his kinsman and his subject,</A><br>
+<A NAME=1.7.14>Strong both against the deed; then, as his host,</A><br>
+<A NAME=1.7.15>Who should against his murderer shut the door,</A><br>
+<A NAME=1.7.16>Not bear the knife myself. Besides, this Duncan</A><br>
+<A NAME=1.7.17>Hath borne his faculties so meek, hath been</A><br>
+<A NAME=1.7.18>So clear in his great office, that his virtues</A><br>
+<A NAME=1.7.19>Will plead like angels, trumpet-tongued, against</A><br>
+<A NAME=1.7.20>The deep damnation of his taking-off;</A><br>
+<A NAME=1.7.21>And pity, like a naked new-born babe,</A><br>
+<A NAME=1.7.22>Striding the blast, or heaven's cherubim, horsed</A><br>
+<A NAME=1.7.23>Upon the sightless couriers of the air,</A><br>
+<A NAME=1.7.24>Shall blow the horrid deed in every eye,</A><br>
+<A NAME=1.7.25>That tears shall drown the wind. I have no spur</A><br>
+<A NAME=1.7.26>To prick the sides of my intent, but only</A><br>
+<A NAME=1.7.27>Vaulting ambition, which o'erleaps itself</A><br>
+<A NAME=1.7.28>And falls on the other.</A><br>
+<p><i>Enter LADY MACBETH</i></p>
+<A NAME=1.7.29>How now! what news?</A><br>
+</blockquote>
+
+<A NAME=speech2><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=1.7.30>He has almost supp'd: why have you left the chamber?</A><br>
+</blockquote>
+
+<A NAME=speech3><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.7.31>Hath he ask'd for me?</A><br>
+</blockquote>
+
+<A NAME=speech4><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=1.7.32>Know you not he has?</A><br>
+</blockquote>
+
+<A NAME=speech5><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.7.33>We will proceed no further in this business:</A><br>
+<A NAME=1.7.34>He hath honour'd me of late; and I have bought</A><br>
+<A NAME=1.7.35>Golden opinions from all sorts of people,</A><br>
+<A NAME=1.7.36>Which would be worn now in their newest gloss,</A><br>
+<A NAME=1.7.37>Not cast aside so soon.</A><br>
+</blockquote>
+
+<A NAME=speech6><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=1.7.38>Was the hope drunk</A><br>
+<A NAME=1.7.39>Wherein you dress'd yourself? hath it slept since?</A><br>
+<A NAME=1.7.40>And wakes it now, to look so green and pale</A><br>
+<A NAME=1.7.41>At what it did so freely? From this time</A><br>
+<A NAME=1.7.42>Such I account thy love. Art thou afeard</A><br>
+<A NAME=1.7.43>To be the same in thine own act and valour</A><br>
+<A NAME=1.7.44>As thou art in desire? Wouldst thou have that</A><br>
+<A NAME=1.7.45>Which thou esteem'st the ornament of life,</A><br>
+<A NAME=1.7.46>And live a coward in thine own esteem,</A><br>
+<A NAME=1.7.47>Letting 'I dare not' wait upon 'I would,'</A><br>
+<A NAME=1.7.48>Like the poor cat i' the adage?</A><br>
+</blockquote>
+
+<A NAME=speech7><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.7.49>Prithee, peace:</A><br>
+<A NAME=1.7.50>I dare do all that may become a man;</A><br>
+<A NAME=1.7.51>Who dares do more is none.</A><br>
+</blockquote>
+
+<A NAME=speech8><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=1.7.52>What beast was't, then,</A><br>
+<A NAME=1.7.53>That made you break this enterprise to me?</A><br>
+<A NAME=1.7.54>When you durst do it, then you were a man;</A><br>
+<A NAME=1.7.55>And, to be more than what you were, you would</A><br>
+<A NAME=1.7.56>Be so much more the man. Nor time nor place</A><br>
+<A NAME=1.7.57>Did then adhere, and yet you would make both:</A><br>
+<A NAME=1.7.58>They have made themselves, and that their fitness now</A><br>
+<A NAME=1.7.59>Does unmake you. I have given suck, and know</A><br>
+<A NAME=1.7.60>How tender 'tis to love the babe that milks me:</A><br>
+<A NAME=1.7.61>I would, while it was smiling in my face,</A><br>
+<A NAME=1.7.62>Have pluck'd my nipple from his boneless gums,</A><br>
+<A NAME=1.7.63>And dash'd the brains out, had I so sworn as you</A><br>
+<A NAME=1.7.64>Have done to this.</A><br>
+</blockquote>
+
+<A NAME=speech9><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.7.65>                  If we should fail?</A><br>
+</blockquote>
+
+<A NAME=speech10><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=1.7.66>We fail!</A><br>
+<A NAME=1.7.67>But screw your courage to the sticking-place,</A><br>
+<A NAME=1.7.68>And we'll not fail. When Duncan is asleep--</A><br>
+<A NAME=1.7.69>Whereto the rather shall his day's hard journey</A><br>
+<A NAME=1.7.70>Soundly invite him--his two chamberlains</A><br>
+<A NAME=1.7.71>Will I with wine and wassail so convince</A><br>
+<A NAME=1.7.72>That memory, the warder of the brain,</A><br>
+<A NAME=1.7.73>Shall be a fume, and the receipt of reason</A><br>
+<A NAME=1.7.74>A limbeck only: when in swinish sleep</A><br>
+<A NAME=1.7.75>Their drenched natures lie as in a death,</A><br>
+<A NAME=1.7.76>What cannot you and I perform upon</A><br>
+<A NAME=1.7.77>The unguarded Duncan? what not put upon</A><br>
+<A NAME=1.7.78>His spongy officers, who shall bear the guilt</A><br>
+<A NAME=1.7.79>Of our great quell?</A><br>
+</blockquote>
+
+<A NAME=speech11><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.7.80>Bring forth men-children only;</A><br>
+<A NAME=1.7.81>For thy undaunted mettle should compose</A><br>
+<A NAME=1.7.82>Nothing but males. Will it not be received,</A><br>
+<A NAME=1.7.83>When we have mark'd with blood those sleepy two</A><br>
+<A NAME=1.7.84>Of his own chamber and used their very daggers,</A><br>
+<A NAME=1.7.85>That they have done't?</A><br>
+</blockquote>
+
+<A NAME=speech12><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=1.7.86>Who dares receive it other,</A><br>
+<A NAME=1.7.87>As we shall make our griefs and clamour roar</A><br>
+<A NAME=1.7.88>Upon his death?</A><br>
+</blockquote>
+
+<A NAME=speech13><b>MACBETH</b></a>
+<blockquote>
+<A NAME=1.7.89>                  I am settled, and bend up</A><br>
+<A NAME=1.7.90>Each corporal agent to this terrible feat.</A><br>
+<A NAME=1.7.91>Away, and mock the time with fairest show:</A><br>
+<A NAME=1.7.92>False face must hide what the false heart doth know.</A><br>
+<p><i>Exeunt</i></p>
+</blockquote><p>
+<H3>ACT II</h3>
+<h3>SCENE I. Court of Macbeth's castle.</h3>
+<p><blockquote>
+<i>Enter BANQUO, and FLEANCE bearing a torch before him</i>
+</blockquote>
+
+<A NAME=speech1><b>BANQUO</b></a>
+<blockquote>
+<A NAME=2.1.1>How goes the night, boy?</A><br>
+</blockquote>
+
+<A NAME=speech2><b>FLEANCE</b></a>
+<blockquote>
+<A NAME=2.1.2>The moon is down; I have not heard the clock.</A><br>
+</blockquote>
+
+<A NAME=speech3><b>BANQUO</b></a>
+<blockquote>
+<A NAME=2.1.3>And she goes down at twelve.</A><br>
+</blockquote>
+
+<A NAME=speech4><b>FLEANCE</b></a>
+<blockquote>
+<A NAME=2.1.4>I take't, 'tis later, sir.</A><br>
+</blockquote>
+
+<A NAME=speech5><b>BANQUO</b></a>
+<blockquote>
+<A NAME=2.1.5>Hold, take my sword. There's husbandry in heaven;</A><br>
+<A NAME=2.1.6>Their candles are all out. Take thee that too.</A><br>
+<A NAME=2.1.7>A heavy summons lies like lead upon me,</A><br>
+<A NAME=2.1.8>And yet I would not sleep: merciful powers,</A><br>
+<A NAME=2.1.9>Restrain in me the cursed thoughts that nature</A><br>
+<A NAME=2.1.10>Gives way to in repose!</A><br>
+<p><i>Enter MACBETH, and a Servant with a torch</i></p>
+<A NAME=2.1.11>Give me my sword.</A><br>
+<A NAME=2.1.12>Who's there?</A><br>
+</blockquote>
+
+<A NAME=speech6><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.1.13>A friend.</A><br>
+</blockquote>
+
+<A NAME=speech7><b>BANQUO</b></a>
+<blockquote>
+<A NAME=2.1.14>What, sir, not yet at rest? The king's a-bed:</A><br>
+<A NAME=2.1.15>He hath been in unusual pleasure, and</A><br>
+<A NAME=2.1.16>Sent forth great largess to your offices.</A><br>
+<A NAME=2.1.17>This diamond he greets your wife withal,</A><br>
+<A NAME=2.1.18>By the name of most kind hostess; and shut up</A><br>
+<A NAME=2.1.19>In measureless content.</A><br>
+</blockquote>
+
+<A NAME=speech8><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.1.20>Being unprepared,</A><br>
+<A NAME=2.1.21>Our will became the servant to defect;</A><br>
+<A NAME=2.1.22>Which else should free have wrought.</A><br>
+</blockquote>
+
+<A NAME=speech9><b>BANQUO</b></a>
+<blockquote>
+<A NAME=2.1.23>All's well.</A><br>
+<A NAME=2.1.24>I dreamt last night of the three weird sisters:</A><br>
+<A NAME=2.1.25>To you they have show'd some truth.</A><br>
+</blockquote>
+
+<A NAME=speech10><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.1.26>I think not of them:</A><br>
+<A NAME=2.1.27>Yet, when we can entreat an hour to serve,</A><br>
+<A NAME=2.1.28>We would spend it in some words upon that business,</A><br>
+<A NAME=2.1.29>If you would grant the time.</A><br>
+</blockquote>
+
+<A NAME=speech11><b>BANQUO</b></a>
+<blockquote>
+<A NAME=2.1.30>At your kind'st leisure.</A><br>
+</blockquote>
+
+<A NAME=speech12><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.1.31>If you shall cleave to my consent, when 'tis,</A><br>
+<A NAME=2.1.32>It shall make honour for you.</A><br>
+</blockquote>
+
+<A NAME=speech13><b>BANQUO</b></a>
+<blockquote>
+<A NAME=2.1.33>So I lose none</A><br>
+<A NAME=2.1.34>In seeking to augment it, but still keep</A><br>
+<A NAME=2.1.35>My bosom franchised and allegiance clear,</A><br>
+<A NAME=2.1.36>I shall be counsell'd.</A><br>
+</blockquote>
+
+<A NAME=speech14><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.1.37>Good repose the while!</A><br>
+</blockquote>
+
+<A NAME=speech15><b>BANQUO</b></a>
+<blockquote>
+<A NAME=2.1.38>Thanks, sir: the like to you!</A><br>
+<p><i>Exeunt BANQUO and FLEANCE</i></p>
+</blockquote>
+
+<A NAME=speech16><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.1.39>Go bid thy mistress, when my drink is ready,</A><br>
+<A NAME=2.1.40>She strike upon the bell. Get thee to bed.</A><br>
+<p><i>Exit Servant</i></p>
+<A NAME=2.1.41>Is this a dagger which I see before me,</A><br>
+<A NAME=2.1.42>The handle toward my hand? Come, let me clutch thee.</A><br>
+<A NAME=2.1.43>I have thee not, and yet I see thee still.</A><br>
+<A NAME=2.1.44>Art thou not, fatal vision, sensible</A><br>
+<A NAME=2.1.45>To feeling as to sight? or art thou but</A><br>
+<A NAME=2.1.46>A dagger of the mind, a false creation,</A><br>
+<A NAME=2.1.47>Proceeding from the heat-oppressed brain?</A><br>
+<A NAME=2.1.48>I see thee yet, in form as palpable</A><br>
+<A NAME=2.1.49>As this which now I draw.</A><br>
+<A NAME=2.1.50>Thou marshall'st me the way that I was going;</A><br>
+<A NAME=2.1.51>And such an instrument I was to use.</A><br>
+<A NAME=2.1.52>Mine eyes are made the fools o' the other senses,</A><br>
+<A NAME=2.1.53>Or else worth all the rest; I see thee still,</A><br>
+<A NAME=2.1.54>And on thy blade and dudgeon gouts of blood,</A><br>
+<A NAME=2.1.55>Which was not so before. There's no such thing:</A><br>
+<A NAME=2.1.56>It is the bloody business which informs</A><br>
+<A NAME=2.1.57>Thus to mine eyes. Now o'er the one halfworld</A><br>
+<A NAME=2.1.58>Nature seems dead, and wicked dreams abuse</A><br>
+<A NAME=2.1.59>The curtain'd sleep; witchcraft celebrates</A><br>
+<A NAME=2.1.60>Pale Hecate's offerings, and wither'd murder,</A><br>
+<A NAME=2.1.61>Alarum'd by his sentinel, the wolf,</A><br>
+<A NAME=2.1.62>Whose howl's his watch, thus with his stealthy pace.</A><br>
+<A NAME=2.1.63>With Tarquin's ravishing strides, towards his design</A><br>
+<A NAME=2.1.64>Moves like a ghost. Thou sure and firm-set earth,</A><br>
+<A NAME=2.1.65>Hear not my steps, which way they walk, for fear</A><br>
+<A NAME=2.1.66>Thy very stones prate of my whereabout,</A><br>
+<A NAME=2.1.67>And take the present horror from the time,</A><br>
+<A NAME=2.1.68>Which now suits with it. Whiles I threat, he lives:</A><br>
+<A NAME=2.1.69>Words to the heat of deeds too cold breath gives.</A><br>
+<p><i>A bell rings</i></p>
+<A NAME=2.1.70>I go, and it is done; the bell invites me.</A><br>
+<A NAME=2.1.71>Hear it not, Duncan; for it is a knell</A><br>
+<A NAME=2.1.72>That summons thee to heaven or to hell.</A><br>
+<p><i>Exit</i></p>
+</blockquote>
+<h3>SCENE II. The same.</h3>
+<p><blockquote>
+<i>Enter LADY MACBETH</i>
+</blockquote>
+
+<A NAME=speech1><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.1>That which hath made them drunk hath made me bold;</A><br>
+<A NAME=2.2.2>What hath quench'd them hath given me fire.</A><br>
+<A NAME=2.2.3>Hark! Peace!</A><br>
+<A NAME=2.2.4>It was the owl that shriek'd, the fatal bellman,</A><br>
+<A NAME=2.2.5>Which gives the stern'st good-night. He is about it:</A><br>
+<A NAME=2.2.6>The doors are open; and the surfeited grooms</A><br>
+<A NAME=2.2.7>Do mock their charge with snores: I have drugg'd</A><br>
+<A NAME=2.2.8>their possets,</A><br>
+<A NAME=2.2.9>That death and nature do contend about them,</A><br>
+<A NAME=2.2.10>Whether they live or die.</A><br>
+</blockquote>
+
+<A NAME=speech2><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.11>[Within]  Who's there? what, ho!</A><br>
+</blockquote>
+
+<A NAME=speech3><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.12>Alack, I am afraid they have awaked,</A><br>
+<A NAME=2.2.13>And 'tis not done. The attempt and not the deed</A><br>
+<A NAME=2.2.14>Confounds us. Hark! I laid their daggers ready;</A><br>
+<A NAME=2.2.15>He could not miss 'em. Had he not resembled</A><br>
+<A NAME=2.2.16>My father as he slept, I had done't.</A><br>
+<p><i>Enter MACBETH</i></p>
+<A NAME=2.2.17>My husband!</A><br>
+</blockquote>
+
+<A NAME=speech4><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.18>I have done the deed. Didst thou not hear a noise?</A><br>
+</blockquote>
+
+<A NAME=speech5><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.19>I heard the owl scream and the crickets cry.</A><br>
+<A NAME=2.2.20>Did not you speak?</A><br>
+</blockquote>
+
+<A NAME=speech6><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.21>                  When?</A><br>
+</blockquote>
+
+<A NAME=speech7><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.22>Now.</A><br>
+</blockquote>
+
+<A NAME=speech8><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.23>As I descended?</A><br>
+</blockquote>
+
+<A NAME=speech9><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.24>Ay.</A><br>
+</blockquote>
+
+<A NAME=speech10><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.25>Hark!</A><br>
+<A NAME=2.2.26>Who lies i' the second chamber?</A><br>
+</blockquote>
+
+<A NAME=speech11><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.27>Donalbain.</A><br>
+</blockquote>
+
+<A NAME=speech12><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.28>This is a sorry sight.</A><br>
+<p><i>Looking on his hands</i></p>
+</blockquote>
+
+<A NAME=speech13><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.29>A foolish thought, to say a sorry sight.</A><br>
+</blockquote>
+
+<A NAME=speech14><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.30>There's one did laugh in's sleep, and one cried</A><br>
+<A NAME=2.2.31>'Murder!'</A><br>
+<A NAME=2.2.32>That they did wake each other: I stood and heard them:</A><br>
+<A NAME=2.2.33>But they did say their prayers, and address'd them</A><br>
+<A NAME=2.2.34>Again to sleep.</A><br>
+</blockquote>
+
+<A NAME=speech15><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.35>                  There are two lodged together.</A><br>
+</blockquote>
+
+<A NAME=speech16><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.36>One cried 'God bless us!' and 'Amen' the other;</A><br>
+<A NAME=2.2.37>As they had seen me with these hangman's hands.</A><br>
+<A NAME=2.2.38>Listening their fear, I could not say 'Amen,'</A><br>
+<A NAME=2.2.39>When they did say 'God bless us!'</A><br>
+</blockquote>
+
+<A NAME=speech17><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.40>Consider it not so deeply.</A><br>
+</blockquote>
+
+<A NAME=speech18><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.41>But wherefore could not I pronounce 'Amen'?</A><br>
+<A NAME=2.2.42>I had most need of blessing, and 'Amen'</A><br>
+<A NAME=2.2.43>Stuck in my throat.</A><br>
+</blockquote>
+
+<A NAME=speech19><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.44>These deeds must not be thought</A><br>
+<A NAME=2.2.45>After these ways; so, it will make us mad.</A><br>
+</blockquote>
+
+<A NAME=speech20><b>MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.46>Methought I heard a voice cry 'Sleep no more!</A><br>
+<A NAME=2.2.47>Macbeth does murder sleep', the innocent sleep,</A><br>
+<A NAME=2.2.48>Sleep that knits up the ravell'd sleeve of care,</A><br>
+<A NAME=2.2.49>The death of each day's life, sore labour's bath,</A><br>
+<A NAME=2.2.50>Balm of hurt minds, great nature's second course,</A><br>
+<A NAME=2.2.51>Chief nourisher in life's feast,--</A><br>
+</blockquote>
+
+<A NAME=speech21><b>LADY MACBETH</b></a>
+<blockquote>
+<A NAME=2.2.52>What do you mean?</A><br>
+</blockquote>
+
+<A NAME=speech22><b>MACBETH</b></a>
+<blockquote>