Merge mozilla-central to autoland a=merge
authorCoroiu Cristina <ccoroiu@mozilla.com>
Sat, 17 Nov 2018 23:40:28 +0200
changeset 446924 2d8c5d46389336d6c4620e947f757e302d3f224c
parent 446923 51abb63ca2464f588229752a5c6070f459a74e6b (current diff)
parent 446922 77223bb2fac278373dfcdde11fcda74b4c80aa61 (diff)
child 446925 75ca66f490c31648aa80c228e043806bdee327d0
push id35058
push usercbrindusan@mozilla.com
push dateSun, 18 Nov 2018 11:14:44 +0000
treeherdermozilla-central@e432617f7098 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone65.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=merge
taskcluster/ci/mar-signing-l10n/kind.yml
taskcluster/ci/mar-signing/kind.yml
taskcluster/taskgraph/transforms/mar_signing.py
--- a/browser/base/content/browser-contentblocking.js
+++ b/browser/base/content/browser-contentblocking.js
@@ -3,32 +3,46 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 var TrackingProtection = {
   reportBreakageLabel: "trackingprotection",
   telemetryIdentifier: "tp",
   PREF_ENABLED_GLOBALLY: "privacy.trackingprotection.enabled",
   PREF_ENABLED_IN_PRIVATE_WINDOWS: "privacy.trackingprotection.pbmode.enabled",
   PREF_UI_ENABLED: "browser.contentblocking.trackingprotection.control-center.ui.enabled",
+  PREF_TRACKING_TABLE: "urlclassifier.trackingTable",
+  PREF_TRACKING_ANNOTATION_TABLE: "urlclassifier.trackingAnnotationTable",
   enabledGlobally: false,
   enabledInPrivateWindows: false,
 
   get categoryItem() {
     delete this.categoryItem;
     return this.categoryItem =
       document.getElementById("identity-popup-content-blocking-category-tracking-protection");
   },
 
+  get subViewList() {
+    delete this.subViewList;
+    return this.subViewList = document.getElementById("identity-popup-trackersView-list");
+  },
+
+  get strictInfo() {
+    delete this.strictInfo;
+    return this.strictInfo = document.getElementById("identity-popup-trackersView-strict-info");
+  },
+
   init() {
     this.updateEnabled();
 
     Services.prefs.addObserver(this.PREF_ENABLED_GLOBALLY, this);
     Services.prefs.addObserver(this.PREF_ENABLED_IN_PRIVATE_WINDOWS, this);
 
     XPCOMUtils.defineLazyPreferenceGetter(this, "visible", this.PREF_UI_ENABLED, false);
+    XPCOMUtils.defineLazyPreferenceGetter(this, "trackingTable", this.PREF_TRACKING_TABLE, false);
+    XPCOMUtils.defineLazyPreferenceGetter(this, "trackingAnnotationTable", this.PREF_TRACKING_ANNOTATION_TABLE, false);
   },
 
   uninit() {
     Services.prefs.removeObserver(this.PREF_ENABLED_GLOBALLY, this);
     Services.prefs.removeObserver(this.PREF_ENABLED_IN_PRIVATE_WINDOWS, this);
   },
 
   observe() {
@@ -46,16 +60,96 @@ var TrackingProtection = {
       Services.prefs.getBoolPref(this.PREF_ENABLED_GLOBALLY);
     this.enabledInPrivateWindows =
       Services.prefs.getBoolPref(this.PREF_ENABLED_IN_PRIVATE_WINDOWS);
   },
 
   isBlockerActivated(state) {
     return state & Ci.nsIWebProgressListener.STATE_BLOCKED_TRACKING_CONTENT;
   },
+
+  isAllowing(state) {
+    return state & Ci.nsIWebProgressListener.STATE_LOADED_TRACKING_CONTENT;
+  },
+
+  async updateSubView() {
+    let previousURI = gBrowser.currentURI.spec;
+    let previousWindow = gBrowser.selectedBrowser.innerWindowID;
+
+    let contentBlockingLogJSON = await gBrowser.selectedBrowser.getContentBlockingLog();
+    let contentBlockingLog = JSON.parse(contentBlockingLogJSON);
+
+    // Don't tell the user to turn on TP if they are already blocking trackers.
+    this.strictInfo.hidden = this.enabled;
+
+    let fragment = document.createDocumentFragment();
+    for (let [origin, actions] of Object.entries(contentBlockingLog)) {
+      let listItem = await this._createListItem(origin, actions);
+      if (listItem) {
+        fragment.appendChild(listItem);
+      }
+    }
+
+    // This might have taken a while. Only update the list if we're still on the same page.
+    if (previousURI == gBrowser.currentURI.spec &&
+        previousWindow == gBrowser.selectedBrowser.innerWindowID) {
+      this.subViewList.textContent = "";
+      this.subViewList.append(fragment);
+    }
+  },
+
+  // Given a URI from a source that was tracking-annotated, figure out
+  // if it's really on the tracking table or just on the annotation table.
+  _isOnTrackingTable(uri) {
+    if (this.trackingTable == this.trackingAnnotationTable) {
+      return true;
+    }
+    return new Promise(resolve => {
+      classifierService.asyncClassifyLocalWithTables(uri, this.trackingTable, [], [],
+        (code, list) => resolve(!!list));
+    });
+  },
+
+  async _createListItem(origin, actions) {
+    // Figure out if this list entry was actually detected by TP or something else.
+    let isDetected = false;
+    let isAllowed = false;
+    for (let [state] of actions) {
+      isAllowed = isAllowed || this.isAllowing(state);
+      isDetected = isDetected || isAllowed || this.isBlockerActivated(state);
+    }
+
+    if (!isDetected) {
+      return null;
+    }
+
+    let uri = Services.io.newURI(origin);
+
+    // Because we might use different lists for annotation vs. blocking, we
+    // need to make sure that this is a tracker that we would actually have blocked
+    // before showing it to the user.
+    let isTracker = await this._isOnTrackingTable(uri);
+    if (!isTracker) {
+      return null;
+    }
+
+    let listItem = document.createXULElement("hbox");
+    listItem.className = "identity-popup-trackersView-list-item";
+    listItem.classList.toggle("allowed", isAllowed);
+
+    let image = document.createXULElement("image");
+    listItem.append(image);
+
+    let label = document.createXULElement("label");
+    label.value = uri.host;
+    label.setAttribute("crop", "end");
+    listItem.append(label);
+
+    return listItem;
+  },
 };
 
 var ThirdPartyCookies = {
   telemetryIdentifier: "cr",
   PREF_ENABLED: "network.cookie.cookieBehavior",
   PREF_REPORT_BREAKAGE_ENABLED: "browser.contentblocking.rejecttrackers.reportBreakage.enabled",
   PREF_ENABLED_VALUES: [
     // These values match the ones exposed under the Content Blocking section
@@ -142,16 +236,21 @@ var ContentBlocking = {
     return this.PREF_INTRO_COUNT_CB;
   },
 
   get appMenuLabel() {
     delete this.appMenuLabel;
     return this.appMenuLabel = document.getElementById("appMenu-tp-label");
   },
 
+  get identityPopup() {
+    delete this.identityPopup;
+    return this.identityPopup = document.getElementById("identity-popup");
+  },
+
   strings: {
     get appMenuTitle() {
       delete this.appMenuTitle;
       return this.appMenuTitle =
         gNavigatorBundle.getString("contentBlocking.title");
     },
 
     get appMenuTooltip() {
@@ -233,30 +332,30 @@ var ContentBlocking = {
         blocker.uninit();
       }
     }
 
     Services.prefs.removeObserver(this.PREF_ANIMATIONS_ENABLED, this.updateAnimationsEnabled);
   },
 
   hideIdentityPopupAndReload() {
-    document.getElementById("identity-popup").hidePopup();
+    this.identityPopup.hidePopup();
     BrowserReload();
   },
 
   openPreferences(origin) {
     openPreferences("privacy-trackingprotection", { origin });
   },
 
   backToMainView() {
     this.identityPopupMultiView.goBack();
   },
 
   submitBreakageReport() {
-    document.getElementById("identity-popup").hidePopup();
+    this.identityPopup.hidePopup();
 
     let reportEndpoint = Services.prefs.getStringPref(this.PREF_REPORT_BREAKAGE_URL);
     if (!reportEndpoint) {
       return;
     }
 
     let formData = new FormData();
     formData.set("title", this.reportURI.host);
@@ -309,16 +408,21 @@ var ContentBlocking = {
     // Save this URI to make sure that the user really only submits the location
     // they see in the report breakage dialog.
     this.reportURI = gBrowser.currentURI;
     let urlWithoutQuery = this.reportURI.asciiSpec.replace("?" + this.reportURI.query, "");
     this.reportBreakageURL.textContent = urlWithoutQuery;
     this.identityPopupMultiView.showSubView("identity-popup-breakageReportView");
   },
 
+  async showTrackersSubview() {
+    await TrackingProtection.updateSubView();
+    this.identityPopupMultiView.showSubView("identity-popup-trackersView");
+  },
+
   shieldHistogramAdd(value) {
     if (PrivateBrowsingUtils.isWindowPrivate(window)) {
       return;
     }
     Services.telemetry.getHistogramById("TRACKING_PROTECTION_SHIELD").add(value);
   },
 
   onSecurityChange(oldState, state, webProgress, isSimulated,
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -138,16 +138,17 @@ XPCOMUtils.defineLazyScriptGetter(this, 
 if (AppConstants.NIGHTLY_BUILD) {
   XPCOMUtils.defineLazyScriptGetter(this, "gWebRender",
                                     "chrome://browser/content/browser-webrender.js");
 }
 
 // lazy service getters
 
 XPCOMUtils.defineLazyServiceGetters(this, {
+  classifierService: ["@mozilla.org/url-classifier/dbservice;1", "nsIURIClassifier"],
   Favicons: ["@mozilla.org/browser/favicon-service;1", "nsIFaviconService"],
   gAboutNewTabService: ["@mozilla.org/browser/aboutnewtab-service;1", "nsIAboutNewTabService"],
   gDNSService: ["@mozilla.org/network/dns-service;1", "nsIDNSService"],
   gSerializationHelper: ["@mozilla.org/network/serialization-helper;1", "nsISerializationHelper"],
   Marionette: ["@mozilla.org/remote/marionette;1", "nsIMarionette"],
   WindowsUIUtils: ["@mozilla.org/windows-ui-utils;1", "nsIWindowsUIUtils"],
 });
 
--- a/browser/base/content/test/trackingUI/browser.ini
+++ b/browser/base/content/test/trackingUI/browser.ini
@@ -18,8 +18,9 @@ support-files =
   file_trackingUI_fetch.js
   file_trackingUI_fetch.js^headers^
 [browser_trackingUI_open_preferences.js]
 [browser_trackingUI_pbmode_exceptions.js]
 [browser_trackingUI_report_breakage.js]
 [browser_trackingUI_state.js]
 [browser_trackingUI_state_all_disabled.js]
 [browser_trackingUI_telemetry.js]
+[browser_trackingUI_trackers_subview.js]
--- a/browser/base/content/test/trackingUI/browser_trackingUI_pbmode_exceptions.js
+++ b/browser/base/content/test/trackingUI/browser_trackingUI_pbmode_exceptions.js
@@ -54,19 +54,19 @@ function testTrackingPage(window) {
     ok(!hidden("#tracking-action-unblock"), "unblockButton is visible");
     ok(hidden("#tracking-action-unblock-private"), "unblockButtonPrivate is hidden");
   }
 
   ok(hidden("#identity-popup-content-blocking-not-detected"), "blocking not detected label is hidden");
   ok(!hidden("#identity-popup-content-blocking-detected"), "blocking detected label is visible");
 
   ok(!hidden("#identity-popup-content-blocking-category-list"), "category list is visible");
-  ok(hidden("#identity-popup-content-blocking-category-tracking-protection > .identity-popup-content-blocking-category-add-blocking"),
-    "TP category item is not showing add blocking");
-  ok(!hidden("#identity-popup-content-blocking-category-tracking-protection > .identity-popup-content-blocking-category-state-label"),
+  ok(hidden("#identity-popup-content-blocking-category-tracking-protection > #identity-popup-content-blocking-tracking-protection-label-allowed"),
+    "TP category item is not showing the allowed label");
+  ok(!hidden("#identity-popup-content-blocking-category-tracking-protection > #identity-popup-content-blocking-tracking-protection-label-blocked"),
     "TP category item is set to blocked");
 }
 
 function testTrackingPageUnblocked() {
   info("Tracking content must be white-listed and not blocked");
   ok(ContentBlocking.content.hasAttribute("detected"), "trackers are detected");
   ok(ContentBlocking.content.hasAttribute("hasException"), "content shows exception");
 
@@ -79,19 +79,19 @@ function testTrackingPageUnblocked() {
   ok(!hidden("#tracking-action-block"), "blockButton is visible");
   ok(hidden("#tracking-action-unblock"), "unblockButton is hidden");
   ok(!hidden("#identity-popup-content-blocking-disabled-label"), "disabled label is visible");
 
   ok(hidden("#identity-popup-content-blocking-not-detected"), "blocking not detected label is hidden");
   ok(!hidden("#identity-popup-content-blocking-detected"), "blocking detected label is visible");
 
   ok(!hidden("#identity-popup-content-blocking-category-list"), "category list is visible");
-  ok(hidden("#identity-popup-content-blocking-category-tracking-protection > .identity-popup-content-blocking-category-add-blocking"),
-    "TP category item is not showing add blocking");
-  ok(hidden("#identity-popup-content-blocking-category-tracking-protection > .identity-popup-content-blocking-category-state-label"),
+  ok(!hidden("#identity-popup-content-blocking-category-tracking-protection > #identity-popup-content-blocking-tracking-protection-label-allowed"),
+    "TP category item is showing the allowed label");
+  ok(hidden("#identity-popup-content-blocking-category-tracking-protection > #identity-popup-content-blocking-tracking-protection-label-blocked"),
     "TP category item is not set to blocked");
 }
 
 add_task(async function testExceptionAddition() {
   await UrlClassifierTestUtils.addTestTrackers();
   let privateWin = await BrowserTestUtils.openNewBrowserWindow({private: true});
   browser = privateWin.gBrowser;
   let tab = await BrowserTestUtils.openNewForegroundTab({ gBrowser: browser, waitForLoad: true, waitForStateStop: true });
--- a/browser/base/content/test/trackingUI/browser_trackingUI_state.js
+++ b/browser/base/content/test/trackingUI/browser_trackingUI_state.js
@@ -129,30 +129,36 @@ function testTrackingPage(window) {
     is(!hidden("#tracking-action-unblock"), blockedByTP,
        "unblockButton is" + (blockedByTP ? "" : " not") + " visible");
   }
 
   ok(hidden("#identity-popup-content-blocking-not-detected"), "blocking not detected label is hidden");
   ok(!hidden("#identity-popup-content-blocking-detected"), "blocking detected label is visible");
 
   ok(!hidden("#identity-popup-content-blocking-category-list"), "category list is visible");
-  let category = Services.prefs.getIntPref(TPC_PREF) == Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER ?
-               "#identity-popup-content-blocking-category-3rdpartycookies" :
-               "#identity-popup-content-blocking-category-tracking-protection";
-  is(hidden(category + " > .identity-popup-content-blocking-category-add-blocking"), blockedByTP,
-    "Category item is" + (blockedByTP ? " not" : "") + " showing add blocking");
-  is(hidden(category + " > .identity-popup-content-blocking-category-state-label"), !blockedByTP,
-    "Category item is" + (blockedByTP ? "" : " not") + " set to blocked");
 
-  if (Services.prefs.getIntPref(TPC_PREF) == Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER) {
+  let cookiesBlocked = Services.prefs.getIntPref(TPC_PREF) == Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER;
+  if (cookiesBlocked) {
+    let category = "#identity-popup-content-blocking-category-3rdpartycookies";
+    is(hidden(category + " > .identity-popup-content-blocking-category-add-blocking"), blockedByTP,
+      "Category item is" + (blockedByTP ? " not" : "") + " showing add blocking");
+    is(hidden(category + " > .identity-popup-content-blocking-category-state-label"), !blockedByTP,
+      "Category item is" + (blockedByTP ? "" : " not") + " set to blocked");
+
     ok(hidden("#identity-popup-content-blocking-category-label-default"),
       "Not showing default cookie restrictions label.");
     ok(!hidden("#identity-popup-content-blocking-category-label-trackers"),
       "Showing trackers cookie restrictions label.");
   } else {
+    let category = "#identity-popup-content-blocking-category-tracking-protection";
+    is(hidden(category + " > #identity-popup-content-blocking-tracking-protection-label-allowed"), blockedByTP,
+      "Category item is" + (blockedByTP ? " not" : "") + " showing the allowed label");
+    is(!hidden(category + " > #identity-popup-content-blocking-tracking-protection-label-blocked"), blockedByTP,
+      "Category item is" + (blockedByTP ? "" : " not") + " set to blocked");
+
     ok(hidden("#identity-popup-content-blocking-category-label-trackers"),
       "Not showing trackers cookie restrictions label.");
     ok(!hidden("#identity-popup-content-blocking-category-label-default"),
       "Showing default cookie restrictions label.");
   }
 }
 
 function testTrackingPageUnblocked(blockedByTP, window) {
@@ -170,24 +176,32 @@ function testTrackingPageUnblocked(block
   ok(!hidden("#tracking-action-block"), "blockButton is visible");
   ok(hidden("#tracking-action-unblock"), "unblockButton is hidden");
   ok(!hidden("#identity-popup-content-blocking-disabled-label"), "disabled label is visible");
 
   ok(hidden("#identity-popup-content-blocking-not-detected"), "blocking not detected label is hidden");
   ok(!hidden("#identity-popup-content-blocking-detected"), "blocking detected label is visible");
 
   ok(!hidden("#identity-popup-content-blocking-category-list"), "category list is visible");
-  let category = Services.prefs.getIntPref(TPC_PREF) == Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER ?
-               "#identity-popup-content-blocking-category-3rdpartycookies" :
-               "#identity-popup-content-blocking-category-tracking-protection";
-  is(hidden(category + " > .identity-popup-content-blocking-category-add-blocking"), blockedByTP,
-    "Category item is" + (blockedByTP ? " not" : "") + " showing add blocking");
-  // Always hidden no matter if blockedByTP or not, since we have an exception.
-  ok(hidden("#identity-popup-content-blocking-category-tracking-protection > .identity-popup-content-blocking-category-state-label"),
-    "TP category item is not set to blocked");
+
+  let cookiesBlocked = Services.prefs.getIntPref(TPC_PREF) == Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER;
+  if (cookiesBlocked) {
+    let category = "#identity-popup-content-blocking-category-3rdpartycookies";
+    is(hidden(category + " > .identity-popup-content-blocking-category-add-blocking"), blockedByTP,
+      "Category item is" + (blockedByTP ? " not" : "") + " showing add blocking");
+    ok(!hidden("#identity-popup-content-blocking-category-tracking-protection > #identity-popup-content-blocking-tracking-protection-label-allowed"),
+      "TP category item is showing the allowed label");
+  } else {
+    let category = "#identity-popup-content-blocking-category-tracking-protection";
+    // If there's an exception we always show the "Allowed" label.
+    ok(!hidden(category + " > #identity-popup-content-blocking-tracking-protection-label-allowed"),
+      "Category item is showing the allowed label");
+    ok(hidden(category + " > #identity-popup-content-blocking-tracking-protection-label-blocked"),
+      "Category item is not set to blocked");
+  }
 }
 
 async function testContentBlocking(tab) {
   info("Testing with Tracking Protection ENABLED.");
 
   info("Load a test page not containing tracking elements");
   await promiseTabLoadEvent(tab, BENIGN_PAGE);
   testBenignPage();
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/trackingUI/browser_trackingUI_trackers_subview.js
@@ -0,0 +1,118 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TRACKING_PAGE = "http://tracking.example.org/browser/browser/base/content/test/trackingUI/trackingPage.html";
+
+const TP_PREF = "privacy.trackingprotection.enabled";
+
+add_task(async function setup() {
+  await UrlClassifierTestUtils.addTestTrackers();
+});
+
+function openIdentityPopup() {
+  let mainView = document.getElementById("identity-popup-mainView");
+  let viewShown = BrowserTestUtils.waitForEvent(mainView, "ViewShown");
+  gIdentityHandler._identityBox.click();
+  return viewShown;
+}
+
+function waitForSecurityChange(blocked) {
+  return new Promise(resolve => {
+    let webProgressListener = {
+      onStateChange: () => {},
+      onStatusChange: () => {},
+      onLocationChange: () => {},
+      onSecurityChange: (webProgress, request, oldState, state) => {
+        if ((!blocked && state & Ci.nsIWebProgressListener.STATE_LOADED_TRACKING_CONTENT) ||
+            (blocked && state & Ci.nsIWebProgressListener.STATE_BLOCKED_TRACKING_CONTENT)) {
+          gBrowser.removeProgressListener(webProgressListener);
+          resolve();
+        }
+      },
+      onProgressChange: () => {},
+      QueryInterface: ChromeUtils.generateQI([Ci.nsIWebProgressListener]),
+    };
+
+    gBrowser.addProgressListener(webProgressListener);
+  });
+}
+
+async function assertSitesListed(blocked) {
+  await BrowserTestUtils.withNewTab(TRACKING_PAGE, async function(browser) {
+    await openIdentityPopup();
+
+    let categoryItem =
+      document.getElementById("identity-popup-content-blocking-category-tracking-protection");
+    ok(BrowserTestUtils.is_visible(categoryItem), "TP category item is visible");
+    let trackersView = document.getElementById("identity-popup-trackersView");
+    let viewShown = BrowserTestUtils.waitForEvent(trackersView, "ViewShown");
+    categoryItem.click();
+    await viewShown;
+
+    ok(true, "Trackers view was shown");
+
+    let listItems = document.querySelectorAll(".identity-popup-trackersView-list-item");
+    is(listItems.length, 1, "We have 1 tracker in the list");
+
+    let strictInfo = document.getElementById("identity-popup-trackersView-strict-info");
+    is(BrowserTestUtils.is_hidden(strictInfo), Services.prefs.getBoolPref(TP_PREF),
+      "Strict info is hidden if TP is enabled.");
+
+    let mainView = document.getElementById("identity-popup-mainView");
+    viewShown = BrowserTestUtils.waitForEvent(mainView, "ViewShown");
+    let backButton = trackersView.querySelector(".subviewbutton-back");
+    backButton.click();
+    await viewShown;
+
+    ok(true, "Main view was shown");
+
+    let change = waitForSecurityChange(blocked);
+
+    await ContentTask.spawn(browser, {}, function() {
+      content.postMessage("more-tracking", "*");
+    });
+
+    await change;
+
+    viewShown = BrowserTestUtils.waitForEvent(trackersView, "ViewShown");
+    categoryItem.click();
+    await viewShown;
+
+    ok(true, "Trackers view was shown");
+
+    listItems = Array.from(document.querySelectorAll(".identity-popup-trackersView-list-item"));
+    is(listItems.length, 2, "We have 2 trackers in the list");
+
+    let listItem = listItems.find(item => item.querySelector("label").value == "trackertest.org");
+    ok(listItem, "Has an item for trackertest.org");
+    ok(BrowserTestUtils.is_visible(listItem), "List item is visible");
+    is(listItem.classList.contains("allowed"), !blocked,
+      "Indicates whether the tracker was blocked or allowed");
+
+    listItem = listItems.find(item => item.querySelector("label").value == "itisatracker.org");
+    ok(listItem, "Has an item for itisatracker.org");
+    ok(BrowserTestUtils.is_visible(listItem), "List item is visible");
+    is(listItem.classList.contains("allowed"), !blocked,
+      "Indicates whether the tracker was blocked or allowed");
+  });
+}
+
+add_task(async function testTrackersSubView() {
+  Services.prefs.setBoolPref(TP_PREF, false);
+  await assertSitesListed(false);
+  Services.prefs.setBoolPref(TP_PREF, true);
+  await assertSitesListed(true);
+  let uri = Services.io.newURI("https://tracking.example.org");
+  Services.perms.add(uri, "trackingprotection", Services.perms.ALLOW_ACTION);
+  await assertSitesListed(false);
+  Services.perms.remove(uri, "trackingprotection");
+  await assertSitesListed(true);
+  Services.prefs.clearUserPref(TP_PREF);
+});
+
+add_task(function cleanup() {
+  Services.prefs.clearUserPref(TP_PREF);
+  UrlClassifierTestUtils.cleanupTestTrackers();
+});
--- a/browser/base/content/test/trackingUI/trackingAPI.js
+++ b/browser/base/content/test/trackingUI/trackingAPI.js
@@ -1,16 +1,22 @@
 onmessage = event => {
   switch (event.data) {
   case "tracking": {
       let ifr = document.createElement("iframe");
       ifr.src = "https://trackertest.org/";
       document.body.appendChild(ifr);
     }
     break;
+  case "more-tracking": {
+      let ifr = document.createElement("iframe");
+      ifr.src = "https://itisatracker.org/";
+      document.body.appendChild(ifr);
+    }
+    break;
   case "cookie": {
       let ifr = document.createElement("iframe");
       ifr.src = "https://trackertest.org/browser/browser/base/content/test/trackingUI/cookieServer.sjs";
       document.body.appendChild(ifr);
     }
     break;
   }
 };
--- a/browser/components/controlcenter/content/panel.inc.xul
+++ b/browser/components/controlcenter/content/panel.inc.xul
@@ -77,25 +77,26 @@
           </hbox>
 
           <description id="identity-popup-content-blocking-detected"
                        crop="end">&contentBlocking.detected;</description>
           <description id="identity-popup-content-blocking-not-detected"
                        crop="end">&contentBlocking.notDetected;</description>
 
           <vbox id="identity-popup-content-blocking-category-list">
-            <hbox id="identity-popup-content-blocking-category-tracking-protection"
-                  class="identity-popup-content-blocking-category" align="center" role="group">
+            <toolbarbutton id="identity-popup-content-blocking-category-tracking-protection"
+                  onclick="ContentBlocking.showTrackersSubview()"
+                  class="identity-popup-content-blocking-category" align="center">
               <image class="identity-popup-content-blocking-category-icon tracking-protection-icon"/>
               <label flex="1" class="identity-popup-content-blocking-category-label">&contentBlocking.trackingProtection3.label;</label>
-              <label flex="1" class="identity-popup-content-blocking-category-state-label">&contentBlocking.trackingProtection.blocking.label;</label>
-              <label flex="1" class="identity-popup-content-blocking-category-add-blocking text-link"
-                     id="identity-popup-tracking-protection-add-blocking"
-                     onclick="ContentBlocking.openPreferences('identityPopup-CB-tracking-protection'); gIdentityHandler.recordClick('tp_add_blocking');">&contentBlocking.trackingProtection.add.label;</label>
-            </hbox>
+              <label flex="1" id="identity-popup-content-blocking-tracking-protection-label-allowed"
+                     class="identity-popup-content-blocking-category-state-label">&contentBlocking.trackingProtection.allowed.label;</label>
+              <label flex="1" id="identity-popup-content-blocking-tracking-protection-label-blocked"
+                     class="identity-popup-content-blocking-category-state-label">&contentBlocking.trackingProtection.blocked.label;</label>
+            </toolbarbutton>
             <hbox id="identity-popup-content-blocking-category-3rdpartycookies"
                   class="identity-popup-content-blocking-category" align="center" role="group">
               <image class="identity-popup-content-blocking-category-icon thirdpartycookies-icon"/>
               <label flex="1" id="identity-popup-content-blocking-category-label-default"
                      class="identity-popup-content-blocking-category-label">&contentBlocking.3rdPartyCookies.label;</label>
               <label flex="1" id="identity-popup-content-blocking-category-label-trackers"
                      hidden="true" class="identity-popup-content-blocking-category-label">&contentBlocking.3rdPartyCookies.trackers.label;</label>
               <label flex="1" class="identity-popup-content-blocking-category-state-label">&contentBlocking.3rdPartyCookies.blocking.label;</label>
@@ -244,16 +245,35 @@
         <!-- More Security Information -->
         <button id="identity-popup-more-info"
                 label="&identity.moreInfoLinkText2;"
                 oncommand="gIdentityHandler.handleMoreInfoClick(event);"/>
       </vbox>
 
     </panelview>
 
+    <!-- Trackers SubView -->
+    <panelview id="identity-popup-trackersView"
+               role="document"
+               title="&contentBlocking.trackersView.label;"
+               descriptionheightworkaround="true">
+        <vbox id="identity-popup-trackersView-list">
+        </vbox>
+        <hbox id="identity-popup-trackersView-strict-info">
+          <image/>
+          <label>&contentBlocking.trackersView.strictInfo.label;</label>
+        </hbox>
+        <vbox class="identity-popup-footer">
+          <button id="identity-popup-trackersView-settings-button"
+                  label="&contentBlocking.manageSettings.label;"
+                  accesskey="&contentBlocking.manageSettings.accesskey;"
+                  oncommand="ContentBlocking.openPreferences();"/>
+        </vbox>
+    </panelview>
+
     <!-- Report Breakage SubView -->
     <panelview id="identity-popup-breakageReportView"
                title="&contentBlocking.breakageReportView.label;"
                descriptionheightworkaround="true">
         <vbox id="identity-popup-breakageReportView-heading">
           <description>&contentBlocking.breakageReportView2.description;</description>
           <label id="identity-popup-breakageReportView-learn-more"
                  class="text-link">&contentBlocking.breakageReportView.learnMore;</label>
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -965,34 +965,29 @@ you can use these alternative items. Oth
 
 <!ENTITY contentBlocking.title "Content Blocking">
 <!ENTITY contentBlocking.detected "Blockable content detected on this site.">
 <!ENTITY contentBlocking.notDetected "No blockable content detected on this page.">
 <!ENTITY contentBlocking.disabled.label "Disabled">
 <!ENTITY contentBlocking.disabled.tooltip "You have disabled Content Blocking.">
 <!ENTITY contentBlocking.exception.tooltip "You have disabled Content Blocking for this site.">
 
-<!ENTITY contentBlocking.trackingProtection2.label "All Detected Trackers">
 <!ENTITY contentBlocking.trackingProtection3.label "Trackers">
+<!-- LOCALIZATION NOTE (contentBlocking.trackingProtection.allowed.label):
+     This label signals that this type of content blocking is turned
+     OFF and is not blocking tracker content, so this is not
+     a positive thing. It forms the end of the (imaginary) sentence
+     "Trackers [are] Allowed"-->
+<!ENTITY contentBlocking.trackingProtection.allowed.label "Allowed">
 <!-- LOCALIZATION NOTE (contentBlocking.trackingProtection.blocked.label):
      This label signals that this type of content blocking is turned
      ON and is successfully blocking tracker content, so this is
      a positive thing. It forms the end of the (imaginary) sentence
      "Trackers [are] Blocked"-->
 <!ENTITY contentBlocking.trackingProtection.blocked.label "Blocked">
-<!-- LOCALIZATION NOTE (contentBlocking.tranckingProtection.blocking.label):
-     This label signals that this type of content blocking is turned
-     ON, so this is a positive thing. It forms the verb in the (imaginary) sentence
-     "Firefox is blocking Trackers"-->
-<!ENTITY contentBlocking.trackingProtection.blocking.label "Blocking">
-<!-- LOCALIZATION NOTE (contentBlocking.trackingProtection.add.label):
-     This is displayed as a link to preferences, where the user can add
-     this specific type of content blocking. When this text is shown
-     the type of content blocking is currently not enabled. -->
-<!ENTITY contentBlocking.trackingProtection.add.label "Add Blocking…">
 
 <!ENTITY contentBlocking.3rdPartyCookies.label "Third-Party Cookies">
 <!ENTITY contentBlocking.3rdPartyCookies.trackers.label "Tracking Cookies">
 <!-- LOCALIZATION NOTE (contentBlocking.3rdPartyCookies.blocked.label):
      This label signals that this type of content blocking is turned
      ON and is successfully blocking third-party cookies, so this is
      a positive thing. It forms the end of the (imaginary) sentence
      "Third-Party Cookies [are] Blocked"-->
@@ -1003,16 +998,22 @@ you can use these alternative items. Oth
      "Firefox is blocking Third-Party Cookies"-->
 <!ENTITY contentBlocking.3rdPartyCookies.blocking.label "Blocking">
 <!-- LOCALIZATION NOTE (contentBlocking.3rdPartyCookies.add.label):
      This is displayed as a link to preferences, where the user can add
      this specific type of content blocking. When this text is shown
      the type of content blocking is currently not enabled. -->
 <!ENTITY contentBlocking.3rdPartyCookies.add.label "Add Blocking…">
 
+<!ENTITY contentBlocking.manageSettings.label "Manage Content Blocking">
+<!ENTITY contentBlocking.manageSettings.accesskey "M">
+
+<!ENTITY contentBlocking.trackersView.label "Trackers">
+<!ENTITY contentBlocking.trackersView.strictInfo.label "To block all trackers, set content blocking to “Strict”.">
+
 <!ENTITY contentBlocking.openBreakageReportView2.label "Report a problem">
 <!ENTITY contentBlocking.breakageReportView.label "Report Problems">
 <!ENTITY contentBlocking.breakageReportView2.description "Content blocking can cause problems with some websites. When you report problems, you’ll help make &brandShortName; better for everyone. (This will send a URL as well as information about your browser settings to Mozilla.)">
 <!ENTITY contentBlocking.breakageReportView.learnMore "Learn More">
 <!ENTITY contentBlocking.breakageReportView.collection.url.label "URL">
 <!ENTITY contentBlocking.breakageReportView.collection.comments.label "What problems did you have? (Optional)">
 <!ENTITY contentBlocking.breakageReportView.sendReport.label "Send Report">
 <!ENTITY contentBlocking.breakageReportView.cancel.label "Cancel">
--- a/browser/locales/l10n-changesets.json
+++ b/browser/locales/l10n-changesets.json
@@ -1424,16 +1424,31 @@
             "macosx64-devedition",
             "win32",
             "win32-devedition",
             "win64",
             "win64-devedition"
         ],
         "revision": "default"
     },
+    "trs": {
+        "platforms": [
+            "linux",
+            "linux-devedition",
+            "linux64",
+            "linux64-devedition",
+            "macosx64",
+            "macosx64-devedition",
+            "win32",
+            "win32-devedition",
+            "win64",
+            "win64-devedition"
+        ],
+        "revision": "default"
+    },
     "uk": {
         "platforms": [
             "linux",
             "linux-devedition",
             "linux64",
             "linux64-devedition",
             "macosx64",
             "macosx64-devedition",
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/controlcenter/info.svg
@@ -0,0 +1,4 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill="context-fill" fill-rule="evenodd" d="M8 1a7 7 0 1 1-7 7 7 7 0 0 1 7-7zm0 3a1 1 0 1 1-1 1 1 1 0 0 1 1-1zm0 3a1 1 0 0 1 1 1v3a1 1 0 0 1-2 0V8a1 1 0 0 1 1-1z"></path></svg>
--- a/browser/themes/shared/controlcenter/panel.inc.css
+++ b/browser/themes/shared/controlcenter/panel.inc.css
@@ -4,16 +4,20 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 %endif
 
 /* Hide all conditional elements by default. */
 :-moz-any([when-connection],[when-mixedcontent],[when-ciphers],[when-loginforms]) {
   display: none;
 }
 
+#identity-popup {
+  --identity-popup-width: 33rem;
+}
+
 /* This is used by screenshots tests to hide intermittently different
  * identity popup shadows (see bug 1425253). */
 #identity-popup.no-shadow {
   -moz-window-shadow: none;
 }
 
 /* Show the right elements for the right connection states. */
 #identity-popup[connection=not-secure] [when-connection~=not-secure],
@@ -63,18 +67,18 @@
   padding: 0;
   /* Set default fill for icons in the identity popup.
      Individual icons can override this. */
   fill: currentColor;
   fill-opacity: .6;
 }
 
 #identity-popup-mainView {
-  min-width: 33em;
-  max-width: 33em;
+  min-width: var(--identity-popup-width);
+  max-width: var(--identity-popup-width);
 }
 
 .identity-popup-section {
   border-top: 1px solid var(--panel-separator-color);
 }
 
 .identity-popup-security-content,
 #identity-popup-permissions-content,
@@ -140,17 +144,22 @@
 }
 
 .identity-popup-preferences-button > .toolbarbutton-text {
   display: none;
 }
 
 /* CONTENT */
 
+.identity-popup-footer,
+.tracking-protection-button,
+#identity-popup-trackersView-strict-info > label,
+.identity-popup-trackersView-list-item > label,
 #identity-popup-mainView-panel-header > label,
+#identity-popup-trackersView > .panel-header,
 #identity-popup-securityView > .panel-header,
 #identity-popup-breakageReportView > .panel-header,
 #identity-popup-content-blocking-report-breakage,
 #identity-popup-content-blocking-disabled-label,
 .identity-popup-content-blocking-category-label,
 .identity-popup-content-blocking-category-state-label,
 .identity-popup-content-blocking-category-add-blocking,
 .identity-popup-permission-label,
@@ -173,17 +182,17 @@
 
 #identity-popup-mainView-panel-header-span {
   display: inline-block;
   font-weight: 600;
   text-align: center;
   overflow-wrap: break-word;
   /* This is needed for the overflow-wrap to work correctly.
    * 33em is the panel width, panel-header has 1em padding on each side. */
-  max-width: calc(33rem - 2em);
+  max-width: calc(var(--identity-popup-width) - 2em);
 }
 
 #identity-popup-permissions-content > description,
 #identity-popup-content-blocking-content > description {
   color: var(--panel-disabled-color);
 }
 
 /* This element needs the pre-wrap because we add newlines to it in the code. */
@@ -196,17 +205,17 @@
   font-size: 150%;
 }
 
 #identity-popup-host {
   overflow-wrap: break-word;
   /* This is needed for the overflow-wrap to work correctly.
    * 1em + 2em + 24px is .identity-popup-security-content padding
    * 33em is the panel width */
-  max-width: calc(33rem - 3rem - 24px);
+  max-width: calc(var(--identity-popup-width) - 3rem - 24px);
 }
 
 .identity-popup-warning-gray {
   padding-inline-start: 24px;
   background: url(chrome://browser/skin/controlcenter/warning.svg) no-repeat 0 50%;
   fill: #808080;
   stroke: #fff;
   -moz-context-properties: fill, stroke;
@@ -377,16 +386,103 @@ description#identity-popup-content-verif
 #identity-popup-breakageReportView-collection-comments {
   height: 120px;
 }
 
 #identity-popup-content-blocking-content {
   background-image: url("chrome://browser/skin/controlcenter/tracking-protection.svg");
 }
 
+#identity-popup-content-blocking-category-tracking-protection {
+  /* Overwrite toolbarbutton styles */
+  margin: 0;
+  padding-inline-start: 0;
+}
+
+#identity-popup-content-blocking-category-tracking-protection:-moz-focusring,
+#identity-popup-content-blocking-category-tracking-protection:hover {
+  border-radius: 2px;
+  background-color: var(--arrowpanel-dimmed-further);
+}
+
+#identity-popup-content-blocking-category-tracking-protection:hover:active {
+  background-color: var(--arrowpanel-dimmed-even-further);
+}
+
+#identity-popup-content-blocking-category-tracking-protection::after {
+  content: url(chrome://browser/skin/back-12.svg);
+  -moz-context-properties: fill, fill-opacity;
+  transform: scaleX(-1) translateY(1px);
+  float: right;
+}
+
+#identity-popup-content-blocking-category-tracking-protection:-moz-locale-dir(rtl)::after {
+  transform: scaleX(1) translateY(1px);
+}
+
+/* This subview could get filled with a lot of trackers, set a maximum size
+ * and allow it to scroll vertically.*/
+#identity-popup-trackersView {
+  max-height: 600px;
+}
+
+#identity-popup-trackersView-list {
+  padding: 5px 20px;
+  -moz-box-flex: 1;
+  overflow: scroll;
+}
+
+.identity-popup-trackersView-list-item {
+  margin: 5px 0;
+  overflow: hidden;
+}
+
+.identity-popup-trackersView-list-item > label {
+  /* Limit to full width - container padding - icon width - icon margin */
+  max-width: calc(var(--identity-popup-width) - 40px - 16px - 10px);
+}
+
+.identity-popup-trackersView-list-item > image {
+  list-style-image: url(chrome://browser/skin/controlcenter/trackers-disabled.svg);
+  margin-inline-end: 10px;
+  -moz-context-properties: fill, fill-opacity;
+}
+
+.identity-popup-trackersView-list-item.allowed > image {
+  list-style-image: url(chrome://browser/skin/controlcenter/trackers.svg);
+}
+
+#identity-popup-trackersView-strict-info {
+  min-height: 40px;
+  /* Limit to full width - margin */
+  max-width: calc(var(--identity-popup-width) - 12px);
+  min-width: calc(var(--identity-popup-width) - 12px);
+  background-color: #45a1ff80;
+  margin: 6px;
+  text-align: center;
+  -moz-box-align: center;
+  -moz-box-pack: center;
+  padding: 5px 15px;
+  border-radius: 3px;
+  color: #002275;
+}
+
+#identity-popup-trackersView-strict-info > image {
+  list-style-image: url(chrome://browser/skin/controlcenter/info.svg);
+  -moz-context-properties: fill;
+  fill: currentColor;
+  margin-inline-end: 10px;
+}
+
+#identity-popup-trackersView-strict-info > label {
+  overflow-wrap: break-word;
+  /* Limit to full width - container margin - container padding - icon width - icon margin */
+  max-width: calc(var(--identity-popup-width) - 12px - 20px - 16px - 10px);
+}
+
 /* Disabled label */
 
 #identity-popup-content-blocking-disabled-label {
   padding: 2px 5px;
   border-radius: 3px;
   margin: 5px;
   display: none;
   color: #fff;
