Merge mozilla-central to autoland.
authorCosmin Sabou <csabou@mozilla.com>
Tue, 14 May 2019 19:06:24 +0300
changeset 532621 2659abca52029881f180395cc0edb45b5292d25d
parent 532614 f2177d43f7d9c4953915d8cd8db019824832ea05 (diff)
parent 532620 b0645d43f221993cd1810a50c27abbf3a34f3055 (current diff)
child 532622 c8fc7c4dc43c27a9c094b38de983425c1a06f43c
push id11270
push userrgurzau@mozilla.com
push dateWed, 15 May 2019 15:07:19 +0000
treeherdermozilla-beta@571bc76da583 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to autoland.
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -846,17 +846,16 @@
                      defaultPlaceholder="&urlbar.placeholder2;"
                      focused="true"
                      type="autocomplete"
                      autocompletesearch="unifiedcomplete"
                      autocompletesearchparam="enable-actions"
                      autocompletepopup="PopupAutoCompleteRichResult"
                      completeselectedindex="true"
                      tabscrolling="true"
-                     newlines="stripsurroundingwhitespace"
                      ontextentered="this.handleCommand(param);"
                      ontextreverted="return this.handleRevert();"
                      pageproxystate="invalid">
               <!-- Use onclick instead of normal popup= syntax since the popup
                    code fires onmousedown, and hence eats our favicon drag events. -->
               <box id="identity-box" role="button"
                    align="center"
                    aria-label="&urlbar.viewSiteInfo.label;"
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -52,17 +52,17 @@ file, You can obtain one at http://mozil
                       tooltiptext="&urlbar.openHistoryPopup.tooltip;"
                       allowevents="true"
                       xbl:inherits="open,parentfocused=focused,usertyping"/>
       <children includes="hbox"/>
     </content>
   </binding>
 
   <binding id="legacy-urlbar" extends="chrome://global/content/bindings/autocomplete.xml#autocomplete">
-    <content>
+    <content newlines="stripsurroundingwhitespace">
       <children includes="box"/>
       <xul:moz-input-box anonid="moz-input-box"
                          tooltip="aHTMLTooltip"
                          class="urlbar-input-box"
                          flex="1">
         <children/>
         <html:input anonid="scheme"
                     class="urlbar-scheme textbox-input"
