Merge autoland to central, a=merge
authorWes Kocher <wkocher@mozilla.com>
Wed, 01 Feb 2017 16:07:09 -0800
changeset 331989 8196774c6b8a858e5145524f4d250df8431ebc78
parent 331963 f3d187bd0733b1182dffc97b5dfe623e18f92a44 (current diff)
parent 331988 8accc9d52c25b0b5f3e7bdb156768ae4cacd77e6 (diff)
child 332040 1ebbde84a39ad617d8e2b8bcda9f8bfb8ca6b8ba
push id31293
push userkwierso@gmail.com
push dateThu, 02 Feb 2017 00:07:12 +0000
treeherdermozilla-central@8196774c6b8a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone54.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge autoland to central, a=merge MozReview-Commit-ID: AFEWjI3UWey
toolkit/modules/tests/MockDocument.jsm
toolkit/modules/tests/PromiseTestUtils.jsm
--- a/browser/.eslintrc.js
+++ b/browser/.eslintrc.js
@@ -1,11 +1,12 @@
 "use strict";
 
 module.exports = {
   "extends": [
     "../toolkit/.eslintrc.js"
   ],
 
   "rules": {
-    "no-shadow": "error"
+    "no-shadow": "error",
+    "no-undef": "off"
   }
 };
--- a/browser/base/content/aboutNetError.xhtml
+++ b/browser/base/content/aboutNetError.xhtml
@@ -346,16 +346,19 @@
         window.addEventListener("AboutNetErrorCaptivePortalFreed", () => {
           document.location.reload();
         });
       }
 
       function initPageCertError() {
         document.body.className = "certerror";
         document.title = document.getElementById("certErrorPageTitle").textContent;
+        for (let host of document.querySelectorAll(".hostname")) {
+          host.textContent = document.location.hostname;
+        }
 
         setupAdvancedButton(true);
 
         document.getElementById("learnMoreContainer").style.display = "block";
 
         let checkbox = document.getElementById("automaticallyReportInFuture");
         checkbox.addEventListener("change", function({target: {checked}}) {
           document.dispatchEvent(new CustomEvent("AboutNetErrorSetAutomatic", {
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -170,17 +170,19 @@ var StarUI = {
           // 3500ms matches the timeout that Pocket uses in
           // browser/extensions/pocket/content/panels/js/saved.js
           let delay = 3500;
           if (this._closePanelQuickForTesting) {
             delay /= 10;
           }
           clearTimeout(this._autoCloseTimer);
           this._autoCloseTimer = setTimeout(() => {
-            this.panel.hidePopup();
+            if (!this.panel.mozMatchesSelector(":hover")) {
+              this.panel.hidePopup();
+            }
           }, delay);
         }
         break;
     }
   },
 
   _overlayLoaded: false,
   _overlayLoading: false,
--- a/browser/base/content/test/general/browser_aboutCertError.js
+++ b/browser/base/content/test/general/browser_aboutCertError.js
@@ -212,30 +212,35 @@ add_task(function* checkWrongSystemTimeW
   message = yield Task.spawn(setUpPage);
 
   is(message.divDisplay, "none", "Wrong time message information is not visible");
 
   yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
 
 add_task(function* checkAdvancedDetails() {
-  info("Loading a bad cert page and verifying the advanced details section");
+  info("Loading a bad cert page and verifying the main error and advanced details section");
   let browser;
   let certErrorLoaded;
   yield BrowserTestUtils.openNewForegroundTab(gBrowser, () => {
     gBrowser.selectedTab = gBrowser.addTab(BAD_CERT);
     browser = gBrowser.selectedBrowser;
     certErrorLoaded = waitForCertErrorLoad(browser);
   }, false);
 
   info("Loading and waiting for the cert error");
   yield certErrorLoaded;
 
   let message = yield ContentTask.spawn(browser, null, function* () {
     let doc = content.document;
+    let shortDescText = doc.getElementById("errorShortDescText");
+    info("Main error text: " + shortDescText.textContent);
+    ok(shortDescText.textContent.includes("expired.example.com"),
+       "Should list hostname in error message.");
+
     let advancedButton = doc.getElementById("advancedButton");
     advancedButton.click();
     let el = doc.getElementById("errorCode");
     return { textContent: el.textContent, tagName: el.tagName };
   });
   is(message.textContent, "SEC_ERROR_EXPIRED_CERTIFICATE",
      "Correct error message found");
   is(message.tagName, "a", "Error message is a link");
--- a/browser/base/content/test/general/browser_bookmark_popup.js
+++ b/browser/base/content/test/general/browser_bookmark_popup.js
@@ -6,16 +6,17 @@
 
 /**
  * Test opening and closing the bookmarks panel.
  */
 
 let bookmarkPanel = document.getElementById("editBookmarkPanel");
 let bookmarkStar = document.getElementById("bookmarks-menu-button");
 let bookmarkPanelTitle = document.getElementById("editBookmarkPanelTitle");
+let editBookmarkPanelRemoveButtonRect;
 
 StarUI._closePanelQuickForTesting = true;
 
 function* test_bookmarks_popup({isNewBookmark, popupShowFn, popupEditFn,
                                 shouldAutoClose, popupHideFn, isBookmarkRemoved}) {
   yield BrowserTestUtils.withNewTab({gBrowser, url: "about:home"}, function*(browser) {
     try {
       if (!isNewBookmark) {
@@ -29,16 +30,19 @@ function* test_bookmarks_popup({isNewBoo
       is(bookmarkStar.hasAttribute("starred"), !isNewBookmark,
          "Page should only be starred prior to popupshown if editing bookmark");
       is(bookmarkPanel.state, "closed", "Panel should be 'closed' to start test");
       let shownPromise = promisePopupShown(bookmarkPanel);
       yield popupShowFn(browser);
       yield shownPromise;
       is(bookmarkPanel.state, "open", "Panel should be 'open' after shownPromise is resolved");
 
+    editBookmarkPanelRemoveButtonRect =
+      document.getElementById("editBookmarkPanelRemoveButton").getBoundingClientRect();
+
       if (popupEditFn) {
         yield popupEditFn();
       }
       let bookmarks = [];
       yield PlacesUtils.bookmarks.fetch({url: "about:home"}, bm => bookmarks.push(bm));
       is(bookmarks.length, 1, "Only one bookmark should exist");
       is(bookmarkStar.getAttribute("starred"), "true", "Page is starred");
       is(bookmarkPanelTitle.value,
@@ -358,11 +362,37 @@ add_task(function* enter_on_remove_bookm
         EventUtils.sendChar("VK_TAB", window);
       }
       EventUtils.sendChar("VK_RETURN", window);
     },
     isBookmarkRemoved: true,
   });
 });
 
+add_task(function* mouse_hovering_panel_should_prevent_autoclose() {
+  if (AppConstants.platform != "win") {
+    // This test requires synthesizing native mouse movement which is
+    // best supported on Windows.
+    return;
+  }
+  yield test_bookmarks_popup({
+    isNewBookmark: true,
+    *popupShowFn(browser) {
+      yield new Promise(resolve => {
+        EventUtils.synthesizeNativeMouseMove(
+          document.documentElement,
+          editBookmarkPanelRemoveButtonRect.left,
+          editBookmarkPanelRemoveButtonRect.top,
+          resolve);
+      });
+      EventUtils.synthesizeKey("D", {accelKey: true}, window);
+    },
+    shouldAutoClose: false,
+    popupHideFn() {
+      document.getElementById("editBookmarkPanelRemoveButton").click();
+    },
+    isBookmarkRemoved: true,
+  });
+});
+
 registerCleanupFunction(function() {
   delete StarUI._closePanelQuickForTesting;
 })
--- a/browser/base/content/test/general/browser_parsable_css.js
+++ b/browser/base/content/test/general/browser_parsable_css.js
@@ -43,16 +43,24 @@ let whitelist = [
    errorMessage: /Unknown property.*-moz-/i,
    isFromDevTools: false},
   // Reserved to UA sheets unless layout.css.overflow-clip-box.enabled flipped to true.
   {sourceName: /res\/forms\.css$/i,
    errorMessage: /Unknown property.*overflow-clip-box/i,
    isFromDevTools: false},
 ];
 
+if (!Services.prefs.getBoolPref("full-screen-api.unprefix.enabled")) {
+  whitelist.push({
+    sourceName: /res\/(ua|html)\.css$/i,
+    errorMessage: /Unknown pseudo-class .*\bfullscreen\b/i,
+    isFromDevTools: false
+  });
+}
+
 // Platform can be "linux", "macosx" or "win". If omitted, the exception applies to all platforms.
 let allowedImageReferences = [
   // Bug 1302691
   {file: "chrome://devtools/skin/images/dock-bottom-minimize@2x.png",
    from: "chrome://devtools/skin/toolbox.css",
    isFromDevTools: true},
   {file: "chrome://devtools/skin/images/dock-bottom-maximize@2x.png",
    from: "chrome://devtools/skin/toolbox.css",
--- a/browser/base/content/test/siteIdentity/browser.ini
+++ b/browser/base/content/test/siteIdentity/browser.ini
@@ -1,7 +1,6 @@
 [DEFAULT]
 support-files =
   head.js
 
 [browser_identityBlock_focus.js]
-skip-if = os == 'mac' # Bug 1334418 (try only)
 support-files = ../general/permissions.html
--- a/browser/base/content/test/siteIdentity/browser_identityBlock_focus.js
+++ b/browser/base/content/test/siteIdentity/browser_identityBlock_focus.js
@@ -9,29 +9,30 @@ function synthesizeKeyAndWaitForFocus(el
   let focused = BrowserTestUtils.waitForEvent(element, "focus");
   EventUtils.synthesizeKey(keyCode, options);
   return focused;
 }
 
 // Checks that the identity block is the next element after the urlbar
 // to be focused if there are no active notification anchors.
 add_task(function* testWithoutNotifications() {
+  yield SpecialPowers.pushPrefEnv({"set": [["accessibility.tabfocus", 7]]});
   yield BrowserTestUtils.withNewTab("https://example.com", function*() {
     yield synthesizeKeyAndWaitForFocus(gURLBar, "l", {accelKey: true})
     is(document.activeElement, gURLBar.inputField, "urlbar should be focused");
     yield synthesizeKeyAndWaitForFocus(gIdentityHandler._identityBox, "VK_TAB", {shiftKey: true})
     is(document.activeElement, gIdentityHandler._identityBox,
        "identity block should be focused");
   });
 });
 
 // Checks that when there is a notification anchor, it will receive
 // focus before the identity block.
 add_task(function* testWithoutNotifications() {
-
+  yield SpecialPowers.pushPrefEnv({"set": [["accessibility.tabfocus", 7]]});
   yield BrowserTestUtils.withNewTab(PERMISSIONS_PAGE, function*(browser) {
     let popupshown = BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popupshown");
     // Request a permission;
     BrowserTestUtils.synthesizeMouseAtCenter("#geo", {}, browser);
     yield popupshown;
 
     yield synthesizeKeyAndWaitForFocus(gURLBar, "l", {accelKey: true})
     is(document.activeElement, gURLBar.inputField, "urlbar should be focused");
@@ -41,16 +42,17 @@ add_task(function* testWithoutNotificati
     yield synthesizeKeyAndWaitForFocus(gIdentityHandler._identityBox, "VK_TAB", {shiftKey: true})
     is(document.activeElement, gIdentityHandler._identityBox,
        "identity block should be focused");
   });
 });
 
 // Checks that with invalid pageproxystate the identity block is ignored.
 add_task(function* testInvalidPageProxyState() {
+  yield SpecialPowers.pushPrefEnv({"set": [["accessibility.tabfocus", 7]]});
   yield BrowserTestUtils.withNewTab("about:blank", function*(browser) {
     // Loading about:blank will automatically focus the urlbar, which, however, can
     // race with the test code. So we only send the shortcut if the urlbar isn't focused yet.
     if (document.activeElement != gURLBar.inputField) {
       yield synthesizeKeyAndWaitForFocus(gURLBar, "l", {accelKey: true})
     }
     is(document.activeElement, gURLBar.inputField, "urlbar should be focused");
     yield synthesizeKeyAndWaitForFocus(gBrowser.getTabForBrowser(browser), "VK_TAB", {shiftKey: true})
--- a/browser/components/search/content/search.xml
+++ b/browser/components/search/content/search.xml
@@ -2119,17 +2119,17 @@
       </method>
 
     </implementation>
 
     <handlers>
 
       <handler event="mousedown"><![CDATA[
         let target = event.originalTarget;
-        if (!target.classList.contains("searchbar-engine-one-off-item")) {
+        if (target.getAttribute("anonid") == "addengine-menu-button") {
           return;
         }
         // Required to receive click events from the buttons on Linux.
         event.preventDefault();
       ]]></handler>
 
       <handler event="mousemove"><![CDATA[
         let target = event.originalTarget;
--- a/browser/modules/ExtensionsUI.jsm
+++ b/browser/modules/ExtensionsUI.jsm
@@ -338,16 +338,17 @@ this.ExtensionsUI = {
       let action = {
         label: bundle.getString("addonPostInstall.okay.label"),
         accessKey: bundle.getString("addonPostInstall.okay.key"),
         callback: resolve,
       };
 
       let options = {
         hideClose: true,
+        timeout: Date.now() + 30000,
         popupIconURL: addon.iconURL || DEFAULT_EXTENSION_ICON,
         eventCallback(topic) {
           if (topic == "showing") {
             let doc = this.browser.ownerDocument;
             doc.getElementById("addon-installed-notification-header")
                .innerHTML = msg1;
             doc.getElementById("addon-installed-notification-message")
                .innerHTML = msg2;
--- a/devtools/client/framework/toolbox-options.js
+++ b/devtools/client/framework/toolbox-options.js
@@ -9,16 +9,18 @@
 const Services = require("Services");
 const defer = require("devtools/shared/defer");
 const {Task} = require("devtools/shared/task");
 const {gDevTools} = require("devtools/client/framework/devtools");
 
 const {LocalizationHelper} = require("devtools/shared/l10n");
 const L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");
 
+loader.lazyRequireGetter(this, "system", "devtools/shared/system");
+
 exports.OptionsPanel = OptionsPanel;
 
 function GetPref(name) {
   let type = Services.prefs.getPrefType(name);
   switch (type) {
     case Services.prefs.PREF_STRING:
       return Services.prefs.getCharPref(name);
     case Services.prefs.PREF_INT:
@@ -86,16 +88,17 @@ OptionsPanel.prototype = {
     // For local debugging we need to make the target remote.
     if (!this.target.isRemote) {
       yield this.target.makeRemote();
     }
 
     this.setupToolsList();
     this.setupToolbarButtonsList();
     this.setupThemeList();
+    this.setupNightlyOptions();
     yield this.populatePreferences();
     this.isReady = true;
     this.emit("ready");
     return this;
   }),
 
   _addListeners: function () {
     Services.prefs.addObserver("devtools.cache.disabled", this._prefChanged, false);
@@ -297,16 +300,65 @@ OptionsPanel.prototype = {
     let themes = gDevTools.getThemeDefinitionArray();
     for (let theme of themes) {
       themeBox.appendChild(createThemeOption(theme));
     }
 
     this.updateCurrentTheme();
   },
 
+  /**
+   * Add common preferences enabled only on Nightly.
+   */
+  setupNightlyOptions: function () {
+    let isNightly = system.constants.NIGHTLY_BUILD;
+    if (!isNightly) {
+      return;
+    }
+
+    // Labels for these new buttons are nightly only and mostly intended for working on
+    // devtools. They should not be localized.
+    let prefDefinitions = [{
+      pref: "devtools.webconsole.new-frontend-enabled",
+      label: "Enable new console frontend",
+      id: "devtools-new-webconsole",
+      parentId: "webconsole-options"
+    }, {
+      pref: "devtools.debugger.new-debugger-frontend",
+      label: "Enable new debugger frontend",
+      id: "devtools-new-debugger",
+      parentId: "debugger-options"
+    }];
+
+    let createPreferenceOption = ({pref, label, id}) => {
+      let inputLabel = this.panelDoc.createElement("label");
+      let checkbox = this.panelDoc.createElement("input");
+      checkbox.setAttribute("type", "checkbox");
+      if (GetPref(pref)) {
+        checkbox.setAttribute("checked", "checked");
+      }
+      checkbox.setAttribute("id", id);
+      checkbox.addEventListener("change", e => {
+        SetPref(pref, e.target.checked);
+      });
+
+      let inputSpanLabel = this.panelDoc.createElement("span");
+      inputSpanLabel.textContent = label;
+      inputLabel.appendChild(checkbox);
+      inputLabel.appendChild(inputSpanLabel);
+
+      return inputLabel;
+    };
+
+    for (let prefDefinition of prefDefinitions) {
+      let parent = this.panelDoc.getElementById(prefDefinition.parentId);
+      parent.appendChild(createPreferenceOption(prefDefinition));
+    }
+  },
+
   populatePreferences: function () {
     let prefCheckboxes = this.panelDoc.querySelectorAll(
       "input[type=checkbox][data-pref]");
     for (let prefCheckbox of prefCheckboxes) {
       if (GetPref(prefCheckbox.getAttribute("data-pref"))) {
         prefCheckbox.setAttribute("checked", true);
       }
       prefCheckbox.addEventListener("change", function (e) {
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -2018,16 +2018,22 @@ Element::GetInlineStyleDeclaration() con
 
   if (attrVal && attrVal->Type() == nsAttrValue::eCSSDeclaration) {
     return attrVal->GetCSSDeclarationValue();
   }
 
   return nullptr;
 }
 
+const nsMappedAttributes*
+Element::GetMappedAttributes() const
+{
+  return mAttrsAndChildren.GetMapped();
+}
+
 nsresult
 Element::SetInlineStyleDeclaration(DeclarationBlock* aDeclaration,
                                    const nsAString* aSerialized,
                                    bool aNotify)
 {
   NS_NOTYETIMPLEMENTED("Element::SetInlineStyleDeclaration");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -277,16 +277,21 @@ public:
   void ClearStyleStateLocks();
 
   /**
    * Get the inline style declaration, if any, for this element.
    */
   DeclarationBlock* GetInlineStyleDeclaration() const;
 
   /**
+   * Get the mapped attributes, if any, for this element.
+   */
+  const nsMappedAttributes* GetMappedAttributes() const;
+
+  /**
    * Set the inline style declaration for this element. This will send
    * an appropriate AttributeChanged notification if aNotify is true.
    */
   virtual nsresult SetInlineStyleDeclaration(DeclarationBlock* aDeclaration,
                                              const nsAString* aSerialized,
                                              bool aNotify);
 
   /**
--- a/dom/base/nsAttrAndChildArray.cpp
+++ b/dom/base/nsAttrAndChildArray.cpp
@@ -767,16 +767,21 @@ nsAttrAndChildArray::MakeMappedUnique(ns
     // nature not modifiable).
     aAttributes->DropStyleSheetReference();
   }
   mapped.swap(mImpl->mMappedAttrs);
 
   return NS_OK;
 }
 
+const nsMappedAttributes*
+nsAttrAndChildArray::GetMapped() const
+{
+  return mImpl ? mImpl->mMappedAttrs : nullptr;
+}
 
 bool
 nsAttrAndChildArray::GrowBy(uint32_t aGrowSize)
 {
   CheckedUint32 size = 0;
   if (mImpl) {
     size += mImpl->mBufferSize;
     size += NS_IMPL_EXTRA_SIZE;
--- a/dom/base/nsAttrAndChildArray.h
+++ b/dom/base/nsAttrAndChildArray.h
@@ -129,16 +129,17 @@ public:
            !AttrSlotIsTaken(ATTRCHILD_ARRAY_MAX_ATTR_COUNT - 1);
   }
 
   size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
   bool HasMappedAttrs() const
   {
     return MappedAttrCount();
   }
+  const nsMappedAttributes* GetMapped() const;
 
 private:
   nsAttrAndChildArray(const nsAttrAndChildArray& aOther) = delete;
   nsAttrAndChildArray& operator=(const nsAttrAndChildArray& aOther) = delete;
 
   void Clear();
 
   uint32_t NonMappedAttrCount() const;
--- a/dom/base/nsMappedAttributes.cpp
+++ b/dom/base/nsMappedAttributes.cpp
@@ -7,33 +7,39 @@
 /*
  * A unique per-element set of attributes that is used as an
  * nsIStyleRule; used to implement presentational attributes.
  */
 
 #include "nsMappedAttributes.h"
 #include "nsHTMLStyleSheet.h"
 #include "nsRuleWalker.h"
+#include "nsRuleData.h"
 #include "mozilla/HashFunctions.h"
 #include "mozilla/MemoryReporting.h"
+#include "mozilla/ServoDeclarationBlock.h"
 
 using namespace mozilla;
 
 nsMappedAttributes::nsMappedAttributes(nsHTMLStyleSheet* aSheet,
                                        nsMapRuleToAttributesFunc aMapRuleFunc)
   : mAttrCount(0),
     mSheet(aSheet),
-    mRuleMapper(aMapRuleFunc)
+    mRuleMapper(aMapRuleFunc),
+    mServoStyle(nullptr)
 {
 }
 
 nsMappedAttributes::nsMappedAttributes(const nsMappedAttributes& aCopy)
   : mAttrCount(aCopy.mAttrCount),
     mSheet(aCopy.mSheet),
-    mRuleMapper(aCopy.mRuleMapper)
+    mRuleMapper(aCopy.mRuleMapper),
+    // This is only called by ::Clone, which is used to create independent
+    // nsMappedAttributes objects which should not share a ServoDeclarationBlock
+    mServoStyle(nullptr)
 {
   NS_ASSERTION(mBufferSize >= aCopy.mAttrCount, "can't fit attributes");
 
   uint32_t i;
   for (i = 0; i < mAttrCount; ++i) {
     new (&Attrs()[i]) InternalAttr(aCopy.Attrs()[i]);
   }
 }
@@ -276,8 +282,25 @@ nsMappedAttributes::SizeOfIncludingThis(
 
   size_t n = aMallocSizeOf(this);
   for (uint16_t i = 0; i < mAttrCount; ++i) {
     n += Attrs()[i].mValue.SizeOfExcludingThis(aMallocSizeOf);
   }
   return n;
 }
 
+void
+nsMappedAttributes::LazilyResolveServoDeclaration(nsRuleData* aRuleData,
+                                                  nsCSSPropertyID* aIndexToIdMapping,
+                                                  size_t aRuleDataSize)
+{
+  MapRuleInfoInto(aRuleData);
+
+  MOZ_ASSERT(!mServoStyle,
+             "LazilyResolveServoDeclaration should not be called if mServoStyle is already set");
+  mServoStyle = Servo_DeclarationBlock_CreateEmpty().Consume();
+  for (size_t i = 0; i < aRuleDataSize; i++) {
+    nsCSSValue& val = aRuleData->mValueStorage[i];
+    if (val.GetUnit() != eCSSUnit_Null) {
+      Servo_DeclarationBlock_AddPresValue(mServoStyle.get(), aIndexToIdMapping[i], &val);
+    }
+  }
+}
--- a/dom/base/nsMappedAttributes.h
+++ b/dom/base/nsMappedAttributes.h
@@ -11,16 +11,17 @@
 
 #ifndef nsMappedAttributes_h___
 #define nsMappedAttributes_h___
 
 #include "nsAttrAndChildArray.h"
 #include "nsMappedAttributeElement.h"
 #include "nsIStyleRule.h"
 #include "mozilla/Attributes.h"
+#include "mozilla/ServoBindings.h"
 #include "mozilla/MemoryReporting.h"
 
 class nsIAtom;
 class nsHTMLStyleSheet;
 
 class nsMappedAttributes final : public nsIStyleRule
 {
 public:
@@ -65,19 +66,37 @@ public:
     NS_ASSERTION(aPos < mAttrCount, "out-of-bounds");
     return &Attrs()[aPos].mValue;
   }
   // Remove the attr at position aPos.  The value of the attr is placed in
   // aValue; any value that was already in aValue is destroyed.
   void RemoveAttrAt(uint32_t aPos, nsAttrValue& aValue);
   const nsAttrName* GetExistingAttrNameFromQName(const nsAString& aName) const;
   int32_t IndexOfAttr(nsIAtom* aLocalName) const;
-  
 
-  // nsIStyleRule 
+  // Apply the contained mapper to an empty nsRuleData object
+  // that is able to contain all properties. Set contained servo declaration block
+  // to result of this computation.
+  // aIndexToIdMapping is
+  // a table that maps from an index in the rule data to the corresponding
+  // property ID. aRuleDataSize is the length of the backing storage
+  // of the rule data.
+  void LazilyResolveServoDeclaration(nsRuleData* aRuleData,
+                                     nsCSSPropertyID* aIndexToIdMapping,
+                                     size_t aRuleDataSize);
+
+  // Obtain the contained servo declaration block
+  // May return null if called before the inner block
+  // has been (lazily) resolved
+  const RefPtr<RawServoDeclarationBlock>& GetServoStyle() const
+  {
+    return mServoStyle;
+  }
+
+  // nsIStyleRule
   virtual void MapRuleInfoInto(nsRuleData* aRuleData) override;
   virtual bool MightMapInheritedStyleData() override;
   virtual bool GetDiscretelyAnimatedCSSValue(nsCSSPropertyID aProperty,
                                              nsCSSValue* aValue) override;
 #ifdef DEBUG
   virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
 #endif
 
@@ -110,12 +129,13 @@ private:
   }
 
   uint16_t mAttrCount;
 #ifdef DEBUG
   uint16_t mBufferSize;
 #endif
   nsHTMLStyleSheet* mSheet; //weak
   nsMapRuleToAttributesFunc mRuleMapper;
+  RefPtr<RawServoDeclarationBlock> mServoStyle;
   void* mAttrs[1];
 };
 
 #endif /* nsMappedAttributes_h___ */
--- a/dom/events/EventListenerManager.cpp
+++ b/dom/events/EventListenerManager.cpp
@@ -115,29 +115,16 @@ IsWebkitPrefixSupportEnabled()
     sIsPrefCached = true;
     Preferences::AddBoolVarCache(&sIsWebkitPrefixSupportEnabled,
                                  "layout.css.prefixes.webkit");
   }
 
   return sIsWebkitPrefixSupportEnabled;
 }
 
-static bool
-IsPrefixedPointerLockEnabled()
-{
-  static bool sIsPrefixedPointerLockEnabled;
-  static bool sIsPrefCached = false;
-  if (!sIsPrefCached) {
-    sIsPrefCached = true;
-    Preferences::AddBoolVarCache(&sIsPrefixedPointerLockEnabled,
-                                 "pointer-lock-api.prefixed.enabled");
-  }
-  return sIsPrefixedPointerLockEnabled;
-}
-
 EventListenerManagerBase::EventListenerManagerBase()
   : mNoListenerForEvent(eVoidEvent)
   , mMayHavePaintEventListener(false)
   , mMayHaveMutationListeners(false)
   , mMayHaveCapturingListeners(false)
   , mMayHaveSystemGroupListeners(false)
   , mMayHaveTouchEventListener(false)
   , mMayHaveMouseEnterLeaveEventListener(false)
@@ -1159,24 +1146,16 @@ EventListenerManager::GetLegacyEventMess
       }
       if (aEventMessage == eAnimationEnd) {
         return eWebkitAnimationEnd;
       }
       if (aEventMessage == eAnimationIteration) {
         return eWebkitAnimationIteration;
       }
     }
-    if (IsPrefixedPointerLockEnabled()) {
-      if (aEventMessage == ePointerLockChange) {
-        return eMozPointerLockChange;
-      }
-      if (aEventMessage == ePointerLockError) {
-        return eMozPointerLockError;
-      }
-    }
   }
 
   switch (aEventMessage) {
     case eFullscreenChange:
       return eMozFullscreenChange;
     case eFullscreenError:
       return eMozFullscreenError;
     default:
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -786,33 +786,38 @@ TrackTypeToStr(TrackInfo::TrackType aTra
 MediaFormatReader::MediaFormatReader(AbstractMediaDecoder* aDecoder,
                                      MediaDataDemuxer* aDemuxer,
                                      VideoFrameContainer* aVideoFrameContainer)
   : MediaDecoderReader(aDecoder)
   , mAudio(this, MediaData::AUDIO_DATA,
            Preferences::GetUint("media.audio-max-decode-error", 3))
   , mVideo(this, MediaData::VIDEO_DATA,
            Preferences::GetUint("media.video-max-decode-error", 2))
-  , mDemuxer(new DemuxerProxy(aDemuxer, aDecoder->AbstractMainThread()))
+  , mDemuxer(nullptr)
   , mDemuxerInitDone(false)
   , mLastReportedNumDecodedFrames(0)
   , mPreviousDecodedKeyframeTime_us(sNoPreviousDecodedKeyframe)
   , mInitDone(false)
   , mTrackDemuxersMayBlock(false)
   , mSeekScheduled(false)
   , mVideoFrameContainer(aVideoFrameContainer)
   , mDecoderFactory(new DecoderFactory(this))
 {
   MOZ_ASSERT(aDemuxer);
   MOZ_COUNT_CTOR(MediaFormatReader);
 
-  if (aDecoder && aDecoder->CompositorUpdatedEvent()) {
-    mCompositorUpdatedListener =
-      aDecoder->CompositorUpdatedEvent()->Connect(
-        mTaskQueue, this, &MediaFormatReader::NotifyCompositorUpdated);
+  if (aDecoder) {
+    mDemuxer = MakeUnique<DemuxerProxy>(aDemuxer,
+      aDecoder->AbstractMainThread());
+
+    if (aDecoder->CompositorUpdatedEvent()) {
+      mCompositorUpdatedListener =
+        aDecoder->CompositorUpdatedEvent()->Connect(
+          mTaskQueue, this, &MediaFormatReader::NotifyCompositorUpdated);
+    }
   }
 }
 
 MediaFormatReader::~MediaFormatReader()
 {
   MOZ_COUNT_DTOR(MediaFormatReader);
 }
 
--- a/dom/media/ipc/RemoteVideoDecoder.cpp
+++ b/dom/media/ipc/RemoteVideoDecoder.cpp
@@ -147,16 +147,23 @@ RemoteDecoderModule::Startup()
 
 bool
 RemoteDecoderModule::SupportsMimeType(const nsACString& aMimeType,
                                       DecoderDoctorDiagnostics* aDiagnostics) const
 {
   return mWrapped->SupportsMimeType(aMimeType, aDiagnostics);
 }
 
+bool
+RemoteDecoderModule::Supports(const TrackInfo& aTrackInfo,
+                              DecoderDoctorDiagnostics* aDiagnostics) const
+{
+  return mWrapped->Supports(aTrackInfo, aDiagnostics);
+}
+
 PlatformDecoderModule::ConversionRequired
 RemoteDecoderModule::DecoderNeedsConversion(const TrackInfo& aConfig) const
 {
   return mWrapped->DecoderNeedsConversion(aConfig);
 }
 
 already_AddRefed<MediaDataDecoder>
 RemoteDecoderModule::CreateVideoDecoder(const CreateDecoderParams& aParams)
--- a/dom/media/ipc/RemoteVideoDecoder.h
+++ b/dom/media/ipc/RemoteVideoDecoder.h
@@ -62,16 +62,18 @@ public:
   explicit RemoteDecoderModule(PlatformDecoderModule* aWrapped)
     : mWrapped(aWrapped)
   {}
 
   nsresult Startup() override;
 
   bool SupportsMimeType(const nsACString& aMimeType,
                         DecoderDoctorDiagnostics* aDiagnostics) const override;
+  bool Supports(const TrackInfo& aTrackInfo,
+                DecoderDoctorDiagnostics* aDiagnostics) const override;
 
   ConversionRequired DecoderNeedsConversion(
     const TrackInfo& aConfig) const override;
 
   already_AddRefed<MediaDataDecoder> CreateVideoDecoder(
     const CreateDecoderParams& aParams) override;
 
   already_AddRefed<MediaDataDecoder> CreateAudioDecoder(
--- a/dom/webidl/Document.webidl
+++ b/dom/webidl/Document.webidl
@@ -249,21 +249,17 @@ partial interface Document {
   [Func="nsDocument::IsUnprefixedFullscreenEnabled"]
   attribute EventHandler onfullscreenerror;
 };
 
 // https://w3c.github.io/pointerlock/#extensions-to-the-document-interface
 // https://w3c.github.io/pointerlock/#extensions-to-the-documentorshadowroot-mixin
 partial interface Document {
   readonly attribute Element? pointerLockElement;
-  [BinaryName="pointerLockElement", Pref="pointer-lock-api.prefixed.enabled"]
-  readonly attribute Element? mozPointerLockElement;
   void exitPointerLock();
-  [BinaryName="exitPointerLock", Pref="pointer-lock-api.prefixed.enabled"]
-  void mozExitPointerLock();
 
   // Event handlers
   attribute EventHandler onpointerlockchange;
   attribute EventHandler onpointerlockerror;
 };
 
 //http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/custom/index.html#dfn-document-register
 partial interface Document {
--- a/dom/webidl/Element.webidl
+++ b/dom/webidl/Element.webidl
@@ -255,11 +255,9 @@ partial interface Element {
   [Throws, UnsafeInPrerendering, BinaryName="requestFullscreen", NeedsCallerType]
   void mozRequestFullScreen();
 };
 
 // https://w3c.github.io/pointerlock/#extensions-to-the-element-interface
 partial interface Element {
   [UnsafeInPrerendering, NeedsCallerType]
   void requestPointerLock();
-  [UnsafeInPrerendering, BinaryName="requestPointerLock", Pref="pointer-lock-api.prefixed.enabled", NeedsCallerType]
-  void mozRequestPointerLock();
 };
--- a/dom/webidl/EventHandler.webidl
+++ b/dom/webidl/EventHandler.webidl
@@ -115,20 +115,16 @@ interface GlobalEventHandlers {
            attribute EventHandler ongotpointercapture;
            [Pref="dom.w3c_pointer_events.enabled"]
            attribute EventHandler onlostpointercapture;
 
            // Mozilla-specific handlers. Unprefixed handlers live in
            // Document rather than here.
            attribute EventHandler onmozfullscreenchange;
            attribute EventHandler onmozfullscreenerror;
-           [Pref="pointer-lock-api.prefixed.enabled"]
-           attribute EventHandler onmozpointerlockchange;
-           [Pref="pointer-lock-api.prefixed.enabled"]
-           attribute EventHandler onmozpointerlockerror;
 
            // CSS-Animation and CSS-Transition handlers.
            attribute EventHandler onanimationend;
            attribute EventHandler onanimationiteration;
            attribute EventHandler onanimationstart;
            attribute EventHandler ontransitioncancel;
            attribute EventHandler ontransitionend;
            attribute EventHandler ontransitionrun;
--- a/dom/webidl/MouseEvent.webidl
+++ b/dom/webidl/MouseEvent.webidl
@@ -72,21 +72,16 @@ dictionary MouseEventInit : EventModifie
   // Pointer Lock
   long           movementX = 0;
   long           movementY = 0;
 };
 
 // Mozilla extensions
 partial interface MouseEvent
 {
-  [BinaryName="movementX", Pref="pointer-lock-api.prefixed.enabled"]
-  readonly attribute long mozMovementX;
-  [BinaryName="movementY", Pref="pointer-lock-api.prefixed.enabled"]
-  readonly attribute long mozMovementY;
-
   // Finger or touch pressure event value
   // ranges between 0.0 and 1.0
   readonly attribute float mozPressure;
 
   const unsigned short    MOZ_SOURCE_UNKNOWN    = 0;
   const unsigned short    MOZ_SOURCE_MOUSE      = 1;
   const unsigned short    MOZ_SOURCE_PEN        = 2;
   const unsigned short    MOZ_SOURCE_ERASER     = 3;
--- a/js/src/jit-test/tests/debug/wasm-05.js
+++ b/js/src/jit-test/tests/debug/wasm-05.js
@@ -52,16 +52,18 @@ for (let descriptor of WebAssembly.Modul
     imports[descriptor.module] = {}
     switch(descriptor.kind) {
         case "function": imports[descriptor.module][descriptor.name] = new Function(''); break;
     }
 }
 var instance = new WebAssembly.Instance(module, imports);
 `);
     var wasmScript = dbg.findScripts().filter(s => s.format == 'wasm')[0];
+    assertEq(wasmScript.startLine, 1);
+    assertEq(wasmScript.lineCount > 0, true);
     var lines = wasmScript.source.text.split('\n');
     var offsetsFound = 0;
     lines.forEach(function (l, n) {
         var offsets = wasmScript.getLineOffsets(n + 1);
         if (offsets.length < 1) return;
         assertEq(offsets.length, 1);
         assertEq(offsets[0] > 0, true);
         offsetsFound++;
@@ -97,15 +99,17 @@ function getWasmScriptAfterDebuggerAttac
     // Attaching after wasm instance is created.
     dbg.addDebuggee(sandbox);
     var wasmScript = dbg.findScripts().filter(s => s.format == 'wasm')[0];
     return wasmScript;
 }
 
 var wasmScript1 = getWasmScriptAfterDebuggerAttached('(module (func (nop)))');
 var wasmLines1 = wasmScript1.source.text.split('\n');
+assertEq(wasmScript1.startLine, 1);
+assertEq(wasmScript1.lineCount, 0);
 assertEq(wasmLines1.every((l, n) => wasmScript1.getLineOffsets(n + 1).length == 0), true);
 
 // Checking that we must not resolve any location for any offset in a wasm
 // instance which debug mode was not enabled.
 var wasmScript2 = getWasmScriptAfterDebuggerAttached('(module (func (nop)))');
 for (var i = wasmTextToBinary('(module (func (nop)))').length - 1; i >= 0; i--)
     assertThrowsInstanceOf(() => wasmScript2.getOffsetLocation(i), Error);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/wasm-09.js
@@ -0,0 +1,35 @@
+// |jit-test| test-also-wasm-baseline
+// Tests debugEnabled state of wasm when allowUnobservedAsmJS == true.
+
+load(libdir + "asserts.js");
+
+if (!wasmIsSupported())
+     quit();
+
+// Checking that there are no offsets are present in a wasm instance script for
+// which debug mode was not enabled.
+function getWasmScriptWithoutAllowUnobservedAsmJS(wast) {
+    var sandbox = newGlobal('');
+    var dbg = new Debugger();
+    dbg.allowUnobservedAsmJS = true;
+    dbg.addDebuggee(sandbox);
+    sandbox.eval(`
+        var wasm = wasmTextToBinary('${wast}');
+        var m = new WebAssembly.Instance(new WebAssembly.Module(wasm));
+    `);
+    // Attaching after wasm instance is created.
+    var wasmScript = dbg.findScripts().filter(s => s.format == 'wasm')[0];
+    return wasmScript;
+}
+
+var wasmScript1 = getWasmScriptWithoutAllowUnobservedAsmJS('(module (func (nop)))');
+var wasmLines1 = wasmScript1.source.text.split('\n');
+assertEq(wasmScript1.startLine, 1);
+assertEq(wasmScript1.lineCount, 0);
+assertEq(wasmLines1.every((l, n) => wasmScript1.getLineOffsets(n + 1).length == 0), true);
+
+// Checking that we must not resolve any location for any offset in a wasm
+// instance which debug mode was not enabled.
+var wasmScript2 = getWasmScriptWithoutAllowUnobservedAsmJS('(module (func (nop)))');
+for (var i = wasmTextToBinary('(module (func (nop)))').length - 1; i >= 0; i--)
+    assertThrowsInstanceOf(() => wasmScript2.getOffsetLocation(i), Error);
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -5672,17 +5672,17 @@ struct DebuggerScriptGetLineCountMatcher
     using ReturnType = bool;
 
     ReturnType match(HandleScript script) {
         totalLines = double(GetScriptLineExtent(script));
         return true;
     }
     ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
         uint32_t result;
-        if (wasmInstance->instance().code().totalSourceLines(cx_, &result))
+        if (!wasmInstance->instance().code().totalSourceLines(cx_, &result))
             return false;
         totalLines = double(result);
         return true;
     }
 };
 
 static bool
 DebuggerScript_getLineCount(JSContext* cx, unsigned argc, Value* vp)
--- a/js/src/wasm/WasmCode.cpp
+++ b/js/src/wasm/WasmCode.cpp
@@ -859,22 +859,24 @@ Code::getOffsetLocation(JSContext* cx, u
     *lineno = loc.lineno + experimentalWarningLinesCount;
     *column = loc.column;
     return true;
 }
 
 bool
 Code::totalSourceLines(JSContext* cx, uint32_t* count)
 {
+    *count = 0;
+    if (!metadata_->debugEnabled)
+        return true;
+
     if (!ensureSourceMap(cx))
         return false;
 
-    if (!maybeSourceMap_)
-        *count = 0;
-    else
+    if (maybeSourceMap_)
         *count = maybeSourceMap_->totalLines() + experimentalWarningLinesCount;
     return true;
 }
 
 bool
 Code::stepModeEnabled(uint32_t funcIndex) const
 {
     return stepModeCounters_.initialized() && stepModeCounters_.lookup(funcIndex);
--- a/layout/style/ServoBindingList.h
+++ b/layout/style/ServoBindingList.h
@@ -167,16 +167,20 @@ SERVO_BINDING_FUNC(Servo_DeclarationBloc
                    nsCSSPropertyID property,
                    nsACString* value, bool is_important)
 SERVO_BINDING_FUNC(Servo_DeclarationBlock_RemoveProperty, void,
                    RawServoDeclarationBlockBorrowed declarations,
                    const nsACString* property)
 SERVO_BINDING_FUNC(Servo_DeclarationBlock_RemovePropertyById, void,
                    RawServoDeclarationBlockBorrowed declarations,
                    nsCSSPropertyID property)
+SERVO_BINDING_FUNC(Servo_DeclarationBlock_AddPresValue, void,
+                   RawServoDeclarationBlockBorrowed declarations,
+                   nsCSSPropertyID property,
+                   nsCSSValueBorrowedMut css_value)
 
 // CSS supports()
 SERVO_BINDING_FUNC(Servo_CSSSupports2, bool,
                    const nsACString* name, const nsACString* value)
 SERVO_BINDING_FUNC(Servo_CSSSupports, bool,
                    const nsACString* cond)
 
 // Computed style data
@@ -217,16 +221,18 @@ SERVO_BINDING_FUNC(Servo_ResolvePseudoSt
 //
 // The tree must be in a consistent state such that a normal traversal could be
 // performed, and this function maintains that invariant.
 SERVO_BINDING_FUNC(Servo_ResolveStyleLazily, ServoComputedValuesStrong,
                    RawGeckoElementBorrowed element, nsIAtom* pseudo_tag,
                    RawServoStyleSetBorrowed set)
 
 // Restyle the given subtree.
+// Use ServoStyleSet::PrepareAndTraverseSubtree instead of calling this
+// directly
 SERVO_BINDING_FUNC(Servo_TraverseSubtree, void,
                    RawGeckoElementBorrowed root, RawServoStyleSetBorrowed set,
                    mozilla::TraversalRootBehavior root_behavior)
 
 // Assert that the tree has no pending or unconsumed restyles.
 SERVO_BINDING_FUNC(Servo_AssertTreeIsClean, void, RawGeckoElementBorrowed root)
 
 // Style-struct management.
--- a/layout/style/ServoBindingTypes.h
+++ b/layout/style/ServoBindingTypes.h
@@ -63,16 +63,17 @@ typedef nsTArray<const RawServoAnimation
 #define DECL_BORROWED_REF_TYPE_FOR(type_) typedef type_ const* type_##Borrowed;
 #define DECL_NULLABLE_BORROWED_REF_TYPE_FOR(type_) typedef type_ const* type_##BorrowedOrNull;
 #define DECL_BORROWED_MUT_REF_TYPE_FOR(type_) typedef type_* type_##BorrowedMut;
 #define DECL_NULLABLE_BORROWED_MUT_REF_TYPE_FOR(type_) typedef type_* type_##BorrowedMutOrNull;
 
 #define SERVO_ARC_TYPE(name_, type_)         \
   DECL_NULLABLE_BORROWED_REF_TYPE_FOR(type_) \
   DECL_BORROWED_REF_TYPE_FOR(type_)          \
+  DECL_BORROWED_MUT_REF_TYPE_FOR(type_)      \
   struct MOZ_MUST_USE_TYPE type_##Strong     \
   {                                          \
     type_* mPtr;                             \
     already_AddRefed<type_> Consume();       \
   };
 #include "mozilla/ServoArcTypeList.h"
 #undef SERVO_ARC_TYPE
 
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -16,16 +16,17 @@
 #include "nsContentUtils.h"
 #include "nsDOMTokenList.h"
 #include "nsIContentInlines.h"
 #include "nsIDOMNode.h"
 #include "nsIDocument.h"
 #include "nsIFrame.h"
 #include "nsINode.h"
 #include "nsIPrincipal.h"
+#include "nsMappedAttributes.h"
 #include "nsMediaFeatures.h"
 #include "nsNameSpaceManager.h"
 #include "nsNetUtil.h"
 #include "nsRuleNode.h"
 #include "nsString.h"
 #include "nsStyleStruct.h"
 #include "nsStyleUtil.h"
 #include "nsTArray.h"
@@ -307,32 +308,47 @@ Gecko_DropElementSnapshot(ServoElementSn
     nsCOMPtr<nsIRunnable> task = NS_NewRunnableFunction([=]() { delete aSnapshot; });
     NS_DispatchToMainThread(task.forget());
   } else {
     delete aSnapshot;
   }
 }
 
 RawServoDeclarationBlockStrongBorrowedOrNull
-Gecko_GetServoDeclarationBlock(RawGeckoElementBorrowed aElement)
+Gecko_GetStyleAttrDeclarationBlock(RawGeckoElementBorrowed aElement)
 {
   DeclarationBlock* decl = aElement->GetInlineStyleDeclaration();
   if (!decl) {
     return nullptr;
   }
   if (decl->IsGecko()) {
     // XXX This can happen when nodes are adopted from a Gecko-style-backend
     //     document into a Servo-style-backend document.  See bug 1330051.
     NS_WARNING("stylo: requesting a Gecko declaration block?");
     return nullptr;
   }
   return reinterpret_cast<const RawServoDeclarationBlockStrong*>
     (decl->AsServo()->RefRaw());
 }
 
+RawServoDeclarationBlockStrongBorrowedOrNull
+Gecko_GetHTMLPresentationAttrDeclarationBlock(RawGeckoElementBorrowed aElement)
+{
+  static_assert(sizeof(RefPtr<RawServoDeclarationBlock>) ==
+                sizeof(RawServoDeclarationBlockStrong),
+                "RefPtr should just be a pointer");
+  const nsMappedAttributes* attrs = aElement->GetMappedAttributes();
+  if (!attrs) {
+    return nullptr;
+  }
+
+  const RefPtr<RawServoDeclarationBlock>& servo = attrs->GetServoStyle();
+  return reinterpret_cast<const RawServoDeclarationBlockStrong*>(&servo);
+}
+
 RawServoDeclarationBlockStrong
 Gecko_GetAnimationRule(RawGeckoElementBorrowed aElement,
                        nsIAtom* aPseudoTag,
                        EffectCompositor::CascadeLevel aCascadeLevel)
 {
   MOZ_ASSERT(aElement, "Invalid GeckoElement");
 
   const RawServoDeclarationBlockStrong emptyDeclarationBlock{ nullptr };
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -153,17 +153,19 @@ nsIAtom* Gecko_GetElementId(RawGeckoElem
 SERVO_DECLARE_ELEMENT_ATTR_MATCHING_FUNCTIONS(Gecko_, RawGeckoElementBorrowed)
 SERVO_DECLARE_ELEMENT_ATTR_MATCHING_FUNCTIONS(Gecko_Snapshot,
                                               const ServoElementSnapshot*)
 
 #undef SERVO_DECLARE_ELEMENT_ATTR_MATCHING_FUNCTIONS
 
 // Style attributes.
 RawServoDeclarationBlockStrongBorrowedOrNull
-Gecko_GetServoDeclarationBlock(RawGeckoElementBorrowed element);
+Gecko_GetStyleAttrDeclarationBlock(RawGeckoElementBorrowed element);
+RawServoDeclarationBlockStrongBorrowedOrNull
+Gecko_GetHTMLPresentationAttrDeclarationBlock(RawGeckoElementBorrowed element);
 
 // Animations
 RawServoDeclarationBlockStrong
 Gecko_GetAnimationRule(RawGeckoElementBorrowed aElement,
                        nsIAtom* aPseudoTag,
                        mozilla::EffectCompositor::CascadeLevel aCascadeLevel);
 
 // Atoms.
--- a/layout/style/ServoStyleSet.cpp
+++ b/layout/style/ServoStyleSet.cpp
@@ -9,16 +9,17 @@
 #include "mozilla/DocumentStyleRootIterator.h"
 #include "mozilla/ServoRestyleManager.h"
 #include "mozilla/dom/ChildIterator.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/ElementInlines.h"
 #include "nsAnimationManager.h"
 #include "nsCSSAnonBoxes.h"
 #include "nsCSSPseudoElements.h"
+#include "nsHTMLStyleSheet.h"
 #include "nsIDocumentInlines.h"
 #include "nsPrintfCString.h"
 #include "nsStyleContext.h"
 #include "nsStyleSet.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
@@ -122,16 +123,18 @@ ServoStyleSet::GetContext(nsIContent* aC
                           nsStyleContext* aParentContext,
                           nsIAtom* aPseudoTag,
                           CSSPseudoElementType aPseudoType,
                           LazyComputeBehavior aMayCompute)
 {
   MOZ_ASSERT(aContent->IsElement());
   Element* element = aContent->AsElement();
 
+
+  ResolveMappedAttrDeclarationBlocks();
   RefPtr<ServoComputedValues> computedValues;
   if (aMayCompute == LazyComputeBehavior::Allow) {
     computedValues =
       Servo_ResolveStyleLazily(element, nullptr, mRawSet.get()).Consume();
   } else {
     computedValues = ResolveServoStyle(element);
   }
 
@@ -171,16 +174,31 @@ ServoStyleSet::GetContext(already_AddRef
     // from nsStyleContext. Also we need to make the process thread safe.
     mPresContext->AnimationManager()->UpdateAnimations(result,
                                                        aElementForAnimation);
   }
 
   return result.forget();
 }
 
+void
+ServoStyleSet::ResolveMappedAttrDeclarationBlocks()
+{
+  if (nsHTMLStyleSheet* sheet = mPresContext->Document()->GetAttributeStyleSheet()) {
+    sheet->CalculateMappedServoDeclarations();
+  }
+}
+
+void
+ServoStyleSet::PrepareAndTraverseSubtree(RawGeckoElementBorrowed aRoot,
+                                         mozilla::TraversalRootBehavior aRootBehavior) {
+  ResolveMappedAttrDeclarationBlocks();
+  Servo_TraverseSubtree(aRoot, mRawSet.get(), aRootBehavior);
+}
+
 already_AddRefed<nsStyleContext>
 ServoStyleSet::ResolveStyleFor(Element* aElement,
                                nsStyleContext* aParentContext,
                                LazyComputeBehavior aMayCompute,
                                TreeMatchContext& aTreeMatchContext)
 {
   // aTreeMatchContext is used to speed up selector matching,
   // but if the element already has a ServoComputedValues computed in
@@ -536,33 +554,32 @@ ServoStyleSet::HasStateDependentStyle(do
 void
 ServoStyleSet::StyleDocument()
 {
   // Restyle the document from the root element and each of the document level
   // NAC subtree roots.
   DocumentStyleRootIterator iter(mPresContext->Document());
   while (Element* root = iter.GetNextStyleRoot()) {
     if (root->ShouldTraverseForServo()) {
-      Servo_TraverseSubtree(root, mRawSet.get(), TraversalRootBehavior::Normal);
+      PrepareAndTraverseSubtree(root, TraversalRootBehavior::Normal);
     }
   }
 }
 
 void
 ServoStyleSet::StyleNewSubtree(Element* aRoot)
 {
   MOZ_ASSERT(!aRoot->HasServoData());
-  Servo_TraverseSubtree(aRoot, mRawSet.get(), TraversalRootBehavior::Normal);
+  PrepareAndTraverseSubtree(aRoot, TraversalRootBehavior::Normal);
 }
 
 void
 ServoStyleSet::StyleNewChildren(Element* aParent)
 {
-  Servo_TraverseSubtree(aParent, mRawSet.get(),
-                        TraversalRootBehavior::UnstyledChildrenOnly);
+  PrepareAndTraverseSubtree(aParent, TraversalRootBehavior::UnstyledChildrenOnly);
 }
 
 void
 ServoStyleSet::NoteStyleSheetsChanged()
 {
   Servo_StyleSet_NoteStyleSheetsChanged(mRawSet.get());
 }
 
--- a/layout/style/ServoStyleSet.h
+++ b/layout/style/ServoStyleSet.h
@@ -194,16 +194,30 @@ private:
                                               dom::Element* aElementForAnimation);
 
   already_AddRefed<nsStyleContext> GetContext(nsIContent* aContent,
                                               nsStyleContext* aParentContext,
                                               nsIAtom* aPseudoTag,
                                               CSSPseudoElementType aPseudoType,
                                               LazyComputeBehavior aMayCompute);
 
+  /**
+   * Resolve all ServoDeclarationBlocks attached to mapped
+   * presentation attributes cached on the document.
+   * Call this before jumping into Servo's style system.
+   */
+  void ResolveMappedAttrDeclarationBlocks();
+
+  /**
+   * Perform all lazy operations required before traversing
+   * a subtree.
+   */
+  void PrepareAndTraverseSubtree(RawGeckoElementBorrowed aRoot,
+                                 mozilla::TraversalRootBehavior aRootBehavior);
+
   nsPresContext* mPresContext;
   UniquePtr<RawServoStyleSet> mRawSet;
   EnumeratedArray<SheetType, SheetType::Count,
                   nsTArray<RefPtr<ServoStyleSheet>>> mSheets;
   int32_t mBatching;
 };
 
 } // namespace mozilla
--- a/layout/style/nsHTMLStyleSheet.cpp
+++ b/layout/style/nsHTMLStyleSheet.cpp
@@ -284,16 +284,17 @@ static const PLDHashTableOps LangRuleTab
 
 // -----------------------------------------------------------
 
 nsHTMLStyleSheet::nsHTMLStyleSheet(nsIDocument* aDocument)
   : mDocument(aDocument)
   , mTableQuirkColorRule(new TableQuirkColorRule())
   , mTableTHRule(new TableTHRule())
   , mMappedAttrTable(&MappedAttrTable_Ops, sizeof(MappedAttrTableEntry))
+  , mMappedAttrsDirty(false)
   , mLangRuleTable(&LangRuleTable_Ops, sizeof(LangRuleTableEntry))
 {
   MOZ_ASSERT(aDocument);
 }
 
 NS_IMPL_ISUPPORTS(nsHTMLStyleSheet, nsIStyleRuleProcessor)
 
 /* virtual */ void
@@ -473,16 +474,17 @@ void
 nsHTMLStyleSheet::Reset()
 {
   mLinkRule          = nullptr;
   mVisitedRule       = nullptr;
   mActiveRule        = nullptr;
 
   mLangRuleTable.Clear();
   mMappedAttrTable.Clear();
+  mMappedAttrsDirty = false;
 }
 
 nsresult
 nsHTMLStyleSheet::ImplLinkColorSetter(RefPtr<HTMLColorRule>& aRule, nscolor aColor)
 {
   if (aRule && aRule->mColor == aColor) {
     return NS_OK;
   }
@@ -521,42 +523,133 @@ nsresult
 nsHTMLStyleSheet::SetVisitedLinkColor(nscolor aColor)
 {
   return ImplLinkColorSetter(mVisitedRule, aColor);
 }
 
 already_AddRefed<nsMappedAttributes>
 nsHTMLStyleSheet::UniqueMappedAttributes(nsMappedAttributes* aMapped)
 {
+  mMappedAttrsDirty = true;
   auto entry = static_cast<MappedAttrTableEntry*>
                           (mMappedAttrTable.Add(aMapped, fallible));
   if (!entry)
     return nullptr;
   if (!entry->mAttributes) {
     // We added a new entry to the hashtable, so we have a new unique set.
     entry->mAttributes = aMapped;
   }
   RefPtr<nsMappedAttributes> ret = entry->mAttributes;
   return ret.forget();
 }
 
 void
 nsHTMLStyleSheet::DropMappedAttributes(nsMappedAttributes* aMapped)
 {
   NS_ENSURE_TRUE_VOID(aMapped);
-
 #ifdef DEBUG
   uint32_t entryCount = mMappedAttrTable.EntryCount() - 1;
 #endif
 
   mMappedAttrTable.Remove(aMapped);
 
   NS_ASSERTION(entryCount == mMappedAttrTable.EntryCount(), "not removed");
 }
 
+namespace {
+  // Struct containing once-initialized information about
+  // our synthesized rule data
+  struct StaticRuleDataInfo
+  {
+    // the bitmask used.
+    uint64_t mMask;
+    // the number of properties contained
+    size_t mPropCount;
+    // offset of given style struct in array
+    size_t mOffsets[nsStyleStructID_Length];
+    // location of property in given array
+    nsCSSPropertyID* mIndexToPropertyMapping;
+  };
+}
+
+static void
+CalculateIndexArray(StaticRuleDataInfo* aInfo)
+{
+  // this will leak at shutdown, but it's not much and this code is temporary
+  // anyway.
+  aInfo->mIndexToPropertyMapping = new nsCSSPropertyID[aInfo->mPropCount];
+  size_t structOffset;
+  size_t propertyIndex;
+  #define CSS_PROP_LIST_EXCLUDE_LOGICAL
+  #define CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_,     \
+                   kwtable_, stylestruct_, stylestructoffset_, animtype_) \
+    structOffset = aInfo->mOffsets[eStyleStruct_##stylestruct_]; \
+    propertyIndex = nsCSSProps::PropertyIndexInStruct(eCSSProperty_##id_); \
+    aInfo->mIndexToPropertyMapping[structOffset + propertyIndex] = eCSSProperty_##id_;
+  #include "nsCSSPropList.h"
+  #undef CSS_PROP
+  #undef CSS_PROP_LIST_EXCLUDE_LOGICAL
+}
+
+static StaticRuleDataInfo
+CalculateRuleDataInfo()
+{
+  StaticRuleDataInfo sizes;
+  sizes.mMask = 0;
+  sizes.mPropCount = 0;
+#define STYLE_STRUCT(name, checkdata_cb) \
+  sizes.mMask |= NS_STYLE_INHERIT_BIT(name); \
+  sizes.mOffsets[eStyleStruct_##name] = sizes.mPropCount; \
+  sizes.mPropCount += nsCSSProps::PropertyCountInStruct(eStyleStruct_##name);
+#include "nsStyleStructList.h"
+#undef STYLE_STRUCT
+
+  CalculateIndexArray(&sizes);
+
+  return sizes;
+}
+
+
+
+void
+nsHTMLStyleSheet::CalculateMappedServoDeclarations()
+{
+  // avoid recalculating or reallocating
+  static StaticRuleDataInfo sizes = CalculateRuleDataInfo();
+
+  if (!mMappedAttrsDirty) {
+    return;
+  }
+  mMappedAttrsDirty = false;
+
+  void* dataStorage = alloca(sizes.mPropCount * sizeof(nsCSSValue));
+  for (auto iter = mMappedAttrTable.Iter(); !iter.Done(); iter.Next()) {
+    MappedAttrTableEntry* attr = static_cast<MappedAttrTableEntry*>(iter.Get());
+    if (attr->mAttributes->GetServoStyle()) {
+      // Only handle cases which haven't been filled in already
+      continue;
+    }
+    // Construction cleans up any values we may have set
+    AutoCSSValueArray dataArray(dataStorage, sizes.mPropCount);
+
+    // synthesized ruleData
+    // we pass null for the style context because the code we call into
+    // doesn't deal with the style context. This is temporary.
+    nsRuleData ruleData(sizes.mMask, dataArray.get(),
+                        mDocument->GetShell()->GetPresContext(), nullptr);
+    // Copy the offsets; ruleData won't know where to find properties otherwise
+    mozilla::PodCopy(ruleData.mValueOffsets,
+                     sizes.mOffsets,
+                     nsStyleStructID_Length);
+    attr->mAttributes->LazilyResolveServoDeclaration(&ruleData,
+                                                     sizes.mIndexToPropertyMapping,
+                                                     sizes.mPropCount);
+  }
+}
+
 nsIStyleRule*
 nsHTMLStyleSheet::LangRuleFor(const nsString& aLanguage)
 {
   auto entry =
     static_cast<LangRuleTableEntry*>(mLangRuleTable.Add(&aLanguage, fallible));
   if (!entry) {
     NS_ASSERTION(false, "out of memory");
     return nullptr;
--- a/layout/style/nsHTMLStyleSheet.h
+++ b/layout/style/nsHTMLStyleSheet.h
@@ -57,16 +57,20 @@ public:
   nsresult SetLinkColor(nscolor aColor);
   nsresult SetActiveLinkColor(nscolor aColor);
   nsresult SetVisitedLinkColor(nscolor aColor);
 
   // Mapped Attribute management methods
   already_AddRefed<nsMappedAttributes>
     UniqueMappedAttributes(nsMappedAttributes* aMapped);
   void DropMappedAttributes(nsMappedAttributes* aMapped);
+  // For each mapped presentation attribute in the cache, resolve
+  // the attached ServoDeclarationBlock by running the mapping
+  // and converting the ruledata to Servo specified values.
+  void CalculateMappedServoDeclarations();
 
   nsIStyleRule* LangRuleFor(const nsString& aLanguage);
 
 private: 
   nsHTMLStyleSheet(const nsHTMLStyleSheet& aCopy) = delete;
   nsHTMLStyleSheet& operator=(const nsHTMLStyleSheet& aCopy) = delete;
 
   ~nsHTMLStyleSheet() {}
@@ -170,12 +174,16 @@ private:
   nsIDocument*            mDocument;
   RefPtr<HTMLColorRule> mLinkRule;
   RefPtr<HTMLColorRule> mVisitedRule;
   RefPtr<HTMLColorRule> mActiveRule;
   RefPtr<TableQuirkColorRule> mTableQuirkColorRule;
   RefPtr<TableTHRule>   mTableTHRule;
 
   PLDHashTable            mMappedAttrTable;
+  // Whether or not the mapped attributes table
+  // has been changed since the last call to
+  // CalculateMappedServoDeclarations()
+  bool                    mMappedAttrsDirty;
   PLDHashTable            mLangRuleTable;
 };
 
 #endif /* !defined(nsHTMLStyleSheet_h_) */
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -2349,54 +2349,35 @@ UnsetPropertiesWithoutFlags(const nsStyl
 
   for (size_t i = 0, i_end = nsCSSProps::PropertyCountInStruct(aSID);
        i != i_end; ++i) {
     if ((flagData[i] & aFlags) != aFlags)
       values[i].Reset();
   }
 }
 
-/**
- * We allocate arrays of CSS values with alloca.  (These arrays are a
- * fixed size per style struct, but we don't want to waste the
- * allocation and construction/destruction costs of the big structs when
- * we're handling much smaller ones.)  Since the lifetime of an alloca
- * allocation is the life of the calling function, the caller must call
- * alloca.  However, to ensure that constructors and destructors are
- * balanced, we do the constructor and destructor calling from this RAII
- * class, AutoCSSValueArray.
- */
-struct AutoCSSValueArray {
-  /**
-   * aStorage must be the result of alloca(aCount * sizeof(nsCSSValue))
-   */
-  AutoCSSValueArray(void* aStorage, size_t aCount) {
-    MOZ_ASSERT(size_t(aStorage) % NS_ALIGNMENT_OF(nsCSSValue) == 0,
-               "bad alignment from alloca");
-    mCount = aCount;
-    // Don't use placement new[], since it might store extra data
-    // for the count (on Windows!).
-    mArray = static_cast<nsCSSValue*>(aStorage);
-    for (size_t i = 0; i < mCount; ++i) {
-      new (KnownNotNull, mArray + i) nsCSSValue();
-    }
-  }
-
-  ~AutoCSSValueArray() {
-    for (size_t i = 0; i < mCount; ++i) {
-      mArray[i].~nsCSSValue();
-    }
-  }
-
-  nsCSSValue* get() { return mArray; }
-
-private:
-  nsCSSValue *mArray;
-  size_t mCount;
-};
+AutoCSSValueArray::AutoCSSValueArray(void* aStorage, size_t aCount)
+{
+  MOZ_ASSERT(size_t(aStorage) % NS_ALIGNMENT_OF(nsCSSValue) == 0,
+             "bad alignment from alloca");
+  mCount = aCount;
+  // Don't use placement new[], since it might store extra data
+  // for the count (on Windows!).
+  mArray = static_cast<nsCSSValue*>(aStorage);
+  for (size_t i = 0; i < mCount; ++i) {
+    new (KnownNotNull, mArray + i) nsCSSValue();
+  }
+}
+
+AutoCSSValueArray::~AutoCSSValueArray()
+{
+  for (size_t i = 0; i < mCount; ++i) {
+    mArray[i].~nsCSSValue();
+  }
+}
 
 /* static */ bool
 nsRuleNode::ResolveVariableReferences(const nsStyleStructID aSID,
                                       nsRuleData* aRuleData,
                                       nsStyleContext* aContext)
 {
   MOZ_ASSERT(aSID != eStyleStruct_Variables);
   MOZ_ASSERT(aRuleData->mSIDs & nsCachedStyleData::GetBitForSID(aSID));
--- a/layout/style/nsRuleNode.h
+++ b/layout/style/nsRuleNode.h
@@ -1080,9 +1080,35 @@ private:
 
   // Store style struct on the style context and tell the style context
   // that it doesn't own the data
   static void StoreStyleOnContext(nsStyleContext* aContext,
                                   nsStyleStructID aSID,
                                   void* aStruct);
 };
 
+/**
+ * We allocate arrays of CSS values with alloca.  (These arrays are a
+ * fixed size per style struct, but we don't want to waste the
+ * allocation and construction/destruction costs of the big structs when
+ * we're handling much smaller ones.)  Since the lifetime of an alloca
+ * allocation is the life of the calling function, the caller must call
+ * alloca.  However, to ensure that constructors and destructors are
+ * balanced, we do the constructor and destructor calling from this RAII
+ * class, AutoCSSValueArray.
+ */
+struct AutoCSSValueArray
+{
+  /**
+   * aStorage must be the result of alloca(aCount * sizeof(nsCSSValue))
+   */
+  AutoCSSValueArray(void* aStorage, size_t aCount);
+
+  ~AutoCSSValueArray();
+
+  nsCSSValue* get() { return mArray; }
+
+private:
+  nsCSSValue *mArray;
+  size_t mCount;
+};
+
 #endif
--- a/layout/tools/reftest/mach_test_package_commands.py
+++ b/layout/tools/reftest/mach_test_package_commands.py
@@ -83,10 +83,11 @@ class ReftestCommands(object):
 
     def __init__(self, context):
         self.context = context
 
     @Command('reftest', category='testing',
              description='Run the reftest harness.',
              parser=setup_argument_parser)
     def reftest(self, **kwargs):
+        self.context.activate_mozharness_venv()
         kwargs['suite'] = 'reftest'
         return run_reftest(self.context, **kwargs)
--- a/media/libstagefright/binding/AnnexB.cpp
+++ b/media/libstagefright/binding/AnnexB.cpp
@@ -40,21 +40,25 @@ AnnexB::ConvertSampleToAnnexB(mozilla::M
 
   mozilla::Vector<uint8_t> tmp;
   ByteWriter writer(tmp);
 
   while (reader.Remaining() >= 4) {
     uint32_t nalLen = reader.ReadU32();
     const uint8_t* p = reader.Read(nalLen);
 
-    writer.Write(kAnnexBDelimiter, ArrayLength(kAnnexBDelimiter));
+    if (!writer.Write(kAnnexBDelimiter, ArrayLength(kAnnexBDelimiter))) {
+      return false;
+    }
     if (!p) {
       break;
     }
-    writer.Write(p, nalLen);
+    if (!writer.Write(p, nalLen)) {
+      return false;
+    }
   }
 
   nsAutoPtr<MediaRawDataWriter> samplewriter(aSample->CreateWriter());
 
   if (!samplewriter->Replace(tmp.begin(), tmp.length())) {
     return false;
   }
 
@@ -183,39 +187,44 @@ FindStartCode(ByteReader& aBr, size_t& a
     if (aBr.ReadU8() == 0) {
       aStartSize = 4;
     }
   }
   aBr.Read(3);
   return true;
 }
 
-static void
+static bool
 ParseNALUnits(ByteWriter& aBw, ByteReader& aBr)
 {
   size_t startSize;
 
   bool rv = FindStartCode(aBr, startSize);
   if (rv) {
     size_t startOffset = aBr.Offset();
     while (FindStartCode(aBr, startSize)) {
       size_t offset = aBr.Offset();
       size_t sizeNAL = offset - startOffset - startSize;
       aBr.Seek(startOffset);
-      aBw.WriteU32(sizeNAL);
-      aBw.Write(aBr.Read(sizeNAL), sizeNAL);
+      if (!aBw.WriteU32(sizeNAL)
+          || !aBw.Write(aBr.Read(sizeNAL), sizeNAL)) {
+        return false;
+      }
       aBr.Read(startSize);
       startOffset = offset;
     }
   }
   size_t sizeNAL = aBr.Remaining();
   if (sizeNAL) {
-    aBw.WriteU32(sizeNAL);
-    aBw.Write(aBr.Read(sizeNAL), sizeNAL);
+    if (!aBw.WriteU32(sizeNAL)
+        || !aBw.Write(aBr.Read(sizeNAL), sizeNAL)) {
+      return false;
+    }
   }
+  return true;
 }
 
 bool
 AnnexB::ConvertSampleToAVCC(mozilla::MediaRawData* aSample)
 {
   if (IsAVCC(aSample)) {
     return ConvertSampleTo4BytesAVCC(aSample);
   }
@@ -223,17 +232,19 @@ AnnexB::ConvertSampleToAVCC(mozilla::Med
     // Not AnnexB, nothing to convert.
     return true;
   }
 
   mozilla::Vector<uint8_t> nalu;
   ByteWriter writer(nalu);
   ByteReader reader(aSample->Data(), aSample->Size());
 
-  ParseNALUnits(writer, reader);
+  if (!ParseNALUnits(writer, reader)) {
+    return false;
+  }
   nsAutoPtr<MediaRawDataWriter> samplewriter(aSample->CreateWriter());
   return samplewriter->Replace(nalu.begin(), nalu.length());
 }
 
 already_AddRefed<mozilla::MediaByteBuffer>
 AnnexB::ExtractExtraData(const mozilla::MediaRawData* aSample)
 {
   RefPtr<mozilla::MediaByteBuffer> extradata = new mozilla::MediaByteBuffer;
@@ -279,22 +290,26 @@ AnnexB::ExtractExtraData(const mozilla::
     uint8_t nalType = reader.PeekU8() & 0x1f;
     const uint8_t* p = reader.Read(nalLen);
     if (!p) {
       return extradata.forget();
     }
 
     if (nalType == 0x7) { /* SPS */
       numSps++;
-      spsw.WriteU16(nalLen);
-      spsw.Write(p, nalLen);
+      if (!spsw.WriteU16(nalLen)
+          || !spsw.Write(p, nalLen)) {
+        return extradata.forget();
+      }
     } else if (nalType == 0x8) { /* PPS */
       numPps++;
-      ppsw.WriteU16(nalLen);
-      ppsw.Write(p, nalLen);
+      if (!ppsw.WriteU16(nalLen)
+          || !ppsw.Write(p, nalLen)) {
+        return extradata.forget();
+      }
     }
   }
 
   if (numSps && sps.length() > 5) {
     extradata->AppendElement(1);        // version
     extradata->AppendElement(sps[3]);   // profile
     extradata->AppendElement(sps[4]);   // profile compat
     extradata->AppendElement(sps[5]);   // level
@@ -394,18 +409,20 @@ AnnexB::ConvertSampleTo4BytesAVCC(mozill
       case 2: nalLen = reader.ReadU16(); break;
       case 3: nalLen = reader.ReadU24(); break;
       case 4: nalLen = reader.ReadU32(); break;
     }
     const uint8_t* p = reader.Read(nalLen);
     if (!p) {
       return true;
     }
-    writer.WriteU32(nalLen);
-    writer.Write(p, nalLen);
+    if (!writer.WriteU32(nalLen)
+        || !writer.Write(p, nalLen)) {
+      return false;
+    }
   }
   nsAutoPtr<MediaRawDataWriter> samplewriter(aSample->CreateWriter());
   return samplewriter->Replace(dest.begin(), dest.length());
 }
 
 bool
 AnnexB::IsAVCC(const mozilla::MediaRawData* aSample)
 {
--- a/media/libstagefright/binding/include/mp4_demuxer/ByteWriter.h
+++ b/media/libstagefright/binding/include/mp4_demuxer/ByteWriter.h
@@ -17,59 +17,59 @@ public:
   explicit ByteWriter(mozilla::Vector<uint8_t>& aData)
     : mPtr(aData)
   {
   }
   ~ByteWriter()
   {
   }
 
-  void WriteU8(uint8_t aByte)
+  MOZ_MUST_USE bool WriteU8(uint8_t aByte)
   {
-    Write(&aByte, 1);
+    return Write(&aByte, 1);
   }
 
-  void WriteU16(uint16_t aShort)
+  MOZ_MUST_USE bool WriteU16(uint16_t aShort)
   {
     uint8_t c[2];
     mozilla::BigEndian::writeUint16(&c[0], aShort);
-    Write(&c[0], 2);
+    return Write(&c[0], 2);
   }
 
-  void WriteU32(uint32_t aLong)
+  MOZ_MUST_USE bool WriteU32(uint32_t aLong)
   {
     uint8_t c[4];
     mozilla::BigEndian::writeUint32(&c[0], aLong);
-    Write(&c[0], 4);
+    return Write(&c[0], 4);
   }
 
-  void Write32(int32_t aLong)
+  MOZ_MUST_USE bool Write32(int32_t aLong)
   {
     uint8_t c[4];
     mozilla::BigEndian::writeInt32(&c[0], aLong);
-    Write(&c[0], 4);
+    return Write(&c[0], 4);
   }
 
-  void WriteU64(uint64_t aLongLong)
+  MOZ_MUST_USE bool WriteU64(uint64_t aLongLong)
   {
     uint8_t c[8];
     mozilla::BigEndian::writeUint64(&c[0], aLongLong);
-    Write(&c[0], 8);
+    return Write(&c[0], 8);
   }
 
-  void Write64(int64_t aLongLong)
+  MOZ_MUST_USE bool Write64(int64_t aLongLong)
   {
     uint8_t c[8];
     mozilla::BigEndian::writeInt64(&c[0], aLongLong);
-    Write(&c[0], 8);
+    return Write(&c[0], 8);
   }
 
-  void Write(const uint8_t* aSrc, size_t aCount)
+  MOZ_MUST_USE bool Write(const uint8_t* aSrc, size_t aCount)
   {
-    MOZ_RELEASE_ASSERT(mPtr.append(aSrc, aCount));
+    return mPtr.append(aSrc, aCount);
   }
 
 private:
   mozilla::Vector<uint8_t>& mPtr;
 };
 
 } // namespace mp4_demuxer
 
--- a/mobile/android/base/java/org/mozilla/gecko/switchboard/SwitchBoard.java
+++ b/mobile/android/base/java/org/mozilla/gecko/switchboard/SwitchBoard.java
@@ -32,16 +32,17 @@ import org.json.JSONException;
 import org.json.JSONObject;
 import org.json.JSONArray;
 
 import android.content.Context;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.os.Build;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.text.TextUtils;
 import android.util.Log;
 
 
 /**
  * SwitchBoard is the core class of the KeepSafe Switchboard mobile A/B testing framework.
  * This class provides a bunch of static methods that can be used in your app to run A/B tests.
  *
  * The SwitchBoard supports production and staging environment.
@@ -194,80 +195,102 @@ public class SwitchBoard {
             if (entry.getString(KEY_NAME).equals(experimentName)) {
                 experiment = entry;
                 break;
             }
         }
         return experiment;
     }
 
-    private static boolean isMatch(Context c, @Nullable JSONObject matchKeys) {
+    /**
+     * Return false if the match object contains any non-matching patterns. Otherwise returns true.
+     */
+    private static boolean isMatch(Context context, @Nullable JSONObject matchKeys) {
         // If no match keys are specified, default to enabling the experiment.
         if (matchKeys == null) {
             return true;
         }
 
         if (matchKeys.has(KEY_APP_ID)) {
-            final String packageName = c.getPackageName();
             try {
-                if (!packageName.matches(matchKeys.getString(KEY_APP_ID))) {
+                final String packageName = context.getPackageName();
+                final String expectedAppIdPattern = matchKeys.getString(KEY_APP_ID);
+
+                if (!TextUtils.isEmpty(expectedAppIdPattern)
+                        && !packageName.matches(expectedAppIdPattern)) {
                     return false;
                 }
             } catch (JSONException e) {
                 Log.e(TAG, "Exception matching appId", e);
             }
         }
 
         if (matchKeys.has(KEY_COUNTRY)) {
             try {
                 final String country = Locale.getDefault().getISO3Country();
-                if (!country.matches(matchKeys.getString(KEY_COUNTRY))) {
+                final String expectedCountryPattern = matchKeys.getString(KEY_COUNTRY);
+
+                if (!TextUtils.isEmpty(expectedCountryPattern)
+                        && !country.matches(expectedCountryPattern)) {
                     return false;
                 }
             } catch (MissingResourceException | JSONException e) {
                 Log.e(TAG, "Exception matching country", e);
             }
         }
 
         if (matchKeys.has(KEY_DEVICE)) {
-            final String device = Build.DEVICE;
             try {
-                if (!device.matches(matchKeys.getString(KEY_DEVICE))) {
+                final String device = Build.DEVICE;
+                final String expectedDevicePattern = matchKeys.getString(KEY_DEVICE);
+
+                if (!TextUtils.isEmpty(expectedDevicePattern)
+                        && !device.matches(expectedDevicePattern)) {
                     return false;
                 }
             } catch (JSONException e) {
                 Log.e(TAG, "Exception matching device", e);
             }
+        }
 
-        }
         if (matchKeys.has(KEY_LANG)) {
             try {
                 final String lang = Locale.getDefault().getISO3Language();
-                if (!lang.matches(matchKeys.getString(KEY_LANG))) {
+                final String expectedLanguagePattern = matchKeys.getString(KEY_LANG);
+
+                if (!TextUtils.isEmpty(expectedLanguagePattern)
+                        && !lang.matches(expectedLanguagePattern)) {
                     return false;
                 }
             } catch (MissingResourceException | JSONException e) {
                 Log.e(TAG, "Exception matching lang", e);
             }
         }
+
         if (matchKeys.has(KEY_MANUFACTURER)) {
-            final String manufacturer = Build.MANUFACTURER;
             try {
-                if (!manufacturer.matches(matchKeys.getString(KEY_MANUFACTURER))) {
+                final String manufacturer = Build.MANUFACTURER;
+                final String expectedManufacturerPattern = matchKeys.getString(KEY_MANUFACTURER);
+
+                if (!TextUtils.isEmpty(expectedManufacturerPattern)
+                        && !manufacturer.matches(expectedManufacturerPattern)) {
                     return false;
                 }
             } catch (JSONException e) {
                 Log.e(TAG, "Exception matching manufacturer", e);
             }
         }
 
         if (matchKeys.has(KEY_VERSION)) {
             try {
-                final String version = c.getPackageManager().getPackageInfo(c.getPackageName(), 0).versionName;
-                if (!version.matches(matchKeys.getString(KEY_VERSION))) {
+                final String version = context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionName;
+                final String expectedVersionPattern = matchKeys.getString(KEY_VERSION);
+
+                if (!TextUtils.isEmpty(expectedVersionPattern)
+                        && !version.matches(expectedVersionPattern)) {
                     return false;
                 }
             } catch (NameNotFoundException | JSONException e) {
                 Log.e(TAG, "Exception matching version", e);
             }
         }
 
         // Default to return true if no matches failed.
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -4813,17 +4813,16 @@ pref("full-screen-api.transition-duratio
 // timeout for black screen in fullscreen transition, unit: ms
 pref("full-screen-api.transition.timeout", 1000);
 // time for the warning box stays on the screen before sliding out, unit: ms
 pref("full-screen-api.warning.timeout", 3000);
 // delay for the warning box to show when pointer stays on the top, unit: ms
 pref("full-screen-api.warning.delay", 500);
 
 // DOM pointerlock API
-pref("pointer-lock-api.prefixed.enabled", false);
 // time for the warning box stays on the screen before sliding out, unit: ms
 pref("pointer-lock-api.warning.timeout", 3000);
 
 // DOM idle observers API
 pref("dom.idle-observers-api.enabled", true);
 
 // Time limit, in milliseconds, for EventStateManager::IsHandlingUserInput().
 // Used to detect long running handlers of user-generated events.
--- a/taskcluster/ci/test/test-platforms.yml
+++ b/taskcluster/ci/test/test-platforms.yml
@@ -14,21 +14,23 @@
 
 ##
 # Linux platforms (matching /linux.*/)
 
 linux32/debug:
     build-platform: linux/debug
     test-sets:
         - linux32-tests
+        - external-media-tests
 linux32/opt:
     build-platform: linux/opt
     test-sets:
         -  linux32-tests
         -  linux32-opt-tests
+        - external-media-tests
 
 linux64/debug:
     build-platform: linux64/debug
     test-sets:
         - common-tests
         - external-media-tests
         - web-platform-tests
 linux64/opt:
@@ -79,40 +81,44 @@ linux64-jsdcov/opt:
 ##
 # Windows platforms (matching /windows.*/)
 
 # win32 vm
 windows7-32-vm/debug:
     build-platform: win32/debug
     test-sets:
         - windows-vm-tests
+        - external-media-tests
 windows7-32-vm/opt:
     build-platform: win32/opt
     test-sets:
         - windows-vm-tests
+        - external-media-tests
 
 # win32 gpu
 #windows7-32/debug:
 #    build-platform: win32/debug
 #    test-sets:
 #        - windows-gpu-tests
 #windows7-32/opt:
 #    build-platform: win32/opt
 #    test-sets:
 #        - windows-gpu-tests
 
 # win64 vm
 windows10-64-vm/debug:
     build-platform: win64/debug
     test-sets:
         - windows-vm-tests
+        - external-media-tests
 windows10-64-vm/opt:
     build-platform: win64/opt
     test-sets:
         - windows-vm-tests
+        - external-media-tests
 
 # win64 gpu
 #windows10-64/debug:
 #    build-platform: win64/debug
 #    test-sets:
 #        - windows-gpu-tests
 #windows10-64/opt:
 #    build-platform: win64/opt
--- a/taskcluster/ci/test/test-sets.yml
+++ b/taskcluster/ci/test/test-sets.yml
@@ -39,17 +39,19 @@ common-tests:
     - xpcshell
 
 web-platform-tests:
     - web-platform-tests
     - web-platform-tests-reftests
     - web-platform-tests-wdspec
 
 external-media-tests:
-    - external-media-tests
+    - external-media-tests-base
+    - external-media-tests-youtube
+    - external-media-tests-twitch
 
 opt-only-tests:
     - mochitest-valgrind
 
 talos:
     - talos-chrome
     - talos-dromaeojs
     - talos-g1
@@ -83,17 +85,16 @@ jsdcov-code-coverage-tests:
     - mochitest-devtools-chrome
 
 ##
 # Test sets still being greened up in various ways
 
 windows-vm-tests:
     - cppunit
     #- crashtest
-    - external-media-tests
     #- gtest
     #- jittest
     - jsreftest
     #- marionette
     #- mochitest
     #- mochitest-browser-chrome
     - mochitest-devtools-chrome
     - mochitest-jetpack
@@ -142,17 +143,16 @@ macosx64-tests-talos:
     - talos-g4
     - talos-other
     - talos-svgr
     - talos-tp5o
 
 linux32-tests:
     - cppunit
     - crashtest
-    - external-media-tests
     - firefox-ui-functional-local
     - firefox-ui-functional-remote
     - gtest
     - jittest
     - jsreftest
     - marionette
     - mochitest
     - mochitest-a11y
--- a/taskcluster/ci/test/tests.yml
+++ b/taskcluster/ci/test/tests.yml
@@ -84,17 +84,17 @@ crashtest:
                             - remove_executables.py
                             - unittests/mac_unittest.py
                         linux.*:
                             - unittests/linux_unittest.py
                             - remove_executables.py
                 extra-options:
                     - --reftest-suite=crashtest
 
-external-media-tests:
+external-media-tests-base:
     description: "External Media Test run"
     suite: external-media-tests
     treeherder-symbol: tc-VP(b-m)
     e10s: false
     tier: 2
     max-run-time: 5400
     docker-image: {"in-tree": "desktop1604-test"}
     run-on-projects:
@@ -106,16 +106,64 @@ external-media-tests:
         config:
             by-test-platform:
                 windows.*:
                     - mediatests/taskcluster_windows_config.py
                 default:
                     - mediatests/taskcluster_posix_config.py
                     - remove_executables.py
 
+external-media-tests-twitch:
+    description: "External Media Test Twitch run"
+    suite: external-media-tests/twitch
+    treeherder-symbol: tc-VP(b-t)
+    e10s: false
+    tier: 2
+    max-run-time: 5400
+    docker-image: {"in-tree": "desktop1604-test"}
+    run-on-projects:
+        by-test-platform:
+            windows.*: ['mozilla-central', 'try']
+            default: ['all']
+    mozharness:
+        script: firefox_media_tests_taskcluster.py
+        config:
+            by-test-platform:
+                windows.*:
+                    - mediatests/taskcluster_windows_config.py
+                default:
+                    - mediatests/taskcluster_posix_config.py
+                    - remove_executables.py
+        extra-options:
+            - "--suite=media-twitch-tests"
+
+external-media-tests-youtube:
+    description: "External Media Test Youtube run"
+    suite: external-media-tests/youtube
+    treeherder-symbol: tc-VP(b-y)
+    e10s: false
+    tier: 2
+    max-run-time: 5400
+    docker-image: {"in-tree": "desktop1604-test"}
+    run-on-projects:
+        by-test-platform:
+            windows.*: ['mozilla-central', 'try']
+            default: ['all']
+    mozharness:
+        script: firefox_media_tests_taskcluster.py
+        config:
+            by-test-platform:
+                windows.*:
+                    - mediatests/taskcluster_windows_config.py
+                default:
+                    - mediatests/taskcluster_posix_config.py
+                    - remove_executables.py
+        extra-options:
+            - "--suite=media-youtube-tests"
+
 firefox-ui-functional-local:
     description: "Firefox-ui-tests functional run"
     suite: "firefox-ui/functional local"
     treeherder-symbol: tc-Fxfn-l(en-US)
     max-run-time: 5400
     tier: 1
     docker-image: {"in-tree": "desktop1604-test"}
     mozharness:
--- a/taskcluster/scripts/tester/run-wizard
+++ b/taskcluster/scripts/tester/run-wizard
@@ -53,23 +53,16 @@ def setup():
     build_dir = os.path.expanduser(os.path.join('~', 'workspace', 'build'))
     mach_src = os.path.join(build_dir, 'tests', 'mach')
     mach_dest = os.path.expanduser(os.path.join('~', 'bin', 'mach'))
 
     if os.path.exists(mach_dest):
         os.remove(mach_dest)
     os.symlink(mach_src, mach_dest)
 
-    activate = os.path.join(build_dir, 'venv', 'bin', 'activate')
-    if os.path.isfile(activate):
-        # TODO Support other shells
-        bashrc = os.path.expanduser(os.path.join('~', '.bashrc'))
-        with open(bashrc, 'ab') as f:
-            f.write(". {}".format(activate))
-
     print("""
 Mozharness has finished downloading the build and tests to:
 {}
 
 A limited mach environment has also been set up and added to the $PATH, but
 it may be missing the command you need. To see a list of commands, run:
     $ mach help
 """.lstrip().format(build_dir))
--- a/taskcluster/taskgraph/target_tasks.py
+++ b/taskcluster/taskgraph/target_tasks.py
@@ -63,17 +63,17 @@ def target_tasks_try_option_syntax(full_
     return target_tasks_labels
 
 
 @_target_task('default')
 def target_tasks_default(full_task_graph, parameters):
     """Target the tasks which have indicated they should be run on this project
     via the `run_on_projects` attributes."""
     def filter(task):
-        run_on_projects = set(t.attributes.get('run_on_projects', []))
+        run_on_projects = set(task.attributes.get('run_on_projects', []))
         if 'all' in run_on_projects:
             return True
         project = parameters['project']
         if 'integration' in run_on_projects:
             if project in INTEGRATION_PROJECTS:
                 return True
         if 'release' in run_on_projects:
             if project in RELEASE_PROJECTS:
--- a/testing/marionette/action.js
+++ b/testing/marionette/action.js
@@ -420,17 +420,17 @@ class InputState {
 }
 
 /** Possible kinds of |InputState| for supported input sources. */
 action.InputState = {};
 
 /**
  * Input state associated with a keyboard-type device.
  */
-action.InputState.Key = class extends InputState {
+action.InputState.Key = class Key extends InputState {
   constructor() {
     super();
     this.pressed = new Set();
     this.alt = false;
     this.shift = false;
     this.ctrl = false;
     this.meta = false;
   }
@@ -493,32 +493,32 @@ action.InputState.Key = class extends In
   release(key) {
     return this.pressed.delete(key);
   }
 };
 
 /**
  * Input state not associated with a specific physical device.
  */
-action.InputState.Null = class extends InputState {
+action.InputState.Null = class Null extends InputState {
   constructor() {
     super();
     this.type = "none";
   }
 };
 
 /**
  * Input state associated with a pointer-type input device.
  *
  * @param {string} subtype
  *     Kind of pointing device: mouse, pen, touch.
  * @param {boolean} primary
  *     Whether the pointing device is primary.
  */
-action.InputState.Pointer = class extends InputState {
+action.InputState.Pointer = class Pointer extends InputState {
   constructor(subtype, primary) {
     super();
     this.pressed = new Set();
     this.subtype = subtype;
     this.primary = primary;
     this.x = 0;
     this.y = 0;
   }
--- a/testing/marionette/mach_test_package_commands.py
+++ b/testing/marionette/mach_test_package_commands.py
@@ -59,9 +59,10 @@ class MachCommands(object):
         self.context = context
 
     @Command(
         'marionette-test', category='testing',
         description='Run a Marionette test (Check UI or the internal JavaScript '
                     'using marionette).',
         parser=setup_marionette_argument_parser)
     def run_marionette_test(self, **kwargs):
+        self.context.activate_mozharness_venv()
         return run_marionette(self.context, **kwargs)
--- a/testing/marionette/test_action.js
+++ b/testing/marionette/test_action.js
@@ -10,17 +10,17 @@ Cu.import("chrome://marionette/content/a
 Cu.import("chrome://marionette/content/element.js");
 Cu.import("chrome://marionette/content/error.js");
 
 action.inputStateMap = new Map();
 
 add_test(function test_createAction() {
   Assert.throws(() => new action.Action(), InvalidArgumentError,
       "Missing Action constructor args");
-  Assert.throws(() => new action.Action(1,2), InvalidArgumentError,
+  Assert.throws(() => new action.Action(1, 2), InvalidArgumentError,
       "Missing Action constructor args");
   Assert.throws(
       () => new action.Action(1, 2, "sometype"), /Expected string/, "Non-string arguments.");
   ok(new action.Action("id", "sometype", "sometype"));
 
   run_next_test();
 });
 
@@ -152,17 +152,17 @@ add_test(function test_processPointerMov
 add_test(function test_processPointerAction() {
   let actionSequence = {
     type: "pointer",
     id: "some_id",
     parameters: {
       pointerType: "touch",
       primary: false,
     },
-  }
+  };
   let actionItems = [
     {
       duration: 2000,
       type: "pause",
     },
     {
       type: "pointerMove",
       duration: 2000,
@@ -359,33 +359,46 @@ add_test(function test_processInputSourc
 });
 
 add_test(function test_processPointerActionInputStateMap() {
   let actionItem = {type: "pointerDown"};
   let id = "1";
   let parameters = {pointerType: "mouse", primary: false};
   let a = new action.Action(id, "pointer", actionItem.type);
   let wrongInputState = new action.InputState.Pointer("pause", true);
-  action.inputStateMap.set(id, wrongInputState)
+  action.inputStateMap.set(id, wrongInputState);
   checkErrors(
       /to be mapped to InputState whose subtype is/, action.processPointerAction,
       [id, parameters, a],
       `$subtype ${actionItem.type} with ${wrongInputState.subtype} in inputState`);
   action.inputStateMap.clear();
   let rightInputState = new action.InputState.Pointer("pointerDown", false);
   action.inputStateMap.set(id, rightInputState);
   action.processPointerAction(id, parameters, a);
   equal(a.primary, parameters.primary);
   action.inputStateMap.clear();
   run_next_test();
 });
 
+add_test(function test_createInputState() {
+  for (let kind in action.InputState) {
+    let state = new action.InputState[kind]();
+    ok(state);
+    if (kind === "Null") {
+      equal(state.type, "none");
+    } else {
+      equal(state.type, kind.toLowerCase());
+    }
+  }
+  run_next_test();
+});
+
 add_test(function test_extractActionChainValidation() {
   for (let actions of [-1, "a", undefined, null]) {
-    let message = `actions: ${getTypeString(actions)}`
+    let message = `actions: ${getTypeString(actions)}`;
     Assert.throws(() => action.Chain.fromJson(actions),
         InvalidArgumentError, message);
     Assert.throws(() => action.Chain.fromJson(actions),
         /Expected 'actions' to be an Array/, message);
   }
   run_next_test();
 });
 
--- a/testing/mochitest/browser.eslintrc.js
+++ b/testing/mochitest/browser.eslintrc.js
@@ -33,16 +33,17 @@ module.exports = {
     "getRootDirectory": false,
     "getTestFilePath": false,
     "gTestPath": false,
     "info": false,
     "ignoreAllUncaughtExceptions": false,
     "is": false,
     "isnot": false,
     "ok": false,
+    "PromiseDebugging": false,
     "privateNoteIntentionalCrash": false,
     "registerCleanupFunction": false,
     "requestLongerTimeout": false,
     "SpecialPowers": false,
     "TestUtils": false,
     "thisTestLeaksUncaughtRejectionsAndShouldBeFixed": false,
     "todo": false,
     "todo_is": false,
--- a/testing/mochitest/mach_test_package_commands.py
+++ b/testing/mochitest/mach_test_package_commands.py
@@ -88,9 +88,10 @@ class MochitestCommands(object):
 
     def __init__(self, context):
         self.context = context
 
     @Command('mochitest', category='testing',
              description='Run the mochitest harness.',
              parser=setup_argument_parser)
     def mochitest(self, **kwargs):
+        self.context.activate_mozharness_venv()
         return run_mochitest(self.context, **kwargs)
--- a/testing/tools/mach_test_package_bootstrap.py
+++ b/testing/tools/mach_test_package_bootstrap.py
@@ -71,28 +71,57 @@ CATEGORIES = {
         'long': 'The disabled commands are hidden by default. Use -v to display them. '
                 'These commands are unavailable for your current context, '
                 'run "mach <command>" to see why.',
         'priority': 0,
     }
 }
 
 
+IS_WIN = sys.platform in ('win32', 'cygwin')
+
+
 def ancestors(path, depth=0):
     """Emit the parent directories of a path."""
     count = 1
     while path and count != depth:
         yield path
         newpath = os.path.dirname(path)
         if newpath == path:
             break
         path = newpath
         count += 1
 
 
+def activate_mozharness_venv(context):
+    """Activate the mozharness virtualenv in-process."""
+    venv = os.path.join(context.mozharness_workdir,
+                        context.mozharness_config.get('virtualenv_path', 'venv'))
+
+    if not os.path.isdir(venv):
+        print("No mozharness virtualenv detected at '{}'.".format(venv))
+        return 1
+
+    venv_bin = os.path.join(venv, 'Scripts' if IS_WIN else 'bin')
+    activate_path = os.path.join(venv_bin, 'activate_this.py')
+
+    execfile(activate_path, dict(__file__=activate_path))
+
+    if isinstance(os.environ['PATH'], unicode):
+        os.environ['PATH'] = os.environ['PATH'].encode('utf-8')
+
+    # sys.executable is used by mochitest-media to start the websocketprocessbridge,
+    # for some reason it doesn't get set when calling `activate_this.py` so set it
+    # here instead.
+    binary = 'python'
+    if IS_WIN:
+        binary += '.exe'
+    sys.executable = os.path.join(venv_bin, binary)
+
+
 def find_firefox(context):
     """Try to automagically find the firefox binary."""
     import mozinstall
     search_paths = []
 
     # Check for a mozharness setup
     config = context.mozharness_config
     if config and 'binary_path' in config:
@@ -171,16 +200,19 @@ def bootstrap(test_package_root):
                         return json.load(f)
             return {}
 
         if key == 'mozharness_workdir':
             config = context.mozharness_config
             if config:
                 return os.path.join(config['base_work_dir'], config['work_dir'])
 
+        if key == 'activate_mozharness_venv':
+            return types.MethodType(activate_mozharness_venv, context)
+
     mach = mach.main.Mach(os.getcwd())
     mach.populate_context_handler = populate_context
 
     for category, meta in CATEGORIES.items():
         mach.define_category(category, meta['short'], meta['long'],
                              meta['priority'])
 
     for path in MACH_MODULES:
--- a/testing/xpcshell/mach_test_package_commands.py
+++ b/testing/xpcshell/mach_test_package_commands.py
@@ -56,9 +56,10 @@ class MochitestCommands(object):
 
     def __init__(self, context):
         self.context = context
 
     @Command('xpcshell-test', category='testing',
              description='Run the xpcshell harness.',
              parser=parser_desktop)
     def xpcshell(self, **kwargs):
+        self.context.activate_mozharness_venv()
         return run_xpcshell(self.context, **kwargs)
--- a/testing/xpcshell/xpcshell.eslintrc.js
+++ b/testing/xpcshell/xpcshell.eslintrc.js
@@ -47,16 +47,17 @@ module.exports = {
     "less": false,
     "lessOrEqual": false,
     "load": false,
     "mozinfo": false,
     "notDeepEqual": false,
     "notEqual": false,
     "notStrictEqual": false,
     "ok": false,
+    "PromiseDebugging": false,
     "runningInParent": false,
     "run_next_test": false,
     "run_test": false,
     "run_test_in_child": false,
     // Defined in XPCShellImpl.
     "sendCommand": false,
     "strictEqual": false,
     "throws": false,
--- a/toolkit/.eslintrc.js
+++ b/toolkit/.eslintrc.js
@@ -164,17 +164,17 @@ module.exports = {
 
     // Disallow sparse arrays
     "no-sparse-arrays": "error",
 
     // No trailing whitespace
     "no-trailing-spaces": "error",
 
     // No using undeclared variables
-    // "no-undef": "error",
+    "no-undef": "error",
 
     // Error on newline where a semicolon is needed
     "no-unexpected-multiline": "error",
 
     // No unreachable statements
     "no-unreachable": "error",
 
     // Disallow control flow statements in finally blocks
--- a/toolkit/components/.eslintrc.js
+++ b/toolkit/components/.eslintrc.js
@@ -1,6 +1,7 @@
 "use strict";
 
 module.exports = {
   "rules": {
+    "no-undef": "off"
   }
 };
--- a/toolkit/components/places/PlacesSyncUtils.jsm
+++ b/toolkit/components/places/PlacesSyncUtils.jsm
@@ -896,16 +896,53 @@ var insertSyncLivemark = Task.async(func
     return null;
   }
 
   let livemarkItem = yield PlacesUtils.livemarks.addLivemark(livemarkInfo);
 
   return insertBookmarkMetadata(livemarkItem, insertInfo);
 });
 
+// Keywords are a 1 to 1 mapping between strings and pairs of (URL, postData).
+// (the postData is not synced, so we ignore it). Sync associates keywords with
+// bookmarks, which is not really accurate. -- We might already have a keyword
+// with that name, or we might already have another bookmark with that URL with
+// a different keyword, etc.
+//
+// If we don't handle those cases by removing the conflicting keywords first,
+// the insertion  will fail, and the keywords will either be wrong, or missing.
+// This function handles those cases.
+var removeConflictingKeywords = Task.async(function* (bookmarkURL, newKeyword) {
+  let entryForURL = yield PlacesUtils.keywords.fetch({
+    url: bookmarkURL
+  });
+  if (entryForURL && entryForURL.keyword !== newKeyword) {
+    yield PlacesUtils.keywords.remove({
+      keyword: entryForURL.keyword,
+      source: SOURCE_SYNC,
+    });
+    // This will cause us to reupload this record for this sync, but without it,
+    // we will risk data corruption.
+    yield BookmarkSyncUtils.addSyncChangesForBookmarksWithURL(entryForURL.url.href);
+  }
+  if (!newKeyword) {
+    return;
+  }
+  let entryForNewKeyword = yield PlacesUtils.keywords.fetch({
+    keyword: newKeyword
+  });
+  if (entryForNewKeyword) {
+    yield PlacesUtils.keywords.remove({
+      keyword: entryForNewKeyword.keyword,
+      source: SOURCE_SYNC,
+    });
+    yield BookmarkSyncUtils.addSyncChangesForBookmarksWithURL(entryForNewKeyword.url.href);
+  }
+});
+
 // Sets annotations, keywords, and tags on a new bookmark. Returns a Sync
 // bookmark object.
 var insertBookmarkMetadata = Task.async(function* (bookmarkItem, insertInfo) {
   let itemId = yield PlacesUtils.promiseItemId(bookmarkItem.guid);
   let newItem = yield placesBookmarkToSyncBookmark(bookmarkItem);
 
   if (insertInfo.query) {
     PlacesUtils.annotations.setItemAnnotation(itemId,
@@ -918,16 +955,17 @@ var insertBookmarkMetadata = Task.async(
   try {
     newItem.tags = yield tagItem(bookmarkItem, insertInfo.tags);
   } catch (ex) {
     BookmarkSyncLog.warn(`insertBookmarkMetadata: Error tagging item ${
       insertInfo.syncId}`, ex);
   }
 
   if (insertInfo.keyword) {
+    yield removeConflictingKeywords(bookmarkItem.url.href, insertInfo.keyword);
     yield PlacesUtils.keywords.insert({
       keyword: insertInfo.keyword,
       url: bookmarkItem.url.href,
       source: SOURCE_SYNC,
     });
     newItem.keyword = insertInfo.keyword;
   }
 
@@ -1131,25 +1169,17 @@ var updateBookmarkMetadata = Task.async(
     newItem.tags = yield tagItem(newBookmarkItem, updateInfo.tags);
   } catch (ex) {
     BookmarkSyncLog.warn(`updateBookmarkMetadata: Error tagging item ${
       updateInfo.syncId}`, ex);
   }
 
   if (updateInfo.hasOwnProperty("keyword")) {
     // Unconditionally remove the old keyword.
-    let entry = yield PlacesUtils.keywords.fetch({
-      url: oldBookmarkItem.url.href,
-    });
-    if (entry) {
-      yield PlacesUtils.keywords.remove({
-        keyword: entry.keyword,
-        source: SOURCE_SYNC,
-      });
-    }
+    yield removeConflictingKeywords(oldBookmarkItem.url.href, updateInfo.keyword);
     if (updateInfo.keyword) {
       yield PlacesUtils.keywords.insert({
         keyword: updateInfo.keyword,
         url: newItem.url.href,
         source: SOURCE_SYNC,
       });
     }
     newItem.keyword = updateInfo.keyword;
--- a/toolkit/components/places/tests/unit/test_sync_utils.js
+++ b/toolkit/components/places/tests/unit/test_sync_utils.js
@@ -614,16 +614,108 @@ add_task(function* test_update_keyword()
     });
     let entry = yield PlacesUtils.keywords.fetch({
       url: "https://mozilla.org",
     });
     ok(!entry,
       "Removing keyword for URL without existing keyword should succeed");
   }
 
+  let item2;
+  do_print("Insert removes other item's keyword if they are the same");
+  {
+    let updatedItem = yield PlacesSyncUtils.bookmarks.update({
+      syncId: item.syncId,
+      keyword: "test",
+    });
+    equal(updatedItem.keyword, "test", "Initial update succeeds");
+    item2 = yield PlacesSyncUtils.bookmarks.insert({
+      kind: "bookmark",
+      parentSyncId: "menu",
+      url: "https://mozilla.org/1",
+      syncId: makeGuid(),
+      keyword: "test",
+    });
+    equal(item2.keyword, "test", "insert with existing should succeed");
+    updatedItem = yield PlacesSyncUtils.bookmarks.fetch(item.syncId);
+    ok(!updatedItem.keyword, "initial item no longer has keyword");
+    let entry = yield PlacesUtils.keywords.fetch({
+      url: "https://mozilla.org",
+    });
+    ok(!entry, "Direct check for original url keyword gives nothing");
+    let newEntry = yield PlacesUtils.keywords.fetch("test");
+    ok(newEntry, "Keyword should exist for new item");
+    equal(newEntry.url.href, "https://mozilla.org/1", "Keyword should point to new url");
+  }
+
+  do_print("Insert updates other item's keyword if they are the same url");
+  {
+    let updatedItem = yield PlacesSyncUtils.bookmarks.update({
+      syncId: item.syncId,
+      keyword: "test2",
+    });
+    equal(updatedItem.keyword, "test2", "Initial update succeeds");
+    let newItem = yield PlacesSyncUtils.bookmarks.insert({
+      kind: "bookmark",
+      parentSyncId: "menu",
+      url: "https://mozilla.org",
+      syncId: makeGuid(),
+      keyword: "test3",
+    });
+    equal(newItem.keyword, "test3", "insert with existing should succeed");
+    updatedItem = yield PlacesSyncUtils.bookmarks.fetch(item.syncId);
+    equal(updatedItem.keyword, "test3", "initial item has new keyword");
+  }
+
+  do_print("Update removes other item's keyword if they are the same");
+  {
+    let updatedItem = yield PlacesSyncUtils.bookmarks.update({
+      syncId: item.syncId,
+      keyword: "test4",
+    });
+    equal(updatedItem.keyword, "test4", "Initial update succeeds");
+    let updatedItem2 = yield PlacesSyncUtils.bookmarks.update({
+      syncId: item2.syncId,
+      keyword: "test4",
+    });
+    equal(updatedItem2.keyword, "test4", "New update succeeds");
+    updatedItem = yield PlacesSyncUtils.bookmarks.fetch(item.syncId);
+    ok(!updatedItem.keyword, "initial item no longer has keyword");
+    let entry = yield PlacesUtils.keywords.fetch({
+      url: "https://mozilla.org",
+    });
+    ok(!entry, "Direct check for original url keyword gives nothing");
+    let newEntry = yield PlacesUtils.keywords.fetch("test4");
+    ok(newEntry, "Keyword should exist for new item");
+    equal(newEntry.url.href, "https://mozilla.org/1", "Keyword should point to new url");
+  }
+
+  do_print("Update url updates it's keyword if url already has keyword");
+  {
+    let updatedItem = yield PlacesSyncUtils.bookmarks.update({
+      syncId: item.syncId,
+      keyword: "test4",
+    });
+    equal(updatedItem.keyword, "test4", "Initial update succeeds");
+    let updatedItem2 = yield PlacesSyncUtils.bookmarks.update({
+      syncId: item2.syncId,
+      keyword: "test5",
+    });
+    equal(updatedItem2.keyword, "test5", "New update succeeds");
+    yield PlacesSyncUtils.bookmarks.update({
+      syncId: item2.syncId,
+      url: item.url.href,
+    });
+    updatedItem2 = yield PlacesSyncUtils.bookmarks.fetch(item2.syncId);
+    equal(updatedItem2.keyword, "test4", "Item keyword has been updated");
+    updatedItem = yield PlacesSyncUtils.bookmarks.fetch(item.syncId);
+    equal(updatedItem.keyword, "test4", "Initial item still has keyword");
+  }
+
+
   yield PlacesUtils.bookmarks.eraseEverything();
   yield PlacesSyncUtils.bookmarks.reset();
 });
 
 add_task(function* test_update_annos() {
   let guids = yield populateTree(PlacesUtils.bookmarks.menuGuid, {
     kind: "folder",
     title: "folder",
--- a/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
+++ b/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
@@ -626,18 +626,21 @@ nsUrlClassifierDBServiceWorker::FinishUp
   urlUtil->GetTelemetryProvider(mUpdateTables.SafeElementAt(0, EmptyCString()), provider);
 
   nsresult updateStatus = mUpdateStatus;
   if (NS_FAILED(mUpdateStatus)) {
    updateStatus = NS_ERROR_GET_MODULE(mUpdateStatus) == NS_ERROR_MODULE_URL_CLASSIFIER ?
      mUpdateStatus : NS_ERROR_UC_UPDATE_UNKNOWN;
   }
 
-  Telemetry::Accumulate(Telemetry::URLCLASSIFIER_UPDATE_ERROR, provider,
-                        NS_ERROR_GET_CODE(updateStatus));
+  // Do not record telemetry for testing tables.
+  if (!provider.Equals(TESTING_TABLE_PROVIDER_NAME)) {
+    Telemetry::Accumulate(Telemetry::URLCLASSIFIER_UPDATE_ERROR, provider,
+                          NS_ERROR_GET_CODE(updateStatus));
+  }
 
   mMissCache.Clear();
 
   if (NS_SUCCEEDED(mUpdateStatus)) {
     LOG(("Notifying success: %d", mUpdateWaitSec));
     mUpdateObserver->UpdateSuccess(mUpdateWaitSec);
   } else if (NS_ERROR_NOT_IMPLEMENTED == mUpdateStatus) {
     LOG(("Treating NS_ERROR_NOT_IMPLEMENTED a successful update "
@@ -984,17 +987,17 @@ nsUrlClassifierLookupCallback::LookupCom
       NS_ENSURE_SUCCESS(rv, rv);
       LOG(("The match from %s needs to be completed at %s",
            result.mTableName.get(), gethashUrl.get()));
       // gethashUrls may be empty in 2 cases: test tables, and on startup where
       // we may have found a prefix in an existing table before the listmanager
       // has registered the table. In the second case we should not call
       // complete.
       if ((!gethashUrl.IsEmpty() ||
-           StringBeginsWith(result.mTableName, NS_LITERAL_CSTRING("test-"))) &&
+           StringBeginsWith(result.mTableName, NS_LITERAL_CSTRING("test"))) &&
           mDBService->GetCompleter(result.mTableName,
                                    getter_AddRefs(completer))) {
 
         // TODO: Figure out how long the partial hash should be sent
         //       for completion. See Bug 1323953.
         nsAutoCString partialHash;
         if (StringEndsWith(result.mTableName, NS_LITERAL_CSTRING("-proto"))) {
           // We send the complete partial hash for v4 at the moment.
--- a/toolkit/components/url-classifier/nsUrlClassifierStreamUpdater.cpp
+++ b/toolkit/components/url-classifier/nsUrlClassifierStreamUpdater.cpp
@@ -19,16 +19,17 @@
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/ErrorNames.h"
 #include "mozilla/Logging.h"
 #include "nsIInterfaceRequestor.h"
 #include "mozilla/LoadContext.h"
 #include "mozilla/Telemetry.h"
 #include "nsContentUtils.h"
 #include "nsIURLFormatter.h"
+#include "Classifier.h"
 
 static const char* gQuitApplicationMessage = "quit-application";
 
 // Limit the list file size to 32mb
 const uint32_t MAX_FILE_SIZE = (32 * 1024 * 1024);
 
 #undef LOG
 
@@ -92,16 +93,17 @@ nsUrlClassifierStreamUpdater::DownloadDo
   LOG(("nsUrlClassifierStreamUpdater::DownloadDone [this=%p]", this));
   mIsUpdating = false;
 
   mPendingUpdates.Clear();
   mDownloadError = false;
   mSuccessCallback = nullptr;
   mUpdateErrorCallback = nullptr;
   mDownloadErrorCallback = nullptr;
+  mTelemetryProvider.Truncate();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 // nsIUrlClassifierStreamUpdater implementation
 
 nsresult
 nsUrlClassifierStreamUpdater::FetchUpdate(nsIURI *aUpdateUrl,
                                           const nsACString & aRequestPayload,
@@ -278,16 +280,24 @@ nsUrlClassifierStreamUpdater::DownloadUp
     request->mDownloadErrorCallback = aDownloadErrorCallback;
     return NS_OK;
   }
 
   if (NS_FAILED(rv)) {
     return rv;
   }
 
+  nsCOMPtr<nsIUrlClassifierUtils> urlUtil =
+    do_GetService(NS_URLCLASSIFIERUTILS_CONTRACTID);
+
+  nsTArray<nsCString> tables;
+  mozilla::safebrowsing::Classifier::SplitTables(aRequestTables, tables);
+  urlUtil->GetTelemetryProvider(tables.SafeElementAt(0, EmptyCString()),
+                                mTelemetryProvider);
+
   mSuccessCallback = aSuccessCallback;
   mUpdateErrorCallback = aUpdateErrorCallback;
   mDownloadErrorCallback = aDownloadErrorCallback;
 
   mIsUpdating = true;
   *_retval = true;
 
   LOG(("FetchUpdate: %s", aUpdateUrl.Data()));
@@ -630,38 +640,32 @@ nsUrlClassifierStreamUpdater::OnStartReq
       if (NS_SUCCEEDED(rv) && uri) {
         uri->GetAsciiSpec(spec);
       }
       LOG(("nsUrlClassifierStreamUpdater::OnStartRequest "
            "(status=%s, uri=%s, this=%p)", errorName.get(),
            spec.get(), this));
     }
 
-    nsCOMPtr<nsIUrlClassifierUtils> urlUtil =
-      do_GetService(NS_URLCLASSIFIERUTILS_CONTRACTID);
-
-    nsCString provider;
-    urlUtil->GetTelemetryProvider(mStreamTable, provider);
-
     if (NS_FAILED(status)) {
       // Assume we're overloading the server and trigger backoff.
       downloadError = true;
       mozilla::Telemetry::Accumulate(mozilla::Telemetry::URLCLASSIFIER_UPDATE_REMOTE_STATUS2,
-                                     provider, 15 /* unknown response code */);
+                                     mTelemetryProvider, 15 /* unknown response code */);
 
     } else {
       bool succeeded = false;
       rv = httpChannel->GetRequestSucceeded(&succeeded);
       NS_ENSURE_SUCCESS(rv, rv);
 
       uint32_t requestStatus;
       rv = httpChannel->GetResponseStatus(&requestStatus);
       NS_ENSURE_SUCCESS(rv, rv);
       mozilla::Telemetry::Accumulate(mozilla::Telemetry::URLCLASSIFIER_UPDATE_REMOTE_STATUS2,
-                                     provider, HTTPStatusToBucket(requestStatus));
+                                     mTelemetryProvider, HTTPStatusToBucket(requestStatus));
       LOG(("nsUrlClassifierStreamUpdater::OnStartRequest %s (%d)", succeeded ?
            "succeeded" : "failed", requestStatus));
       if (!succeeded) {
         // 404 or other error, pass error status back
         strStatus.AppendInt(requestStatus);
         downloadError = true;
       }
     }
--- a/toolkit/components/url-classifier/nsUrlClassifierStreamUpdater.h
+++ b/toolkit/components/url-classifier/nsUrlClassifierStreamUpdater.h
@@ -68,17 +68,20 @@ private:
   // Fetches the next request, from mPendingRequests
   nsresult FetchNextRequest();
 
 
   bool mIsUpdating;
   bool mInitialized;
   bool mDownloadError;
   bool mBeganStream;
+
+  // Note that mStreamTable is only used by v2, it is empty for v4 update.
   nsCString mStreamTable;
+
   nsCOMPtr<nsIChannel> mChannel;
   nsCOMPtr<nsIUrlClassifierDBService> mDBService;
   nsCOMPtr<nsITimer> mTimer;
 
   struct PendingRequest {
     nsCString mTables;
     nsCString mRequestPayload;
     bool mIsPostRequest;
@@ -93,11 +96,15 @@ private:
     nsCString mUrl;
     nsCString mTable;
   };
   nsTArray<PendingUpdate> mPendingUpdates;
 
   nsCOMPtr<nsIUrlClassifierCallback> mSuccessCallback;
   nsCOMPtr<nsIUrlClassifierCallback> mUpdateErrorCallback;
   nsCOMPtr<nsIUrlClassifierCallback> mDownloadErrorCallback;
+
+  // The provider for current update request and should be only used by telemetry
+  // since it would show up as "other" for any other providers.
+  nsCString mTelemetryProvider;
 };
 
 #endif // nsUrlClassifierStreamUpdater_h_
--- a/toolkit/components/url-classifier/nsUrlClassifierUtils.cpp
+++ b/toolkit/components/url-classifier/nsUrlClassifierUtils.cpp
@@ -274,34 +274,37 @@ nsUrlClassifierUtils::ConvertListNameToT
 }
 
 NS_IMETHODIMP
 nsUrlClassifierUtils::GetProvider(const nsACString& aTableName,
                                   nsACString& aProvider)
 {
   MutexAutoLock lock(mProviderDictLock);
   nsCString* provider = nullptr;
-  if (mProviderDict.Get(aTableName, &provider)) {
+  if (StringBeginsWith(aTableName, NS_LITERAL_CSTRING("test"))) {
+    aProvider = NS_LITERAL_CSTRING(TESTING_TABLE_PROVIDER_NAME);
+  } else if (mProviderDict.Get(aTableName, &provider)) {
     aProvider = provider ? *provider : EmptyCString();
   } else {
     aProvider = EmptyCString();
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsUrlClassifierUtils::GetTelemetryProvider(const nsACString& aTableName,
                                   nsACString& aProvider)
 {
   GetProvider(aTableName, aProvider);
   // Filter out build-in providers: mozilla, google, google4
   // Empty provider is filtered as "other"
   if (!NS_LITERAL_CSTRING("mozilla").Equals(aProvider) &&
       !NS_LITERAL_CSTRING("google").Equals(aProvider) &&
-      !NS_LITERAL_CSTRING("google4").Equals(aProvider)) {
+      !NS_LITERAL_CSTRING("google4").Equals(aProvider) &&
+      !NS_LITERAL_CSTRING(TESTING_TABLE_PROVIDER_NAME).Equals(aProvider)) {
     aProvider.Assign(NS_LITERAL_CSTRING("other"));
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsUrlClassifierUtils::GetProtocolVersion(const nsACString& aProvider,
--- a/toolkit/components/url-classifier/nsUrlClassifierUtils.h
+++ b/toolkit/components/url-classifier/nsUrlClassifierUtils.h
@@ -5,16 +5,18 @@
 #ifndef nsUrlClassifierUtils_h_
 #define nsUrlClassifierUtils_h_
 
 #include "nsAutoPtr.h"
 #include "nsIUrlClassifierUtils.h"
 #include "nsClassHashtable.h"
 #include "nsIObserver.h"
 
+#define TESTING_TABLE_PROVIDER_NAME "test"
+
 class nsUrlClassifierUtils final : public nsIUrlClassifierUtils,
                                    public nsIObserver
 {
 public:
   typedef nsClassHashtable<nsCStringHashKey, nsCString> ProviderDictType;
 
 private:
   /**
new file mode 100644
--- /dev/null
+++ b/toolkit/content/.eslintrc.js
@@ -0,0 +1,7 @@
+"use strict";
+
+module.exports = {
+  "rules": {
+    "no-undef": "off"
+  }
+};
--- a/toolkit/crashreporter/test/unit/crasher_subprocess_tail.js
+++ b/toolkit/crashreporter/test/unit/crasher_subprocess_tail.js
@@ -1,8 +1,10 @@
+/* import-globals-from crasher_subprocess_head.js */
+
 // Let the event loop process a bit before crashing.
 if (shouldDelay) {
   let shouldCrashNow = false;
   let thr = Components.classes["@mozilla.org/thread-manager;1"]
                           .getService().currentThread;
   thr.dispatch({ run: () => { shouldCrashNow = true; } },
                Components.interfaces.nsIThread.DISPATCH_NORMAL);
 
--- a/toolkit/crashreporter/test/unit/test_crashreporter_appmem.js
+++ b/toolkit/crashreporter/test/unit/test_crashreporter_appmem.js
@@ -1,11 +1,11 @@
 function run_test() {
   do_crash(function() {
-	     appAddr = CrashTestUtils.saveAppMemory();
-	     crashReporter.registerAppMemory(appAddr, 32);
-           },
-	   function(mdump, extra) {
-             do_check_true(mdump.exists());
-             do_check_true(mdump.fileSize > 0);
-             do_check_true(CrashTestUtils.dumpCheckMemory(mdump.path));
-           });
+    let appAddr = CrashTestUtils.saveAppMemory();
+    crashReporter.registerAppMemory(appAddr, 32);
+  },
+  function(mdump, extra) {
+    do_check_true(mdump.exists());
+    do_check_true(mdump.fileSize > 0);
+    do_check_true(CrashTestUtils.dumpCheckMemory(mdump.path));
+  });
 }
--- a/toolkit/modules/GMPInstallManager.jsm
+++ b/toolkit/modules/GMPInstallManager.jsm
@@ -133,16 +133,17 @@ GMPInstallManager.prototype = {
    *         The promise will reject with an error object:
    *           target: The XHR request object
    *           status: The HTTP status code
    *           type: A string to represent the type of error
    *                 downloaderr, verifyerr or previouserrorencountered
    */
   installAddon(gmpAddon) {
     if (this._deferred) {
+        let log = getScopedLogger("GMPInstallManager.installAddon");
         log.error("previous error encountered");
         return Promise.reject({type: "previouserrorencountered"});
     }
     this.gmpDownloader = new GMPDownloader(gmpAddon);
     return this.gmpDownloader.start();
   },
   _getTimeSinceLastCheck() {
     let now = Math.round(Date.now() / 1000);
--- a/toolkit/modules/ObjectUtils.jsm
+++ b/toolkit/modules/ObjectUtils.jsm
@@ -15,16 +15,18 @@ this.EXPORTED_SYMBOLS = [
 const {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
 
 // Used only to cause test failures.
 XPCOMUtils.defineLazyModuleGetter(this, "Promise",
   "resource://gre/modules/Promise.jsm");
 
+var pSlice = Array.prototype.slice;
+
 this.ObjectUtils = {
   /**
    * This tests objects & values for deep equality.
    *
    * We check using the most exact approximation of equality between two objects
    * to keep the chance of false positives to a minimum.
    * `JSON.stringify` is not designed to be used for this purpose; objects may
    * have ambiguous `toJSON()` implementations that would influence the test.
--- a/toolkit/modules/Promise-backend.js
+++ b/toolkit/modules/Promise-backend.js
@@ -34,16 +34,21 @@
 // the chrome module. Otherwise, this file is loaded as a subscript, and we can
 // obtain an instance of Cu from Components directly.
 //
 // If the file is loaded as a CommonJS module on a worker thread, the instance
 // of Cu obtained from the chrome module will be null. The reason for this is
 // that Components is not defined in worker threads, so no instance of Cu can
 // be obtained.
 
+// As this can be loaded in several ways, allow require and module to be defined.
+/* global module:false require:false */
+// This is allowed in workers.
+/* global setImmediate:false */
+
 var Cu = this.require ? require("chrome").Cu : Components.utils;
 var Cc = this.require ? require("chrome").Cc : Components.classes;
 var Ci = this.require ? require("chrome").Ci : Components.interfaces;
 // If we can access Components, then we use it to capture an async
 // parent stack trace; see scheduleWalkerLoop.  However, as it might
 // not be available (see above), users of this must check it first.
 var Components_ = this.require ? require("chrome").components : Components;
 
--- a/toolkit/modules/ZipUtils.jsm
+++ b/toolkit/modules/ZipUtils.jsm
@@ -52,23 +52,23 @@ function saveStreamAsync(aPath, aStream,
                createInstance(Ci.nsIBinaryInputStream);
   source.setInputStream(input);
 
 
   function readFailed(error) {
     try {
       aStream.close();
     } catch (e) {
-      logger.error("Failed to close JAR stream for " + aPath);
+      Cu.reportError("Failed to close JAR stream for " + aPath);
     }
 
     aFile.close().then(function() {
       deferred.reject(error);
     }, function(e) {
-      logger.error("Failed to close file for " + aPath);
+      Cu.reportError("Failed to close file for " + aPath);
       deferred.reject(error);
     });
   }
 
   function readData() {
     try {
       let count = Math.min(source.available(), EXTRACTION_BUFFER);
       let data = new Uint8Array(count);
--- a/toolkit/modules/moz.build
+++ b/toolkit/modules/moz.build
@@ -5,18 +5,18 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell/xpcshell.ini']
 BROWSER_CHROME_MANIFESTS += ['tests/browser/browser.ini']
 MOCHITEST_MANIFESTS += ['tests/mochitest/mochitest.ini']
 MOCHITEST_CHROME_MANIFESTS += ['tests/chrome/chrome.ini']
 
 TESTING_JS_MODULES += [
-    'tests/MockDocument.jsm',
-    'tests/PromiseTestUtils.jsm',
+    'tests/modules/MockDocument.jsm',
+    'tests/modules/PromiseTestUtils.jsm',
     'tests/xpcshell/TestIntegration.jsm',
 ]
 
 SPHINX_TREES['toolkit_modules'] = 'docs'
 
 EXTRA_JS_MODULES += [
     'addons/MatchPattern.jsm',
     'addons/WebNavigation.jsm',
--- a/toolkit/modules/tests/browser/browser_InlineSpellChecker.js
+++ b/toolkit/modules/tests/browser/browser_InlineSpellChecker.js
@@ -1,12 +1,14 @@
+var InlineSpellChecker;
+
 function test() {
   let tempScope = {};
   Components.utils.import("resource://gre/modules/InlineSpellChecker.jsm", tempScope);
-  let InlineSpellChecker = tempScope.InlineSpellChecker;
+  InlineSpellChecker = tempScope.InlineSpellChecker;
 
   ok(InlineSpellChecker, "InlineSpellChecker class exists");
   for (var fname in tests) {
     tests[fname]();
   }
 }
 
 var tests = {
--- a/toolkit/modules/tests/browser/testremotepagemanager.html
+++ b/toolkit/modules/tests/browser/testremotepagemanager.html
@@ -1,13 +1,15 @@
 <!DOCTYPE HTML>
 
 <html>
 <head>
 <script type="text/javascript">
+/* global addMessageListener, sendAsyncMessage, removeMessageListener */
+
 addMessageListener("Ping", function(message) {
   sendAsyncMessage("Pong", {
     str: message.data.str,
     counter: message.data.counter + 1
   });
 });
 
 addMessageListener("Ping2", function(message) {
new file mode 100644
--- /dev/null
+++ b/toolkit/modules/tests/modules/.eslintrc.js
@@ -0,0 +1,7 @@
+"use strict";
+
+module.exports = {
+  "extends": [
+    "../../../../testing/xpcshell/xpcshell.eslintrc.js"
+  ]
+};
rename from toolkit/modules/tests/MockDocument.jsm
rename to toolkit/modules/tests/modules/MockDocument.jsm
rename from toolkit/modules/tests/PromiseTestUtils.jsm
rename to toolkit/modules/tests/modules/PromiseTestUtils.jsm
--- a/toolkit/modules/tests/PromiseTestUtils.jsm
+++ b/toolkit/modules/tests/modules/PromiseTestUtils.jsm
@@ -1,16 +1,19 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /*
  * Detects and reports unhandled rejections during test runs. Test harnesses
  * will fail tests in this case, unless the test whitelists itself.
  */
 
+// For this test we allow the global Assert to be modified.
+/* global Assert:true */
+
 "use strict";
 
 this.EXPORTED_SYMBOLS = [
   "PromiseTestUtils",
 ];
 
 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
--- a/toolkit/modules/tests/xpcshell/test_ObjectUtils.js
+++ b/toolkit/modules/tests/xpcshell/test_ObjectUtils.js
@@ -96,9 +96,18 @@ add_task(function* test_deepEqual() {
   let c = {};
   c.b = c;
 
   try {
     Assert.ok(!deepEqual(b, c));
   } catch (e) {
     Assert.ok(true, "Didn't recurse infinitely.");
   }
+
+  let e = {a: 3, b: 4};
+  let f = {b: 4, a: 3};
+
+  function checkEquiv() {
+    return arguments;
+  }
+
+  Assert.ok(deepEqual(checkEquiv(e, f), checkEquiv(f, e)));
 });
--- a/toolkit/modules/tests/xpcshell/test_sqlite.js
+++ b/toolkit/modules/tests/xpcshell/test_sqlite.js
@@ -845,17 +845,16 @@ add_task(function* test_direct() {
   statement.executeAsync({
     handleResult(resultSet) {
     },
 
     handleError(error) {
       print("Error when executing SQL (" + error.result + "): " +
             error.message);
       print("Original error: " + error.error);
-      errors.push(error);
       deferred.reject();
     },
 
     handleCompletion(reason) {
       print("Completed.");
       deferred.resolve();
     }
   });
--- a/toolkit/mozapps/installer/js-compare-ast.js
+++ b/toolkit/mozapps/installer/js-compare-ast.js
@@ -7,16 +7,19 @@
  * The script exits with a 0 status code if both files parse properly and the
  * ASTs of both files are identical modulo location differences. The script
  * exits with status code 1 if any of these conditions don't hold.
  *
  * This script is used as part of packaging to verify minified JavaScript files
  * are identical to their original files.
  */
 
+// Available to the js shell.
+/* global snarf, scriptArgs, quit */
+
 "use strict";
 
 function ast(filename) {
   return JSON.stringify(Reflect.parse(snarf(filename), {loc: 0}));
 }
 
 if (scriptArgs.length !== 2) {
   throw "usage: js js-compare-ast.js FILE1.js FILE2.js";
--- a/tools/lint/eslint/eslint-plugin-mozilla/lib/globals.js
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/globals.js
@@ -60,57 +60,91 @@ function parseBooleanConfig(string, comm
 const globalCache = new Map();
 
 /**
  * An object that returns found globals for given AST node types. Each prototype
  * property should be named for a node type and accepts a node parameter and a
  * parents parameter which is a list of the parent nodes of the current node.
  * Each returns an array of globals found.
  *
- * @param  {String} path
+ * @param  {String} filePath
  *         The absolute path of the file being parsed.
  */
-function GlobalsForNode(path) {
-  this.path = path;
-  this.root = helpers.getRootDir(path);
+function GlobalsForNode(filePath) {
+  this.path = filePath;
+  this.dirname = path.dirname(this.path)
+  this.root = helpers.getRootDir(this.path);
+  this.isWorker = helpers.getIsWorker(this.path);
 }
 
 GlobalsForNode.prototype = {
+  Program(node) {
+    if (!this.isWorker) {
+      return [];
+    }
+
+    return [
+      {name: "importScripts", writable: false},
+      // Only available to workers.
+      {name: "FileReaderSync", writable: false},
+      {name: "onmessage", writable: true},
+      // Only available to chrome workers, but since we don't know which is which,
+      // we make it available anyway.
+      {name: "ctypes", writable: false}
+    ];
+  },
+
   BlockComment(node, parents) {
     let value = node.value.trim();
     let match = /^import-globals-from\s+(.+)$/.exec(value);
     if (!match) {
       return [];
     }
 
     let filePath = match[1].trim();
 
     if (!path.isAbsolute(filePath)) {
-      let dirName = path.dirname(this.path);
-      filePath = path.resolve(dirName, filePath);
+      filePath = path.resolve(this.dirname, filePath);
     }
 
     return module.exports.getGlobalsForFile(filePath);
   },
 
   ExpressionStatement(node, parents) {
     let isGlobal = helpers.getIsGlobalScope(parents);
-    let names = helpers.convertExpressionToGlobals(node, isGlobal, this.root);
-    return names.map(name => { return { name, writable: true }});
+    let globals = helpers.convertExpressionToGlobals(node, isGlobal, this.root);
+    // Map these globals now, as getGlobalsForFile is pre-mapped.
+    globals = globals.map(name => { return { name, writable: true }});
+
+    if (this.isWorker) {
+      let workerDetails = helpers.convertWorkerExpressionToGlobals(node,
+        isGlobal, this.root, this.dirname);
+      globals = globals.concat(workerDetails.map(name => {
+        return { name, writable: true };
+      }));
+    }
+
+    return globals;
   },
 };
 
 module.exports = {
   /**
    * Returns all globals for a given file. Recursively searches through
    * import-globals-from directives and also includes globals defined by
    * standard eslint directives.
    *
    * @param  {String} path
    *         The absolute path of the file to be parsed.
+   * @return {Array}
+   *         An array of objects that contain details about the globals:
+   *         - {String} name
+   *                    The name of the global.
+   *         - {Boolean} writable
+   *                     If the global is writeable or not.
    */
   getGlobalsForFile(path) {
     if (globalCache.has(path)) {
       return globalCache.get(path);
     }
 
     let content = fs.readFileSync(path, "utf8");
 
@@ -174,16 +208,19 @@ module.exports = {
       }
     };
 
     // Install thin wrappers around GlobalsForNode
     let handler = new GlobalsForNode(helpers.getAbsoluteFilePath(context));
 
     for (let type of Object.keys(GlobalsForNode.prototype)) {
       parser[type] = function(node) {
+        if (type === "Program") {
+          globalScope = context.getScope();
+        }
         let globals = handler[type](node, context.getAncestors());
         helpers.addGlobals(globals, globalScope);
       }
     }
 
     return parser;
   }
 };
--- a/tools/lint/eslint/eslint-plugin-mozilla/lib/helpers.js
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/helpers.js
@@ -33,16 +33,18 @@ var definitions = [
   /^this\.__defineGetter__\("(\w+)"/,
   /^this\.(\w+) =/
 ];
 
 var imports = [
   /^(?:Cu|Components\.utils)\.import\(".*\/((.*?)\.jsm?)"(?:, this)?\)/,
 ];
 
+var workerImportFilenameMatch = /(.*\/)*(.*?\.jsm?)/;
+
 module.exports = {
   /**
    * Gets the abstract syntax tree (AST) of the JavaScript source code contained
    * in sourceText.
    *
    * @param  {String} sourceText
    *         Text containing valid JavaScript.
    *
@@ -163,18 +165,50 @@ module.exports = {
    * @param  {Object} node
    *         The AST node to convert.
    * @param  {boolean} isGlobal
    *         True if the current node is in the global scope.
    * @param  {String} repository
    *         The root of the repository.
    *
    * @return {Array}
-   *         An array of variable names defined.
+   *         An array of global variable names defined.
    */
+  convertWorkerExpressionToGlobals: function(node, isGlobal, repository, dirname) {
+    var getGlobalsForFile = require("./globals").getGlobalsForFile;
+
+    if (!modules) {
+      modules = require(path.join(repository, "tools", "lint", "eslint", "modules.json"));
+    }
+
+    let results = [];
+    let expr = node.expression;
+
+    if (node.expression.type === "CallExpression" &&
+        expr.callee &&
+        expr.callee.type === "Identifier" &&
+        expr.callee.name === "importScripts") {
+      for (var arg of expr.arguments) {
+        var match = arg.value.match(workerImportFilenameMatch);
+        if (match) {
+          if (!match[1]) {
+            let filePath = path.resolve(dirname, match[2]);
+            if (fs.existsSync(filePath)) {
+              let additionalGlobals = getGlobalsForFile(filePath);
+              results = results.concat(additionalGlobals);
+            }
+          } else if (match[2] in modules) {
+            results.push(modules[match[2]]);
+          }
+        }
+      }
+    }
+    return results;
+  },
+
   convertExpressionToGlobals: function(node, isGlobal, repository) {
     if (!modules) {
       modules = require(path.join(repository, "tools", "lint", "eslint", "modules.json"));
     }
 
     try {
       var source = this.getASTSource(node);
     }
@@ -455,16 +489,22 @@ module.exports = {
 
     if (filename.startsWith("test_")) {
       return "xpcshell";
     }
 
     return null;
   },
 
+  getIsWorker: function(filePath) {
+    let filename = path.basename(this.cleanUpPath(filePath)).toLowerCase();
+
+    return filename.includes("worker");
+  },
+
   /**
    * Gets the root directory of the repository by walking up directories until
    * a .eslintignore file is found.
    * @param {String} fileName
    *        The absolute path of a file in the repository
    *
    * @return {String} The absolute path of the repository directory
    */
--- a/tools/lint/eslint/eslint-plugin-mozilla/package.json
+++ b/tools/lint/eslint/eslint-plugin-mozilla/package.json
@@ -1,11 +1,11 @@
 {
   "name": "eslint-plugin-mozilla",
-  "version": "0.2.16",
+  "version": "0.2.17",
   "description": "A collection of rules that help enforce JavaScript coding standard in the Mozilla project.",
   "keywords": [
     "eslint",
     "eslintplugin",
     "eslint-plugin",
     "mozilla",
     "firefox"
   ],
--- a/tools/lint/eslint/modules.json
+++ b/tools/lint/eslint/modules.json
@@ -65,16 +65,17 @@
   "engines.js": ["EngineManager", "Engine", "SyncEngine", "Tracker", "Store", "Changeset"],
   "enginesync.js": ["EngineSynchronizer"],
   "errors.js": ["BaseError", "ApplicationQuitError", "AssertionError", "TimeoutError"],
   "evaluate.js": ["evaluate", "sandbox", "Sandboxes"],
   "event-emitter.js": ["EventEmitter"],
   "EventUtils.js": ["disableNonTestMouseEvents", "sendMouseEvent", "sendChar", "sendString", "sendKey", "synthesizeMouse", "synthesizeTouch", "synthesizeMouseAtPoint", "synthesizeTouchAtPoint", "synthesizeMouseAtCenter", "synthesizeTouchAtCenter", "synthesizeWheel", "synthesizeKey", "synthesizeMouseExpectEvent", "synthesizeKeyExpectEvent", "synthesizeText", "synthesizeComposition", "synthesizeQuerySelectedText"],
   "Extension.jsm": ["Extension", "ExtensionData"],
   "ExtensionAPI.jsm": ["ExtensionAPI", "ExtensionAPIs"],
+  "ExtensionsUI.jsm": ["ExtensionsUI"],
   "extension-storage.js": ["ExtensionStorageEngine", "EncryptionRemoteTransformer", "KeyRingEncryptionRemoteTransformer"],
   "ExtensionXPCShellUtils.jsm": ["ExtensionTestUtils"],
   "fakeservices.js": ["FakeCryptoService", "FakeFilesystemService", "FakeGUIDService", "fakeSHA256HMAC"],
   "file_expandosharing.jsm": ["checkFromJSM"],
   "file_stringencoding.jsm": ["checkFromJSM"],
   "file_url.jsm": ["checkFromJSM"],
   "file_worker_url.jsm": ["checkFromJSM"],
   "Finder.jsm": ["Finder", "GetClipboardSearchString"],
@@ -105,16 +106,17 @@
   "history.js": ["HistoryEngine", "HistoryRec"],
   "history.jsm": ["HistoryEntry", "DumpHistory"],
   "Http.jsm": ["httpRequest", "percentEncode"],
   "httpd.js": ["HTTP_400", "HTTP_401", "HTTP_402", "HTTP_403", "HTTP_404", "HTTP_405", "HTTP_406", "HTTP_407", "HTTP_408", "HTTP_409", "HTTP_410", "HTTP_411", "HTTP_412", "HTTP_413", "HTTP_414", "HTTP_415", "HTTP_417", "HTTP_500", "HTTP_501", "HTTP_502", "HTTP_503", "HTTP_504", "HTTP_505", "HttpError", "HttpServer"],
   "import_module.jsm": ["MODULE_IMPORTED", "MODULE_URI", "SUBMODULE_IMPORTED", "same_scope", "SUBMODULE_IMPORTED_TO_SCOPE"],
   "import_sub_module.jsm": ["SUBMODULE_IMPORTED", "test_obj"],
   "InlineSpellChecker.jsm": ["InlineSpellChecker", "SpellCheckHelper"],
   "JNI.jsm": ["JNI", "android_log"],
+  "JSDOMParser.js": ["JSDOMParser"],
   "Jsbeautify.jsm": ["jsBeautify"],
   "jsdebugger.jsm": ["addDebuggerToGlobal"],
   "json2.js": ["JSON"],
   "keys.js": ["BulkKeyBundle", "SyncKeyBundle"],
   "KeyValueParser.jsm": ["parseKeyValuePairsFromLines", "parseKeyValuePairs", "parseKeyValuePairsFromFile"],
   "kinto-http-client.js": ["KintoHttpClient"],
   "kinto-offline-client.js": ["Kinto"],
   "kinto-storage-adapter.js": ["FirefoxAdapter"],
@@ -144,17 +146,17 @@
   "NetworkPrioritizer.jsm": ["trackBrowserWindow"],
   "NotificationDB.jsm": [],
   "nsFormAutoCompleteResult.jsm": ["FormAutoCompleteResult"],
   "objects.js": ["getLength"],
   "observers.js": ["Observers"],
   "offlineAppCache.jsm": ["OfflineAppCacheHelper"],
   "OrientationChangeHandler.jsm": [],
   "os.js": ["listDirectory", "getFileForPath", "abspath", "getPlatform"],
-  "osfile.jsm": ["OS"],
+  "osfile.jsm": ["OS", "require"],
   "osfile_async_front.jsm": ["OS"],
   "osfile_native.jsm": ["read"],
   "osfile_shared_allthreads.jsm": ["LOG", "clone", "Config", "Constants", "Type", "HollowStructure", "OSError", "Library", "declareFFI", "declareLazy", "declareLazyFFI", "normalizeBufferArgs", "projectValue", "isArrayBuffer", "isTypedArray", "defineLazyGetter", "OS"],
   "osfile_unix_allthreads.jsm": ["declareFFI", "libc", "Error", "AbstractInfo", "AbstractEntry", "Type", "POS_START", "POS_CURRENT", "POS_END"],
   "osfile_win_allthreads.jsm": ["declareFFI", "libc", "Error", "AbstractInfo", "AbstractEntry", "Type", "POS_START", "POS_CURRENT", "POS_END"],
   "ospath_unix.jsm": ["basename", "dirname", "join", "normalize", "split", "toFileURI", "fromFileURI"],
   "ospath_win.jsm": ["basename", "dirname", "join", "normalize", "split", "winGetDrive", "winIsAbsolute", "toFileURI", "fromFileURI"],
   "OutputGenerator.jsm": ["UtteranceGenerator", "BrailleGenerator"],
@@ -171,27 +173,29 @@
   "PointerAdapter.jsm": ["PointerRelay", "PointerAdapter"],
   "policies.js": ["ErrorHandler", "SyncScheduler"],
   "prefs.js": ["PrefsEngine", "PrefRec"],
   "prefs.jsm": ["Preference"],
   "PresentationDeviceInfoManager.jsm": ["PresentationDeviceInfoService"],
   "PromiseWorker.jsm": ["BasePromiseWorker"],
   "PushCrypto.jsm": ["PushCrypto", "concatArray"],
   "quit.js": ["goQuitApplication"],
+  "Readability.js": ["Readability"],
   "record.js": ["WBORecord", "RecordManager", "CryptoWrapper", "CollectionKeyManager", "Collection"],
   "recursive_importA.jsm": ["foo", "bar"],
   "recursive_importB.jsm": ["baz", "qux"],
   "reflect.jsm": ["Reflect"],
   "RemoteFinder.jsm": ["RemoteFinder", "RemoteFinderListener"],
   "RemotePageManager.jsm": ["RemotePages", "RemotePageManager", "PageListener"],
   "RemoteWebProgress.jsm": ["RemoteWebProgressManager"],
   "resource.js": ["AsyncResource", "Resource"],
   "responsivedesign.jsm": ["ResponsiveUIManager"],
   "rest.js": ["RESTRequest", "RESTResponse", "TokenAuthenticatedRESTRequest", "SyncStorageRequest"],
   "rotaryengine.js": ["RotaryEngine", "RotaryRecord", "RotaryStore", "RotaryTracker"],
+  "require.js": ["require"],
   "RTCStatsReport.jsm": ["convertToRTCStatsReport"],
   "scratchpad-manager.jsm": ["ScratchpadManager"],
   "server.js": ["MarionetteServer"],
   "service.js": ["Service"],
   "SharedPromptUtils.jsm": ["PromptUtils", "EnableDelayHelper"],
   "ShutdownLeaksCollector.jsm": ["ContentCollector"],
   "SignInToWebsite.jsm": ["SignInToWebsiteController"],
   "Social.jsm": ["Social", "OpenGraphBuilder", "DynamicResizeWatcher", "sizeSocialPanelToContent"],