@@ -428,21 +524,30 @@ description#identity-popup-content-verif
 }
 
 /* Show the "detected"/"not detected" message depending on the content state. */
 #identity-popup-content-blocking-content:not([detected]) > #identity-popup-content-blocking-detected,
 #identity-popup-content-blocking-content[detected] > #identity-popup-content-blocking-not-detected {
   display: none;
 }
 
-#identity-popup-content-blocking-content[hasException] .identity-popup-content-blocking-category-state-label,
-.identity-popup-content-blocking-category:not(.blocked) .identity-popup-content-blocking-category-state-label {
+.identity-popup-content-blocking-category-state-label {
   display: none;
 }
 
+/* TODO: This will be cleaned up by bug 1501992 */
+/* Hide the state label unless we blocked something only for third party cookies */
+#identity-popup-content-blocking-content:not([hasException]) #identity-popup-content-blocking-category-3rdpartycookies.blocked .identity-popup-content-blocking-category-state-label,
+/* For trackers, either show a "blocked" or "allowed" label depending on the state. */
+#identity-popup-content-blocking-content:not([hasException]) #identity-popup-content-blocking-category-tracking-protection.blocked > #identity-popup-content-blocking-tracking-protection-label-blocked,
+#identity-popup-content-blocking-category-tracking-protection:not(.blocked) > #identity-popup-content-blocking-tracking-protection-label-allowed,
+#identity-popup-content-blocking-content[hasException] #identity-popup-content-blocking-tracking-protection-label-allowed {
+  display: -moz-box;
+}
+
 .identity-popup-content-blocking-category.blocked .identity-popup-content-blocking-category-add-blocking {
   display: none;
 }
 
 .tracking-protection-icon {
   list-style-image: url(chrome://browser/skin/controlcenter/trackers.svg);
 }
 
@@ -463,16 +568,17 @@ description#identity-popup-content-verif
 .tracking-protection-button {
   list-style-image: url(chrome://browser/skin/tracking-protection.svg);
   -moz-appearance: none;
   margin: 1em 0 0;
   display: none;
   height: 32px;
   background-color: var(--arrowpanel-dimmed);
   color: inherit;
+  margin-inline-end: 8px;
 }
 
 .tracking-protection-button:hover {
   background-color: var(--arrowpanel-dimmed-further);
 }
 
 .tracking-protection-button:hover:active {
   background-color: var(--arrowpanel-dimmed-even-further);
@@ -546,32 +652,41 @@ description#identity-popup-content-verif
   min-height: 24px;
 }
 
 #identity-popup-content-blocking-category-list,
 #identity-popup-permission-list {
   /* Offset the padding set on #identity-popup-permissions-content so that it
      shows up just below the section. The permission icons are 16px wide and
      should be right aligned with the section icon. */
-  margin-inline-start: calc(-1em - 16px);
+  margin-inline-start: calc(-1em - 24px);
 }
 
 .identity-popup-content-blocking-category,
 .identity-popup-permission-item {
   min-height: 24px;
 }
 
+.identity-popup-content-blocking-category {
+  padding-inline-end: 12px;
+}
+
+.identity-popup-permission-item {
+  padding-inline-end: 8px;
+}
+
 #identity-popup-permission-list:not(:empty) {
   margin-top: 5px;
 }
 
 .identity-popup-content-blocking-category-icon,
 .identity-popup-permission-icon {
   width: 16px;
   height: 16px;
+  margin-inline-start: 12px;
 }
 
 .identity-popup-permission-icon.in-use {
   -moz-context-properties: fill;
   fill: rgb(224, 41, 29);
   animation: 1.5s ease in-use-blink infinite;
 }
 