--- a/browser/components/urlbar/UrlbarInput.jsm
+++ b/browser/components/urlbar/UrlbarInput.jsm
@@ -170,16 +170,19 @@ class UrlbarInput {
     this._copyCutController = new CopyCutController(this);
     this.inputField.controllers.insertControllerAt(0, this._copyCutController);
 
     this._initPasteAndGo();
 
     // Tracks IME composition.
     this._compositionState = UrlbarUtils.COMPOSITION.NONE;
     this._compositionClosedPopup = false;
+
+    this.editor.QueryInterface(Ci.nsIPlaintextEditor).newlineHandling =
+      Ci.nsIPlaintextEditor.eNewlinesStripSurroundingWhitespace;
   }
 
   /**
    * Uninitializes this input object, detaching it from the inputField.
    */
   uninit() {
     for (let name of this._inputFieldEvents) {
       this.inputField.removeEventListener(name, this);
--- a/browser/components/urlbar/tests/browser/browser_urlbarStop.js
+++ b/browser/components/urlbar/tests/browser/browser_urlbarStop.js
@@ -27,13 +27,30 @@ add_task(async function() {
   is(gURLBar.textValue, gURLBar.trimValue(badURL), "location bar reflects stopped page in an empty tab");
   gBrowser.removeCurrentTab();
 });
 
 async function typeAndSubmitAndStop(url) {
   await promiseAutocompleteResultPopup(url, window, true);
   is(gURLBar.textValue, gURLBar.trimValue(url), "location bar reflects loading page");
 
-  let promise =
+  let docLoadPromise =
     BrowserTestUtils.waitForDocLoadAndStopIt(url, gBrowser.selectedBrowser, false);
+
+  // When the load is stopped, tabbrowser calls URLBarSetURI and then calls
+  // onStateChange on its progress listeners.  So to properly wait until the
+  // urlbar value has been updated, add our own progress listener here.
+  let progressPromise = new Promise(resolve => {
+    let listener = {
+      onStateChange(browser, webProgress, request, stateFlags, status) {
+        if (webProgress.isTopLevel &&
+            (stateFlags & Ci.nsIWebProgressListener.STATE_STOP)) {
+          gBrowser.removeTabsProgressListener(listener);
+          resolve();
+        }
+      },
+    };
+    gBrowser.addTabsProgressListener(listener);
+  });
+
   gURLBar.handleCommand();
-  await promise;
+  await Promise.all([docLoadPromise, progressPromise]);
 }
--- a/build/moz.configure/toolchain.configure
+++ b/build/moz.configure/toolchain.configure
@@ -1025,16 +1025,22 @@ def compiler(language, host_or_target, c
             if host_or_target.os == 'Android':
                 raise FatalCheckError('GCC is not supported on Android.\n'
                                       'Please use clang from the Android NDK instead.')
             if info.version < '6.1.0':
                 raise FatalCheckError(
                     'Only GCC 6.1 or newer is supported (found version %s).'
                     % info.version)
 
+        if info.type == 'clang-cl':
+            if info.version < '8.0.0':
+                raise FatalCheckError(
+                    'Only clang-cl 8.0 or newer is supported (found version %s)'
+                    % info.version)
+
         # If you want to bump the version check here search for
         # builtin_bitreverse8 above, and see the associated comment.
         if info.type == 'clang' and not info.version:
             raise FatalCheckError(
                 'Only clang/llvm 3.9 or newer is supported.')
 
         if info.flags:
             raise FatalCheckError(
--- a/devtools/client/accessibility/components/AccessibilityTree.js
+++ b/devtools/client/accessibility/components/AccessibilityTree.js
@@ -179,17 +179,17 @@ class AccessibilityTree extends Componen
       }));
     };
     const className = filtered ? "filtered" : undefined;
 
     return (
       TreeView({
         object: walker,
         mode: MODE.SHORT,
-        provider: new Provider(accessibles, dispatch),
+        provider: new Provider(accessibles, filtered, dispatch),
         columns: columns,
         className,
         renderValue: this.renderValue,
         renderRow,
         label: L10N.getStr("accessibility.treeName"),
         header: true,
         expandedNodes: expanded,
         selected,
--- a/devtools/client/accessibility/components/Checks.js
+++ b/devtools/client/accessibility/components/Checks.js
@@ -16,17 +16,17 @@ const { L10N } = require("../utils/l10n"
 
 const { accessibility: { AUDIT_TYPE } } = require("devtools/shared/constants");
 
 function EmptyChecks() {
   return (
     div({
       className: "checks-empty",
       role: "presentation",
-    }, L10N.getStr("accessibility.checks.empty"))
+    }, L10N.getStr("accessibility.checks.empty2"))
   );
 }
 
 // Component that is responsible for rendering accessible audit data in the a11y panel
 // sidebar.
 class Checks extends Component {
   static get propTypes() {
     return {
--- a/devtools/client/accessibility/provider.js
+++ b/devtools/client/accessibility/provider.js
@@ -9,18 +9,19 @@ const { fetchChildren } = require("./act
  * Data provider that is responsible for mapping of an accessibles cache to the
  * data format that is supported by the TreeView component.
  * @param {Map}      accessibles accessibles object cache
  * @param {Function} dispatch    react dispatch function that triggers a redux
  *                               action.
  */
 
 class Provider {
-  constructor(accessibles, dispatch) {
+  constructor(accessibles, filtered, dispatch) {
     this.accessibles = accessibles;
+    this.filtered = filtered;
     this.dispatch = dispatch;
   }
 
   /**
    * Get accessible's cached children if available, if not fetch them from
    * backend.
    * @param {Object}  accessible accessible object whose children to get.
    * @returns {Array} arraof of accessible children.
@@ -82,11 +83,28 @@ class Provider {
    * Get a type of an accesible object. Corresponds to the type of an accessible
    * front.
    * @param {Object}   accessible accessible object
    * @returns {String} accessible object type
    */
   getType(accessible) {
     return accessible.typeName;
   }
+
+  /**
+   * Get the depth of the accesible object in the accessibility tree. When the
+   * tree is filtered it is flattened and the level is set to 0. Otherwise use
+   * internal TreeView level.
+   *
+   * @param {Object}   accessible
+   *                   accessible object
+   * @param {Number}   defaultLevel
+   *                   default level provided by the TreeView component.
+   *
+   * @returns {null|Number}
+   *          depth level of the accessible object.
+   */
+  getLevel(accessible, defaultLevel) {
+    return this.filtered ? 0 : defaultLevel;
+  }
 }
 
 exports.Provider = Provider;
--- a/devtools/client/accessibility/test/browser/browser_accessibility_tree_audit.js
+++ b/devtools/client/accessibility/test/browser/browser_accessibility_tree_audit.js
@@ -34,69 +34,80 @@ const tests = [{
   setup: async ({ doc }) => {
     await toggleRow(doc, 0);
     await toggleRow(doc, 1);
   },
   expected: {
     tree: [{
       role: "document",
       name: `"Accessibility Panel Test"`,
+      level: 1,
     }, {
       role: "heading",
       name: `"Top level header"`,
+      level: 2,
     }, {
       role: "text leaf",
       name: `"Top level header "contrast`,
       badges: [ "contrast" ],
+      level: 3,
     }, {
       role: "heading",
       name: `"Second level header"`,
+      level: 2,
     }],
   },
 }, {
   desc: "Click on the contrast filter.",
   setup: async ({ doc }) => {
     await toggleFilter(doc, 0);
   },
   expected: {
     tree: [{
       role: "text leaf",
       name: `"Top level header "contrast`,
       badges: [ "contrast" ],
+      level: 1,
     }, {
       role: "text leaf",
       name: `"Second level header "contrast`,
       badges: [ "contrast" ],
       selected: true,
+      level: 1,
     }],
   },
 }, {
   desc: "Click on the contrast filter again.",
   setup: async ({ doc }) => {
     await toggleFilter(doc, 0);
   },
   expected: {
     tree: [{
       role: "document",
       name: `"Accessibility Panel Test"`,
+      level: 1,
     }, {
       role: "heading",
       name: `"Top level header"`,
+      level: 2,
     }, {
       role: "text leaf",
       name: `"Top level header "contrast`,
       badges: [ "contrast" ],
+      level: 3,
     }, {
       role: "heading",
       name: `"Second level header"`,
+      level: 2,
     }, {
       role: "text leaf",
       name: `"Second level header "contrast`,
       badges: [ "contrast" ],
       selected: true,
+      level: 3,
     }],
   },
 }];
 
 /**
  * Simple test that checks content of the Accessibility panel tree when one of
  * the tree rows has a "contrast" badge and auditing is activated via toolbar
  * filter.
--- a/devtools/client/accessibility/test/browser/head.js
+++ b/devtools/client/accessibility/test/browser/head.js
@@ -232,29 +232,48 @@ function checkSelected(row, expected) {
   if (row.classList.contains("selected") !== expected) {
     return false;
   }
 
   return isVisible(row);
 }
 
 /**
+ * Check level for a given row in the accessibility tree.
+ * @param   {DOMNode} row
+ *          DOMNode for a given accessibility row.
+ * @param   {Boolean} expected
+ *          Expected row level (aria-level).
+ *
+ * @returns {Boolean}
+ *          True if the aria-level for the row is as expected.
+ */
+function checkLevel(row, expected) {
+  if (!expected) {
+    return true;
+  }
+
+  return parseInt(row.getAttribute("aria-level"), 10) === expected;
+}
+
+/**
  * Check the state of the accessibility tree.
  * @param  {document} doc       panel documnent.
  * @param  {Array}    expected  an array that represents an expected row list.
  */
 async function checkTreeState(doc, expected) {
   info("Checking tree state.");
   const hasExpectedStructure = await BrowserTestUtils.waitForCondition(() =>
     [...doc.querySelectorAll(".treeRow")].every((row, i) => {
-      const { role, name, badges, selected } = expected[i];
+      const { role, name, badges, selected, level } = expected[i];
       return row.querySelector(".treeLabelCell").textContent === role &&
         row.querySelector(".treeValueCell").textContent === name &&
         compareBadges(row.querySelector(".badges"), badges) &&
-        checkSelected(row, selected);
+        checkSelected(row, selected) &&
+        checkLevel(row, level);
     }), "Wait for the right tree update.");
 
   ok(hasExpectedStructure, "Tree structure is correct.");
 }
 
 /**
  * Check if relations object matches what is expected. Note: targets are matched by their
  * name and role.
--- a/devtools/client/locales/en-US/accessibility.properties
+++ b/devtools/client/locales/en-US/accessibility.properties
@@ -106,20 +106,20 @@ accessibility.description.oldVersion=You
 # context menu item for printing an accessible tree to JSON is rendered after triggering a
 # context menu for an accessible tree row.
 accessibility.tree.menu.printToJSON=Print to JSON
 
 # LOCALIZATION NOTE (accessibility.checks): A title text used for header for checks
 # section in Accessibility details sidebar.
 accessibility.checks=Checks
 
-# LOCALIZATION NOTE (accessibility.checks.empty): A title text used for indicating that
+# LOCALIZATION NOTE (accessibility.checks.empty2): A title text used for indicating that
 # accessibility checks for a node yielded no results and another node should be
 # selected.
-accessibility.checks.empty=Select another node to continue.
+accessibility.checks.empty2=No checks for this node.
 
 # LOCALIZATION NOTE (accessibility.contrast.header): A title text used for header for
 # checks related to color and contrast.
 accessibility.contrast.header=Color and Contrast
 
 # LOCALIZATION NOTE (accessibility.contrast.error): A title text for the color
 # contrast ratio, used when the tool is unable to calculate the contrast ratio value.
 accessibility.contrast.error=Unable to calculate
--- a/devtools/client/shared/components/Accordion.js
+++ b/devtools/client/shared/components/Accordion.js
@@ -69,25 +69,25 @@ class Accordion extends Component {
   renderContainer(item, i) {
     const { buttons, className, component, componentProps, labelledby, header } = item;
     const opened = this.state.opened[i];
 
     return (
       li(
         {
           className,
-          "aria-expanded": opened,
           "aria-labelledby": labelledby,
           key: labelledby,
         },
         h2(
           {
             className: "accordion-header",
             id: labelledby,
             tabIndex: 0,
+            "aria-expanded": opened,
             onKeyDown: e => this.onHandleHeaderKeyDown(e, i),
             onClick: () => this.handleHeaderClick(i),
           },
           div(
             {
               className: `arrow theme-twisty${opened ? " open" : ""}`,
               role: "presentation",
             }
--- a/devtools/client/shared/components/test/mochitest/accordion.snapshots.js
+++ b/devtools/client/shared/components/test/mochitest/accordion.snapshots.js
@@ -10,26 +10,26 @@ window._snapshots = {
       "className": "accordion",
       "tabIndex": -1,
     },
     "children": [
       {
         "type": "li",
         "props": {
           "className": "accordion-item-1",
-          "aria-expanded": false,
           "aria-labelledby": "label-id-1",
         },
         "children": [
           {
             "type": "h2",
             "props": {
               "className": "accordion-header",
               "id": "label-id-1",
               "tabIndex": 0,
+              "aria-expanded": false,
               "onKeyDown": "e => this.onHandleHeaderKeyDown(e, i)",
               "onClick": "() => this.handleHeaderClick(i)",
             },
             "children": [
               {
                 "type": "div",
                 "props": {
                   "className": "arrow theme-twisty",
@@ -41,26 +41,26 @@ window._snapshots = {
             ],
           },
         ],
       },
       {
         "type": "li",
         "props": {
           "className": "accordion-item-2",
-          "aria-expanded": false,
           "aria-labelledby": "label-id-2",
         },
         "children": [
           {
             "type": "h2",
             "props": {
               "className": "accordion-header",
               "id": "label-id-2",
               "tabIndex": 0,
+              "aria-expanded": false,
               "onKeyDown": "e => this.onHandleHeaderKeyDown(e, i)",
               "onClick": "() => this.handleHeaderClick(i)",
             },
             "children": [
               {
                 "type": "div",
                 "props": {
                   "className": "arrow theme-twisty",
@@ -86,26 +86,26 @@ window._snapshots = {
             ],
           },
         ],
       },
       {
         "type": "li",
         "props": {
           "className": "accordion-item-3",
-          "aria-expanded": true,
           "aria-labelledby": "label-id-3",
         },
         "children": [
           {
             "type": "h2",
             "props": {
               "className": "accordion-header",
               "id": "label-id-3",
               "tabIndex": 0,
+              "aria-expanded": true,
               "onKeyDown": "e => this.onHandleHeaderKeyDown(e, i)",
               "onClick": "() => this.handleHeaderClick(i)",
             },
             "children": [
               {
                 "type": "div",
                 "props": {
                   "className": "arrow theme-twisty open",
--- a/devtools/client/shared/components/tree/TreeRow.js
+++ b/devtools/client/shared/components/tree/TreeRow.js
@@ -23,16 +23,17 @@ define(function(require, exports, module
 
   const { focusableSelector } = require("devtools/client/shared/focus");
 
   const UPDATE_ON_PROPS = [
     "name",
     "open",
     "value",
     "loading",
+    "level",
     "selected",
     "active",
     "hasChildren",
   ];
 
   /**
    * This template represents a node in TreeView component. It's rendered
    * using <tr> element (the entire tree is one big <table>).
@@ -225,17 +226,17 @@ define(function(require, exports, module
     render() {
       const member = this.props.member;
       const decorator = this.props.decorator;
 
       const props = {
         id: this.props.id,
         ref: this.treeRowRef,
         role: "treeitem",
-        "aria-level": member.level,
+        "aria-level": member.level + 1,
         "aria-selected": !!member.selected,
         onClick: this.props.onClick,
         onContextMenu: this.props.onContextMenu,
         onKeyDownCapture: member.active ? this._onKeyDown : undefined,
         onMouseOver: this.props.onMouseOver,
         onMouseOut: this.props.onMouseOut,
       };
 
--- a/devtools/client/shared/components/tree/TreeView.js
+++ b/devtools/client/shared/components/tree/TreeView.js
@@ -60,16 +60,17 @@ define(function(require, exports, module
    * The tree is maintaining its (presentation) state, which consists
    * from list of expanded nodes and list of columns.
    *
    * Complete data provider interface:
    * var TreeProvider = {
    *   getChildren: function(object);
    *   hasChildren: function(object);
    *   getLabel: function(object, colId);
+   *   getLevel: function(object); // optional
    *   getValue: function(object, colId);
    *   getKey: function(object);
    *   getType: function(object);
    * }
    *
    * Complete tree decorator interface:
    * var TreeDecorator = {
    *   getRowClass: function(object);
@@ -516,17 +517,17 @@ define(function(require, exports, module
           object: child,
           // A label for the child node
           name: provider.getLabel(child),
           // Data type of the child node (used for CSS customization)
           type: type,
           // Class attribute computed from the type.
           rowClass: "treeRow-" + type,
           // Level of the child within the hierarchy (top == 0)
-          level: level,
+          level: provider.getLevel ? provider.getLevel(child, level) : level,
           // True if this node has children.
           hasChildren: hasChildren,
           // Value associated with this node (as provided by the data provider)
           value: value,
           // True if the node is expanded.
           open: this.isExpanded(nodePath),
           // Node path
           path: nodePath,
--- a/dom/base/RemoteOuterWindowProxy.cpp
+++ b/dom/base/RemoteOuterWindowProxy.cpp
@@ -14,22 +14,17 @@
 namespace mozilla {
 namespace dom {
 
 /**
  * RemoteOuterWindowProxy is the proxy handler for the WindowProxy objects for
  * Window objects that live in a different process.
  *
  * RemoteOuterWindowProxy holds a BrowsingContext, which is cycle collected.
- * However, RemoteOuterWindowProxy only holds BrowsingContexts that don't have a
- * reference to a docshell, so there's no need to declare the edge from
- * RemoteOuterWindowProxy to its BrowsingContext to the cycle collector.
- *
- * FIXME Verify that this is correct:
- *       https://bugzilla.mozilla.org/show_bug.cgi?id=1516350.
+ * This reference is declared to the cycle collector via NoteChildren().
  */
 
 class RemoteOuterWindowProxy
     : public RemoteObjectProxy<BrowsingContext,
                                Window_Binding::sCrossOriginAttributes,
                                Window_Binding::sCrossOriginMethods> {
  public:
   typedef RemoteObjectProxy Base;
--- a/dom/html/HTMLFormElement.cpp
+++ b/dom/html/HTMLFormElement.cpp
@@ -4,17 +4,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/HTMLFormElement.h"
 
 #include "jsapi.h"
 #include "mozilla/ContentEvents.h"
 #include "mozilla/EventDispatcher.h"
-#include "mozilla/EventStateManager.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/dom/nsCSPUtils.h"
 #include "mozilla/dom/nsCSPContext.h"
 #include "mozilla/dom/nsMixedContentBlocker.h"
 #include "mozilla/dom/CustomEvent.h"
 #include "mozilla/dom/HTMLFormControlsCollection.h"
 #include "mozilla/dom/HTMLFormElementBinding.h"
 #include "mozilla/Move.h"
@@ -110,17 +109,16 @@ HTMLFormElement::HTMLFormElement(
       mSubmitPopupState(PopupBlocker::openAbused),
       mInvalidElementsCount(0),
       mGeneratingSubmit(false),
       mGeneratingReset(false),
       mIsSubmitting(false),
       mDeferSubmission(false),
       mNotifiedObservers(false),
       mNotifiedObserversResult(false),
-      mSubmitInitiatedFromUserInput(false),
       mEverTriedInvalidSubmit(false) {
   // We start out valid.
   AddStatesSilently(NS_EVENT_STATE_VALID);
 }
 
 HTMLFormElement::~HTMLFormElement() {
   if (mControls) {
     mControls->DropFormReference();
@@ -566,18 +564,16 @@ nsresult HTMLFormElement::DoSubmit(Widge
   // be a window...
   nsPIDOMWindowOuter* window = OwnerDoc()->GetWindow();
   if (window) {
     mSubmitPopupState = PopupBlocker::GetPopupControlState();
   } else {
     mSubmitPopupState = PopupBlocker::openAbused;
   }
 
-  mSubmitInitiatedFromUserInput = EventStateManager::IsHandlingUserInput();
-
   if (mDeferSubmission) {
     // we are in an event handler, JS submitted so we have to
     // defer this submission. let's remember it and return
     // without submitting
     mPendingSubmission = submission;
     // ensure reentrancy
     mIsSubmitting = false;
     return NS_OK;
@@ -691,29 +687,29 @@ nsresult HTMLFormElement::SubmitSubmissi
   // Submit
   //
   nsCOMPtr<nsIDocShell> docShell;
 
   {
     nsAutoPopupStatePusher popupStatePusher(mSubmitPopupState);
 
     AutoHandlingUserInputStatePusher userInpStatePusher(
-        mSubmitInitiatedFromUserInput, nullptr, doc);
+        aFormSubmission->IsInitiatedFromUserInput(), nullptr, doc);
 
     nsCOMPtr<nsIInputStream> postDataStream;
     rv = aFormSubmission->GetEncodedSubmission(
         actionURI, getter_AddRefs(postDataStream), actionURI);
     NS_ENSURE_SUBMIT_SUCCESS(rv);
 
     nsAutoString target;
     aFormSubmission->GetTarget(target);
     rv = linkHandler->OnLinkClickSync(
         this, actionURI, target, VoidString(), postDataStream, nullptr, false,
         getter_AddRefs(docShell), getter_AddRefs(mSubmittingRequest),
-        EventStateManager::IsHandlingUserInput());
+        aFormSubmission->IsInitiatedFromUserInput());
     NS_ENSURE_SUBMIT_SUCCESS(rv);
   }
 
   // Even if the submit succeeds, it's possible for there to be no docshell
   // or request; for example, if it's to a named anchor within the same page
   // the submit will not really do anything.
   if (docShell) {
     // If the channel is pending, we have to listen for web progress.
--- a/dom/html/HTMLFormElement.h
+++ b/dom/html/HTMLFormElement.h
@@ -589,18 +589,16 @@ class HTMLFormElement final : public nsG
   /** Whether we are submitting currently */
   bool mIsSubmitting;
   /** Whether the submission is to be deferred in case a script triggers it */
   bool mDeferSubmission;
   /** Whether we notified NS_FORMSUBMIT_SUBJECT listeners already */
   bool mNotifiedObservers;
   /** If we notified the listeners early, what was the result? */
   bool mNotifiedObserversResult;
-  /** Keep track of whether a submission was user-initiated or not */
-  bool mSubmitInitiatedFromUserInput;
   /**
    * Whether the submission of this form has been ever prevented because of
    * being invalid.
    */
   bool mEverTriedInvalidSubmit;
 
  private:
   ~HTMLFormElement();
--- a/dom/html/HTMLFormSubmission.h
+++ b/dom/html/HTMLFormSubmission.h
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_HTMLFormSubmission_h
 #define mozilla_dom_HTMLFormSubmission_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/EventStateManager.h"
 #include "nsCOMPtr.h"
 #include "mozilla/Encoding.h"
 #include "nsString.h"
 
 class nsIURI;
 class nsIInputStream;
 class nsGenericHTMLElement;
 class nsIMultiplexInputStream;
@@ -98,44 +99,53 @@ class HTMLFormSubmission {
    */
   nsIURI* GetActionURL() const { return mActionURL; }
 
   /**
    * Get the target that will be used for submission.
    */
   void GetTarget(nsAString& aTarget) { aTarget = mTarget; }
 
+  /**
+   * Return true if this form submission was user-initiated.
+   */
+  bool IsInitiatedFromUserInput() const { return mInitiatedFromUserInput; }
+
  protected:
   /**
    * Can only be constructed by subclasses.
    *
    * @param aEncoding the character encoding of the form
    * @param aOriginatingElement the originating element (can be null)
    */
   HTMLFormSubmission(nsIURI* aActionURL, const nsAString& aTarget,
                      mozilla::NotNull<const mozilla::Encoding*> aEncoding,
                      Element* aOriginatingElement)
       : mActionURL(aActionURL),
         mTarget(aTarget),
         mEncoding(aEncoding),
-        mOriginatingElement(aOriginatingElement) {
+        mOriginatingElement(aOriginatingElement),
+        mInitiatedFromUserInput(EventStateManager::IsHandlingUserInput()) {
     MOZ_COUNT_CTOR(HTMLFormSubmission);
   }
 
   // The action url.
   nsCOMPtr<nsIURI> mActionURL;
 
   // The target.
   nsString mTarget;
 
   // The character encoding of this form submission
   mozilla::NotNull<const mozilla::Encoding*> mEncoding;
 
   // Originating element.
   RefPtr<Element> mOriginatingElement;
+
+  // Keep track of whether this form submission was user-initiated or not
+  bool mInitiatedFromUserInput;
 };
 
 class EncodingFormSubmission : public HTMLFormSubmission {
  public:
   EncodingFormSubmission(nsIURI* aActionURL, const nsAString& aTarget,
                          mozilla::NotNull<const mozilla::Encoding*> aEncoding,
                          Element* aOriginatingElement);
 
--- a/mobile/android/app/build.gradle
+++ b/mobile/android/app/build.gradle
@@ -172,16 +172,17 @@ android {
         test {
             java {
                 // Bug 1229149 tracks pushing this into a :services Gradle project.
                 srcDir "${topsrcdir}/mobile/android/services/src/test/java"
 
                 if (!mozconfig.substs.MOZ_ANDROID_GCM) {
                     exclude 'org/mozilla/gecko/gcm/**/*.java'
                     exclude 'org/mozilla/gecko/push/**/*.java'
+                    exclude 'org/mozilla/gecko/advertising/**'
                 }
             }
             resources {
                 // Bug 1229149 tracks pushing this into a :services Gradle project.
                 srcDir "${topsrcdir}/mobile/android/services/src/test/resources"
             }
         }
 
@@ -239,16 +240,18 @@ dependencies {
         implementation "com.google.android.gms:play-services-ads-identifier:$google_play_services_version"
         implementation "com.google.android.gms:play-services-basement:$google_play_services_version"
     }
 
     if (mozconfig.substs.MOZ_ANDROID_GCM) {
         implementation "com.google.android.gms:play-services-basement:$google_play_services_version"
         implementation "com.google.android.gms:play-services-base:$google_play_services_version"
         implementation "com.google.android.gms:play-services-gcm:$google_play_services_version"
+        implementation "com.google.android.gms:play-services-ads-identifier:$google_play_services_version"
+        implementation "org.mindrot:jbcrypt:0.4"
     }
 
     if (mozconfig.substs.MOZ_ANDROID_GOOGLE_PLAY_SERVICES) {
         implementation "com.google.android.gms:play-services-fido:$google_play_services_fido_version"
     }
 
     // Include LeakCanary in local builds, but not in official builds.
     if (mozconfig.substs.MOZILLA_OFFICIAL) {
--- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
@@ -133,16 +133,17 @@ import org.mozilla.gecko.tabqueue.TabQue
 import org.mozilla.gecko.tabs.TabHistoryController;
 import org.mozilla.gecko.tabs.TabHistoryController.OnShowTabHistory;
 import org.mozilla.gecko.tabs.TabHistoryFragment;
 import org.mozilla.gecko.tabs.TabHistoryPage;
 import org.mozilla.gecko.tabs.TabsPanel;
 import org.mozilla.gecko.telemetry.TelemetryCorePingDelegate;
 import org.mozilla.gecko.telemetry.TelemetryUploadService;
 import org.mozilla.gecko.telemetry.measurements.SearchCountMeasurements;
+import org.mozilla.gecko.telemetry.TelemetryActivationPingDelegate;
 import org.mozilla.gecko.toolbar.AutocompleteHandler;
 import org.mozilla.gecko.toolbar.BrowserToolbar;
 import org.mozilla.gecko.toolbar.BrowserToolbar.CommitEventSource;
 import org.mozilla.gecko.toolbar.BrowserToolbar.TabEditingState;
 import org.mozilla.gecko.toolbar.PwaConfirm;
 import org.mozilla.gecko.updater.PostUpdateHandler;
 import org.mozilla.gecko.updater.UpdateServiceHelper;
 import org.mozilla.gecko.util.ActivityUtils;
@@ -312,23 +313,25 @@ public class BrowserApp extends GeckoApp
     // (starting the animation), the HomePager is hidden, and the HomePager animation completes,
     // both the web content and the HomePager will be hidden. This flag is used to prevent the
     // race by determining if the web content should be hidden at the animation's end.
     private boolean mHideWebContentOnAnimationEnd;
 
     private final DynamicToolbar mDynamicToolbar = new DynamicToolbar();
 
     private final TelemetryCorePingDelegate mTelemetryCorePingDelegate = new TelemetryCorePingDelegate();
+    private final TelemetryActivationPingDelegate mTelemetryActivationPingDelegate = new TelemetryActivationPingDelegate();
 
     private final List<BrowserAppDelegate> delegates = Collections.unmodifiableList(Arrays.asList(
             new ScreenshotDelegate(),
             new BookmarkStateChangeDelegate(),
             new ReaderViewBookmarkPromotion(),
             new PostUpdateHandler(),
             mTelemetryCorePingDelegate,
+            mTelemetryActivationPingDelegate,
             new OfflineTabStatusDelegate(),
             new AdjustBrowserAppDelegate(mTelemetryCorePingDelegate)
     ));
 
     @NonNull
     private SearchEngineManager mSearchEngineManager; // Contains reference to Context - DO NOT LEAK!
     private OnboardingHelper mOnboardingHelper;       // Contains reference to Context - DO NOT LEAK!
 
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/advertising/AdvertisingUtil.java
@@ -0,0 +1,35 @@
+package org.mozilla.gecko.advertising;
+
+import android.content.Context;
+import android.util.Log;
+
+import com.google.android.gms.ads.identifier.AdvertisingIdClient;
+
+
+import org.mindrot.jbcrypt.BCrypt;
+import org.mozilla.gecko.annotation.ReflectionTarget;
+
+@ReflectionTarget
+public class AdvertisingUtil {
+    private static final String LOG_TAG = AdvertisingUtil.class.getCanonicalName();
+
+    /* Use the same SALT for all BCrypt hashings. We want the SALT to be stable for all Fennec users but it should differ from the one from Fenix.
+     * Generated using Bcrypt.gensalt(). */
+    private static final String BCRYPT_SALT = "$2a$10$ZfglUfcbmTyaBbAQ7SL9OO";
+
+    /**
+     * Retrieves the advertising ID hashed with BCrypt. Requires Google Play Services. Note: This method must not run on
+     * the main thread.
+     */
+    @ReflectionTarget
+    public static String getAdvertisingId(Context caller) {
+        try {
+            AdvertisingIdClient.Info info = AdvertisingIdClient.getAdvertisingIdInfo(caller);
+            String advertisingId = info.getId();
+            return advertisingId != null ? BCrypt.hashpw(advertisingId, BCRYPT_SALT) : null;
+        } catch (Throwable t) {
+            Log.e(LOG_TAG, "Error retrieving advertising ID. " + t.getMessage());
+        }
+        return null;
+    }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/telemetry/TelemetryActivationPingDelegate.java
@@ -0,0 +1,99 @@
+package org.mozilla.gecko.telemetry;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.annotation.WorkerThread;
+import android.util.Log;
+
+import org.mozilla.gecko.AppConstants;
+import org.mozilla.gecko.BrowserApp;
+import org.mozilla.gecko.GeckoProfile;
+import org.mozilla.gecko.GeckoThread;
+import org.mozilla.gecko.delegates.BrowserAppDelegate;
+import org.mozilla.gecko.telemetry.pingbuilders.TelemetryActivationPingBuilder;
+import org.mozilla.gecko.util.StringUtils;
+import org.mozilla.gecko.util.ThreadUtils;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+
+/**
+ * An activity-lifecycle delegate for uploading the activation ping.
+ */
+public class TelemetryActivationPingDelegate extends BrowserAppDelegate {
+    private static final String LOGTAG = StringUtils.safeSubstring(
+            "Gecko" + TelemetryActivationPingDelegate.class.getSimpleName(), 0, 23);
+
+    private TelemetryDispatcher telemetryDispatcher; // lazy
+
+
+    @Override
+    public void onCreate(BrowserApp browserApp, Bundle savedInstanceState) {
+        super.onCreate(browserApp, savedInstanceState);
+        uploadActivationPing(browserApp);
+    }
+
+    private void uploadActivationPing(final BrowserApp activity) {
+        if (!AppConstants.MOZ_ANDROID_GCM) {
+            return;
+        }
+
+        if (TelemetryActivationPingBuilder.activationPingAlreadySent(activity)) {
+            return;
+        }
+
+        ThreadUtils.postToBackgroundThread(() -> {
+            if (activity == null) {
+                return;
+            }
+
+            if (!TelemetryUploadService.isUploadEnabledByAppConfig(activity)) {
+                Log.d(LOGTAG, "Activation ping upload disabled by app config. Returning.");
+                return;
+            }
+
+            String identifier = null;
+
+            try {
+                final Class<?> clazz = Class.forName("org.mozilla.gecko.advertising.AdvertisingUtil");
+                final Method getAdvertisingId = clazz.getMethod("getAdvertisingId", Context.class);
+                identifier = (String) getAdvertisingId.invoke(null, activity);
+            } catch (Exception e) {
+                Log.w(LOGTAG, "Unable to get identifier: " + e);
+            }
+
+            final GeckoProfile profile = GeckoThread.getActiveProfile();
+            String clientID = null;
+            try {
+                clientID = profile.getClientId();
+            } catch (final IOException e) {
+                Log.w(LOGTAG, "Unable to get client ID: " + e);
+                if (identifier == null) {
+                    //Activation ping is mandatory to be sent with either the identifier or the clientID.
+                    Log.d(LOGTAG, "Activation ping failed to send - both identifier and clientID were unable to be retrieved.");
+                    return;
+                }
+            }
+
+            final TelemetryActivationPingBuilder pingBuilder = new TelemetryActivationPingBuilder(activity);
+            if (identifier != null) {
+                pingBuilder.setIdentifier(identifier);
+            } else {
+                pingBuilder.setClientID(clientID);
+            }
+
+            getTelemetryDispatcher().queuePingForUpload(activity, pingBuilder);
+        });
+    }
+
+    @WorkerThread // via constructor
+    private TelemetryDispatcher getTelemetryDispatcher() {
+        if (telemetryDispatcher == null) {
+            final GeckoProfile profile = GeckoThread.getActiveProfile();
+            final String profilePath = profile.getDir().getAbsolutePath();
+            final String profileName = profile.getName();
+            telemetryDispatcher = new TelemetryDispatcher(profilePath, profileName);
+        }
+        return telemetryDispatcher;
+    }
+}
--- a/mobile/android/base/java/org/mozilla/gecko/telemetry/TelemetryCorePingDelegate.java
+++ b/mobile/android/base/java/org/mozilla/gecko/telemetry/TelemetryCorePingDelegate.java
@@ -16,21 +16,21 @@ import android.view.accessibility.Access
 import org.mozilla.gecko.BrowserApp;
 import org.mozilla.gecko.GeckoApp;
 import org.mozilla.gecko.GeckoProfile;
 import org.mozilla.gecko.GeckoSharedPrefs;
 import org.mozilla.gecko.GeckoThread;
 import org.mozilla.gecko.Telemetry;
 import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.adjust.AttributionHelperListener;
-import org.mozilla.gecko.telemetry.measurements.CampaignIdMeasurements;
 import org.mozilla.gecko.delegates.BrowserAppDelegateWithReference;
 import org.mozilla.gecko.distribution.DistributionStoreCallback;
 import org.mozilla.gecko.search.SearchEngineManager;
 import org.mozilla.gecko.sync.ExtendedJSONObject;
+import org.mozilla.gecko.telemetry.measurements.CampaignIdMeasurements;
 import org.mozilla.gecko.telemetry.measurements.SearchCountMeasurements;
 import org.mozilla.gecko.telemetry.measurements.SessionMeasurements;
 import org.mozilla.gecko.telemetry.pingbuilders.TelemetryCorePingBuilder;
 import org.mozilla.gecko.util.StringUtils;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import java.io.IOException;
 import java.util.List;
@@ -134,57 +134,53 @@ public class TelemetryCorePingDelegate e
         if (getBrowserApp() == null) {
             return;
         }
 
         // The containing method can be called from onStart: queue this work so that
         // the first launch of the activity doesn't trigger profile init too early.
         //
         // Additionally, getAndIncrementSequenceNumber must be called from a worker thread.
-        ThreadUtils.postToBackgroundThread(new Runnable() {
-            @WorkerThread
-            @Override
-            public void run() {
-                final BrowserApp activity = getBrowserApp();
-                if (activity == null) {
-                    return;
-                }
+        ThreadUtils.postToBackgroundThread(() -> {
+            final BrowserApp activity = getBrowserApp();
+            if (activity == null) {
+                return;
+            }
 
-                final GeckoProfile profile = GeckoThread.getActiveProfile();
-                if (!TelemetryUploadService.isUploadEnabledByProfileConfig(activity, profile)) {
-                    Log.d(LOGTAG, "Core ping upload disabled by profile config. Returning.");
-                    return;
-                }
+            final GeckoProfile profile = GeckoThread.getActiveProfile();
+            if (!TelemetryUploadService.isUploadEnabledByProfileConfig(activity, profile)) {
+                Log.d(LOGTAG, "Core ping upload disabled by profile config. Returning.");
+                return;
+            }
 
-                final String clientID;
-                final boolean hadCanaryClientId;
-                try {
-                    clientID = profile.getClientId();
-                    hadCanaryClientId = profile.getIfHadCanaryClientId();
-                } catch (final IOException e) {
-                    Log.w(LOGTAG, "Unable to get client ID properties to generate core ping: " + e);
-                    return;
-                }
+            final String clientID;
+            final boolean hadCanaryClientId;
+            try {
+                clientID = profile.getClientId();
+                hadCanaryClientId = profile.getIfHadCanaryClientId();
+            } catch (final IOException e) {
+                Log.w(LOGTAG, "Unable to get client ID properties to generate core ping: " + e);
+                return;
+            }
 
-                // Each profile can have different telemetry data so we intentionally grab the shared prefs for the profile.
-                final SharedPreferences sharedPrefs = getSharedPreferences(activity);
-                final SessionMeasurements.SessionMeasurementsContainer sessionMeasurementsContainer =
-                        sessionMeasurements.getAndResetSessionMeasurements(activity);
-                final TelemetryCorePingBuilder pingBuilder = new TelemetryCorePingBuilder(activity)
-                        .setClientID(clientID)
-                        .setHadCanaryClientId(hadCanaryClientId)
-                        .setDefaultSearchEngine(TelemetryCorePingBuilder.getEngineIdentifier(engine))
-                        .setProfileCreationDate(TelemetryCorePingBuilder.getProfileCreationDate(activity, profile))
-                        .setSequenceNumber(TelemetryCorePingBuilder.getAndIncrementSequenceNumber(sharedPrefs))
-                        .setSessionCount(sessionMeasurementsContainer.sessionCount)
-                        .setSessionDuration(sessionMeasurementsContainer.elapsedSeconds);
-                maybeSetOptionalMeasurements(activity, sharedPrefs, pingBuilder);
+            // Each profile can have different telemetry data so we intentionally grab the shared prefs for the profile.
+            final SharedPreferences sharedPrefs = getSharedPreferences(activity);
+            final SessionMeasurements.SessionMeasurementsContainer sessionMeasurementsContainer =
+                    sessionMeasurements.getAndResetSessionMeasurements(activity);
+            final TelemetryCorePingBuilder pingBuilder = new TelemetryCorePingBuilder(activity)
+                    .setClientID(clientID)
+                    .setHadCanaryClientId(hadCanaryClientId)
+                    .setDefaultSearchEngine(TelemetryCorePingBuilder.getEngineIdentifier(engine))
+                    .setProfileCreationDate(TelemetryCorePingBuilder.getProfileCreationDate(activity, profile))
+                    .setSequenceNumber(TelemetryCorePingBuilder.getAndIncrementSequenceNumber(sharedPrefs))
+                    .setSessionCount(sessionMeasurementsContainer.sessionCount)
+                    .setSessionDuration(sessionMeasurementsContainer.elapsedSeconds);
+            maybeSetOptionalMeasurements(activity, sharedPrefs, pingBuilder);
 
-                getTelemetryDispatcher(activity).queuePingForUpload(activity, pingBuilder);
-            }
+            getTelemetryDispatcher(activity).queuePingForUpload(activity, pingBuilder);
         });
     }
 
     private void maybeSetOptionalMeasurements(final Context context, final SharedPreferences sharedPrefs,
                                               final TelemetryCorePingBuilder pingBuilder) {
         final String distributionId = sharedPrefs.getString(DistributionStoreCallback.PREF_DISTRIBUTION_ID, null);
         if (distributionId != null) {
             pingBuilder.setOptDistributionID(distributionId);
--- a/mobile/android/base/java/org/mozilla/gecko/telemetry/TelemetryDispatcher.java
+++ b/mobile/android/base/java/org/mozilla/gecko/telemetry/TelemetryDispatcher.java
@@ -4,16 +4,18 @@
  * file, you can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
 package org.mozilla.gecko.telemetry;
 
 import android.content.Context;
 import android.support.annotation.WorkerThread;
 import android.util.Log;
+
+import org.mozilla.gecko.telemetry.pingbuilders.TelemetryActivationPingBuilder;
 import org.mozilla.gecko.telemetry.pingbuilders.TelemetryCorePingBuilder;
 import org.mozilla.gecko.telemetry.pingbuilders.TelemetryCrashPingBuilder;
 import org.mozilla.gecko.telemetry.schedulers.TelemetryUploadScheduler;
 import org.mozilla.gecko.telemetry.schedulers.TelemetryUploadAllPingsImmediatelyScheduler;
 import org.mozilla.gecko.telemetry.stores.TelemetryJSONFilePingStore;
 import org.mozilla.gecko.telemetry.stores.TelemetryPingStore;
 import org.mozilla.gecko.util.ThreadUtils;
 
@@ -86,16 +88,24 @@ public class TelemetryDispatcher {
      * Queues the given ping for upload and potentially schedules upload. This method can be called from any thread.
      */
     public void queuePingForUpload(final Context context, final TelemetryCorePingBuilder pingBuilder) {
         final TelemetryOutgoingPing ping = pingBuilder.build();
         queuePingForUpload(context, ping, coreStore, uploadAllPingsImmediatelyScheduler);
     }
 
     /**
+     * Queues the given ping for upload and potentially schedules upload. This method can be called from any thread.
+     */
+    public void queuePingForUpload(final Context context, final TelemetryActivationPingBuilder pingBuilder) {
+        final TelemetryOutgoingPing ping = pingBuilder.build();
+        queuePingForUpload(context, ping, coreStore, uploadAllPingsImmediatelyScheduler);
+    }
+
+    /**
      * Queues the given crash ping for upload and potentially schedules upload. This method can be called from any thread.
      */
     public void queuePingForUpload(final Context context, final TelemetryCrashPingBuilder pingBuilder) {
         final TelemetryOutgoingPing ping = pingBuilder.build();
         queuePingForUpload(context, ping, crashStore, uploadAllPingsImmediatelyScheduler);
     }
 
     /* package-private */ static class QueuePingRunnable implements Runnable {
--- a/mobile/android/base/java/org/mozilla/gecko/telemetry/TelemetryUploadService.java
+++ b/mobile/android/base/java/org/mozilla/gecko/telemetry/TelemetryUploadService.java
@@ -14,16 +14,17 @@ import org.mozilla.gecko.GeckoProfile;
 import org.mozilla.gecko.JobIdsConstants;
 import org.mozilla.gecko.preferences.GeckoPreferences;
 import org.mozilla.gecko.restrictions.Restrictable;
 import org.mozilla.gecko.restrictions.Restrictions;
 import org.mozilla.gecko.sync.ExtendedJSONObject;
 import org.mozilla.gecko.sync.net.BaseResource;
 import org.mozilla.gecko.sync.net.BaseResourceDelegate;
 import org.mozilla.gecko.sync.net.Resource;
+import org.mozilla.gecko.telemetry.pingbuilders.TelemetryActivationPingBuilder;
 import org.mozilla.gecko.telemetry.stores.TelemetryPingStore;
 import org.mozilla.gecko.util.DateUtil;
 import org.mozilla.gecko.util.NetworkUtils;
 import org.mozilla.gecko.util.StringUtils;
 
 import java.io.IOException;
 import java.net.URISyntaxException;
 import java.security.GeneralSecurityException;
@@ -120,27 +121,46 @@ public class TelemetryUploadService exte
             delegate.setDocID(ping.getDocID());
             final String url = serverSchemeHostPort + "/" + ping.getURLPath();
             uploadPayload(url, ping.getPayload(), delegate);
 
             // There are minimal gains in trying to upload if we already failed one attempt.
             if (delegate.hadConnectionError()) {
                 break;
             }
+
+            checkPingsPersistence(context, ping.getDocID());
         }
 
         final boolean wereAllUploadsSuccessful = !delegate.hadConnectionError();
         if (wereAllUploadsSuccessful) {
             // We don't log individual successful uploads to avoid log spam.
             Log.d(LOGTAG, "Telemetry upload success!");
         }
         store.onUploadAttemptComplete(successfulUploadIDs);
         return wereAllUploadsSuccessful;
     }
 
+    /**
+     * Check if we have any pings that need to persist their succesful upload status in order to prevent further attempts.
+     * E.g. {@link TelemetryActivationPingBuilder}
+     * @param context
+     */
+    private static void checkPingsPersistence(Context context, String successfulUploadID) {
+        final String activationID = TelemetryActivationPingBuilder.getActivationPingId(context);
+
+        if (activationID == null) {
+            return;
+        }
+
+        if (activationID.equals(successfulUploadID)) {
+            TelemetryActivationPingBuilder.setActivationPingSent(context, true);
+        }
+    }
+
     private static void uploadPayload(final String url, final ExtendedJSONObject payload, final ResultDelegate delegate) {
         final BaseResource resource;
         try {
             resource = new BaseResource(url);
         } catch (final URISyntaxException e) {
             Log.w(LOGTAG, "URISyntaxException for server URL when creating BaseResource: returning.");
             return;
         }
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/telemetry/pingbuilders/TelemetryActivationPingBuilder.java
@@ -0,0 +1,128 @@
+package org.mozilla.gecko.telemetry.pingbuilders;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Build;
+import android.support.annotation.NonNull;
+
+import org.mozilla.gecko.AppConstants;
+import org.mozilla.gecko.GeckoSharedPrefs;
+import org.mozilla.gecko.Locales;
+import org.mozilla.gecko.distribution.DistributionStoreCallback;
+import org.mozilla.gecko.telemetry.TelemetryOutgoingPing;
+import org.mozilla.gecko.util.DateUtil;
+import org.mozilla.gecko.util.StringUtils;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Locale;
+
+/**
+ * Builds a {@link TelemetryOutgoingPing} representing a activation ping.
+ *
+ */
+public class TelemetryActivationPingBuilder extends TelemetryPingBuilder {
+    private static final String LOGTAG = StringUtils.safeSubstring(TelemetryActivationPingBuilder.class.getSimpleName(), 0, 23);
+
+    //Using MOZ_APP_BASENAME would be more elegant but according to the server side schema we need to be sure that we always send the "Fennec" value.
+    private static final String APP_NAME_VALUE = "Fennec";
+
+    private static final String PREFS_ACTIVATION_ID = "activation_ping_id";
+    private static final String PREFS_ACTIVATION_SENT = "activation_ping_sent";
+
+    private static final String NAME = "activation";
+    private static final int VERSION_VALUE = 1;
+
+    private static final String IDENTIFIER = "identifier";
+    private static final String CLIENT_ID = "clientId";
+    private static final String MANUFACTURER = "manufacturer";
+    private static final String MODEL = "model";
+    private static final String DISTRIBUTION_ID = "distribution_id";
+    private static final String LOCALE = "locale";
+    private static final String OS_ATTR = "os";
+    private static final String OS_VERSION = "osversion";
+    private static final String PING_CREATION_DATE = "created";
+    private static final String TIMEZONE_OFFSET = "tz";
+    private static final String APP_NAME = "app_name";
+    private static final String CHANNEL = "channel";
+
+    public TelemetryActivationPingBuilder(final Context context) {
+        super(VERSION_VALUE, true);
+        initPayloadConstants(context);
+    }
+
+    private void initPayloadConstants(final Context context) {
+        payload.put(MANUFACTURER, Build.MANUFACTURER);
+        payload.put(MODEL, Build.MODEL);
+        payload.put(LOCALE, Locales.getLanguageTag(Locale.getDefault()));
+        payload.put(OS_ATTR, TelemetryPingBuilder.OS_NAME);
+        payload.put(OS_VERSION, Integer.toString(Build.VERSION.SDK_INT)); // A String for cross-platform reasons.
+
+        final Calendar nowCalendar = Calendar.getInstance();
+        final DateFormat pingCreationDateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
+        payload.put(PING_CREATION_DATE, pingCreationDateFormat.format(nowCalendar.getTime()));
+        payload.put(TIMEZONE_OFFSET, DateUtil.getTimezoneOffsetInMinutesForGivenDate(nowCalendar));
+        payload.put(APP_NAME, APP_NAME_VALUE);
+        payload.put(CHANNEL, AppConstants.ANDROID_PACKAGE_NAME);
+
+        SharedPreferences prefs = GeckoSharedPrefs.forApp(context);
+        final String distributionId = prefs.getString(DistributionStoreCallback.PREF_DISTRIBUTION_ID, null);
+        if (distributionId != null) {
+            payload.put(DISTRIBUTION_ID, distributionId);
+        }
+
+        prefs.edit().putString(PREFS_ACTIVATION_ID, docID).apply();
+    }
+
+    public static boolean activationPingAlreadySent(Context context) {
+        return GeckoSharedPrefs.forApp(context).getBoolean(PREFS_ACTIVATION_SENT, false);
+    }
+
+    public static void setActivationPingSent(Context context, boolean value) {
+        SharedPreferences prefs = GeckoSharedPrefs.forApp(context);
+        prefs.edit().putBoolean(PREFS_ACTIVATION_SENT, value).apply();
+        prefs.edit().remove(PREFS_ACTIVATION_ID).apply();
+    }
+
+    public static String getActivationPingId(Context context) {
+        return GeckoSharedPrefs.forApp(context).getString(PREFS_ACTIVATION_ID, null);
+    }
+
+    @Override
+    public String getDocType() {
+        return NAME;
+    }
+
+    @Override
+    public String[] getMandatoryFields() {
+        return new String[] {
+                MANUFACTURER,
+                MODEL,
+                LOCALE,
+                OS_ATTR,
+                OS_VERSION,
+                PING_CREATION_DATE,
+                TIMEZONE_OFFSET,
+                APP_NAME,
+                CHANNEL
+        };
+    }
+
+    public TelemetryActivationPingBuilder setIdentifier(@NonNull final String identifier) {
+        if (identifier == null) {
+            throw new IllegalArgumentException("Expected non-null identifier");
+        }
+
+        payload.put(IDENTIFIER, identifier);
+        return this;
+    }
+
+    public TelemetryActivationPingBuilder setClientID(@NonNull final String clientID) {
+        if (clientID == null) {
+            throw new IllegalArgumentException("Expected non-null clientID");
+        }
+        payload.put(CLIENT_ID, clientID);
+        return this;
+    }
+}
--- a/mobile/android/base/java/org/mozilla/gecko/telemetry/pingbuilders/TelemetryPingBuilder.java
+++ b/mobile/android/base/java/org/mozilla/gecko/telemetry/pingbuilders/TelemetryPingBuilder.java
@@ -20,16 +20,19 @@ import java.util.UUID;
  * This base class handles the common ping operations under the hood:
  *   * Validating mandatory fields
  *   * Forming the server url
  */
 abstract class TelemetryPingBuilder {
     // In the server url, the initial path directly after the "scheme://host:port/"
     private static final String SERVER_INITIAL_PATH = "submit/telemetry";
 
+    // Modern pings now use a structured ingestion where we capture the schema version as one of the URI parameters.
+    private static final String SERVER_INITIAL_PATH_MODERN = "submit/mobile";
+
     // By default Fennec ping's use the old telemetry version, this can be overridden
     private static final int DEFAULT_TELEMETRY_VERSION = 1;
 
     // Unified telemetry is version 4
     public static final int UNIFIED_TELEMETRY_VERSION = 4;
 
     // We deliberately call the OS/platform Android to avoid confusion with desktop Linux
     public static final String OS_NAME = "Android";
@@ -43,16 +46,22 @@ abstract class TelemetryPingBuilder {
     }
 
     public TelemetryPingBuilder(int version) {
         docID = UUID.randomUUID().toString();
         serverPath = getTelemetryServerPath(getDocType(), docID, version);
         payload = new ExtendedJSONObject();
     }
 
+    public TelemetryPingBuilder(int version, boolean modernPing) {
+        docID = UUID.randomUUID().toString();
+        serverPath = modernPing ? getModernTelemetryServerPath(getDocType(), docID, version) : getTelemetryServerPath(getDocType(), docID, version);
+        payload = new ExtendedJSONObject();
+    }
+
     /**
      * @return the name of the ping (e.g. "core")
      */
     public abstract String getDocType();
 
     /**
      * @return the fields that are mandatory for the resultant ping to be uploaded to
      *         the server. These will be validated before the ping is built.
@@ -95,9 +104,27 @@ abstract class TelemetryPingBuilder {
                 docID + '/' +
                 docType + '/' +
                 appName + '/' +
                 appVersion + '/' +
                 appUpdateChannel + '/' +
                 appBuildId +
                 (version == UNIFIED_TELEMETRY_VERSION ? "?v=4" : "");
     }
+
+    /**
+     * Returns a url of the format:
+     *   http://hostname/submit/mobile/docType/appVersion/docId/
+     *
+     *   User for modern structured ingestion.
+     *
+     * @param docType The name of the ping (e.g. "main")
+     * @param docID A UUID that identifies the ping
+     * @param version The ping format version
+     * @return a url at which to POST the telemetry data to
+     */
+    private static String getModernTelemetryServerPath(final String docType, final String docID, int version) {
+        return SERVER_INITIAL_PATH_MODERN + '/' +
+                docType + '/' +
+                version + '/' +
+                docID;
+    }
 }
--- a/netwerk/protocol/http/nsHttpAuthCache.cpp
+++ b/netwerk/protocol/http/nsHttpAuthCache.cpp
@@ -46,121 +46,129 @@ static bool StrEquivalent(const char16_t
 }
 
 //-----------------------------------------------------------------------------
 // nsHttpAuthCache <public>
 //-----------------------------------------------------------------------------
 
 nsHttpAuthCache::nsHttpAuthCache()
     : mDB(128), mObserver(new OriginClearObserver(this)) {
+  LOG(("nsHttpAuthCache::nsHttpAuthCache %p", this));
+
   nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
   if (obsSvc) {
     obsSvc->AddObserver(mObserver, "clear-origin-attributes-data", false);
   }
 }
 
 nsHttpAuthCache::~nsHttpAuthCache() {
+  LOG(("nsHttpAuthCache::~nsHttpAuthCache %p", this));
+
   DebugOnly<nsresult> rv = ClearAll();
   MOZ_ASSERT(NS_SUCCEEDED(rv));
   nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
   if (obsSvc) {
     obsSvc->RemoveObserver(mObserver, "clear-origin-attributes-data");
     mObserver->mOwner = nullptr;
   }
 }
 
 nsresult nsHttpAuthCache::GetAuthEntryForPath(const char* scheme,
                                               const char* host, int32_t port,
                                               const char* path,
                                               nsACString const& originSuffix,
                                               nsHttpAuthEntry** entry) {
-  LOG(("nsHttpAuthCache::GetAuthEntryForPath [key=%s://%s:%d path=%s]\n",
-       scheme, host, port, path));
+  LOG(("nsHttpAuthCache::GetAuthEntryForPath %p [path=%s]\n", this, path));
 
   nsAutoCString key;
   nsHttpAuthNode* node = LookupAuthNode(scheme, host, port, originSuffix, key);
   if (!node) return NS_ERROR_NOT_AVAILABLE;
 
   *entry = node->LookupEntryByPath(path);
+  LOG(("  returning %p", *entry));
   return *entry ? NS_OK : NS_ERROR_NOT_AVAILABLE;
 }
 
 nsresult nsHttpAuthCache::GetAuthEntryForDomain(const char* scheme,
                                                 const char* host, int32_t port,
                                                 const char* realm,
                                                 nsACString const& originSuffix,
                                                 nsHttpAuthEntry** entry)
 
 {
-  LOG(("nsHttpAuthCache::GetAuthEntryForDomain [key=%s://%s:%d realm=%s]\n",
-       scheme, host, port, realm));
+  LOG(("nsHttpAuthCache::GetAuthEntryForDomain %p [realm=%s]\n", this, realm));
 
   nsAutoCString key;
   nsHttpAuthNode* node = LookupAuthNode(scheme, host, port, originSuffix, key);
   if (!node) return NS_ERROR_NOT_AVAILABLE;
 
   *entry = node->LookupEntryByRealm(realm);
+  LOG(("  returning %p", *entry));
   return *entry ? NS_OK : NS_ERROR_NOT_AVAILABLE;
 }
 
 nsresult nsHttpAuthCache::SetAuthEntry(const char* scheme, const char* host,
                                        int32_t port, const char* path,
                                        const char* realm, const char* creds,
                                        const char* challenge,
                                        nsACString const& originSuffix,
                                        const nsHttpAuthIdentity* ident,
                                        nsISupports* metadata) {
   nsresult rv;
 
-  LOG(
-      ("nsHttpAuthCache::SetAuthEntry [key=%s://%s:%d realm=%s path=%s "
-       "metadata=%p]\n",
-       scheme, host, port, realm, path, metadata));
+  LOG(("nsHttpAuthCache::SetAuthEntry %p [realm=%s path=%s metadata=%p]\n",
+       this, realm, path, metadata));
 
   nsAutoCString key;
   nsHttpAuthNode* node = LookupAuthNode(scheme, host, port, originSuffix, key);
 
   if (!node) {
     // create a new entry node and set the given entry
     node = new nsHttpAuthNode();
+    LOG(("  new nsHttpAuthNode %p for key='%s'", node, key.get()));
     rv = node->SetAuthEntry(path, realm, creds, challenge, ident, metadata);
     if (NS_FAILED(rv))
       delete node;
     else
       mDB.Put(key, node);
     return rv;
   }
 
   return node->SetAuthEntry(path, realm, creds, challenge, ident, metadata);
 }
 
 void nsHttpAuthCache::ClearAuthEntry(const char* scheme, const char* host,
                                      int32_t port, const char* realm,
                                      nsACString const& originSuffix) {
   nsAutoCString key;
   GetAuthKey(scheme, host, port, originSuffix, key);
+  LOG(("nsHttpAuthCache::ClearAuthEntry %p key='%s'\n", this, key.get()));
   mDB.Remove(key);
 }
 
 nsresult nsHttpAuthCache::ClearAll() {
-  LOG(("nsHttpAuthCache::ClearAll\n"));
+  LOG(("nsHttpAuthCache::ClearAll %p\n", this));
   mDB.Clear();
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // nsHttpAuthCache <private>
 //-----------------------------------------------------------------------------
 
 nsHttpAuthNode* nsHttpAuthCache::LookupAuthNode(const char* scheme,
                                                 const char* host, int32_t port,
                                                 nsACString const& originSuffix,
                                                 nsCString& key) {
   GetAuthKey(scheme, host, port, originSuffix, key);
-  return mDB.Get(key);
+  nsHttpAuthNode* result = mDB.Get(key);
+
+  LOG(("nsHttpAuthCache::LookupAuthNode %p key='%s' found node=%p", this,
+       key.get(), result));
+  return result;
 }
 
 NS_IMPL_ISUPPORTS(nsHttpAuthCache::OriginClearObserver, nsIObserver)
 
 NS_IMETHODIMP
 nsHttpAuthCache::OriginClearObserver::Observe(nsISupports* subject,
                                               const char* topic,
                                               const char16_t* data_unicode) {
@@ -172,16 +180,18 @@ nsHttpAuthCache::OriginClearObserver::Ob
     return NS_ERROR_FAILURE;
   }
 
   mOwner->ClearOriginData(pattern);
   return NS_OK;
 }
 
 void nsHttpAuthCache::ClearOriginData(OriginAttributesPattern const& pattern) {
+  LOG(("nsHttpAuthCache::ClearOriginData %p", this));
+
   for (auto iter = mDB.Iter(); !iter.Done(); iter.Next()) {
     const nsACString& key = iter.Key();
 
     // Extract the origin attributes suffix from the key.
     int32_t colon = key.FindChar(':');
     MOZ_ASSERT(colon != kNotFound);
     nsDependentCSubstring oaSuffix = StringHead(key, colon);
 
--- a/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp
+++ b/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp
@@ -394,24 +394,24 @@ nsresult nsHttpChannelAuthProvider::GenC
   if (NS_FAILED(rv)) return rv;
 
     // don't log this in release build since it could contain sensitive info.
 #ifdef DEBUG
   LOG(("generated creds: %s\n", *result));
 #endif
 
   return UpdateCache(auth, scheme, host, port, directory, realm, challenge,
-                     ident, *result, generateFlags, sessionState);
+                     ident, *result, generateFlags, sessionState, proxyAuth);
 }
 
 nsresult nsHttpChannelAuthProvider::UpdateCache(
     nsIHttpAuthenticator* auth, const char* scheme, const char* host,
     int32_t port, const char* directory, const char* realm,
     const char* challenge, const nsHttpAuthIdentity& ident, const char* creds,
-    uint32_t generateFlags, nsISupports* sessionState) {
+    uint32_t generateFlags, nsISupports* sessionState, bool aProxyAuth) {
   nsresult rv;
 
   uint32_t authFlags;
   rv = auth->GetAuthFlags(&authFlags);
   if (NS_FAILED(rv)) return rv;
 
   // find out if this authenticator allows reuse of credentials and/or
   // challenge.
@@ -421,19 +421,23 @@ nsresult nsHttpChannelAuthProvider::Upda
       0 != (authFlags & nsIHttpAuthenticator::REUSABLE_CHALLENGE);
 
   bool saveIdentity =
       0 == (generateFlags & nsIHttpAuthenticator::USING_INTERNAL_IDENTITY);
 
   // this getter never fails
   nsHttpAuthCache* authCache = gHttpHandler->AuthCache(mIsPrivate);
 
-  nsCOMPtr<nsIChannel> chan = do_QueryInterface(mAuthChannel);
   nsAutoCString suffix;
-  GetOriginAttributesSuffix(chan, suffix);
+  if (!aProxyAuth) {
+    // We don't isolate proxy credentials cache entries with the origin suffix
+    // as it would only annoy users with authentication dialogs popping up.
+    nsCOMPtr<nsIChannel> chan = do_QueryInterface(mAuthChannel);
+    GetOriginAttributesSuffix(chan, suffix);
+  }
 
   // create a cache entry.  we do this even though we don't yet know that
   // these credentials are valid b/c we need to avoid prompting the user
   // more than once in case the credentials are valid.
   //
   // if the credentials are not reusable, then we don't bother sticking
   // them in the auth cache.
   rv = authCache->SetAuthEntry(scheme, host, port, directory, realm,
@@ -1388,17 +1392,17 @@ NS_IMETHODIMP nsHttpChannelAuthProvider:
   ParseRealm(mCurrentChallenge.get(), realm);
 
   rv = GetAuthorizationMembers(mProxyAuth, scheme, host, port, directory, ident,
                                unusedContinuationState);
   if (NS_FAILED(rv)) return rv;
 
   rv = UpdateCache(auth, scheme.get(), host, port, directory.get(), realm.get(),
                    mCurrentChallenge.get(), *ident, aGeneratedCreds, aFlags,
-                   aSessionState);
+                   aSessionState, mProxyAuth);
   MOZ_ASSERT(NS_SUCCEEDED(rv));
   mCurrentChallenge.Truncate();
 
   rv = ContinueOnAuthAvailable(nsDependentCString(aGeneratedCreds));
   MOZ_ASSERT(NS_SUCCEEDED(rv));
   return NS_OK;
 }
 
--- a/netwerk/protocol/http/nsHttpChannelAuthProvider.h
+++ b/netwerk/protocol/http/nsHttpChannelAuthProvider.h
@@ -126,17 +126,18 @@ class nsHttpChannelAuthProvider final : 
 
   // Store credentials to the cache when appropriate aFlags are set.
   MOZ_MUST_USE nsresult UpdateCache(nsIHttpAuthenticator* aAuth,
                                     const char* aScheme, const char* aHost,
                                     int32_t aPort, const char* aDirectory,
                                     const char* aRealm, const char* aChallenge,
                                     const nsHttpAuthIdentity& aIdent,
                                     const char* aCreds, uint32_t aGenerateFlags,
-                                    nsISupports* aSessionState);
+                                    nsISupports* aSessionState,
+                                    bool aProxyAuth);
 
  private:
   nsIHttpAuthenticableChannel* mAuthChannel;  // weak ref
 
   nsCOMPtr<nsIURI> mURI;
   nsCOMPtr<nsProxyInfo> mProxyInfo;
   nsCString mHost;
   int32_t mPort;
--- a/python/mozbuild/mozbuild/test/configure/test_toolchain_configure.py
+++ b/python/mozbuild/mozbuild/test/configure/test_toolchain_configure.py
@@ -256,16 +256,23 @@ VS_PLATFORM_X86_64 = {
 # -Xclang.
 CLANG_CL_3_9 = (CLANG_BASE('3.9.0') + VS('18.00.00000') + DEFAULT_C11 +
                 SUPPORTS_GNU99 + SUPPORTS_GNUXX11 + SUPPORTS_CXX14) + {
     '*.cpp': {
         '__STDC_VERSION__': False,
         '__cplusplus': '201103L',
     },
 }
+CLANG_CL_8_0 = (CLANG_BASE('8.0.0') + VS('18.00.00000') + DEFAULT_C11 +
+                SUPPORTS_GNU99 + SUPPORTS_GNUXX11 + SUPPORTS_CXX14) + {
+    '*.cpp': {
+        '__STDC_VERSION__': False,
+        '__cplusplus': '201103L',
+    },
+}
 
 CLANG_CL_PLATFORM_X86 = FakeCompiler(
     VS_PLATFORM_X86, GCC_PLATFORM_X86[None], GCC_PLATFORM_LITTLE_ENDIAN)
 CLANG_CL_PLATFORM_X86_64 = FakeCompiler(
     VS_PLATFORM_X86_64, GCC_PLATFORM_X86_64[None], GCC_PLATFORM_LITTLE_ENDIAN)
 
 LIBRARY_NAME_INFOS = {
     'linux-gnu': {
@@ -869,42 +876,45 @@ class OSXToolchainTest(BaseToolchainTest
 
 class WindowsToolchainTest(BaseToolchainTest):
     HOST = 'i686-pc-mingw32'
 
     # For the purpose of this test, it doesn't matter that the paths are not
     # real Windows paths.
     PATHS = {
         '/usr/bin/cl': VS_2017u8 + VS_PLATFORM_X86,
-        '/usr/bin/clang-cl': CLANG_CL_3_9 + CLANG_CL_PLATFORM_X86,
+        '/usr/bin/clang-cl-3.9': CLANG_CL_3_9 + CLANG_CL_PLATFORM_X86,
+        '/usr/bin/clang-cl': CLANG_CL_8_0 + CLANG_CL_PLATFORM_X86,
         '/usr/bin/gcc': DEFAULT_GCC + GCC_PLATFORM_X86_WIN,
         '/usr/bin/g++': DEFAULT_GXX + GCC_PLATFORM_X86_WIN,
         '/usr/bin/gcc-4.9': GCC_4_9 + GCC_PLATFORM_X86_WIN,
         '/usr/bin/g++-4.9': GXX_4_9 + GCC_PLATFORM_X86_WIN,
         '/usr/bin/gcc-5': GCC_5 + GCC_PLATFORM_X86_WIN,
         '/usr/bin/g++-5': GXX_5 + GCC_PLATFORM_X86_WIN,
         '/usr/bin/gcc-6': GCC_6 + GCC_PLATFORM_X86_WIN,
         '/usr/bin/g++-6': GXX_6 + GCC_PLATFORM_X86_WIN,
         '/usr/bin/clang': DEFAULT_CLANG + CLANG_PLATFORM_X86_WIN,
         '/usr/bin/clang++': DEFAULT_CLANGXX + CLANG_PLATFORM_X86_WIN,
         '/usr/bin/clang-3.6': CLANG_3_6 + CLANG_PLATFORM_X86_WIN,
         '/usr/bin/clang++-3.6': CLANGXX_3_6 + CLANG_PLATFORM_X86_WIN,
         '/usr/bin/clang-3.3': CLANG_3_3 + CLANG_PLATFORM_X86_WIN,
         '/usr/bin/clang++-3.3': CLANGXX_3_3 + CLANG_PLATFORM_X86_WIN,
     }
 
-    CLANG_CL_3_9_RESULT = CompilerResult(
-        version='3.9.0',
+    CLANG_CL_3_9_RESULT = 'Only clang-cl 8.0 or newer is supported (found version 3.9.0)'
+    CLANG_CL_8_0_RESULT = CompilerResult(
+        version='8.0.0',
         flags=['-Xclang', '-std=gnu99'],
         type='clang-cl',
         compiler='/usr/bin/clang-cl',
         language='C',
     )
-    CLANGXX_CL_3_9_RESULT = CompilerResult(
-        version='3.9.0',
+    CLANGXX_CL_3_9_RESULT = 'Only clang-cl 8.0 or newer is supported (found version 3.9.0)'
+    CLANGXX_CL_8_0_RESULT = CompilerResult(
+        version='8.0.0',
         flags=['-Xclang', '-std=c++14'],
         type='clang-cl',
         compiler='/usr/bin/clang-cl',
         language='C++',
     )
     CLANG_3_3_RESULT = LinuxToolchainTest.CLANG_3_3_RESULT
     CLANGXX_3_3_RESULT = LinuxToolchainTest.CLANGXX_3_3_RESULT
     DEFAULT_CLANG_RESULT = LinuxToolchainTest.DEFAULT_CLANG_RESULT
@@ -920,20 +930,27 @@ class WindowsToolchainTest(BaseToolchain
 
     def test_unsupported_msvc(self):
         self.do_toolchain_test(self.PATHS, {
             'c_compiler': 'Unknown compiler or compiler not supported.'
         }, environ={
             'CC': '/usr/bin/cl',
         })
 
+    def test_unsupported_clang_cl(self):
+        self.do_toolchain_test(self.PATHS, {
+            'c_compiler': self.CLANG_CL_3_9_RESULT,
+        }, environ={
+            'CC': '/usr/bin/clang-cl-3.9',
+        })
+
     def test_clang_cl(self):
         self.do_toolchain_test(self.PATHS, {
-            'c_compiler': self.CLANG_CL_3_9_RESULT,
-            'cxx_compiler': self.CLANGXX_CL_3_9_RESULT,
+            'c_compiler': self.CLANG_CL_8_0_RESULT,
+            'cxx_compiler': self.CLANGXX_CL_8_0_RESULT,
         })
 
     def test_gcc(self):
         # We'll pick GCC if msvc and clang-cl can't be found.
         paths = {
             k: v for k, v in self.PATHS.iteritems()
             if os.path.basename(k) not in ('cl', 'clang-cl')
         }
@@ -974,17 +991,18 @@ class WindowsToolchainTest(BaseToolchain
 
 class Windows64ToolchainTest(WindowsToolchainTest):
     HOST = 'x86_64-pc-mingw32'
 
     # For the purpose of this test, it doesn't matter that the paths are not
     # real Windows paths.
     PATHS = {
         '/usr/bin/cl': VS_2017u8 + VS_PLATFORM_X86_64,
-        '/usr/bin/clang-cl': CLANG_CL_3_9 + CLANG_CL_PLATFORM_X86_64,
+        '/usr/bin/clang-cl': CLANG_CL_8_0 + CLANG_CL_PLATFORM_X86_64,
+        '/usr/bin/clang-cl-3.9': CLANG_CL_3_9 + CLANG_CL_PLATFORM_X86_64,
         '/usr/bin/gcc': DEFAULT_GCC + GCC_PLATFORM_X86_64_WIN,
         '/usr/bin/g++': DEFAULT_GXX + GCC_PLATFORM_X86_64_WIN,
         '/usr/bin/gcc-4.9': GCC_4_9 + GCC_PLATFORM_X86_64_WIN,
         '/usr/bin/g++-4.9': GXX_4_9 + GCC_PLATFORM_X86_64_WIN,
         '/usr/bin/gcc-5': GCC_5 + GCC_PLATFORM_X86_64_WIN,
         '/usr/bin/g++-5': GXX_5 + GCC_PLATFORM_X86_64_WIN,
         '/usr/bin/gcc-6': GCC_6 + GCC_PLATFORM_X86_64_WIN,
         '/usr/bin/g++-6': GXX_6 + GCC_PLATFORM_X86_64_WIN,
@@ -1336,22 +1354,22 @@ class OSXCrossToolchainTest(BaseToolchai
 
 class WindowsCrossToolchainTest(BaseToolchainTest):
     TARGET = 'x86_64-pc-mingw32'
     DEFAULT_CLANG_RESULT = LinuxToolchainTest.DEFAULT_CLANG_RESULT
     DEFAULT_CLANGXX_RESULT = LinuxToolchainTest.DEFAULT_CLANGXX_RESULT
 
     def test_clang_cl_cross(self):
         paths = {
-            '/usr/bin/clang-cl': CLANG_CL_3_9 + CLANG_CL_PLATFORM_X86_64,
+            '/usr/bin/clang-cl': CLANG_CL_8_0 + CLANG_CL_PLATFORM_X86_64,
         }
         paths.update(LinuxToolchainTest.PATHS)
         self.do_toolchain_test(paths, {
-            'c_compiler': WindowsToolchainTest.CLANG_CL_3_9_RESULT,
-            'cxx_compiler': WindowsToolchainTest.CLANGXX_CL_3_9_RESULT,
+            'c_compiler': WindowsToolchainTest.CLANG_CL_8_0_RESULT,
+            'cxx_compiler': WindowsToolchainTest.CLANGXX_CL_8_0_RESULT,
             'host_c_compiler': self.DEFAULT_CLANG_RESULT,
             'host_cxx_compiler': self.DEFAULT_CLANGXX_RESULT,
         })
 
 
 class OpenBSDToolchainTest(BaseToolchainTest):
     HOST = 'x86_64-unknown-openbsd6.1'
     TARGET = 'x86_64-unknown-openbsd6.1'
--- a/testing/web-platform/meta/service-workers/service-worker/navigate-window.https.html.ini
+++ b/testing/web-platform/meta/service-workers/service-worker/navigate-window.https.html.ini
@@ -1,6 +1,4 @@
 [navigate-window.https.html]
-  expected:
-    if sw-e10s: CRASH
   [Clients.matchAll() should not show an old window after it navigates.]
     expected: FAIL
 
--- a/toolkit/components/telemetry/core/TelemetryOrigin.cpp
+++ b/toolkit/components/telemetry/core/TelemetryOrigin.cpp
@@ -229,17 +229,17 @@ nsresult AppEncodeTo(const StaticMutexAu
           size_t shardIndex = index / PrioEncoder::gNumBooleans;
           MOZ_ASSERT(shardIndex < metricData.Length());
           MOZ_ASSERT(index % PrioEncoder::gNumBooleans <
                      metricData[shardIndex].Length());
           metricData[shardIndex][index % PrioEncoder::gNumBooleans] = true;
         }
       }
       aResult.AppendElement(MakePair(id, metricData));
-    } while (generation < maxGeneration);
+    } while (generation++ < maxGeneration);
   }
   return NS_OK;
 }
 
 }  // anonymous namespace
 
 ////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////
--- a/widget/nsIPrintSettingsWin.idl
+++ b/widget/nsIPrintSettingsWin.idl
@@ -13,17 +13,17 @@
  * Native types
  */
   [ptr] native nsDevMode(DEVMODEW);
   native nsHdc(HDC);
 
 /**
  * Simplified PrintSettings for Windows interface
  */
-[scriptable, uuid(c63eed41-6ac5-459e-8a64-033eb9ad770a)]
+[uuid(c63eed41-6ac5-459e-8a64-033eb9ad770a)]
 interface nsIPrintSettingsWin : nsISupports
 {
   /**
    * Data Members
    *
    * Each of these data members make a copy
    * of the contents. If you get the value,
    * you own the memory.
--- a/widget/nsITaskbarTabPreview.idl
+++ b/widget/nsITaskbarTabPreview.idl
@@ -15,17 +15,17 @@ interface imgIContainer;
  * nsITaskbarTabPreview for a window will hide that window's
  * nsITaskbarWindowPreview in the taskbar - the native API performs this
  * unconditionally. When there are no more tab previews for a window, the
  * nsITaskbarWindowPreview will automatically become visible again.
  *
  * An application may have as many tab previews per window as memory allows.
  *
  */
-[scriptable, uuid(11E4C8BD-5C2D-4E1A-A9A1-79DD5B0FE544)]
+[scriptable, builtinclass, uuid(11E4C8BD-5C2D-4E1A-A9A1-79DD5B0FE544)]
 interface nsITaskbarTabPreview : nsITaskbarPreview
 {
   /**
    * The title displayed above the thumbnail
    *
    * Default: an empty string
    */
   attribute AString title;