--- a/browser/themes/shared/jar.inc.mn
+++ b/browser/themes/shared/jar.inc.mn
@@ -23,16 +23,17 @@
   skin/classic/browser/addons/addon-install-installed.svg      (../shared/addons/addon-install-installed.svg)
   skin/classic/browser/addons/addon-install-warning.svg        (../shared/addons/addon-install-warning.svg)
   skin/classic/browser/controlcenter/3rdpartycookies.svg       (../shared/controlcenter/3rdpartycookies.svg)
   skin/classic/browser/controlcenter/3rdpartycookies-disabled.svg (../shared/controlcenter/3rdpartycookies-disabled.svg)
   skin/classic/browser/controlcenter/conn-not-secure.svg       (../shared/controlcenter/conn-not-secure.svg)
   skin/classic/browser/controlcenter/connection.svg            (../shared/controlcenter/connection.svg)
   skin/classic/browser/controlcenter/mcb-disabled.svg          (../shared/controlcenter/mcb-disabled.svg)
   skin/classic/browser/controlcenter/extension.svg             (../shared/controlcenter/extension.svg)
+  skin/classic/browser/controlcenter/info.svg                  (../shared/controlcenter/info.svg)
   skin/classic/browser/controlcenter/permissions.svg           (../shared/controlcenter/permissions.svg)
   skin/classic/browser/controlcenter/trackers.svg              (../shared/controlcenter/trackers.svg)
   skin/classic/browser/controlcenter/trackers-disabled.svg     (../shared/controlcenter/trackers-disabled.svg)
   skin/classic/browser/controlcenter/tracking-protection.svg   (../shared/controlcenter/tracking-protection.svg)
   skin/classic/browser/controlcenter/warning.svg               (../shared/controlcenter/warning.svg)
   skin/classic/browser/customizableui/empty-overflow-panel.png     (../shared/customizableui/empty-overflow-panel.png)
   skin/classic/browser/customizableui/empty-overflow-panel@2x.png  (../shared/customizableui/empty-overflow-panel@2x.png)
   skin/classic/browser/customizableui/density-compact.svg      (../shared/customizableui/density-compact.svg)
index 4c453a7a9c928a97ab4ee4350298d709927494ce..cdbda27802a0b77d2d09e78becd64ffa26529d6c
GIT binary patch
literal 229376
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
z_ML~$-;J-wzF+2UMjowoL3zoVO}9+0o%0-Qyzl(^BOk4(!$-Xh)I2<?V;w0y?ry^j
z!=p21g{w0ptVUM0Q8SX?ZDw@5K1*-(oH6_Sp>uSv(WQe~L$^Omd>|LO=w3Pg0XdGl
z)={Obyc$ni-0HXQXtn;>vSFv`K2xc*sbh5Y20pk*Oxk$cq~Jub@yUBbbJra^CMUyO
zu&CH%t(S9IyaDN<;hh=b8ms3Tgn1a}((cTh`^hu-?SrQMPZ*R4o#_$L>*_{s)VQ<r
z?0nkt(3W?Gm8o1~HSRsUYe8_H!jAFW6ZkVH=MM_qG0{1HfxpqfS8V%<(l0H~NDkS(
zo#<AwLjiyErPRyjn+wphulFO?Pn{m!Mt@m;J1n_NF}y!{PkHxv*Lml7$9eU<oxF{_
zYThDV8E+~tpErRwo|njr=8fPD;ra9W@|<|qJUmZ}r^u7wzURK=KH@fWFL6(C4{`T!
zw{X|+7Y6_U0000000000000000000000000000000000000000000000000000000
z000000000000000000000000000000000000002szfKw_j`iGG?`kPXY`WKGAxLc8
zvd>(QSYO386D4v?1&OuzyC$N<9b-XaRgf57l)xGZ5-XZ_8;TOU3<Qa)loEYWVyd1X
zu~a8TSCpXX2ojYW^R-2ZJS{<DAz4gQl)!2T5)00>tBVq^)C7sSZ*HiH64zA(iL!oP
z%A$m)k{~g&EKO0A;3x<Z(-rFFMTz}#f<(!wzOtf39~nVn3O+zul<=1lBqpcaloTay
zNC*-I@;c(8gf>o)$Ui?+ObRD1!%u|B@_X2lMZ~bYS$$c=^!)S$b`G1wBC!I}`=x7S
zY|O|{6X#rHA7anvP2(AG*Kj>@4(CK<X-!x@!8vn(=Fp7m87k?OY0J_G918m->nSTI
zn~_!2+s6z50000000000000000000000000000000000000000000000000000000
z0000000000000000000000000000000O(!fI1JVjomij~b3I%iETTbBLsTSj5#l&`
z*$8?vosmw*`}vstMV{$+@mw+%Ng`Mw7J^JQqv4d4R0fm8%*6Xqm~3i1HIBrl;C(pk
zL<W<}&Ln6fnrKN`HKiep2~--5gda{}v8nU~f*E3h=E$k(g)$lOR2l_8kV7R?$oQeD
zY-$R10*Os!&`o~(8jb&={rJDgGqS@)VG)bZE5XU@`Ow%DCLP_l;Y<#T&0o)#jWqn3
z!Un&+5A=Wg8ufmrLv(+pu+DF9i}r6{qm~tJI2JJ>;1NS%hiI`Vw0L_KHGxhcJ5Vx6
zDXBDyLmVST^G6G6{Kb*3{ug;_zrF3MzkQ7=KXdpg|4d<}-`*C*-@ZnL-@Zoq-@Zn<
z-@ZoK-@ZnfT(Wq6Fc!(ZMvy~f(DIUMViDn=pGd2T`S}nu5q0#5l$yd228YfjQR(;*
zR7yI*6fqXATle$2?ePI4@MKCXCjrmqZw5b<5DXAK;by3ZQ{p)2Hsb^62~;|T!bG={
zh`0z_YR|7J+H|}Xe=X=)mCj(Y6Em&(i*clrQYc=OG<$Th_HiVF9bzLXV^a`V(4l~p
z!D<@BC6bsl5}QQ#W(ybcbsUubf6+>*-+uH+{`NIW{Ps19|MoTFe)}55Qu%9?`K^(T
ze>Y>9m4)FQ<1OQj=N52>agm&7IeT+v<uG&nb9A#$XOGU-&w7wmmo+OVo$Jb5nROt?
zC}&>Qyqut%23}g$W9~@aschq%_U!Z7<G53@rex*w7Y6_U00000000000000000000
z00000000000000000000000000000000000000000000000000000000000000000
z000000Km_Z!l`1HV<Ig@KP^N*&BbvTteFKa6pP>qhKRo4?|{o|i2g=Bh5Yqjnm0wu
znwa8(v4{ad577~pRb?fT2o%cK1&z^ycn4ez7O^5&AZEgX245CxPsvE7GP^F^2(4r2
zhGSw80>Kfn7uK<$(&K0xGR2<7i6t{qNK`t@A(cv}Q|Sp`S2jQ^>;LvYpst6mR@YxV
zR~CzS5&9w?!i{!{C6OI^-f(+zCY_W*jr+O*I_Q$L&2WRUh#^5A(G@O8t($(*LQ85|
z;f7-o69OJF6qeLtQE2h@ENTM1YiSL%w7MRy4;IlNs39uC(y|fsWI7|Aj`#CXLkp;y
z<3g~A5y1e_6BbbC?^7pNV%NQ@f)-V_!$n~ci_iUolh^a1u_;VC+ONZfd$+THmCzcB
zeQ+6A#D(C5Ai^5fv77`Kg0lzW;gCVMw@=I<CB-IE5}ZgAoJlmntfUMQEtO15RY2>@
z>*GAJh$cZDQ5Dvg`)Zry&=RuW9ck(5=?<|ZdNKhmgAqO>+b3``(dRN~9qC-Mcs~C?
z&b>yELuAlul4@cR;h&#KtBLvf5Ht~W^of+3!Vm`k;N|Z`{0J%~onVR>3lAIJ&xegY
zK41i%OyRc-&*mRN{Eq$HnR06C;gmQI+8y`+dIFVBp)k<{jEJ}h4^n%6P0>+@w-TKq
z=?o@2G1HpA7)So0Lh+)c*`tfKk0TN65F1Gun}WcC4h5_XR?{FZk;J5t*d)3)Tey&~
zcbOF0RLSplndktt@8(RBKx>E_;|5?6J%SFRC9I)5gb|lW6|^jlMy1f%IJB4;f{T|y
zYzfwgrLZaZpp+DT1L<s9Cf=XQ5}ob*W}^=_zXxRbfyk1pj^R1;hH$s?Ht}+JbGc2t
z`@A#UWxNFLeQq#MgImD;$aUey^KSBv@>cVv@i@F_UI5RQr@(#9y~#b!UB@lwa=GKU
zzFa%58CRdbH~;_u00000000000000000000000000000000000000000000000000
z0000000000000000000000000000000000000000008)(Aca%KF2_V#ihf#%ewvGZ
znknE6u^hVSZ^@gA=9;MCY_P0E5+RyGC-WcZAD1^4<>9q(PMtMKbTTC)mCB@$jYRo|
z>Nq<ri%nuv<3g!)I+dQ_OClSHGWBI}n%L9?MjS~`G)Gqh=YS<=(n%>_m)8;HYRlnt
zv7!}diRNi4;_z4&g%;1ECeSHl4bg0MbT)^cOlPFiqvJ?wqS>nG^7&11awUG%7!^^j
zvI@=uOQNwUOgf1_VKCTAq8vpuCzg}oLU8s#JmM%!wt^@}9_=!I8(0)(8igq*nk$Qz
zr?BEksT2~MO^Qn<$cS>JRdH5WN*a~UUqK3mOy#dgN|Yt3i9^0={6GeSEFsDkN7t0X
zh)blhDJ(WlG*?U;N0gzaq)_Snw$L)8$yAnTXUOvRw`I-(46lWEkXOws;wAHj@Emz6
z+?U+*-0j@?+$`=`Zhx*Z7ngIFr^%hj_0MVKJ>VVVt>vZihVfjue{!#IYq^WKv0MwT
zbk3uk&Ad|n;s5{u00000000000000000000000000000000000000000000000000
z0000000000000000000000000000000000000000007{>NE_#ZofW+3jJAUOs<Pxu
z)ruSD-0=#NXtG2zva@RY{V~V<S#34Xen4V#qU)NCwiajxF`;r#{R7<l_FNt8lhR`I
zYq<(B=HfUE)~u&$rf9B7Pr1fuF21K+BQ)2rr}Y}3x%xfT)kAZ2d#bC0=4$s;R}0P6
z>?v0R%~kIyR}IZo?I~9U%~kGctCY}O#h&Uapt<rr<;tPCvOVR>pt;gL?Q<zKSF)$N
V5@@b?Pq{cWS4<1%BGX;&{{d`Z_=x}j
index 06c967ea7e9f3a56b1e5e8736f55a4402a41534a..fbdfa8c914a76beef0a1447cb45696e254af6bec
GIT binary patch
literal 294912
zc%1Cr2{@Du-#31Hw#X7G!r1qHERpQ{PNKqKF!p`lrX*CdM=O%8A}OSlrL2W)DJeV2
zS|MrqOZWYD-*?wDGuQh+j`uyD_xXLgoX(j!&(qg$>N_VhToz`Am_Rg_i;uq-Dv(Qt
zD3*wrnCJi(7ZDMW=;j+0(Qg!+Z)E>+nV1MaQ6h0ckL@v^i73O(i6Y5jnj#)Wgha@N
zFNd3l*AuWm0000000000000000000000000000000000000000000000000000000
z00000000000000000000000000000000000006-M13b5FW98r=#)<@@96ixq=s=Vv
zDiHP0_mmpuT56VBT$XC;hFV<zyp~H4<IH7hu4klXZo_4uWy57<tY>DW#bsj5rD0;M
zZK$VV$z`r(YN)26CCY^fM0-j7dM7O;K}o@?!9h%f@peXs2KaeyUTcpE3iR2!Z2!;W
z+e`g(jY^$@f|Zq<`0&;SP97+Xvv)v%sXrzd6^P#Uk8pa%np%grwr>0x;r9kyf+#6T
zX`ut;TgR~8I)*385$*YV6N-O3#@3C0979G(n~Z{W4>z&>uh>D3?r5jL-<y#C+cExe
z^Is&964D^0U}gK~XM+g{2txb+-iGYo4)g2QKaQdxC3I4mcpED>H*vDY)?N1d=HF{1
z|8}?i>jCiJacb%&=9Z#dD9O#Y&HD`{z4<2dbA9WIpVvtJyh`EcMTMUi6}B$^+(be4
z=R)r10{L?x|8t@6bD{Wif%>^{+*~Mb9ZK@oa_fka(p$@4A4hg;DYvymZY|}vmI_--
z#jPc3Yw5VPblM8`E8eesTkF!l*0+u<y>(>its_fs9a(zo$kJO!mfkwD%+_8qTYJfD
z?Ip9dm(12)$gNwETel*&Zbfe0iu`r!)-jP=$CTgNQsLKfYl2&!skG#;^`HAnZ>5!$
z+$6mf`5&uWv)<ZTW@~Git*vDxe{KD1!(Z3PY%OKCmU3ImpC4Ox>!ZqUeN@@4`$cYR
zU%9P)<s^Sy^K)M(w498rjGWZ(Rq5ZW|FVJX?^U_qtH|H0^1oLV{?{t<_io7FyCHw?
zhWx!7@}IjQf5$=oj)VLi2l+b=@^>8M?>NZcage{`Y_2N)UPb+0b^N{R^n2C$_bU4L
zs>}ac-TI60Yt`|09LJw=oaIoG&W>9XSCHKDd%no6zqitgLNX*2tUBEPRR#Y3w`wo-
zd!2N%s40K8_4ZITB2xSBME2i(HC;RYFF$ku000000000000000000000000000000
z00000000000000000000000000000000000000000093joz*9fA!DYa6aCK{nEhqx
zh&KPw(Gd~RF*3%{5;2}2Iu`lQKmXW@?&s}v|GOW2^PogT%D3)3+I&Y!OdJr13dA^>
zV!XXE-mdB>=bxKMN=ZpbicyeSk&)8!W4xU_gPhS~0YQ$=K3*t{cYwI>udT(=p(rn3
zPqes`j~CMQN?qd&>DLu38}UMN65ng=b_e30h^`UYZvNL0)NJlTbtQyS*fn*zCb%i)
z$fUw8PyJ_zijF>?Lfcou!)mRKdP$G3RyM3MpZv53lc-h~dyQS0kwo)JT)|^q8S+V{
z)5H{Aln>9x3pmtH<<ETd;d%1%IMcni<?_e1UUi!F4ZQ9t>`-o@c>he&cP{(s1ogGl
z7ecl%eor2{U^qm?zPIXW+R!|?I;;0Bz2b$%0ejYf;GE2O7e1YH^XeGN1WE>VE=+ck
z#`lTSQzx;*h^oSiJ;#HXIMYQKPmjfaiQ;3Y5blXyVp%@<tl#*!h+IuNePC@$>8Tc!
zE3a_HH!nx#?Ykb=*EFAP3XHRs)_?IPe#%OMV=o(rAGcfYePSYF$z#MwNkIx4;dtHn
zwe7^T<iw<sY-G%F0o%W{HX35@G>Fxqgl62b9x}O-5EGLfBc?Q(tnD6Va1_3?OK;UQ
z74fJg{U+zd%*HWx4{8-V-6qSLTm_RT1<c+Sa>r2Cb5!GvER*UVuDB$T>gHPqufB3m
zrstISu9uzB`sBS>!1N)u<4Wp8cG}XDVdO#=icDJdMlr6PF%A_c>6fg;MEFYt=}*52
z%&i)dSk3os=)5?CQV|kBU-UkXx?)S@_UN486L|qch0eAOOT@QIsfHu@=LGX4ZG9hd
z+TCV9$emdJVKpz{Ox^Hm-55$SOIx_tHmZDbV4L0zD}lkr^F;K!9A5jXBFO6^UhCwt
zrANkhp1yqGlUZIAxt8?uzO@I|&-9*gbem8f(Rlp{^TQcg%m38$h`YL&E$`<9N$lOf
z7+eS6*B=a`!jDeM7(`HI)b)~aFC_mQ95!LW&TPorQIS`d>^$ObH+yYEgygG>!3M0C
zLcFUV7=~TArjohKUHZYK!}<1xJ!AZzg<g3!IykOaPW2+0in{{*)-vc<Zd_cBuFh&X
z@mZ|ph#!j6j;Kh?JDNx2`Z)8f%-zX0r?t@CVT&3$-}bWZNWZe<Q`}n?A=T5-hb0R2
zb&4P9UO7BdL!0&2xpxrBja-p$c%8#ACF}E?@4W<Rk^QgSWwhbdZN!ZCd?$RdeW{4>
zoupp*x0+q0X&<>9mkBI<BJORy(&EZ7VA5t97(4z#t55rDLfOsFn6OjIE+1u|ymk0a
zv5<YCC$q`G+$Ys;<Nmfb?tR8BE!ze~qy~zEPBdRzaj#1Geu!(aH)Q45KAThRuMHCC
z+w2(&O%6^TX1L@gH6^G2>QQCDU*}dF3J<kb!RA$^7Ei^0`9AI)^;G3oZp_<PAv%2h
z*^k(!P*f*qN4ie?erF2jFOQh=On)Jl>O?X4g0D&Nw8qka!t5EoWZQ!eUK%{`*Ik+L
z^fLcSI@w$8CUn_?CXu${&V1K;pI2OqMFpu`xZOGJg*K;;47NGfvxl6hlsg_=Ht6EL
z&PR_TyM(Mbao}uE;N|JJ_VS{F-8DlY?8k4v`0L!%pVWhnY26vvrzYZ~Ia8;#dg}7I
zU%5%c-_`VeuR4Dm^KPl@n?s*_jjiNT;(mpBiHqy~>&Iid1a^<=(350j-P@T%vnc1S
zX~<$`7V)9T=%~GMU;C*S3PW9sv1dq}26l(%i@xML#C=dlIOC4pu=z`YLGJyO0w=3v
zyXLfp-b~XxRS@5iyYMOPp}cyq0LRI=xs`X%uf)bMTc)8$+b;ceZrW?jD*}kuUqas0
z@z&AO8-|}3pZ}G+m0x3V;POQw@nas?=)qk_?2h#bI*ZAaHH11hRZ><JH4+DAuhh>}
z^`#iwZzS~Up-m*jXLI<1CnfKExqke?yHx(AFO9}CZ$h;rUfyVtj8fvZBYwM#M!rf+
zw-VMQ#oi!ppqZq9{ME_faF4`$`Sc=}(iM6=2ZPt=4!G?9^zPEdd{Zwn7jiS=&BqGj
z|LM)15=@FE{`qJw_{XF9asB^O59jw9Wq&=Kqk}QtfoOj(v@-@3h~9cI$G(=Lk)-<h
zXimXIVrB8q3zSSG8fsE3lFUCZY-8GP<P+o_xcTtTbr6FNk>Zi${&_t$({3Z5aEzxX
zic5@3>mZji+A+wLD-azJ$hEmMhvc508__T^TcDkS{4s%HTw30)7;iM%fAjP}3MnbO
zbq>KKwz;R}=02NOatUsJ6#t-rzz`q*K({cV&BI7|qrA{6=wPv*hZS=|Nr_4dQ&9cm
z9|ILJ3x|^%%HI<ei1I!V`0F75dg76;fBE^Y?X)GC9)m@6<yjLdX5_v9dg5_cFY(AF
zq8(=#^471MEE#`(ZRqHix#fgc){mT-+|G(l_>2D7k)qHLNYbVxtfNvV=svIdy4R`8
zR){%-xNdOQ3)JCtIgkF{RvGuw(^P)QB#NeIny7pG&D4!qHSdd;_PJkI=wx%_&f+}j
zr_bVqQKbrIe{1BC$2Z&A@=##A!r9O(o^M0Tq%`LRAKQfqDL5773+rZ6BMa#K`@`Dq
z@xA@rl8%)4=60^AA;{3~T$^jt?C|cgr2MCyvPs048al!J7qq*xXnkbt#d0rPa8|W_
zRI4{rT7rG&Fcx)EmuA>%ID_@eF4HUbtqi-c1&Yz9iW!q+>pp~{8xki2Ol}L2|JNrT
z`9?dt+D6Sw>e4pS%HQ5{xNwPb@E<20O?!_nOzw4LNLtYR#=b)J1hHEFbcWoAZMx@&
zZ^Nl=)!!8Fc4yw~VDgor&^hHade8ollMrd0!;ZQ|MU98OtY-H};>~YMQyjkjbt%~I
zn=NC{`15jxiy>W8OrN9G%tp%P%H<ido>R5uP_kF>?NoU@FWzyatetO{LF)8<E`!$Y
z(aN|mgk^o`Gc}%17TNvJxU=MfBZnqTEE`$e7X=t*E<4;Pe%<$7^pf%eX)R8D$KJ<f
zdfa~R9iz*xy%Eai(TqG#v?m}_Hvdq`22le|kmY)BMs3U$K5S~WLT`b3Zah7|PHC^q
zuICA6rsuD$McPPJ?RbI`e(}Sp>zwMuQJq#x<!#g1QOX>DUCj4c3fW!LZI!qO-;!0U
zE^%ZU(i;9MW*wDmowvh&f%9f<I`a$-W9^PZd>u=BVy-o!eFCzmGW<We@L6B?sq@m1
ze5}8}=fVq_gwCjlXT3Jmw5b9I_&T=L>|cNTqb%s?US_wGbKOfG{NbJZ1=>&R2sEt<
zeO#nHeX&HZ=HSjmt`4sdj<X*`0z^Ly#)ZZe=hU|C9pI@Q7$TeV+V4~@_Sd=TD!Q;b
z^>+?A-e~9!!*WL0Fr8@lmHRB8XMtQ*JCWQQGMCEw>xuDek{6n)wx6Hl8+q)+hr04Q
zS~Y7mkABBNqEdU6v4HM|^7j)dmC4guds5%4U2)80&_5l-%*GJw6s?>{J*Uh2TESaz
zIL-#ieKh~n4z^nb6#^e}Liw}SUuiOJFbiP>?QhMS+?2%lvya^84MOIe*=ZIP5p-Tp
z<*#$MXUiVl<HzT~p{++Vj97EX8MycES8j_(c8{7k0v_dR90<wWSZ}}R#(uCT{bBqk
zj!OqsRx-O1SQ3~oR7O4i;qI(Eu+zyT$85*c&i=<bS&br7o!M8<JuuG<YN&c}D`Rb;
zT8!UL@x#MC!upnBVF}@@0YNm4l9G&AmwAa*_Vv`A{=By<(xwV%HEL@+2kc806NrRS
z><8$D#gpHMP5*W70Dr{U8{%{H{@o2Sh~%{7qUDqQzjA*JS!#KH&@$^lzne$7VaFVj
zNp0k$;k@Qt{9Fj5+Y$1&<3zg_lNAq-p!dq3iQL<CF)O}Q;UE{?Az|_U_%z>#?(5@?
zk893;KeoDC>mubI8HO)5QCIR<cB?BssPXJqu=~bF?EX-5s>ZUZYEQZvx#A_IVt4D?
zQqyi+JsdSxybtYtFm^oiD|PN)=dQcip(F9>sp?05yVAxX7YC{BNYt;~toI6^ykgv2
zMfRvy&@DzJ`@zk`5ea36J|>Dc?}=U?YxI;a7f`32qdc6intRqno$fQMwkW*-B~zu4
zS+kw?greW$o2!?XXS%1@8K#jXLA^(XEpKuh$VfiW+Hz0E;iC~!zl`FFKmCx)e&Wou
zyulmIJvHy0Z-@{zDn}BvRAF}NGp4?`q-5K(b>i_qz1h!(ll(gIVEN<31MB>M>cr#r
z)cap29=@(VPN=Q(4R<L)N&a6C{G66(FJB*jlz$kP2HHOm<AQPe`F}4}3kr1e`FXk^
zwM&xa*J%d*KTk9Mah@Q>D~b4ZZn4MI-^T^xiRRJ?!Z@RyxlDWmF<zK(R3OI3J2K+I
z{<ti@r3XbZV%lz_ZgSUeiTvw>e{F(Uv1T&MfZ8|jsssw;4i#mlm$MUc`gi75+^ju$
zBN|%xibn=H>nbi%T=3o<|G{Id+&0L=-Pw;a{F-f%*2y*Ye%^~D(a$u#?!|N~a0#Vf
z=W5cM7`kWJ=;28>zw;RVa`a8i?uuR#rBlLc3q`&o^xMyTbD<Hr#hX?TbuR%GmQrDz
zqSCYP$w-YZXR&;5g2ti%%b-=Yi*0E%Ns5G0#Ew`B-ly!I=Wb@94p)z5G1do3U8PMO
zzSF+;^(F^jFV*5bR=Og$(aH!3gb!x?PEsa{V}XBw@6z>DN@HXPJ1?2&BbHMVlgnpw
zSfy$zzw7aQ@i%)c)lYik)h=qe`7{6Zga1QyikfG996C{6;Yz$|y)W1DTNVHD;ICND
zbJWy$XJ)aa8o9u8)_xsk@8#gJgai6tp1AXyw6~=v40&4UrZC&_R%^K&j@@24Ak^-7
z$xH_E9W``L>ne+vrgycTna1?NFln8cMvV{d=T4$3T+cf!qo!92PB>SqjQcCg)O|ly
zKwV*6*ViFlr;MhI4DVU?D5|)x&SoGgRlp^+;`v!4@M_I=mZTN`G6TuHx{sIThQ58}
zN!iAiXFcC9=-p}k<{h!DQt>|es3&(=1O*ab2i(~{Ylr5sf8XHKwJKuRPfQp0?m5Fi
z2+6Bh9?58nt4^bKaXZR~pT^_~yg$F%R&1PRSmuDZUx_Zojt<d1Q#BcA{~>xNhHUWz
z>f<LNlc0}(U5<Rz5se?uY*>8YAVJKrpL<ds9`x*2Ig)g=+Q$=JITZS>?<$w@5oeVP
z{snwadC62W$35~Z%Tvqc$^@hD`5L=U?M~Q_Fbrgl4?DO2+BZ4#-0wj~KK1BtW29<D
zd<-$Sgi4<_w=xubx?8vRp7g+l_hUlanrU~{6>{x5S1fXk_L=*`p5FIoCZ01*vF1MU
zVhLkh3g1^+$v&r@c0D)R(&w*p=XgdJIQUL`B)MT<igmBj>dd$({L0O8HSfG<@<Asb
zPHRf1XSd|Lo1{cW)VlKJ9tu=PiV4^K;8oidS=D`Rb@#e`2?yU-e}O23n40Q%>qFk5
zXABdLshx{e-I(5EUkzUGm2e|Fpy9&nbs@vtz+_3R&&c6LEfsl``6$*vJoun+7YmEg
zhPR#E7&o$PU3UJ{WY~_C!Ksds7iIf*$^Uik4-r=PNec!ty@tGNNP|Cpv!C2A^DB49
zk)7#rm-l|3Fz8G#UXx_WFkq`VN$d1-yIy%e=3Lt~ZhEqI|KWt!6&50qG}jhWDk)#E
zwXnWL@SWf+{;2(>n@Nf&ykZZl$c&NSz9BY`tefovvG-L_MMu=m8}3|Tx|-}^aQu+$
zo(IcD0Z&%hlV<OBUR5iN>d5b|F8Lm_Xr&wy=|!EP`DO91b3c9bkk3<gcU!1C%`*#6
z7EOoOcF143c}EVV-qaXBeWW%|>bA4g;)q8_XRUE*UU1Y*aEPOg8e3GxyvRopsS7DH
zg}wHBIcw@3N@(x)-1p!uSMqkB**8)R3vYgWDk&0bPnvuuxWj(Qf94YUZSuQ!4h%gX
z<qG$$?prM-HF)`yx^d4yex`AuY_pWZ7r6uCow?i6QIDb~S*pSw>}@c5{MWe$%Isz`
z9`Cb!?oNF4rLf)Q(lU>{U%B1iGSif)?$C9tJgy$WJAsH`@FTCCH9R+Mgo&>BH2QP{
z+4ty$X=QhE{TPGy6RD6|hcK+f_8o)dubVFx`IPeVxtQ&6G*^7lH=Dend6Utg@&291
z3o>lEyq$gMlec}XTVB1qz(k)>s{eW$n&wlC`1X^J-@novZj;Z`qw7KLvfd-pd(+F|
zSjpCd|Nr!6CnDPR=zsT-8vp<R0RLA|Z@!VnOcBL=j#=D%2LJ#7000000000000000
z00000000000000000000000000000000000000000000000000000000000000000
z00000000000002|Ka7%;j9B?zqb%vx%I&H5B-=>IsFZKrc|^T=D{0IJqL{BSYn$%?
z00000000000000000000000000000000000000000000000000000000000000000
z0000000000000000000000000000000D%7?$ViEZNq;U#NJ)vPsAKwwNTaogqP1ci
zqa*)sX9EBL0000000000000000000000000000000000000000000000000000000
z00000000000000000000000000000000000004mh=8&ZRRFYCs5|UyRq*i34wEP%v
zC(j^fv{*opqqC0}3gaCh?u+sE#(2AmqeD?%zMg1tCm%1YGud%{VoB_4DH=(tpL<d;
zkyu&$^8zIkiH4dKizM^U3)`5s8~Frz2W}pU>mUXlBE=)g{quTirrkz9;TTU(6qgv6
z)<G_3v}2GfS0FkdkZW^i4#_<~H=<!;wm>@t`C|gZxU{@oG2Upj|7H{^q@?UtUM8{4
zJuNr)*}RfVaPy=12L%L%`1l99g$ZpQM#3BAg;qfai~T&Tm=j7$R8p9NN;qCOer-E3
z6)_8klN-w46BUT^J`nipApiPFlKPQhorxvgr35AUe|<DgOSG4-k3Y&kj7tOUABb_m
zIH3a3TxvmqZazOVO6`(l`SrQc|MPSEM>r{7NyM*DXpgDCj|;{V&7~8BaYj3HnfL}`
zyfERYK#Y$!$>u#GO6Xlj9kWP88uO4SrYB}{^Bn*H0000000000000000000000000
z00000000000000000000000000000000000000000000000000000000000000000
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
index b79fb90c8518548dfb6fdf9d1c3a4a8ab8343058..01c1f53efb1b5068c0ca7282b5b6831f319d6c0c
GIT binary patch
literal 2448
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
Az5oCK
--- a/build/pgo/server-locations.txt
+++ b/build/pgo/server-locations.txt
@@ -170,16 +170,17 @@ https://sub.sectest1.example.org:443
 #
 http://malware.example.com:80
 http://unwanted.example.com:80
 http://tracking.example.com:80
 http://not-tracking.example.com:80
 http://tracking.example.org:80
 http://another-tracking.example.net:80
 http://itisatracker.org:80
+https://itisatracker.org:443
 http://trackertest.org:80
 
 https://malware.example.com:443
 https://unwanted.example.com:443
 https://tracking.example.com:443
 https://not-tracking.example.com:443
 https://tracking.example.org:443
 https://another-tracking.example.net:443
--- a/js/src/builtin/intl/CommonFunctions.js
+++ b/js/src/builtin/intl/CommonFunctions.js
@@ -1,30 +1,41 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* Portions Copyright Norbert Lindenberg 2011-2012. */
 
+#ifdef DEBUG
+#define assertIsValidAndCanonicalLanguageTag(locale, desc) \
+    do { \
+        let localeObj = parseLanguageTag(locale); \
+        assert(localeObj !== null, \
+               `${desc} is a structurally valid language tag`); \
+        assert(CanonicalizeLanguageTagFromObject(localeObj) === locale, \
+               `${desc} is a canonicalized language tag`); \
+    } while (false)
+#else
+#define assertIsValidAndCanonicalLanguageTag(locale, desc) ; // Elided assertion.
+#endif
+
 /**
  * Returns the start index of a "Unicode locale extension sequence", which the
  * specification defines as: "any substring of a language tag that starts with
  * a separator '-' and the singleton 'u' and includes the maximum sequence of
  * following non-singleton subtags and their preceding '-' separators."
  *
  * Alternatively, this may be defined as: the components of a language tag that
  * match the extension production in RFC 5646, where the singleton component is
  * "u".
  *
  * Spec: ECMAScript Internationalization API Specification, 6.2.1.
  */
 function startOfUnicodeExtensions(locale) {
     assert(typeof locale === "string", "locale is a string");
-    assert(IsStructurallyValidLanguageTag(locale), "locale is a language tag");
-    assert(CanonicalizeLanguageTag(locale) === locale, "locale is a canonicalized language tag");
 
     #define HYPHEN 0x2D
     assert(std_String_fromCharCode(HYPHEN) === "-",
            "code unit constant should match the expected character");
 
     // A wholly-privateuse or grandfathered locale has no extension sequences.
     if (callFunction(std_String_charCodeAt, locale, 1) === HYPHEN) {
         assert(locale[0] === "x" || locale[0] === "i",
@@ -48,18 +59,16 @@ function startOfUnicodeExtensions(locale
     return start;
 }
 
 /**
  * Returns the end index of a Unicode locale extension sequence.
  */
 function endOfUnicodeExtensions(locale, start) {
     assert(typeof locale === "string", "locale is a string");
-    assert(IsStructurallyValidLanguageTag(locale), "locale is a language tag");
-    assert(CanonicalizeLanguageTag(locale) === locale, "locale is a canonicalized language tag");
     assert(0 <= start && start < locale.length, "start is an index into locale");
     assert(Substring(locale, start, 3) === "-u-", "start points to Unicode extension sequence");
 
     #define HYPHEN 0x2D
     assert(std_String_fromCharCode(HYPHEN) === "-",
            "code unit constant should match the expected character");
 
     // Search for the start of the next singleton or privateuse subtag.
@@ -86,38 +95,41 @@ function endOfUnicodeExtensions(locale, 
     // sequence extends until the end of the string.
     return locale.length;
 }
 
 /**
  * Removes Unicode locale extension sequences from the given language tag.
  */
 function removeUnicodeExtensions(locale) {
+    assertIsValidAndCanonicalLanguageTag(locale, "locale with possible Unicode extension");
+
     var start = startOfUnicodeExtensions(locale);
     if (start < 0)
         return locale;
 
     var end = endOfUnicodeExtensions(locale, start);
 
     var left = Substring(locale, 0, start);
     var right = Substring(locale, end, locale.length - end);
     var combined = left + right;
 
-    assert(IsStructurallyValidLanguageTag(combined),
-           "recombination produced an invalid language tag");
+    assertIsValidAndCanonicalLanguageTag(combined, "the recombined locale");
     assert(startOfUnicodeExtensions(combined) < 0,
            "recombination failed to remove all Unicode locale extension sequences");
 
     return combined;
 }
 
 /**
  * Returns Unicode locale extension sequences from the given language tag.
  */
 function getUnicodeExtensions(locale) {
+    assertIsValidAndCanonicalLanguageTag(locale, "locale with Unicode extension");
+
     var start = startOfUnicodeExtensions(locale);
     assert(start >= 0, "start of Unicode extension sequence not found");
     var end = endOfUnicodeExtensions(locale, start);
 
     return Substring(locale, start, end - start);
 }
 
 /* eslint-disable complexity */
@@ -787,18 +799,17 @@ function DefaultLocaleIgnoringAvailableL
         if (hasOwn(candidate, oldStyleLanguageTagMappings))
             candidate = oldStyleLanguageTagMappings[candidate];
     }
 
     // Cache the candidate locale until the runtime default locale changes.
     localeCandidateCache.candidateDefaultLocale = candidate;
     localeCandidateCache.runtimeDefaultLocale = runtimeDefaultLocale;
 
-    assert(IsStructurallyValidLanguageTag(candidate),
-           "the candidate must be structurally valid");
+    assertIsValidAndCanonicalLanguageTag(candidate, "the candidate locale");
     assert(startOfUnicodeExtensions(candidate) < 0,
            "the candidate must not contain a Unicode extension sequence");
 
     return candidate;
 }
 
 /**
  * Returns the BCP 47 language tag for the host environment's current locale.
@@ -825,20 +836,17 @@ function DefaultLocale() {
                                                         dateTimeFormatInternalProperties),
                                            candidate))
     {
         locale = candidate;
     } else {
         locale = lastDitchLocale();
     }
 
-    assert(IsStructurallyValidLanguageTag(locale),
-           "the computed default locale must be structurally valid");
-    assert(locale === CanonicalizeLanguageTag(locale),
-           "the computed default locale must be canonical");
+    assertIsValidAndCanonicalLanguageTag(locale, "the computed default locale");
     assert(startOfUnicodeExtensions(locale) < 0,
            "the computed default locale must not contain a Unicode extension sequence");
 
     localeCache.defaultLocale = locale;
     localeCache.runtimeDefaultLocale = runtimeDefaultLocale;
 
     return locale;
 }
@@ -919,18 +927,17 @@ function CanonicalizeLocaleList(locales)
         k++;
     }
 
     // Step 8.
     return seen;
 }
 
 function BestAvailableLocaleHelper(availableLocales, locale, considerDefaultLocale) {
-    assert(IsStructurallyValidLanguageTag(locale), "invalid BestAvailableLocale locale structure");
-    assert(locale === CanonicalizeLanguageTag(locale), "non-canonical BestAvailableLocale locale");
+    assertIsValidAndCanonicalLanguageTag(locale, "BestAvailableLocale locale");
     assert(startOfUnicodeExtensions(locale) < 0, "locale must contain no Unicode extensions");
 
     // In the spec, [[availableLocales]] is formally a list of all available
     // locales.  But in our implementation, it's an *incomplete* list, not
     // necessarily including the default locale (and all locales implied by it,
     // e.g. "de" implied by "de-CH"), if that locale isn't in every
     // [[availableLocales]] list (because that locale is supported through
     // fallback, e.g. "de-CH" supported through "de").
@@ -1244,22 +1251,19 @@ function ResolveLocale(availableLocales,
         if (privateIndex === -1) {
             foundLocale += supportedExtension;
         } else {
             var preExtension = callFunction(String_substring, foundLocale, 0, privateIndex);
             var postExtension = callFunction(String_substring, foundLocale, privateIndex);
             foundLocale = preExtension + supportedExtension + postExtension;
         }
 
-        // Step 9.d.
-        assert(IsStructurallyValidLanguageTag(foundLocale), "invalid locale after concatenation");
-
-        // Step 9.e (Not required in this implementation, because we don't
-        // canonicalize Unicode extension subtags).
-        assert(foundLocale === CanonicalizeLanguageTag(foundLocale), "same locale with extension");
+        // Steps 9.d-e (Step 9.e is not required in this implementation,
+        // because we don't canonicalize Unicode extension subtags).
+        assertIsValidAndCanonicalLanguageTag(foundLocale, "locale after concatenation");
     }
 
     // Step 10.
     result.locale = foundLocale;
 
     // Step 11.
     return result;
 }
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -2459,23 +2459,31 @@ RelocateArena(Arena* arena, SliceBudget&
         TenuredCell* dest = Forwarded(src);
         MOZ_ASSERT(src->isMarkedBlack() == dest->isMarkedBlack());
         MOZ_ASSERT(src->isMarkedGray() == dest->isMarkedGray());
     }
 #endif
 }
 
 static inline bool
+CanProtectArenas()
+{
+    // On some systems the page size is larger than the size of an arena so we
+    // can't change the mapping permissions per arena.
+    return SystemPageSize() <= ArenaSize;
+}
+
+static inline bool
 ShouldProtectRelocatedArenas(JS::gcreason::Reason reason)
 {
     // For zeal mode collections we don't release the relocated arenas
     // immediately. Instead we protect them and keep them around until the next
     // collection so we can catch any stray accesses to them.
 #ifdef DEBUG
-    return reason == JS::gcreason::DEBUG_GC;
+    return reason == JS::gcreason::DEBUG_GC && CanProtectArenas();
 #else
     return false;
 #endif
 }
 
 /*
  * Relocate all arenas identified by pickArenasToRelocate: for each arena,
  * relocate each cell within it, then add it to a list of relocated arenas.
@@ -2973,40 +2981,39 @@ GCRuntime::updateCellPointers(Zone* zone
 //   - Updating a typed object makes use of its type descriptor object
 //
 // This means we require at least three phases for update:
 //
 //  1) shapes
 //  2) typed object type descriptor objects
 //  3) all other objects
 //
-// Also, JSScripts and LazyScripts can have pointers to each other. Each can be
-// updated safely without requiring the referent to be up-to-date, but TSAN can
-// warn about data races when calling IsForwarded() on the new location of a
-// cell that is being updated in parallel. To avoid this, we update these in
-// separate phases.
+// Also, there can be data races calling IsForwarded() on the new location of a
+// cell that is being updated in parallel on another thread. This can be avoided
+// by updating some kinds of cells in different phases. This is done for JSScripts
+// and LazyScripts, and JSScripts and Scopes.
 //
 // Since we want to minimize the number of phases, arrange kinds into three
 // arbitrary phases.
 
 static const AllocKinds UpdatePhaseOne {
     AllocKind::SCRIPT,
     AllocKind::BASE_SHAPE,
     AllocKind::SHAPE,
     AllocKind::ACCESSOR_SHAPE,
     AllocKind::OBJECT_GROUP,
     AllocKind::STRING,
-    AllocKind::JITCODE,
-    AllocKind::SCOPE
+    AllocKind::JITCODE
 };
 
 // UpdatePhaseTwo is typed object descriptor objects.
 
 static const AllocKinds UpdatePhaseThree {
     AllocKind::LAZY_SCRIPT,
+    AllocKind::SCOPE,
     AllocKind::FUNCTION,
     AllocKind::FUNCTION_EXTENDED,
     AllocKind::OBJECT0,
     AllocKind::OBJECT0_BACKGROUND,
     AllocKind::OBJECT2,
     AllocKind::OBJECT2_BACKGROUND,
     AllocKind::OBJECT4,
     AllocKind::OBJECT4_BACKGROUND,
--- a/taskcluster/ci/beetmover-repackage/kind.yml
+++ b/taskcluster/ci/beetmover-repackage/kind.yml
@@ -19,22 +19,20 @@ kind-dependencies:
     - repackage-signing
     - nightly-l10n
     - nightly-l10n-signing
     - repackage-l10n
     - repackage-signing-l10n
     - partials
     - partials-signing
     - repackage-signing-msi
-    - mar-signing
-    - mar-signing-l10n
 
 primary-dependency:
-    - repackage
-    - repackage-l10n
+    - repackage-signing-l10n
+    - repackage-signing
 
 only-for-build-platforms:
     - linux-nightly/opt
     - linux64-nightly/opt
     - macosx64-nightly/opt
     - win32-nightly/opt
     - win64-nightly/opt
     - linux-devedition-nightly/opt
--- a/taskcluster/ci/config.yml
+++ b/taskcluster/ci/config.yml
@@ -72,17 +72,16 @@ treeherder:
         'TW64': 'Toolchain builds for Windows 64-bits'
         'WMC32': 'MinGW-Clang builds for Windows 32-bits'
         'WMC64': 'MinGW-Clang builds for Windows 64-bits'
         'Searchfox': 'Searchfox builds'
         'SM': 'Spidermonkey builds'
         'pub': 'APK publishing'
         'p': 'Partial generation'
         'ps': 'Partials signing'
-        'ms': 'Complete MAR signing'
         'Rel': 'Release promotion'
         'Snap': 'Snap image generation'
         'langpack': 'Langpack sigatures and uploads'
         'TPS': 'Sync tests'
         'UV': 'Update verify'
         'pipfu': 'pipfile update'
 
 index:
deleted file mode 100644
--- a/taskcluster/ci/mar-signing-l10n/kind.yml
+++ /dev/null
@@ -1,33 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-loader: taskgraph.loader.single_dep:loader
-
-transforms:
-    - taskgraph.transforms.name_sanity:transforms
-    - taskgraph.transforms.mar_signing:transforms
-    - taskgraph.transforms.task:transforms
-
-kind-dependencies:
-    - repackage-l10n
-
-only-for-build-platforms:
-    - linux-nightly/opt
-    - linux-devedition-nightly/opt
-    - linux64-nightly/opt
-    - linux64-devedition-nightly/opt
-    - linux64-asan-reporter-nightly/opt
-    - macosx64-nightly/opt
-    - macosx64-devedition-nightly/opt
-    - win32-nightly/opt
-    - win32-devedition-nightly/opt
-    - win64-nightly/opt
-    - win64-devedition-nightly/opt
-
-job-template:
-    shipping-phase: promote
-    treeherder-group: ms
-    description-suffix: 'mar signing'
-    required_signoffs:
-        - mar-signing
deleted file mode 100644
--- a/taskcluster/ci/mar-signing/kind.yml
+++ /dev/null
@@ -1,35 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-loader: taskgraph.loader.single_dep:loader
-
-transforms:
-    - taskgraph.transforms.name_sanity:transforms
-    - taskgraph.transforms.mar_signing:transforms
-    - taskgraph.transforms.task:transforms
-
-kind-dependencies:
-    - repackage
-
-only-for-build-platforms:
-    - linux-nightly/opt
-    - linux-devedition-nightly/opt
-    - linux64-nightly/opt
-    - linux64-devedition-nightly/opt
-    - linux64-asan-reporter-nightly/opt
-    - macosx64-nightly/opt
-    - macosx64-devedition-nightly/opt
-    - win32-nightly/opt
-    - win32-devedition-nightly/opt
-    - win64-nightly/opt
-    - win64-devedition-nightly/opt
-    - linux64-asan-reporter-nightly/opt
-    - win64-asan-reporter-nightly/opt
-
-job-template:
-    shipping-phase: promote
-    treeherder-group: ms
-    description-suffix: 'mar signing'
-    required_signoffs:
-        - mar-signing
--- a/taskcluster/ci/partials-signing/kind.yml
+++ b/taskcluster/ci/partials-signing/kind.yml
@@ -1,20 +1,16 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 loader: taskgraph.loader.single_dep:loader
 
 transforms:
   - taskgraph.transforms.name_sanity:transforms
-  - taskgraph.transforms.mar_signing:transforms
+  - taskgraph.transforms.partials_signing:transforms
   - taskgraph.transforms.task:transforms
 
 kind-dependencies:
   - partials
 
 job-template:
   shipping-phase: promote
-  treeherder-group: ps
-  description-suffix: 'partial signing'
-  required_signoffs:
-    - mar-signing
--- a/taskcluster/ci/partials/kind.yml
+++ b/taskcluster/ci/partials/kind.yml
@@ -5,18 +5,18 @@
 loader: taskgraph.loader.single_dep:loader
 
 transforms:
    - taskgraph.transforms.name_sanity:transforms
    - taskgraph.transforms.partials:transforms
    - taskgraph.transforms.task:transforms
 
 kind-dependencies:
-   - repackage
-   - repackage-l10n
+   - repackage-signing
+   - repackage-signing-l10n
 
 only-for-attributes:
    - nightly
 
 only-for-build-platforms:
    - macosx64-nightly/opt
    - macosx64-devedition-nightly/opt
    - win32-nightly/opt
--- a/taskcluster/ci/repackage-signing-l10n/kind.yml
+++ b/taskcluster/ci/repackage-signing-l10n/kind.yml
@@ -9,15 +9,22 @@ transforms:
    - taskgraph.transforms.repackage_signing:transforms
    - taskgraph.transforms.repackage_routes:transforms
    - taskgraph.transforms.task:transforms
 
 kind-dependencies:
    - repackage-l10n
 
 only-for-build-platforms:
+   - linux-nightly/opt
+   - linux-devedition-nightly/opt
+   - linux64-nightly/opt
+   - linux64-devedition-nightly/opt
+   - linux64-asan-reporter-nightly/opt
+   - macosx64-nightly/opt
+   - macosx64-devedition-nightly/opt
    - win32-nightly/opt
    - win32-devedition-nightly/opt
    - win32/opt
    - win64-nightly/opt
    - win64-devedition-nightly/opt
    - win64/opt
    - win64-asan-reporter-nightly/opt
--- a/taskcluster/ci/repackage-signing/kind.yml
+++ b/taskcluster/ci/repackage-signing/kind.yml
@@ -9,15 +9,22 @@ transforms:
    - taskgraph.transforms.repackage_signing:transforms
    - taskgraph.transforms.repackage_routes:transforms
    - taskgraph.transforms.task:transforms
 
 kind-dependencies:
    - repackage
 
 only-for-build-platforms:
+   - linux-nightly/opt
+   - linux-devedition-nightly/opt
+   - linux64-nightly/opt
+   - linux64-devedition-nightly/opt
+   - linux64-asan-reporter-nightly/opt
+   - macosx64-nightly/opt
+   - macosx64-devedition-nightly/opt
    - win32-nightly/opt
    - win32-devedition-nightly/opt
    - win32/opt
    - win64-nightly/opt
    - win64-devedition-nightly/opt
    - win64/opt
    - win64-asan-reporter-nightly/opt
--- a/taskcluster/docs/attributes.rst
+++ b/taskcluster/docs/attributes.rst
@@ -261,15 +261,8 @@ cache_digest
 Some tasks generate artifacts that are cached between pushes. This is the unique string used
 to identify the current version of the artifacts. See :py:mod:`taskgraph.util.cached_task`.
 
 cache_type
 ==========
 Some tasks generate artifacts that are cached between pushes. This is the type of cache that is
 used for the this task. See :py:mod:`taskgraph.util.cached_task`.
 
-required_signoffs
-=================
-A list of release signoffs that this kind requires, should the release also
-require these signoffs. For example, ``mar-signing`` signoffs may be required
-by some releases in the future; for any releases that require ``mar-signing``
-signoffs, the kinds that also require that signoff are marked with this
-attribute.
--- a/taskcluster/docs/kinds.rst
+++ b/taskcluster/docs/kinds.rst
@@ -443,29 +443,23 @@ and this task would package that up as a
 
 repackage-l10n
 --------------
 Repackage-L10n is a ```Repackage``` task split up to be suitable for use after l10n repacks.
 
 
 repackage-signing
 -----------------
-Repackage-signing take the repackaged installers (windows) and signs them.
+Repackage-signing take the repackaged installers (windows) and update packaging (with
+the signed internal bits) and signs them.
 
 repackage-signing-l10n
 ----------------------
-Repackage-signing-l10n take the repackaged installers (windows) and signs them for localized versions.
-
-mar-signing
------------
-Mar-signing takes the complete update MARs and signs them.
-
-mar-signing-l10n
-----------------
-Mar-signing-l10n takes the complete update MARs and signs them for localized versions.
+Repackage-signing take the repackaged installers (windows) and update packaging (with
+the signed internal bits) and signs them for localized versions.
 
 repackage-msi
 -------------
 Repackage-msi takes the signed full installer and produces an msi installer (that wraps the full installer)
 Using the ```./mach repackage``` command
 
 repackage-signing-msi
 ---------------------
--- a/taskcluster/docs/parameters.rst
+++ b/taskcluster/docs/parameters.rst
@@ -171,22 +171,16 @@ Release Promotion
    The build number for partner repacks. We sometimes have multiple partner build numbers per release build number; this parameter lets us bump them independently. Defaults to 1.
 
 ``release_enable_emefree``
    Boolean which controls repacking vanilla Firefox builds into EME-free builds.
 
 ``release_product``
    The product that is being released.
 
-``required_signoffs``
-   A list of signoffs that are required for this release promotion flavor. If specified, and if the corresponding `signoff_urls` url isn't specified, tasks that require these signoffs will not be scheduled.
-
-``signoff_urls``
-   A dictionary of signoff keys to url values. These are the urls marking the corresponding ``required_signoffs`` as signed off.
-
 Comm Push Information
 ---------------------
 
 These parameters correspond to the repository and revision of the comm-central
 repository to checkout. Their meaning is the same as the corresponding
 parameters for the gecko repository above. They are optional, but if any of
 them are specified, they must all be specified.
 
--- a/taskcluster/taskgraph/actions/release_promotion.py
+++ b/taskcluster/taskgraph/actions/release_promotion.py
@@ -24,51 +24,27 @@ from taskgraph.util.partners import (
     get_token
 )
 from taskgraph.taskgraph import TaskGraph
 from taskgraph.decision import taskgraph_decision
 from taskgraph.parameters import Parameters
 from taskgraph.util.attributes import RELEASE_PROMOTION_PROJECTS
 
 
-RELEASE_PROMOTION_SIGNOFFS = ('mar-signing', )
-
-
 def is_release_promotion_available(parameters):
     return parameters['project'] in RELEASE_PROMOTION_PROJECTS
 
 
 def get_partner_config(partner_url_config, github_token):
     partner_config = {}
     for kind, url in partner_url_config.items():
         partner_config[kind] = get_partner_config_by_url(url, kind, github_token)
     return partner_config
 
 
-def get_signoff_properties():
-    props = {}
-    for signoff in RELEASE_PROMOTION_SIGNOFFS:
-        props[signoff] = {
-            'type': 'string',
-        }
-    return props
-
-
-def get_required_signoffs(input, parameters):
-    input_signoffs = set(input.get('required_signoffs', []))
-    params_signoffs = set(parameters['required_signoffs'] or [])
-    return sorted(list(input_signoffs | params_signoffs))
-
-
-def get_signoff_urls(input, parameters):
-    signoff_urls = parameters['signoff_urls']
-    signoff_urls.update(input.get('signoff_urls', {}))
-    return signoff_urls
-
-
 def get_flavors(graph_config, param):
     """
     Get all flavors with the given parameter enabled.
     """
     promotion_flavors = graph_config['release-promotion']['flavors']
     return sorted([
         flavor for (flavor, config) in promotion_flavors.items()
         if config.get(param, False)
@@ -216,29 +192,16 @@ def get_flavors(graph_config, param):
                 'properties': {},
                 'additionalProperties': True,
             },
             'release_enable_emefree': {
                 'type': 'boolean',
                 'default': False,
                 'description': ('Toggle for creating EME-free repacks'),
             },
-            'required_signoffs': {
-                'type': 'array',
-                'description': ('The flavor of release promotion to perform.'),
-                'items': {
-                    'enum': RELEASE_PROMOTION_SIGNOFFS,
-                }
-            },
-            'signoff_urls': {
-                'type': 'object',
-                'default': {},
-                'additionalProperties': False,
-                'properties': get_signoff_properties(),
-            },
         },
         "required": ['release_promotion_flavor', 'build_number'],
     }
 )
 def release_promotion_action(parameters, graph_config, input, task_group_id, task_id, task):
     release_promotion_flavor = input['release_promotion_flavor']
     promotion_config = graph_config['release-promotion']['flavors'][release_promotion_flavor]
     release_history = {}
@@ -340,15 +303,12 @@ def release_promotion_action(parameters,
         parameters['release_partner_build_number'] = input['release_partner_build_number']
 
     if partner_config:
         parameters['release_partner_config'] = fix_partner_config(partner_config)
 
     if input['version']:
         parameters['version'] = input['version']
 
-    parameters['required_signoffs'] = get_required_signoffs(input, parameters)
-    parameters['signoff_urls'] = get_signoff_urls(input, parameters)
-
     # make parameters read-only
     parameters = Parameters(**parameters)
 
     taskgraph_decision({'root': graph_config.root_dir}, parameters=parameters)
--- a/taskcluster/taskgraph/decision.py
+++ b/taskcluster/taskgraph/decision.py
@@ -233,18 +233,16 @@ def get_decision_parameters(config, opti
     parameters['release_type'] = ''
     parameters['release_eta'] = ''
     parameters['release_enable_partners'] = False
     parameters['release_partners'] = []
     parameters['release_partner_config'] = {}
     parameters['release_partner_build_number'] = 1
     parameters['release_enable_emefree'] = False
     parameters['release_product'] = None
-    parameters['required_signoffs'] = []
-    parameters['signoff_urls'] = {}
     parameters['try_mode'] = None
     parameters['try_task_config'] = None
     parameters['try_options'] = None
 
     # owner must be an email, but sometimes (e.g., for ffxbld) it is not, in which
     # case, fake it
     if '@' not in parameters['owner']:
         parameters['owner'] += '@noreply.mozilla.org'
--- a/taskcluster/taskgraph/loader/multi_dep.py
+++ b/taskcluster/taskgraph/loader/multi_dep.py
@@ -4,17 +4,16 @@
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 import copy
 
 from voluptuous import Required
 
 from ..task import Task
-from ..util.attributes import sorted_unique_list
 from ..util.schema import Schema
 
 schema = Schema({
     Required('primary-dependency', 'primary dependency task'): Task,
     Required(
         'dependent-tasks',
         'dictionary of dependent tasks, keyed by kind',
     ): {basestring: Task},
@@ -62,21 +61,16 @@ def loader(kind, path, config, params, l
             job.update(copy.deepcopy(job_template))
         # copy shipping_product from upstream
         product = job['primary-dependency'].attributes.get(
             'shipping_product',
             job['primary-dependency'].task.get('shipping-product')
         )
         if product:
             job.setdefault('shipping-product', product)
-        job.setdefault('attributes', {})['required_signoffs'] = sorted_unique_list(
-            *[task.attributes.get('required_signoffs', [])
-              for task in dep_tasks.values()
-              ]
-        )
 
         yield job
 
 
 def group_tasks(config, tasks):
     group_by_fn = GROUP_BY_MAP[config['group-by']]
 
     groups = group_by_fn(config, tasks)
--- a/taskcluster/taskgraph/parameters.py
+++ b/taskcluster/taskgraph/parameters.py
@@ -73,18 +73,16 @@ PARAMETERS = {
     'release_enable_partners': False,
     'release_eta': '',
     'release_history': {},
     'release_partners': None,
     'release_partner_config': None,
     'release_partner_build_number': 1,
     'release_type': 'nightly',
     'release_product': None,
-    'required_signoffs': [],
-    'signoff_urls': {},
     'target_tasks_method': 'default',
     'try_mode': None,
     'try_options': None,
     'try_task_config': None,
     'version': get_version(),
 }
 
 COMM_PARAMETERS = {
--- a/taskcluster/taskgraph/target_tasks.py
+++ b/taskcluster/taskgraph/target_tasks.py
@@ -80,24 +80,16 @@ def filter_release_tasks(task, parameter
             return False
 
     if task.attributes.get('shipping_phase') not in (None, 'build'):
         return False
 
     return True
 
 
-def filter_out_missing_signoffs(task, parameters):
-    for signoff in parameters['required_signoffs']:
-        if signoff not in parameters['signoff_urls'] and \
-           signoff in task.attributes.get('required_signoffs', []):
-            return False
-    return True
-
-
 def standard_filter(task, parameters):
     return all(
         filter_func(task, parameters) for filter_func in
         (filter_out_cron, filter_for_project, filter_for_hg_branch)
     )
 
 
 def _try_task_config(full_task_graph, parameters, graph_config):
@@ -323,36 +315,31 @@ def target_tasks_promote_desktop(full_ta
         if task.attributes.get('shipping_product') != parameters['release_product']:
             return False
 
         # 'secondary' balrog/update verify/final verify tasks only run for RCs
         if parameters.get('release_type') != 'release-rc':
             if 'secondary' in task.kind:
                 return False
 
-        if not filter_out_missing_signoffs(task, parameters):
-            return False
-
         if task.attributes.get('shipping_phase') == 'promote':
             return True
 
     return [l for l, t in full_task_graph.tasks.iteritems() if filter(t)]
 
 
 @_target_task('push_desktop')
 def target_tasks_push_desktop(full_task_graph, parameters, graph_config):
     """Select the set of tasks required to push a build of desktop to cdns.
     Previous build deps will be optimized out via action task."""
     filtered_for_candidates = target_tasks_promote_desktop(
         full_task_graph, parameters, graph_config,
     )
 
     def filter(task):
-        if not filter_out_missing_signoffs(task, parameters):
-            return False
         # Include promotion tasks; these will be optimized out
         if task.label in filtered_for_candidates:
             return True
         if task.attributes.get('shipping_product') == parameters['release_product'] and \
                 task.attributes.get('shipping_phase') == 'push':
             return True
 
     return [l for l, t in full_task_graph.tasks.iteritems() if filter(t)]
@@ -371,18 +358,16 @@ def target_tasks_ship_desktop(full_task_
         )
     else:
         # ship_firefox runs after `push`; include all push tasks.
         filtered_for_candidates = target_tasks_push_desktop(
             full_task_graph, parameters, graph_config,
         )
 
     def filter(task):
-        if not filter_out_missing_signoffs(task, parameters):
-            return False
         # Include promotion tasks; these will be optimized out
         if task.label in filtered_for_candidates:
             return True
         if task.attributes.get('shipping_product') != parameters['release_product'] or \
                 task.attributes.get('shipping_phase') != 'ship':
             return False
 
         if 'secondary' in task.kind:
--- a/taskcluster/taskgraph/transforms/balrog_submit.py
+++ b/taskcluster/taskgraph/transforms/balrog_submit.py
@@ -33,18 +33,16 @@ balrog_description_schema = schema.exten
     # unique label to describe this balrog task, defaults to balrog-{dep.label}
     Optional('label'): basestring,
 
     # treeherder is allowed here to override any defaults we use for beetmover.  See
     # taskcluster/taskgraph/transforms/task.py for the schema details, and the
     # below transforms for defaults of various values.
     Optional('treeherder'): task_description_schema['treeherder'],
 
-    Optional('attributes'): task_description_schema['attributes'],
-
     # Shipping product / phase
     Optional('shipping-product'): task_description_schema['shipping-product'],
     Optional('shipping-phase'): task_description_schema['shipping-phase'],
 })
 
 
 @transforms.add
 def validate(config, jobs):
--- a/taskcluster/taskgraph/transforms/beetmover_repackage.py
+++ b/taskcluster/taskgraph/transforms/beetmover_repackage.py
@@ -125,28 +125,26 @@ UPSTREAM_ARTIFACT_SIGNED_PATHS = _compil
 UPSTREAM_ARTIFACT_REPACKAGE_PATHS = [
     'target.dmg',
 ]
 # Until bug 1331141 is fixed, if you are adding any new artifacts here that
 # need to be transfered to S3, please be aware you also need to follow-up
 # with a beetmover patch in https://github.com/mozilla-releng/beetmoverscript/.
 # See example in bug 1348286
 UPSTREAM_ARTIFACT_SIGNED_REPACKAGE_PATHS = [
+    'target.complete.mar',
+    'target.bz2.complete.mar',
     'target.installer.exe',
     'target.stub-installer.exe',
 ]
 
 UPSTREAM_ARTIFACT_SIGNED_MSI_PATHS = [
     'target.installer.msi',
 ]
 
-UPSTREAM_ARTIFACT_SIGNED_MAR_PATHS = [
-    'target.complete.mar',
-]
-
 # Voluptuous uses marker objects as dictionary *keys*, but they are not
 # comparable, so we cast all of the keys back to regular strings
 task_description_schema = {str(k): v for k, v in task_description_schema.schema.iteritems()}
 
 transforms = TransformSequence()
 
 # shortcut for a string where task references are allowed
 taskref_or_string = Any(
@@ -160,18 +158,16 @@ beetmover_description_schema = schema.ex
     # unique label to describe this beetmover task, defaults to {dep.label}-beetmover
     Required('label'): basestring,
 
     # treeherder is allowed here to override any defaults we use for beetmover.  See
     # taskcluster/taskgraph/transforms/task.py for the schema details, and the
     # below transforms for defaults of various values.
     Optional('treeherder'): task_description_schema['treeherder'],
 
-    Optional('attributes'): task_description_schema['attributes'],
-
     # locale is passed only for l10n beetmoving
     Optional('locale'): basestring,
     Required('shipping-phase'): task_description_schema['shipping-phase'],
     # Optional until we fix asan (run_on_projects?)
     Optional('shipping-product'): task_description_schema['shipping-product'],
 })
 
 
@@ -206,43 +202,39 @@ def make_task_description(config, jobs):
                 locale=attributes.get('locale', 'en-US'),
                 build_platform=attributes.get('build_platform'),
                 build_type=attributes.get('build_type')
             )
         )
 
         upstream_deps = job['dependent-tasks']
 
+        # TODO fix the upstreamArtifact generation to not need this?
         signing_name = "build-signing"
         build_name = "build"
         repackage_name = "repackage"
         repackage_signing_name = "repackage-signing"
         msi_signing_name = "repackage-signing-msi"
-        mar_signing_name = "mar-signing"
         if job.get('locale'):
             signing_name = "nightly-l10n-signing"
             build_name = "nightly-l10n"
             repackage_name = "repackage-l10n"
             repackage_signing_name = "repackage-signing-l10n"
-            mar_signing_name = "mar-signing-l10n"
         dependencies = {
             "build": upstream_deps[build_name],
             "repackage": upstream_deps[repackage_name],
+            "repackage-signing": upstream_deps[repackage_signing_name],
             "signing": upstream_deps[signing_name],
-            "mar-signing": upstream_deps[mar_signing_name],
         }
         if 'partials-signing' in upstream_deps:
             dependencies['partials-signing'] = upstream_deps['partials-signing']
         if msi_signing_name in upstream_deps:
             dependencies[msi_signing_name] = upstream_deps[msi_signing_name]
-        if repackage_signing_name in upstream_deps:
-            dependencies["repackage-signing"] = upstream_deps[repackage_signing_name]
 
         attributes = copy_attributes_from_dependent_job(dep_job)
-        attributes.update(job.get('attributes', {}))
         if job.get('locale'):
             attributes['locale'] = job['locale']
 
         bucket_scope = get_beetmover_bucket_scope(config)
         action_scope = get_beetmover_action_scope(config)
 
         task = {
             'label': label,
@@ -262,17 +254,16 @@ def make_task_description(config, jobs):
 
 def generate_upstream_artifacts(job, dependencies, platform, locale=None, project=None):
 
     build_mapping = UPSTREAM_ARTIFACT_UNSIGNED_PATHS
     build_signing_mapping = UPSTREAM_ARTIFACT_SIGNED_PATHS
     repackage_mapping = UPSTREAM_ARTIFACT_REPACKAGE_PATHS
     repackage_signing_mapping = UPSTREAM_ARTIFACT_SIGNED_REPACKAGE_PATHS
     msi_signing_mapping = UPSTREAM_ARTIFACT_SIGNED_MSI_PATHS
-    mar_signing_mapping = UPSTREAM_ARTIFACT_SIGNED_MAR_PATHS
 
     artifact_prefix = get_artifact_prefix(job)
     if locale:
         artifact_prefix = '{}/{}'.format(artifact_prefix, locale)
         platform = "{}-l10n".format(platform)
 
     upstream_artifacts = []
 
@@ -306,17 +297,16 @@ def generate_upstream_artifacts(job, dep
                         "paths": ["{}/{}".format(artifact_prefix, path) for path in usable_paths],
                         "locale": locale or "en-US",
                     })
 
     for task_type, cot_type, paths in [
         ('repackage', 'repackage', repackage_mapping),
         ('repackage-signing', 'repackage', repackage_signing_mapping),
         ('repackage-signing-msi', 'repackage', msi_signing_mapping),
-        ('mar-signing', 'signing', mar_signing_mapping),
     ]:
         if task_type not in dependencies:
             continue
 
         paths = ["{}/{}".format(artifact_prefix, path) for path in paths]
         paths = [
             path for path in paths
             if path in dependencies[task_type].release_artifacts]
--- a/taskcluster/taskgraph/transforms/beetmover_repackage_l10n.py
+++ b/taskcluster/taskgraph/transforms/beetmover_repackage_l10n.py
@@ -31,15 +31,14 @@ def make_beetmover_description(config, j
         treeherder = {
             'symbol': join_symbol(group, symbol),
         }
 
         beet_description = {
             'label': job['label'],
             'primary-dependency': dep_job,
             'dependent-tasks': job['dependent-tasks'],
-            'attributes': job['attributes'],
             'treeherder': treeherder,
             'locale': locale,
             'shipping-phase': job['shipping-phase'],
             'shipping-product': job['shipping-product'],
         }
         yield beet_description
--- a/taskcluster/taskgraph/transforms/partials.py
+++ b/taskcluster/taskgraph/transforms/partials.py
@@ -53,40 +53,51 @@ def make_task_description(config, jobs):
 
         treeherder.setdefault('platform',
                               "{}/opt".format(dep_th_platform))
         treeherder.setdefault('kind', 'build')
         treeherder.setdefault('tier', 1)
 
         dependent_kind = str(dep_job.kind)
         dependencies = {dependent_kind: dep_job.label}
+        signing_dependencies = dep_job.dependencies
+        # This is so we get the build task etc in our dependencies to
+        # have better beetmover support.
+        dependencies.update(signing_dependencies)
 
         attributes = copy_attributes_from_dependent_job(dep_job)
         locale = dep_job.attributes.get('locale')
         if locale:
             attributes['locale'] = locale
             treeherder['symbol'] = "p({})".format(locale)
-        attributes['shipping_phase'] = job['shipping-phase']
 
         build_locale = locale or 'en-US'
 
         builds = get_builds(config.params['release_history'], dep_th_platform,
                             build_locale)
 
         # If the list is empty there's no available history for this platform
         # and locale combination, so we can't build any partials.
         if not builds:
             continue
 
-        dep_task_ref = '<{}>'.format(dependent_kind)
+        signing_task = None
+        for dependency in sorted(dependencies.keys()):
+            if 'repackage-signing-l10n' in dependency:
+                signing_task = dependency
+                break
+            if 'repackage-signing' in dependency:
+                signing_task = dependency
+                break
+        signing_task_ref = '<{}>'.format(signing_task)
 
         extra = {'funsize': {'partials': list()}}
         update_number = 1
         artifact_path = "{}{}".format(
-            get_taskcluster_artifact_prefix(dep_job, dep_task_ref, locale=locale),
+            get_taskcluster_artifact_prefix(dep_job, signing_task_ref, locale=locale),
             'target.complete.mar'
         )
         for build in sorted(builds):
             partial_info = {
                 'locale': build_locale,
                 'from_mar': builds[build]['mar_url'],
                 'to_mar': {'task-reference': artifact_path},
                 'platform': get_balrog_platform_name(dep_th_platform),
rename from taskcluster/taskgraph/transforms/mar_signing.py
rename to taskcluster/taskgraph/transforms/partials_signing.py
--- a/taskcluster/taskgraph/transforms/mar_signing.py
+++ b/taskcluster/taskgraph/transforms/partials_signing.py
@@ -1,33 +1,32 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 """
-Transform the {partials,mar}-signing task into an actual task description.
+Transform the partials task into an actual task description.
 """
 from __future__ import absolute_import, print_function, unicode_literals
 
 from taskgraph.transforms.base import TransformSequence
-from taskgraph.util.attributes import copy_attributes_from_dependent_job, sorted_unique_list
+from taskgraph.util.attributes import copy_attributes_from_dependent_job
 from taskgraph.util.scriptworker import (
     get_signing_cert_scope_per_platform,
     get_worker_type_for_scope,
 )
 from taskgraph.util.partials import get_balrog_platform_name, get_partials_artifacts
 from taskgraph.util.taskcluster import get_artifact_prefix
-from taskgraph.util.treeherder import join_symbol
 
 import logging
 logger = logging.getLogger(__name__)
 
 transforms = TransformSequence()
 
 
-def generate_partials_artifacts(job, release_history, platform, locale=None):
+def generate_upstream_artifacts(job, release_history, platform, locale=None):
     artifact_prefix = get_artifact_prefix(job)
     if locale:
         artifact_prefix = '{}/{}'.format(artifact_prefix, locale)
     else:
         locale = 'en-US'
 
     artifacts = get_partials_artifacts(release_history, platform, locale)
 
@@ -58,93 +57,65 @@ def generate_partials_artifacts(job, rel
     }
 
     if old_mar_upstream_artifacts["paths"]:
         upstream_artifacts.append(old_mar_upstream_artifacts)
 
     return upstream_artifacts
 
 
-def generate_complete_artifacts(job, platform, locale=None):
-    artifact_prefix = get_artifact_prefix(job)
-    if locale:
-        artifact_prefix = '{}/{}'.format(artifact_prefix, locale)
-
-    upstream_artifacts = [{
-        "taskId": {"task-reference": '<{}>'.format(job.kind)},
-        "taskType": 'build',
-        "paths": [
-            "{}/target.complete.mar".format(artifact_prefix)
-        ],
-        "formats": ["autograph_hash_only_mar384"],
-    }]
-
-    return upstream_artifacts
-
-
 @transforms.add
 def make_task_description(config, jobs):
     for job in jobs:
         dep_job = job['primary-dependency']
-        locale = dep_job.attributes.get('locale')
 
         treeherder = job.get('treeherder', {})
-        treeherder['symbol'] = join_symbol(
-            job.get('treeherder-group', 'ms'),
-            locale or 'N'
-        )
+        treeherder.setdefault('symbol', 'ps(N)')
 
         dep_th_platform = dep_job.task.get('extra', {}).get(
             'treeherder', {}).get('machine', {}).get('platform', '')
-        label = job.get('label', "{}-{}".format(config.kind, dep_job.label))
+        label = job.get('label', "partials-signing-{}".format(dep_job.label))
         dep_th_platform = dep_job.task.get('extra', {}).get(
             'treeherder', {}).get('machine', {}).get('platform', '')
         treeherder.setdefault('platform',
                               "{}/opt".format(dep_th_platform))
         treeherder.setdefault('kind', 'build')
         treeherder.setdefault('tier', 1)
 
         dependent_kind = str(dep_job.kind)
         dependencies = {dependent_kind: dep_job.label}
         signing_dependencies = dep_job.dependencies
         # This is so we get the build task etc in our dependencies to
         # have better beetmover support.
         dependencies.update(signing_dependencies)
 
         attributes = copy_attributes_from_dependent_job(dep_job)
-        attributes['required_signoffs'] = sorted_unique_list(
-            attributes.get('required_signoffs', []),
-            job.pop('required_signoffs')
-        )
-        attributes['shipping_phase'] = job['shipping-phase']
+        locale = dep_job.attributes.get('locale')
         if locale:
             attributes['locale'] = locale
+            treeherder['symbol'] = 'ps({})'.format(locale)
 
         balrog_platform = get_balrog_platform_name(dep_th_platform)
-        if config.kind == 'partials-signing':
-            upstream_artifacts = generate_partials_artifacts(
-                dep_job, config.params['release_history'], balrog_platform, locale)
-        else:
-            upstream_artifacts = generate_complete_artifacts(
-                dep_job, balrog_platform, locale)
+        upstream_artifacts = generate_upstream_artifacts(
+            dep_job, config.params['release_history'], balrog_platform, locale)
 
         build_platform = dep_job.attributes.get('build_platform')
         is_nightly = dep_job.attributes.get('nightly')
         signing_cert_scope = get_signing_cert_scope_per_platform(
             build_platform, is_nightly, config
         )
 
         scopes = [signing_cert_scope, 'project:releng:signing:format:autograph_hash_only_mar384']
         if any("mar" in upstream_details["formats"] for upstream_details in upstream_artifacts):
             scopes.append('project:releng:signing:format:mar')
 
         task = {
             'label': label,
-            'description': "{} {}".format(
-                dep_job.task["metadata"]["description"], job['description-suffix']),
+            'description': "{} Partials".format(
+                dep_job.task["metadata"]["description"]),
             'worker-type': get_worker_type_for_scope(config, signing_cert_scope),
             'worker': {'implementation': 'scriptworker-signing',
                        'upstream-artifacts': upstream_artifacts,
                        'max-run-time': 3600},
             'dependencies': dependencies,
             'attributes': attributes,
             'scopes': scopes,
             'run-on-projects': dep_job.attributes.get('run_on_projects'),
--- a/taskcluster/taskgraph/transforms/per_platform_dummy.py
+++ b/taskcluster/taskgraph/transforms/per_platform_dummy.py
@@ -3,29 +3,28 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 """
 Transform the repackage task into an actual task description.
 """
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 from taskgraph.transforms.base import TransformSequence
-from taskgraph.util.attributes import copy_attributes_from_dependent_job
 
 transforms = TransformSequence()
 
 
 @transforms.add
 def one_task_per_product_and_platform(config, jobs):
     unique_products_and_platforms = set()
     for job in jobs:
         dep_task = job["primary-dependency"]
         if 'primary-dependency' in job:
             del job['primary-dependency']
         product = dep_task.attributes.get("shipping_product")
         platform = dep_task.attributes.get("build_platform")
         if (product, platform) not in unique_products_and_platforms:
-            attributes = copy_attributes_from_dependent_job(dep_task)
-            attributes.update(job.get('attributes', {}))
-            job['attributes'] = attributes
+            job.setdefault("attributes", {})
+            job["attributes"]["shipping_product"] = product
+            job["attributes"]["build_platform"] = platform
             job["name"] = "{}-{}".format(product, platform)
             yield job
             unique_products_and_platforms.add((product, platform))
--- a/taskcluster/taskgraph/transforms/release_deps.py
+++ b/taskcluster/taskgraph/transforms/release_deps.py
@@ -19,17 +19,16 @@ def add_dependencies(config, jobs):
     for job in jobs:
         dependencies = {}
         # Add any kind_dependencies_tasks with matching product as dependencies
         product = job.get('shipping-product')
         phase = job.get('shipping-phase')
         if product is None:
             continue
 
-        required_signoffs = set(job.setdefault('attributes', {}).get('required_signoffs', []))
         for dep_task in config.kind_dependencies_tasks:
             # Weed out unwanted tasks.
             # XXX we have run-on-projects which specifies the on-push behavior;
             # we need another attribute that specifies release promotion,
             # possibly which action(s) each task belongs in.
             if product == 'fennec':
                 # Don't ship single locale fennec anymore - Bug 1408083
                 attr = dep_task.attributes.get
@@ -43,15 +42,12 @@ def add_dependencies(config, jobs):
             if dep_task.attributes.get("build_platform") and \
                job.get("attributes", {}).get("build_platform"):
                 if dep_task.attributes["build_platform"] != job["attributes"]["build_platform"]:
                     continue
             # Add matching product tasks to deps
             if dep_task.task.get('shipping-product') == product or \
                     dep_task.attributes.get('shipping_product') == product:
                 dependencies[dep_task.label] = dep_task.label
-                required_signoffs.update(dep_task.attributes.get('required_signoffs', []))
 
         job.setdefault('dependencies', {}).update(dependencies)
-        if required_signoffs:
-            job['attributes']['required_signoffs'] = sorted(required_signoffs)
 
         yield job
--- a/taskcluster/taskgraph/transforms/release_generate_checksums_beetmover.py
+++ b/taskcluster/taskgraph/transforms/release_generate_checksums_beetmover.py
@@ -50,18 +50,16 @@ release_generate_checksums_beetmover_sch
 
     # treeherder is allowed here to override any defaults we use for beetmover.  See
     # taskcluster/taskgraph/transforms/task.py for the schema details, and the
     # below transforms for defaults of various values.
     Optional('treeherder'): task_description_schema['treeherder'],
 
     Optional('shipping-phase'): task_description_schema['shipping-phase'],
     Optional('shipping-product'): task_description_schema['shipping-product'],
-
-    Optional('attributes'): task_description_schema['attributes'],
 })
 
 
 @transforms.add
 def validate(config, jobs):
     for job in jobs:
         label = job.get('primary-dependency', object).__dict__.get('label', '?no-label?')
         validate_schema(
--- a/taskcluster/taskgraph/transforms/release_snap_push.py
+++ b/taskcluster/taskgraph/transforms/release_snap_push.py
@@ -29,17 +29,16 @@ push_snap_description_schema = Schema({
     Required('treeherder'): task_description_schema['treeherder'],
     Required('run-on-projects'): task_description_schema['run-on-projects'],
     Required('worker-type'): optionally_keyed_by('release-level', basestring),
     Required('worker'): object,
     Required('scopes'): optionally_keyed_by('project', [basestring]),
     Required('shipping-phase'): task_description_schema['shipping-phase'],
     Required('shipping-product'): task_description_schema['shipping-product'],
     Optional('extra'): task_description_schema['extra'],
-    Optional('attributes'): task_description_schema['attributes'],
 })
 
 
 @transforms.add
 def validate_jobs_schema_transform(config, jobs):
     for job in jobs:
         label = job.get('label', '?no-label?')
         validate_schema(
--- a/taskcluster/taskgraph/transforms/repackage_signing.py
+++ b/taskcluster/taskgraph/transforms/repackage_signing.py
@@ -31,16 +31,18 @@ repackage_signing_description_schema = s
     Required('depname', default='repackage'): basestring,
     Optional('label'): basestring,
     Optional('treeherder'): task_description_schema['treeherder'],
     Optional('shipping-product'): task_description_schema['shipping-product'],
     Optional('shipping-phase'): task_description_schema['shipping-phase'],
 })
 
 SIGNING_FORMATS = {
+    'target.complete.mar': ["autograph_hash_only_mar384"],
+    'target.bz2.complete.mar': ["mar"],
     "target.installer.exe": ["sha2signcode"],
     "target.stub-installer.exe": ["sha2signcodestub"],
     "target.installer.msi": ["sha2signcode"],
 }
 
 
 @transforms.add
 def validate(config, jobs):
--- a/taskcluster/taskgraph/util/attributes.py
+++ b/taskcluster/taskgraph/util/attributes.py
@@ -31,17 +31,16 @@ RELEASE_PROMOTION_PROJECTS = {
     'try-comm-central',
 } | RELEASE_PROJECTS
 
 _OPTIONAL_ATTRIBUTES = (
     'artifact_prefix',
     'l10n_chunk',
     'locale',
     'nightly',
-    'required_signoffs',
     'signed',
     'shipping_phase',
     'shipping_product',
     'stub-installer',
 )
 
 
 def attrmatch(attributes, **kwargs):
@@ -127,14 +126,8 @@ def copy_attributes_from_dependent_job(d
     }
 
     attributes.update({
         attr: dep_job.attributes[attr]
         for attr in _OPTIONAL_ATTRIBUTES if attr in dep_job.attributes
     })
 
     return attributes
-
-
-def sorted_unique_list(*args):
-    """Join one or more lists, and return a sorted list of unique members"""
-    combined = set().union(*args)
-    return sorted(combined)
--- a/taskcluster/taskgraph/util/verify.py
+++ b/taskcluster/taskgraph/util/verify.py
@@ -176,43 +176,16 @@ def verify_dependency_tiers(task, taskgr
                     continue
                 if tier < tiers[d]:
                     raise Exception(
                         '{} (tier {}) cannot depend on {} (tier {})'
                         .format(task.label, printable_tier(tier),
                                 d, printable_tier(tiers[d])))
 
 
-@verifications.add('full_task_graph')
-def verify_required_signoffs(task, taskgraph, scratch_pad):
-    """
-    Task with required signoffs can't be dependencies of tasks with less
-    required signoffs.
-    """
-    all_required_signoffs = scratch_pad
-    if task is not None:
-        all_required_signoffs[task.label] = set(task.attributes.get('required_signoffs', []))
-    else:
-        def printable_signoff(signoffs):
-            if len(signoffs) == 1:
-                return 'required signoff {}'.format(*signoffs)
-            elif signoffs:
-                return 'required signoffs {}'.format(', '.join(signoffs))
-            else:
-                return 'no required signoffs'
-        for task in taskgraph.tasks.itervalues():
-            required_signoffs = all_required_signoffs[task.label]
-            for d in task.dependencies.itervalues():
-                if required_signoffs < all_required_signoffs[d]:
-                    raise Exception(
-                        '{} ({}) cannot depend on {} ({})'
-                        .format(task.label, printable_signoff(required_signoffs),
-                                d, printable_signoff(all_required_signoffs[d])))
-
-
 @verifications.add('optimized_task_graph')
 def verify_always_optimized(task, taskgraph, scratch_pad):
     """
         This function ensures that always-optimized tasks have been optimized.
     """
     if task is None:
         return
     if task.task.get('workerType') == 'always-optimized':