Merge inbound to mozilla-central. a=merge
authorBogdan Tara <btara@mozilla.com>
Fri, 28 Dec 2018 23:50:12 +0200
changeset 452072 0cf7daf34a373c26eecab2b19baf7bff67756170
parent 452040 d57dde190f67e7964bb3a908d33a4d086da696b1 (current diff)
parent 452071 bc7d4e24509b42ff4b1407fdee01b4db83b3345c (diff)
child 452086 6c185cbc16ecd264634a37aa2c93c4fcc9bb8f53
child 452097 ff3c0c9ee64bf5a4d9f8e4719149a03fd516953d
push id35282
push userbtara@mozilla.com
push dateFri, 28 Dec 2018 21:50:42 +0000
treeherdermozilla-central@0cf7daf34a37 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone66.0a1
first release with
nightly linux32
0cf7daf34a37 / 66.0a1 / 20181228215042 / files
nightly linux64
0cf7daf34a37 / 66.0a1 / 20181228215042 / files
nightly mac
0cf7daf34a37 / 66.0a1 / 20181228215042 / files
nightly win32
0cf7daf34a37 / 66.0a1 / 20181228215042 / files
nightly win64
0cf7daf34a37 / 66.0a1 / 20181228215042 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
browser/components/pocket/content/AboutPocket.jsm
browser/components/pocket/content/pocket-content-process.js
browser/components/pocket/skin/library-pocket-animation.svg
browser/components/pocket/skin/pocket-animation.svg
browser/components/pocket/skin/pocket-outline.svg
browser/components/pocket/skin/pocket.css
browser/components/pocket/skin/pocket.svg
dom/base/nsIDocument.h
dom/smil/nsISMILType.h
dom/smil/nsSMILAnimationController.cpp
dom/smil/nsSMILAnimationController.h
dom/svg/SVGTransform.cpp
dom/svg/SVGTransform.h
layout/base/PresShell.cpp
--- a/.taskcluster.yml
+++ b/.taskcluster.yml
@@ -141,16 +141,19 @@ tasks:
             $merge:
               - GECKO_BASE_REPOSITORY: 'https://hg.mozilla.org/mozilla-unified'
                 GECKO_HEAD_REPOSITORY: '${repoUrl}'
                 GECKO_HEAD_REF: '${push.revision}'
                 GECKO_HEAD_REV: '${push.revision}'
                 GECKO_COMMIT_MSG: {$if: 'tasks_for != "action"', then: '${push.comment}'}
                 HG_STORE_PATH: /builds/worker/checkouts/hg-store
                 TASKCLUSTER_CACHES: /builds/worker/checkouts
+                # someday, these will be provided by the worker - Bug 1492664
+                TASKCLUSTER_ROOT_URL: https://taskcluster.net
+                TASKCLUSTER_PROXY_URL: http://taskcluster
               - $if: 'tasks_for == "action"'
                 then:
                   ACTION_TASK_GROUP_ID: '${action.taskGroupId}'     # taskGroupId of the target task
                   ACTION_TASK_ID: {$json: {$eval: 'taskId'}} # taskId of the target task (JSON-encoded)
                   ACTION_INPUT: {$json: {$eval: 'input'}}
                   ACTION_CALLBACK: '${action.cb_name}'
                   ACTION_PARAMETERS: {$json: {$eval: 'parameters'}}
 
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1646,18 +1646,20 @@ pref("dom.ipc.processPrelaunch.enabled",
 
 // See comments in bug 1340115 on how we got to these numbers.
 pref("browser.migrate.chrome.history.limit", 2000);
 pref("browser.migrate.chrome.history.maxAgeInDays", 180);
 
 // Enable browser frames for use on desktop.  Only exposed to chrome callers.
 pref("dom.mozBrowserFramesEnabled", true);
 
+pref("extensions.pocket.api", "api.getpocket.com");
 pref("extensions.pocket.enabled", true);
 pref("extensions.pocket.oAuthConsumerKey", "40249-e88c401e1b1f2242d9e441c4");
+pref("extensions.pocket.site", "getpocket.com");
 
 pref("signon.schemeUpgrades", true);
 
 // Enable the "Simplify Page" feature in Print Preview. This feature
 // is disabled by default in toolkit.
 pref("print.use_simplify_page", true);
 
 // Space separated list of URLS that are allowed to send objects (instead of
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -38,16 +38,17 @@ XPCOMUtils.defineLazyModuleGetters(this,
   PageActions: "resource:///modules/PageActions.jsm",
   PageThumbs: "resource://gre/modules/PageThumbs.jsm",
   PanelMultiView: "resource:///modules/PanelMultiView.jsm",
   PanelView: "resource:///modules/PanelMultiView.jsm",
   PlacesUtils: "resource://gre/modules/PlacesUtils.jsm",
   PlacesUIUtils: "resource:///modules/PlacesUIUtils.jsm",
   PlacesTransactions: "resource://gre/modules/PlacesTransactions.jsm",
   PluralForm: "resource://gre/modules/PluralForm.jsm",
+  Pocket: "chrome://pocket/content/Pocket.jsm",
   PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
   ProcessHangMonitor: "resource:///modules/ProcessHangMonitor.jsm",
   PromiseUtils: "resource://gre/modules/PromiseUtils.jsm",
   ReaderMode: "resource://gre/modules/ReaderMode.jsm",
   ReaderParent: "resource:///modules/ReaderParent.jsm",
   SafeBrowsing: "resource://gre/modules/SafeBrowsing.jsm",
   Sanitizer: "resource:///modules/Sanitizer.jsm",
   SessionStartup: "resource:///modules/sessionstore/SessionStartup.jsm",
@@ -134,16 +135,18 @@ XPCOMUtils.defineLazyScriptGetter(this, 
                                   "chrome://browser/content/places/editBookmark.js");
 XPCOMUtils.defineLazyScriptGetter(this, "SearchOneOffs",
                                   "chrome://browser/content/search/search-one-offs.js");
 if (AppConstants.NIGHTLY_BUILD) {
   XPCOMUtils.defineLazyScriptGetter(this, "gWebRender",
                                     "chrome://browser/content/browser-webrender.js");
 }
 
+XPCOMUtils.defineLazyScriptGetter(this, "pktUI", "chrome://pocket/content/main.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"],
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -1030,16 +1030,28 @@ xmlns="http://www.w3.org/1999/xhtml"
                        hidden="true"/>
                 <box id="pageActionSeparator" class="urlbar-page-action"/>
                 <image id="pageActionButton"
                        class="urlbar-icon urlbar-page-action"
                        role="button"
                        tooltiptext="&pageActionButton.tooltip;"
                        onmousedown="BrowserPageActions.mainButtonClicked(event);"
                        onkeypress="BrowserPageActions.mainButtonClicked(event);"/>
+                <hbox id="pocket-button-box"
+                      hidden="true"
+                      class="urlbar-icon-wrapper urlbar-page-action"
+                      onclick="BrowserPageActions.doCommandForAction(PageActions.actionForID('pocket'), event, this);">
+                  <image id="pocket-button"
+                         class="urlbar-icon"
+                         role="button"/>
+                  <hbox id="pocket-button-animatable-box">
+                    <image id="pocket-button-animatable-image"
+                           role="presentation"/>
+                  </hbox>
+                </hbox>
                 <hbox id="star-button-box"
                       hidden="true"
                       class="urlbar-icon-wrapper urlbar-page-action"
                       onclick="BrowserPageActions.doCommandForAction(PageActions.actionForID('bookmark'), event, this);">
                   <image id="star-button"
                          class="urlbar-icon"
                          role="button"/>
                   <hbox id="star-button-animatable-box">
--- a/browser/base/content/test/performance/browser_startup_content.js
+++ b/browser/base/content/test/performance/browser_startup_content.js
@@ -54,19 +54,16 @@ const whitelist = {
     "resource:///actors/PageStyleChild.jsm",
     "resource:///actors/SearchTelemetryChild.jsm",
     "resource://gre/modules/ActorChild.jsm",
     "resource://gre/modules/ActorManagerChild.jsm",
     "resource://gre/modules/E10SUtils.jsm",
     "resource://gre/modules/Readerable.jsm",
     "resource://gre/modules/WebProgressChild.jsm",
 
-    // Pocket
-    "chrome://pocket/content/AboutPocket.jsm",
-
     // Telemetry
     "resource://gre/modules/TelemetryController.jsm", // bug 1470339
     "resource://gre/modules/TelemetryUtils.jsm", // bug 1470339
 
     // Extensions
     "resource://gre/modules/ExtensionProcessScript.jsm",
     "resource://gre/modules/ExtensionUtils.jsm",
     "resource://gre/modules/MessageChannel.jsm",
--- a/browser/components/about/AboutRedirector.cpp
+++ b/browser/components/about/AboutRedirector.cpp
@@ -78,16 +78,24 @@ static const RedirEntry kRedirMap[] = {
     {"welcome", "about:blank",
      nsIAboutModule::URI_MUST_LOAD_IN_CHILD |
          nsIAboutModule::URI_CAN_LOAD_IN_PRIVILEGED_CHILD |
          nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
          nsIAboutModule::ALLOW_SCRIPT},
     {"library", "chrome://browser/content/aboutLibrary.xhtml",
      nsIAboutModule::URI_MUST_LOAD_IN_CHILD |
          nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT},
+    {"pocket-saved", "chrome://pocket/content/panels/saved.html",
+     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
+         nsIAboutModule::URI_CAN_LOAD_IN_CHILD | nsIAboutModule::ALLOW_SCRIPT |
+         nsIAboutModule::HIDE_FROM_ABOUTABOUT},
+    {"pocket-signup", "chrome://pocket/content/panels/signup.html",
+     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
+         nsIAboutModule::URI_CAN_LOAD_IN_CHILD | nsIAboutModule::ALLOW_SCRIPT |
+         nsIAboutModule::HIDE_FROM_ABOUTABOUT},
     {"preferences",
      "chrome://browser/content/preferences/in-content/preferences.xul",
      nsIAboutModule::ALLOW_SCRIPT},
     {"downloads",
      "chrome://browser/content/downloads/contentAreaDownloadsView.xul",
      nsIAboutModule::ALLOW_SCRIPT},
     {"reader", "chrome://global/content/reader/aboutReader.html",
      nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
--- a/browser/components/build/nsModule.cpp
+++ b/browser/components/build/nsModule.cpp
@@ -108,16 +108,18 @@ static const mozilla::Module::ContractID
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "newtab", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "library", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "preferences", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "downloads", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "reader", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "restartrequired", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "welcome", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "policies", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
+    { NS_ABOUT_MODULE_CONTRACTID_PREFIX "pocket-saved", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
+    { NS_ABOUT_MODULE_CONTRACTID_PREFIX "pocket-signup", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
 #if defined(XP_WIN)
     { NS_IEHISTORYENUMERATOR_CONTRACTID, &kNS_WINIEHISTORYENUMERATOR_CID },
 #elif defined(XP_MACOSX)
     { NS_SHELLSERVICE_CONTRACTID, &kNS_SHELLSERVICE_CID },
 #endif
 #if defined(MOZ_WIDGET_COCOA)
     { NS_MACATTRIBUTIONSERVICE_CONTRACTID, &kNS_MACATTRIBUTIONSERVICE_CID },
 #endif
deleted file mode 100644
--- a/browser/components/pocket/content/AboutPocket.jsm
+++ /dev/null
@@ -1,94 +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/. */
-"use strict";
-
-const Cm = Components.manager;
-
-ChromeUtils.import("resource://gre/modules/Services.jsm");
-ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
-
-// See LOG_LEVELS in Console.jsm. Common examples: "All", "Info", "Warn", & "Error".
-const PREF_LOG_LEVEL = "loop.debug.loglevel";
-
-// eslint-disable-next-line no-unused-vars
-XPCOMUtils.defineLazyGetter(this, "log", () => {
-  let ConsoleAPI = ChromeUtils.import("resource://gre/modules/Console.jsm", {}).ConsoleAPI;
-  let consoleOptions = {
-    maxLogLevelPref: PREF_LOG_LEVEL,
-    prefix: "Loop",
-  };
-  return new ConsoleAPI(consoleOptions);
-});
-
-
-function AboutPage(chromeURL, aboutHost, classID, description, uriFlags) {
-  this.chromeURL = chromeURL;
-  this.aboutHost = aboutHost;
-  this.classID = Components.ID(classID);
-  this.description = description;
-  this.uriFlags = uriFlags;
-}
-
-AboutPage.prototype = {
-  QueryInterface: ChromeUtils.generateQI([Ci.nsIAboutModule]),
-  getURIFlags(aURI) { // eslint-disable-line no-unused-vars
-    return this.uriFlags;
-  },
-
-  newChannel(aURI, aLoadInfo) {
-    let newURI = Services.io.newURI(this.chromeURL);
-    let channel = Services.io.newChannelFromURIWithLoadInfo(newURI,
-                                                            aLoadInfo);
-    channel.originalURI = aURI;
-
-    if (this.uriFlags & Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT) {
-      let principal = Services.scriptSecurityManager.createCodebasePrincipal(aURI, {});
-      channel.owner = principal;
-    }
-    return channel;
-  },
-
-  createInstance(outer, iid) {
-    if (outer !== null) {
-      throw Cr.NS_ERROR_NO_AGGREGATION;
-    }
-    return this.QueryInterface(iid);
-  },
-
-  register() {
-    Cm.QueryInterface(Ci.nsIComponentRegistrar).registerFactory(
-      this.classID, this.description,
-      "@mozilla.org/network/protocol/about;1?what=" + this.aboutHost, this);
-  },
-
-  unregister() {
-    Cm.QueryInterface(Ci.nsIComponentRegistrar).unregisterFactory(
-      this.classID, this);
-  },
-};
-
-/* exported AboutPocket */
-var AboutPocket = {};
-
-XPCOMUtils.defineLazyGetter(AboutPocket, "aboutSaved", () =>
-  new AboutPage("chrome://pocket/content/panels/saved.html",
-                "pocket-saved",
-                "{3e759f54-37af-7843-9824-f71b5993ceed}",
-                "About Pocket Saved",
-                Ci.nsIAboutModule.ALLOW_SCRIPT |
-                Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT |
-                Ci.nsIAboutModule.HIDE_FROM_ABOUTABOUT)
-);
-
-XPCOMUtils.defineLazyGetter(AboutPocket, "aboutSignup", () =>
-  new AboutPage("chrome://pocket/content/panels/signup.html",
-                "pocket-signup",
-                "{8548329d-00c4-234e-8f17-75026db3b56e}",
-                "About Pocket Signup",
-                Ci.nsIAboutModule.ALLOW_SCRIPT |
-                Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT |
-                Ci.nsIAboutModule.HIDE_FROM_ABOUTABOUT)
-);
-
-var EXPORTED_SYMBOLS = ["AboutPocket"];
--- a/browser/components/pocket/content/SaveToPocket.jsm
+++ b/browser/components/pocket/content/SaveToPocket.jsm
@@ -1,72 +1,31 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
-ChromeUtils.import("resource://services-common/utils.js");
-ChromeUtils.defineModuleGetter(this, "AboutPocket",
-                               "chrome://pocket/content/AboutPocket.jsm");
-ChromeUtils.defineModuleGetter(this, "AddonManagerPrivate",
-                               "resource://gre/modules/AddonManager.jsm");
+ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.defineModuleGetter(this, "BrowserUtils",
                                "resource://gre/modules/BrowserUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "PageActions",
                                "resource:///modules/PageActions.jsm");
 ChromeUtils.defineModuleGetter(this, "Pocket",
                                "chrome://pocket/content/Pocket.jsm");
 ChromeUtils.defineModuleGetter(this, "ReaderMode",
                                "resource://gre/modules/ReaderMode.jsm");
-ChromeUtils.defineModuleGetter(this, "Services",
-                               "resource://gre/modules/Services.jsm");
 XPCOMUtils.defineLazyGetter(this, "gPocketBundle", function() {
   return Services.strings.createBundle("chrome://pocket/locale/pocket.properties");
 });
-XPCOMUtils.defineLazyGetter(this, "gPocketStyleURI", function() {
-  return Services.io.newURI("chrome://pocket/skin/pocket.css");
-});
 
 var EXPORTED_SYMBOLS = ["SaveToPocket"];
 
-// Due to bug 1051238 frame scripts are cached forever, so we can't update them
-// as a restartless add-on. The Math.random() is the work around for this.
-const PROCESS_SCRIPT = "chrome://pocket/content/pocket-content-process.js?" + Math.random();
-
-const PREF_BRANCH = "extensions.pocket.";
-const PREFS = {
-  enabled: true, // bug 1229937, figure out ui tour support
-  api: "api.getpocket.com",
-  site: "getpocket.com",
-};
-
-function setDefaultPrefs() {
-  let branch = Services.prefs.getDefaultBranch(PREF_BRANCH);
-  for (let [key, val] of Object.entries(PREFS)) {
-    // If someone beat us to setting a default, don't overwrite it.  This can
-    // happen if distribution.ini sets the default first.
-    if (branch.getPrefType(key) != branch.PREF_INVALID)
-      continue;
-    switch (typeof val) {
-      case "boolean":
-        branch.setBoolPref(key, val);
-        break;
-      case "number":
-        branch.setIntPref(key, val);
-        break;
-      case "string":
-        branch.setCharPref(key, val);
-        break;
-    }
-  }
-}
-
 function createElementWithAttrs(document, type, attrs) {
   let element = document.createXULElement(type);
   Object.keys(attrs).forEach(function(attr) {
     element.setAttribute(attr, attrs[attr]);
   });
   return element;
 }
 
@@ -93,56 +52,27 @@ var PocketPageAction = {
         pinnedToUrlbar: true,
         wantsIframe: true,
         urlbarIDOverride: "pocket-button-box",
         anchorIDOverride: "pocket-button",
         _insertBeforeActionID: PageActions.ACTION_ID_BOOKMARK_SEPARATOR,
         _urlbarNodeInMarkup: true,
         onBeforePlacedInWindow(window) {
           let doc = window.document;
-
-          if (doc.getElementById("pocket-button-box")) {
-            return;
-          }
-
-          let wrapper = doc.createXULElement("hbox");
-          wrapper.id = "pocket-button-box";
-          wrapper.classList.add("urlbar-icon-wrapper", "urlbar-page-action");
-          let animatableBox = doc.createXULElement("hbox");
-          animatableBox.id = "pocket-animatable-box";
-          let animatableImage = doc.createXULElement("image");
-          animatableImage.id = "pocket-animatable-image";
-          animatableImage.setAttribute("role", "presentation");
           let tooltip =
             gPocketBundle.GetStringFromName("pocket-button.tooltiptext");
-          animatableImage.setAttribute("tooltiptext", tooltip);
-          let pocketButton = doc.createXULElement("image");
-          pocketButton.id = "pocket-button";
-          pocketButton.classList.add("urlbar-icon");
-          pocketButton.setAttribute("role", "button");
-          pocketButton.setAttribute("tooltiptext", tooltip);
-
-          wrapper.appendChild(pocketButton);
-          wrapper.appendChild(animatableBox);
-          animatableBox.appendChild(animatableImage);
-          let iconBox = doc.getElementById("page-action-buttons");
-          iconBox.appendChild(wrapper);
-          wrapper.hidden = true;
-
-          wrapper.addEventListener("click", event => {
-            let {BrowserPageActions} = wrapper.ownerGlobal;
-            BrowserPageActions.doCommandForAction(this, event, wrapper);
-          });
+          doc.getElementById("pocket-button").setAttribute("tooltiptext", tooltip);
+          doc.getElementById("pocket-button-animatable-image").setAttribute("tooltiptext", tooltip);
         },
         onIframeShowing(iframe, panel) {
           Pocket.onShownInPhotonPageActionPanel(panel, iframe);
 
           let doc = panel.ownerDocument;
           let urlbarNode = doc.getElementById("pocket-button-box");
-          if (!urlbarNode || urlbarNode.hidden) {
+          if (!urlbarNode) {
             return;
           }
 
           BrowserUtils.setToolbarButtonHeightProperty(urlbarNode);
 
           PocketPageAction.urlbarNode = urlbarNode;
           PocketPageAction.urlbarNode.setAttribute("open", "true");
           if (Services.prefs.getBoolPref("toolkit.cosmeticAnimations.enabled")) {
@@ -204,17 +134,17 @@ var PocketPageAction = {
   updateUrlbarNodeState(browserWindow) {
     if (!this.pageAction) {
       return;
     }
     let {BrowserPageActions} = browserWindow;
     let urlbarNode = browserWindow.document.getElementById(
       BrowserPageActions.urlbarButtonNodeIDForActionID(this.pageAction.id)
     );
-    if (!urlbarNode) {
+    if (!urlbarNode || urlbarNode.hidden) {
       return;
     }
     let browser = browserWindow.gBrowser.selectedBrowser;
     let pocketedInnerWindowID = this.innerWindowIDsByBrowser.get(browser);
     if (pocketedInnerWindowID == browser.innerWindowID) {
       // The current window in this browser is pocketed.
       urlbarNode.setAttribute("pocketed", "true");
     } else {
@@ -226,19 +156,17 @@ var PocketPageAction = {
   shutdown() {
     if (!this.pageAction) {
       return;
     }
 
     for (let win of browserWindows()) {
       let doc = win.document;
       let pocketButtonBox = doc.getElementById("pocket-button-box");
-      if (pocketButtonBox) {
-        pocketButtonBox.remove();
-      }
+      pocketButtonBox.setAttribute("hidden", "true");
     }
 
     this.pageAction.remove();
     this.pageAction = null;
   },
 };
 
 // PocketContextMenu
@@ -374,97 +302,48 @@ var PocketReader = {
         }
         break;
       }
     }
   },
 };
 
 
-function pktUIGetter(prop, window) {
-  return {
-    get() {
-      // delete any getters for properties loaded from main.js so we only load main.js once
-      delete window.pktUI;
-      delete window.pktApi;
-      delete window.pktUIMessaging;
-      Services.scriptloader.loadSubScript("chrome://pocket/content/main.js", window);
-      return window[prop];
-    },
-    configurable: true,
-    enumerable: true,
-  };
-}
-
 var PocketOverlay = {
   startup() {
-    let styleSheetService = Cc["@mozilla.org/content/style-sheet-service;1"]
-                              .getService(Ci.nsIStyleSheetService);
-    this._sheetType = styleSheetService.AUTHOR_SHEET;
-    this._cachedSheet = styleSheetService.preloadSheet(gPocketStyleURI,
-                                                       this._sheetType);
-    Services.ppmm.loadProcessScript(PROCESS_SCRIPT, true);
     Services.obs.addObserver(this, "browser-delayed-startup-finished");
     PocketReader.startup();
     PocketPageAction.init();
     PocketContextMenu.init();
     for (let win of browserWindows()) {
-      this.onWindowOpened(win);
+      this.updateWindow(win);
     }
   },
   shutdown() {
-    Services.ppmm.broadcastAsyncMessage("PocketShuttingDown");
     Services.obs.removeObserver(this, "browser-delayed-startup-finished");
-    // Although the ppmm loads the scripts into the chrome process as well,
-    // we need to manually unregister here anyway to ensure these aren't part
-    // of the chrome process and avoid errors.
-    AboutPocket.aboutSaved.unregister();
-    AboutPocket.aboutSignup.unregister();
 
     PocketPageAction.shutdown();
 
     for (let window of browserWindows()) {
       for (let id of ["appMenu-library-pocket-button"]) {
         let element = window.document.getElementById(id) ||
                       window.gNavToolbox.palette.querySelector("#" + id);
         if (element)
           element.remove();
       }
-      this.removeStyles(window);
-      // remove script getters/objects
-      window.Pocket = undefined;
-      window.pktApi = undefined;
-      window.pktUI = undefined;
-      window.pktUIMessaging = undefined;
     }
 
     PocketContextMenu.shutdown();
     PocketReader.shutdown();
   },
   observe(subject, topic, detail) {
     if (topic == "browser-delayed-startup-finished") {
-      this.onWindowOpened(subject);
+      this.updateWindow(subject);
     }
   },
-  onWindowOpened(window) {
-    if (window.hasOwnProperty("pktUI"))
-      return;
-    this.setWindowScripts(window);
-    this.addStyles(window);
-    this.updateWindow(window);
-  },
-  setWindowScripts(window) {
-    ChromeUtils.defineModuleGetter(window, "Pocket",
-                                   "chrome://pocket/content/Pocket.jsm");
-    // Can't use XPCOMUtils for these because the scripts try to define the variables
-    // on window, and so the defineProperty inside defineLazyGetter fails.
-    Object.defineProperty(window, "pktApi", pktUIGetter("pktApi", window));
-    Object.defineProperty(window, "pktUI", pktUIGetter("pktUI", window));
-    Object.defineProperty(window, "pktUIMessaging", pktUIGetter("pktUIMessaging", window));
-  },
   // called for each window as it is opened
   updateWindow(window) {
     // insert our three menu items
     let document = window.document;
     let hidden = !isPocketEnabled();
 
     // Add to library panel
     let sib = document.getElementById("appMenu-library-history-button");
@@ -477,27 +356,16 @@ var PocketOverlay = {
         "hidden": hidden,
       });
       sib.parentNode.insertBefore(menu, sib);
     }
 
     // enable or disable reader button
     PocketReader.hidden = hidden;
   },
-
-  addStyles(win) {
-    let utils = win.windowUtils;
-    utils.addSheet(this._cachedSheet, this._sheetType);
-  },
-
-  removeStyles(win) {
-    let utils = win.windowUtils;
-    utils.removeSheet(gPocketStyleURI, this._sheetType);
-  },
-
 };
 
 // use enabled pref as a way for tests (e.g. test_contextmenu.html) to disable
 // the addon when running.
 function prefObserver(aSubject, aTopic, aData) {
   let enabled = Services.prefs.getBoolPref("extensions.pocket.enabled");
   if (enabled)
     PocketOverlay.startup();
@@ -506,20 +374,16 @@ function prefObserver(aSubject, aTopic, 
 }
 
 function browserWindows() {
   return Services.wm.getEnumerator("navigator:browser");
 }
 
 var SaveToPocket = {
   init() {
-    if (AddonManagerPrivate.addonIsActive("isreaditlater@ideashower.com"))
-      return;
-
-    setDefaultPrefs();
     // migrate enabled pref
     if (Services.prefs.prefHasUserValue("browser.pocket.enabled")) {
       Services.prefs.setBoolPref("extensions.pocket.enabled", Services.prefs.getBoolPref("browser.pocket.enabled"));
       Services.prefs.clearUserPref("browser.pocket.enabled");
     }
     // watch pref change and enable/disable if necessary
     Services.prefs.addObserver("extensions.pocket.enabled", prefObserver);
     if (!Services.prefs.getBoolPref("extensions.pocket.enabled"))
deleted file mode 100644
--- a/browser/components/pocket/content/pocket-content-process.js
+++ /dev/null
@@ -1,51 +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/. */
-"use strict";
-
-// This file is loaded as a process script, it will be loaded in the parent
-// process as well as all content processes.
-
-ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
-ChromeUtils.import("resource://gre/modules/Services.jsm");
-ChromeUtils.import("chrome://pocket/content/AboutPocket.jsm");
-
-function AboutPocketChildListener() {
-}
-AboutPocketChildListener.prototype = {
-  onStartup: function onStartup() {
-
-    // Only do this in content processes since, as the broadcaster of this
-    // message, the parent process doesn't also receive it.  We handlers
-    // the shutting down separately.
-    if (Services.appinfo.processType ==
-        Services.appinfo.PROCESS_TYPE_CONTENT) {
-
-      Services.cpmm.addMessageListener("PocketShuttingDown", this, true);
-    }
-
-    AboutPocket.aboutSaved.register();
-    AboutPocket.aboutSignup.register();
-  },
-
-  onShutdown: function onShutdown() {
-    AboutPocket.aboutSignup.unregister();
-    AboutPocket.aboutSaved.unregister();
-
-    Services.cpmm.removeMessageListener("PocketShuttingDown", this);
-    Cu.unload("chrome://pocket/content/AboutPocket.jsm");
-  },
-
-  receiveMessage: function receiveMessage(message) {
-    switch (message.name) {
-      case "PocketShuttingDown":
-        this.onShutdown();
-        break;
-      default:
-        break;
-    }
-  },
-};
-
-const listener = new AboutPocketChildListener();
-listener.onStartup();
--- a/browser/components/pocket/jar.mn
+++ b/browser/components/pocket/jar.mn
@@ -1,10 +1,8 @@
 # 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/.
 
 browser.jar:
 % content pocket %content/pocket/ contentaccessible=yes
-% skin pocket classic/1.0 %skin/classic/browser/pocket/
   content/pocket/  (content/*)
-  skin/classic/browser/pocket/ (skin/*)
 
deleted file mode 100644
--- a/browser/components/pocket/skin/pocket.css
+++ /dev/null
@@ -1,183 +0,0 @@
-#pageActionActivatedActionPanel[actionID="pocket"] > .panel-arrowcontainer > .panel-arrowcontent {
-  padding-top: 0;
-  padding-bottom: 0;
-}
-
-#pageActionActivatedActionPanel[actionID="pocket"] > .panel-arrowcontainer > .panel-arrowbox > .panel-arrow {
-  fill: #fbfbfb;
-}
-
-#pocket-button,
-#pageAction-panel-pocket {
-  list-style-image: url("chrome://pocket/skin/pocket-outline.svg");
-}
-
-#appMenu-library-pocket-button,
-#pocket-button-box[open="true"] > #pocket-button,
-#pocket-button-box[pocketed="true"] > #pocket-button {
-  list-style-image: url("chrome://pocket/skin/pocket.svg");
-}
-
-#pocket-button-box[animate="true"] > #pocket-button,
-#pocket-button[open="true"][animationsenabled] > .toolbarbutton-icon {
-  fill: transparent;
-}
-
-#pocket-button-box[open="true"] > #pocket-button,
-#pocket-button-box[pocketed="true"] > #pocket-button {
-  fill: #ef4056;
-  fill-opacity: 1;
-}
-
-@keyframes pocket-animation {
-  from {
-    transform: translateX(0);
-  }
-  to {
-    transform: translateX(-240px);
-  }
-}
-
-@keyframes pocket-animation-rtl {
-  from {
-    transform: scaleX(-1) translateX(0);
-  }
-  to {
-    transform: scaleX(-1) translateX(-240px);
-  }
-}
-
-#pocket-button-box[animate="true"] > #pocket-animatable-box,
-#pocket-button > .toolbarbutton-animatable-box {
-  top: calc(50% - 8px); /* 8px is half the height of the sprite */
-  /* Since .toolbarbutton-icon uses a different width than the animatable box,
-     we need to set a padding relative to the difference in widths. */
-  margin-inline-start: calc((16px + 2 * var(--toolbarbutton-inner-padding) - 20px) / 2);
-  width: 20px; /* Width of each frame within the SVG sprite */
-  height: 16px; /* Height of each frame within the SVG sprite */
-}
-
-#pocket-button-box[animate="true"] > #pocket-animatable-box {
-  position: absolute;
-  overflow: hidden;
-  /* .urlbar-icon has width 28px. Each frame is 20px wide. Set margin-inline-start
-     to be half the difference, 4px. */
-  margin-inline-start: 4px;
-}
-
-:root[uidensity=compact] #pocket-button-box[animate="true"] > #pocket-animatable-box {
-  /* .urlbar-icon has width 24px in this case */
-  margin-inline-start: 2px;
-}
-
-:root[uidensity=touch] #pocket-button-box[animate="true"] > #pocket-animatable-box {
-  /* .urlbar-icon has width 30px in this case */
-  margin-inline-start: 5px;
-}
-
-#pocket-button-box[animate="true"] > #pocket-animatable-box > #pocket-animatable-image,
-#pocket-button > .toolbarbutton-animatable-box > .toolbarbutton-animatable-image {
-  height: var(--toolbarbutton-height); /* Height must be equal to height of toolbarbutton padding-box */
-}
-
-#pocket-button-box[animate="true"],
-#pocket-button[open="true"][animationsenabled][cui-areatype="toolbar"]:not([overflowedItem="true"]) {
-  position: relative;
-}
-
-#pocket-button-box:not([animate="true"]):not(:hover) > #pocket-animatable-box {
-  display: none;
-}
-
-/* Preload pocket-animation.svg and library-pocket-animation.svg to prevent
-   a flicker at the start of either animation. The preloading of the library
-   animation is triggered off of hovering the pocket button since the pocket
-   button always animates before the library button. */
-#pocket-button-box:not([animate="true"]):hover > #pocket-animatable-box > #pocket-animatable-image,
-#pocket-button[animationsenabled][cui-areatype="toolbar"]:not([overflowedItem="true"]):not([open="true"]):hover > .toolbarbutton-animatable-box > .toolbarbutton-animatable-image {
-  background-image: url("chrome://pocket/skin/pocket-animation.svg"),
-                    url("chrome://pocket/skin/library-pocket-animation.svg");
-  background-size: 0, 0;
-}
-
-#pocket-button-box[animate="true"] > #pocket-animatable-box > #pocket-animatable-image,
-#pocket-button[open="true"][animationsenabled][cui-areatype="toolbar"]:not([overflowedItem="true"]) > .toolbarbutton-animatable-box > .toolbarbutton-animatable-image {
-  animation-name: pocket-animation;
-  animation-timing-function: steps(12);
-  animation-duration: 200ms;
-  background-image: url("chrome://pocket/skin/pocket-animation.svg");
-  fill: #ef4056;
-  -moz-context-properties: fill;
-  width: 260px;
-}
-
-#pocket-button-box[animate="true"]:-moz-locale-dir(rtl) > #pocket-animatable-box > #pocket-animatable-image,
-#pocket-button[open="true"][animationsenabled][cui-areatype="toolbar"]:not([overflowedItem="true"]):-moz-locale-dir(rtl) > .toolbarbutton-animatable-box > .toolbarbutton-animatable-image {
-  animation-name: pocket-animation-rtl;
-}
-
-@keyframes library-pocket-animation {
-  from {
-    transform: translateX(0);
-    fill: inherit;
-  }
-  25% {
-    fill: inherit;
-  }
-  50% {
-    fill: #ef4056;
-  }
-  to {
-    transform: translateX(-1056px);
-    fill: #ef4056;
-  }
-}
-
-@keyframes library-pocket-animation-rtl {
-  from {
-    transform: translateX(1056px);
-    fill: inherit;
-  }
-  25% {
-    fill: inherit;
-  }
-  50% {
-    fill: #ef4056;
-  }
-  to {
-    transform: translateX(0);
-    fill: #ef4056;
-  }
-}
-
-/* We need to use an animation here instead of a transition
-   to guarantee that the animation succeeds. With transitions
-   if the starting value is already equal to the end value
-   then no transition will occur and thus no transitionend event. */
-@keyframes library-pocket-fade {
-  from {
-    fill: #ef4056;
-  }
-  to {
-    fill: inherit;
-  }
-}
-
-#library-animatable-box[animate="pocket"] > .toolbarbutton-animatable-image {
-  background-image: url("chrome://pocket/skin/library-pocket-animation.svg");
-  width: 1078px;
-  animation-name: library-pocket-animation;
-  animation-duration: 800ms;
-  animation-timing-function: steps(48);
-}
-
-#library-animatable-box[animate="pocket"]:-moz-locale-dir(rtl) > .toolbarbutton-animatable-image {
-  animation-name: library-pocket-animation-rtl;
-}
-
-#library-animatable-box[animate="pocket"][fade] > .toolbarbutton-animatable-image {
-  animation-name: library-pocket-fade;
-  animation-duration: 2s;
-  animation-timing-function: ease-out;
-}
-
--- a/browser/components/pocket/test/browser_pocket_ui_check.js
+++ b/browser/components/pocket/test/browser_pocket_ui_check.js
@@ -10,18 +10,19 @@ add_task(async function test_setup() {
       Services.prefs.setBoolPref("extensions.pocket.enabled", enabledOnStartup);
     }
   });
 });
 
 add_task(async function() {
   await promisePocketEnabled();
 
-  checkWindowProperties(true, ["Pocket", "pktUI", "pktUIMessaging"]);
   checkElements(true, ["pocket-button", "appMenu-library-pocket-button"]);
+  let buttonBox = document.getElementById("pocket-button-box");
+  is(buttonBox.hidden, false, "Button should not have been hidden");
 
   // check context menu exists
   info("checking content context menu");
   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "https://example.com/browser/browser/components/pocket/test/test.html");
 
   let contextMenu = document.getElementById("contentAreaContextMenu");
   let popupShown = BrowserTestUtils.waitForEvent(contextMenu, "popupshown");
   let popupHidden = BrowserTestUtils.waitForEvent(contextMenu, "popuphidden");
@@ -34,14 +35,15 @@ add_task(async function() {
   checkElements(true, ["context-pocket", "context-savelinktopocket"]);
 
   contextMenu.hidePopup();
   await popupHidden;
   BrowserTestUtils.removeTab(tab);
 
   await promisePocketDisabled();
 
-  checkWindowProperties(false, ["Pocket", "pktUI", "pktUIMessaging"]);
-  checkElements(false, ["pocket-button", "appMenu-library-pocket-button",
+  checkElements(false, ["appMenu-library-pocket-button",
                         "context-pocket", "context-savelinktopocket"]);
+  buttonBox = document.getElementById("pocket-button-box");
+  is(buttonBox.hidden, true, "Button should have been hidden");
 
   await promisePocketReset();
 });
--- a/browser/components/pocket/test/head.js
+++ b/browser/components/pocket/test/head.js
@@ -26,37 +26,26 @@ function promisePocketDisabled() {
     return Promise.resolve(true);
   }
   info("reset pocket enabled pref");
   // testing/profiles/common/user.js uses user_pref to disable pocket, set
   // back to false.
   Services.prefs.setBoolPref("extensions.pocket.enabled", false);
   return BrowserTestUtils.waitForCondition(() => {
     return !PageActions.actionForID("pocket");
-  }).then(() => {
-    // wait for a full unload of pocket
-    return BrowserTestUtils.waitForCondition(() => {
-      return !window.hasOwnProperty("pktUI") || !window.pktUI;
-    });
   });
 }
 
 function promisePocketReset() {
   if (enabledOnStartup) {
     info("reset is enabling pocket addon");
     return promisePocketEnabled();
   }
   info("reset is disabling pocket addon");
   return promisePocketDisabled();
 }
 
-function checkWindowProperties(expectPresent, l) {
-  for (let name of l) {
-    is(window.hasOwnProperty(name) && !!window[name], expectPresent, "property " + name + (expectPresent ? " is" : " is not") + " present");
-  }
-}
-
 function checkElements(expectPresent, l) {
   for (let id of l) {
     let el = document.getElementById(id) || gNavToolbox.palette.querySelector("#" + id);
     is(!!el, expectPresent, "element " + id + (expectPresent ? " is" : " is not") + " present");
   }
 }
rename from browser/components/pocket/skin/library-pocket-animation.svg
rename to browser/themes/shared/icons/library-pocket-animation.svg
rename from browser/components/pocket/skin/pocket-animation.svg
rename to browser/themes/shared/icons/pocket-animation.svg
rename from browser/components/pocket/skin/pocket-outline.svg
rename to browser/themes/shared/icons/pocket-outline.svg
rename from browser/components/pocket/skin/pocket.svg
rename to browser/themes/shared/icons/pocket.svg
--- a/browser/themes/shared/jar.inc.mn
+++ b/browser/themes/shared/jar.inc.mn
@@ -151,24 +151,28 @@
   skin/classic/browser/forget.svg                     (../shared/icons/forget.svg)
   skin/classic/browser/forward.svg                    (../shared/icons/forward.svg)
   skin/classic/browser/fullscreen.svg                 (../shared/icons/fullscreen.svg)
   skin/classic/browser/fullscreen-exit.svg            (../shared/icons/fullscreen-exit.svg)
   skin/classic/browser/history.svg                    (../shared/icons/history.svg)
   skin/classic/browser/home.svg                       (../shared/icons/home.svg)
   skin/classic/browser/library.svg                    (../shared/icons/library.svg)
   skin/classic/browser/library-bookmark-animation.svg (../shared/icons/library-bookmark-animation.svg)
+  skin/classic/browser/library-pocket-animation.svg   (../shared/icons/library-pocket-animation.svg)
   skin/classic/browser/link.svg                       (../shared/icons/link.svg)
   skin/classic/browser/mail.svg                       (../shared/icons/mail.svg)
   skin/classic/browser/menu.svg                       (../shared/icons/menu.svg)
   skin/classic/browser/menu-badged.svg                (../shared/icons/menu-badged.svg)
   skin/classic/browser/new-tab.svg                    (../shared/icons/new-tab.svg)
   skin/classic/browser/new-window.svg                 (../shared/icons/new-window.svg)
   skin/classic/browser/open.svg                       (../shared/icons/open.svg)
   skin/classic/browser/page-action.svg                (../shared/icons/page-action.svg)
+  skin/classic/browser/pocket.svg                     (../shared/icons/pocket.svg)
+  skin/classic/browser/pocket-animation.svg           (../shared/icons/pocket-animation.svg)
+  skin/classic/browser/pocket-outline.svg             (../shared/icons/pocket-outline.svg)
   skin/classic/browser/print.svg                      (../shared/icons/print.svg)
   skin/classic/browser/private-browsing.svg           (../shared/icons/private-browsing.svg)
   skin/classic/browser/privateBrowsing.svg            (../shared/icons/privateBrowsing.svg)
   skin/classic/browser/restore-session.svg            (../shared/icons/restore-session.svg)
   skin/classic/browser/quit.svg                       (../shared/icons/quit.svg)
   skin/classic/browser/reload.svg                     (../shared/icons/reload.svg)
   skin/classic/browser/reload-to-stop.svg             (../shared/icons/reload-to-stop.svg)
   skin/classic/browser/save.svg                       (../shared/icons/save.svg)
--- a/browser/themes/shared/menupanel.inc.css
+++ b/browser/themes/shared/menupanel.inc.css
@@ -68,16 +68,20 @@
 #appMenu-fullscreen-button {
   list-style-image: url(chrome://browser/skin/fullscreen.svg);
 }
 
 #appMenu-fullscreen-button[checked] {
   list-style-image: url(chrome://browser/skin/fullscreen-exit.svg);
 }
 
+#appMenu-library-pocket-button {
+  list-style-image: url("chrome://browser/skin/pocket.svg");
+}
+
 #appMenu-library-history-button {
   list-style-image: url(chrome://browser/skin/history.svg);
 }
 
 #appMenuRecentlyClosedTabs,
 #appMenu-library-remotetabs-button {
   list-style-image: url("chrome://browser/skin/tab.svg");
 }
--- a/browser/themes/shared/toolbarbutton-icons.inc.css
+++ b/browser/themes/shared/toolbarbutton-icons.inc.css
@@ -466,36 +466,99 @@ toolbar[brighttext] {
      is positioned using `left`. */
   left: calc(var(--library-icon-x) + (16px + 2 * var(--toolbarbutton-inner-padding) - 22px) / 2);
   width: 22px; /* Width of each frame within the SVG sprite */
   height: 54px; /* Height of each frame within the SVG sprite */
 }
 
 #library-animatable-box[animate] > .toolbarbutton-animatable-image {
   min-height: 54px; /* Minimum height must be equal to the height of the SVG sprite */
+  width: 1078px;
+  animation-duration: 800ms;
+  animation-timing-function: steps(48);
+}
+
+#library-animatable-box[animate][fade] > .toolbarbutton-animatable-image {
+  animation-duration: 2s;
+  animation-timing-function: ease-out;
 }
 
 #library-animatable-box[animate="bookmark"] > .toolbarbutton-animatable-image {
   background-image: url("chrome://browser/skin/library-bookmark-animation.svg");
-  width: 1078px;
   animation-name: library-bookmark-animation;
-  animation-duration: 800ms;
-  animation-timing-function: steps(48);
   -moz-context-properties: fill, fill-opacity, stroke;
   stroke: var(--toolbarbutton-icon-fill-attention);
 }
 
 #library-animatable-box[animate="bookmark"]:-moz-locale-dir(rtl) > .toolbarbutton-animatable-image {
   animation-name: library-bookmark-animation-rtl;
 }
 
 #library-animatable-box[animate="bookmark"][fade] > .toolbarbutton-animatable-image {
   animation-name: library-bookmark-fade;
-  animation-duration: 2s;
-  animation-timing-function: ease-out;
+}
+
+@keyframes library-pocket-animation {
+  from {
+    transform: translateX(0);
+    fill: inherit;
+  }
+  25% {
+    fill: inherit;
+  }
+  50% {
+    fill: #ef4056;
+  }
+  to {
+    transform: translateX(-1056px);
+    fill: #ef4056;
+  }
+}
+
+@keyframes library-pocket-animation-rtl {
+  from {
+    transform: translateX(1056px);
+    fill: inherit;
+  }
+  25% {
+    fill: inherit;
+  }
+  50% {
+    fill: #ef4056;
+  }
+  to {
+    transform: translateX(0);
+    fill: #ef4056;
+  }
+}
+
+/* We need to use an animation here instead of a transition
+   to guarantee that the animation succeeds. With transitions
+   if the starting value is already equal to the end value
+   then no transition will occur and thus no transitionend event. */
+@keyframes library-pocket-fade {
+  from {
+    fill: #ef4056;
+  }
+  to {
+    fill: inherit;
+  }
+}
+
+#library-animatable-box[animate="pocket"] > .toolbarbutton-animatable-image {
+  background-image: url("chrome://browser/skin/library-pocket-animation.svg");
+  animation-name: library-pocket-animation;
+}
+
+#library-animatable-box[animate="pocket"]:-moz-locale-dir(rtl) > .toolbarbutton-animatable-image {
+  animation-name: library-pocket-animation-rtl;
+}
+
+#library-animatable-box[animate="pocket"][fade] > .toolbarbutton-animatable-image {
+  animation-name: library-pocket-fade;
 }
 
 /* ----- BOOKMARK BUTTONS ----- */
 
 .bookmark-item {
   list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.svg");
   -moz-context-properties: fill;
   fill: currentColor;
--- a/browser/themes/shared/urlbar-searchbar.inc.css
+++ b/browser/themes/shared/urlbar-searchbar.inc.css
@@ -128,16 +128,37 @@
 #star-button[starred] {
   list-style-image: url("chrome://browser/skin/bookmark.svg");
 }
 #star-button[starred] {
   fill-opacity: 1;
   fill: var(--toolbarbutton-icon-fill-attention);
 }
 
+#pocket-button,
+#pageAction-panel-pocket {
+  list-style-image: url("chrome://browser/skin/pocket-outline.svg");
+}
+
+#pocket-button-box[open="true"] > #pocket-button,
+#pocket-button-box[pocketed="true"] > #pocket-button {
+  list-style-image: url("chrome://browser/skin/pocket.svg");
+}
+
+#pocket-button-box[animate="true"] > #pocket-button,
+#pocket-button[open="true"][animationsenabled] > .toolbarbutton-icon {
+  fill: transparent;
+}
+
+#pocket-button-box[open="true"] > #pocket-button,
+#pocket-button-box[pocketed="true"] > #pocket-button {
+  fill: #ef4056;
+  fill-opacity: 1;
+}
+
 #pageAction-panel-copyURL,
 #pageAction-urlbar-copyURL {
   list-style-image: url("chrome://browser/skin/link.svg");
 }
 
 #pageAction-panel-emailLink,
 #pageAction-urlbar-emailLink {
   list-style-image: url("chrome://browser/skin/mail.svg");
@@ -183,16 +204,25 @@
   margin: -4px 0 0 !important;
   margin-inline-end: -4px !important;
   width: 11px;
   height: 11px;
   min-width: 11px;
   min-height: 11px;
 }
 
+#pageActionActivatedActionPanel[actionID="pocket"] > .panel-arrowcontainer > .panel-arrowcontent {
+  padding-top: 0;
+  padding-bottom: 0;
+}
+
+#pageActionActivatedActionPanel[actionID="pocket"] > .panel-arrowcontainer > .panel-arrowbox > .panel-arrow {
+  fill: #fbfbfb;
+}
+
 /* URL bar and page action buttons */
 
 #page-action-buttons {
   -moz-box-align: center;
 }
 
 #pageActionSeparator {
   /* This draws the separator the same way that #urlbar-display-box draws its
@@ -377,16 +407,104 @@
   -moz-context-properties: fill, stroke;
   stroke: var(--toolbarbutton-icon-fill-attention);
 }
 
 #star-button-box[animationsenabled] > #star-button[starred][animate]:-moz-locale-dir(rtl) + #star-button-animatable-box > #star-button-animatable-image {
   animation-name: bookmark-animation-rtl;
 }
 
+@keyframes pocket-animation {
+  from {
+    transform: translateX(0);
+  }
+  to {
+    transform: translateX(-240px);
+  }
+}
+
+@keyframes pocket-animation-rtl {
+  from {
+    transform: scaleX(-1) translateX(0);
+  }
+  to {
+    transform: scaleX(-1) translateX(-240px);
+  }
+}
+
+#pocket-button-box[animate="true"] > #pocket-animatable-box,
+#pocket-button > .toolbarbutton-animatable-box {
+  top: calc(50% - 8px); /* 8px is half the height of the sprite */
+  /* Since .toolbarbutton-icon uses a different width than the animatable box,
+     we need to set a padding relative to the difference in widths. */
+  margin-inline-start: calc((16px + 2 * var(--toolbarbutton-inner-padding) - 20px) / 2);
+  width: 20px; /* Width of each frame within the SVG sprite */
+  height: 16px; /* Height of each frame within the SVG sprite */
+}
+
+#pocket-button-box[animate="true"] > #pocket-animatable-box {
+  position: absolute;
+  overflow: hidden;
+  /* .urlbar-icon has width 28px. Each frame is 20px wide. Set margin-inline-start
+     to be half the difference, 4px. */
+  margin-inline-start: 4px;
+}
+
+:root[uidensity=compact] #pocket-button-box[animate="true"] > #pocket-animatable-box {
+  /* .urlbar-icon has width 24px in this case */
+  margin-inline-start: 2px;
+}
+
+:root[uidensity=touch] #pocket-button-box[animate="true"] > #pocket-animatable-box {
+  /* .urlbar-icon has width 30px in this case */
+  margin-inline-start: 5px;
+}
+
+#pocket-button-box[animate="true"] > #pocket-animatable-box > #pocket-animatable-image,
+#pocket-button > .toolbarbutton-animatable-box > .toolbarbutton-animatable-image {
+  height: var(--toolbarbutton-height); /* Height must be equal to height of toolbarbutton padding-box */
+}
+
+#pocket-button-box[animate="true"],
+#pocket-button[open="true"][animationsenabled][cui-areatype="toolbar"]:not([overflowedItem="true"]) {
+  position: relative;
+}
+
+#pocket-button-box:not([animate="true"]):not(:hover) > #pocket-animatable-box {
+  display: none;
+}
+
+/* Preload pocket-animation.svg and library-pocket-animation.svg to prevent
+   a flicker at the start of either animation. The preloading of the library
+   animation is triggered off of hovering the pocket button since the pocket
+   button always animates before the library button. */
+#pocket-button-box:not([animate="true"]):hover > #pocket-animatable-box > #pocket-animatable-image,
+#pocket-button[animationsenabled][cui-areatype="toolbar"]:not([overflowedItem="true"]):not([open="true"]):hover > .toolbarbutton-animatable-box > .toolbarbutton-animatable-image {
+  background-image: url("chrome://browser/skin/pocket-animation.svg"),
+                    url("chrome://browser/skin/library-pocket-animation.svg");
+  background-size: 0, 0;
+}
+
+#pocket-button-box[animate="true"] > #pocket-animatable-box > #pocket-animatable-image,
+#pocket-button[open="true"][animationsenabled][cui-areatype="toolbar"]:not([overflowedItem="true"]) > .toolbarbutton-animatable-box > .toolbarbutton-animatable-image {
+  animation-name: pocket-animation;
+  animation-timing-function: steps(12);
+  animation-duration: 200ms;
+  background-image: url("chrome://browser/skin/pocket-animation.svg");
+  fill: #ef4056;
+  -moz-context-properties: fill;
+  width: 260px;
+}
+
+#pocket-button-box[animate="true"]:-moz-locale-dir(rtl) > #pocket-animatable-box > #pocket-animatable-image,
+#pocket-button[open="true"][animationsenabled][cui-areatype="toolbar"]:not([overflowedItem="true"]):-moz-locale-dir(rtl) > .toolbarbutton-animatable-box > .toolbarbutton-animatable-image {
+  animation-name: pocket-animation-rtl;
+}
+
+
 /**
  * Contextual Feature Recommendation
  *
  * Animate the recommendation icon to expand outwards and display a text label.
  * Fake the effect of a smoothly expanding width without animating any widths
  * by (continuously) animating only `mask-position-x` and `transform`.
  *
  * In a few places, transition a property using the timing-function `step-start`
--- a/build/virtualenv_packages.txt
+++ b/build/virtualenv_packages.txt
@@ -40,16 +40,17 @@ mozilla.pth:third_party/python/scandir
 mozilla.pth:third_party/python/slugid
 mozilla.pth:third_party/python/taskcluster
 mozilla.pth:third_party/python/taskcluster-urls
 mozilla.pth:third_party/python/py
 mozilla.pth:third_party/python/pytest/src
 mozilla.pth:third_party/python/pytoml
 mozilla.pth:third_party/python/redo
 mozilla.pth:third_party/python/six
+mozilla.pth:third_party/python/taskcluster-urls
 mozilla.pth:third_party/python/voluptuous
 mozilla.pth:third_party/python/json-e
 mozilla.pth:build
 objdir:build
 mozilla.pth:build/pymake
 mozilla.pth:config
 mozilla.pth:config/mozunit
 mozilla.pth:dom/bindings
--- a/devtools/client/debugger/new/src/actions/source-tree.js
+++ b/devtools/client/debugger/new/src/actions/source-tree.js
@@ -1,17 +1,27 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 // @flow
 
-import type { Action, ThunkArgs } from "./types";
+import type { Action, FocusItem, ThunkArgs } from "./types";
 
-export function setExpandedState(expanded: Set<string>) {
+export function setExpandedState(thread: string, expanded: Set<string>) {
   return ({ dispatch, getState }: ThunkArgs) => {
     dispatch(
       ({
         type: "SET_EXPANDED_STATE",
+        thread,
         expanded
       }: Action)
     );
   };
 }
+
+export function focusItem(item: FocusItem) {
+  return ({ dispatch, getState }: ThunkArgs) => {
+    dispatch({
+      type: "SET_FOCUSED_SOURCE_ITEM",
+      item
+    });
+  };
+}
--- a/devtools/client/debugger/new/src/actions/tabs.js
+++ b/devtools/client/debugger/new/src/actions/tabs.js
@@ -21,37 +21,39 @@ import {
   removeSourceFromTabList,
   removeSourcesFromTabList
 } from "../selectors";
 
 import type { Action, ThunkArgs } from "./types";
 import type { Source } from "../types";
 
 export function updateTab(source: Source, framework: string): Action {
-  const { url, id: sourceId } = source;
+  const { url, id: sourceId, thread } = source;
   const isOriginal = isOriginalId(source.id);
 
   return {
     type: "UPDATE_TAB",
     url,
     framework,
     isOriginal,
-    sourceId
+    sourceId,
+    thread
   };
 }
 
 export function addTab(source: Source): Action {
-  const { url, id: sourceId } = source;
+  const { url, id: sourceId, thread } = source;
   const isOriginal = isOriginalId(source.id);
 
   return {
     type: "ADD_TAB",
     url,
     isOriginal,
-    sourceId
+    sourceId,
+    thread
   };
 }
 
 export function moveTab(url: string, tabIndex: number): Action {
   return {
     type: "MOVE_TAB",
     url,
     tabIndex
--- a/devtools/client/debugger/new/src/components/Editor/Tab.js
+++ b/devtools/client/debugger/new/src/components/Editor/Tab.js
@@ -28,17 +28,18 @@ import {
 import { shouldShowPrettyPrint } from "../../utils/editor";
 import { copyToTheClipboard } from "../../utils/clipboard";
 import { getTabMenuItems } from "../../utils/tabs";
 
 import {
   getSelectedSource,
   getActiveSearch,
   getSourcesForTabs,
-  getHasSiblingOfSameName
+  getHasSiblingOfSameName,
+  getWorkerDisplayName
 } from "../../selectors";
 import type { ActiveSearchType } from "../../selectors";
 
 import classnames from "classnames";
 
 type SourcesList = List<Source>;
 
 type Props = {
@@ -46,17 +47,18 @@ type Props = {
   selectedSource: Source,
   source: Source,
   activeSearch: ActiveSearchType,
   hasSiblingOfSameName: boolean,
   selectSource: typeof actions.selectSource,
   closeTab: typeof actions.closeTab,
   closeTabs: typeof actions.closeTabs,
   togglePrettyPrint: typeof actions.togglePrettyPrint,
-  showSource: typeof actions.showSource
+  showSource: typeof actions.showSource,
+  threadName: string
 };
 
 class Tab extends PureComponent<Props> {
   onTabContextMenu = (event, tab: string) => {
     event.preventDefault();
     this.showContextMenu(event, tab);
   };
 
@@ -156,17 +158,18 @@ class Tab extends PureComponent<Props> {
 
   render() {
     const {
       selectedSource,
       selectSource,
       closeTab,
       source,
       tabSources,
-      hasSiblingOfSameName
+      hasSiblingOfSameName,
+      threadName
     } = this.props;
     const sourceId = source.id;
     const active =
       selectedSource &&
       sourceId == selectedSource.id &&
       (!this.isProjectSearchEnabled() && !this.isSourceSearchEnabled());
     const isPrettyCode = isPretty(source);
 
@@ -183,33 +186,34 @@ class Tab extends PureComponent<Props> {
 
     const className = classnames("source-tab", {
       active,
       pretty: isPrettyCode
     });
 
     const path = getDisplayPath(source, tabSources);
     const query = hasSiblingOfSameName ? getSourceQueryString(source) : "";
+    const threadNamePrefix = `${threadName}${threadName ? ": " : ""}`;
 
     return (
       <div
         className={className}
         key={sourceId}
         onClick={handleTabClick}
         // Accommodate middle click to close tab
         onMouseUp={e => e.button === 1 && closeTab(source)}
         onContextMenu={e => this.onTabContextMenu(e, sourceId)}
         title={getFileURL(source, false)}
       >
         <SourceIcon
           source={source}
           shouldHide={icon => ["file", "javascript"].includes(icon)}
         />
         <div className="filename">
-          {getTruncatedFileName(source, query)}
+          {`${threadNamePrefix}${getTruncatedFileName(source, query)}`}
           {path && <span>{`../${path}/..`}</span>}
         </div>
         <CloseButton
           handleClick={onClickClose}
           tooltip={L10N.getStr("sourceTabs.closeTabButtonTooltip")}
         />
       </div>
     );
@@ -218,17 +222,18 @@ class Tab extends PureComponent<Props> {
 
 const mapStateToProps = (state, { source }) => {
   const selectedSource = getSelectedSource(state);
 
   return {
     tabSources: getSourcesForTabs(state),
     selectedSource: selectedSource,
     activeSearch: getActiveSearch(state),
-    hasSiblingOfSameName: getHasSiblingOfSameName(state, source)
+    hasSiblingOfSameName: getHasSiblingOfSameName(state, source),
+    threadName: getWorkerDisplayName(state, source.thread)
   };
 };
 
 export default connect(
   mapStateToProps,
   {
     selectSource: actions.selectSource,
     closeTab: actions.closeTab,
--- a/devtools/client/debugger/new/src/components/PrimaryPanes/SourcesTree.js
+++ b/devtools/client/debugger/new/src/components/PrimaryPanes/SourcesTree.js
@@ -11,18 +11,20 @@ import { connect } from "../../utils/con
 
 // Selectors
 import {
   getShownSource,
   getSelectedSource,
   getDebuggeeUrl,
   getExpandedState,
   getProjectDirectoryRoot,
-  getRelativeSources,
-  getSourceCount
+  getRelativeSourcesForThread,
+  getSourceCount,
+  getFocusedSourceItem,
+  getWorkerDisplayName
 } from "../../selectors";
 
 import { getGeneratedSourceByURL } from "../../reducers/sources";
 
 // Actions
 import actions from "../../actions";
 
 // Components
@@ -46,30 +48,33 @@ import type {
   TreeDirectory,
   ParentMap
 } from "../../utils/sources-tree/types";
 import type { Source } from "../../types";
 import type { SourcesMap, State as AppState } from "../../reducers/types";
 import type { Item } from "../shared/ManagedTree";
 
 type Props = {
+  thread: string,
   sources: SourcesMap,
   sourceCount: number,
   shownSource?: Source,
   selectedSource?: Source,
   debuggeeUrl: string,
   projectRoot: string,
-  expanded: Set<string> | null,
+  expanded: Set<string>,
   selectSource: typeof actions.selectSource,
   setExpandedState: typeof actions.setExpandedState,
-  clearProjectDirectoryRoot: typeof actions.clearProjectDirectoryRoot
+  clearProjectDirectoryRoot: typeof actions.clearProjectDirectoryRoot,
+  focusItem: typeof actions.focusItem,
+  focused: TreeNode,
+  workerDisplayName: string
 };
 
 type State = {
-  focusedItem: ?TreeNode,
   parentMap: ParentMap,
   sourceTree: TreeDirectory,
   uncollapsedTree: TreeDirectory,
   listItems?: any,
   highlightItems?: any
 };
 
 type SetExpanded = (item: TreeNode, expanded: boolean, altKey: boolean) => void;
@@ -85,16 +90,17 @@ class SourcesTree extends Component<Prop
       projectRoot,
       debuggeeUrl,
       sources
     });
   }
 
   componentWillReceiveProps(nextProps: Props) {
     const {
+      thread,
       projectRoot,
       debuggeeUrl,
       sources,
       shownSource,
       selectedSource
     } = this.props;
     const { uncollapsedTree, sourceTree } = this.state;
 
@@ -135,33 +141,32 @@ class SourcesTree extends Component<Prop
     if (nextProps.sources != this.props.sources) {
       this.setState(
         updateTree({
           newSources: nextProps.sources,
           prevSources: sources,
           debuggeeUrl,
           projectRoot,
           uncollapsedTree,
-          sourceTree,
-          focusedItem: this.state.focusedItem
+          sourceTree
         })
       );
     }
   }
 
-  focusItem = (item: TreeNode) => {
-    this.setState({ focusedItem: item });
-  };
-
   selectItem = (item: TreeNode) => {
     if (item.type == "source" && !Array.isArray(item.contents)) {
       this.props.selectSource(item.contents.id);
     }
   };
 
+  onFocus = (item: TreeNode) => {
+    this.props.focusItem({ thread: this.props.thread, item });
+  };
+
   // NOTE: we get the source from sources because item.contents is cached
   getSource(item: TreeNode): ?Source {
     const source = getSourceFromNode(item);
     if (source) {
       return this.props.sources[source.id];
     }
 
     return null;
@@ -175,28 +180,28 @@ class SourcesTree extends Component<Prop
       return path;
     }
 
     const blackBoxedPart = source.isBlackBoxed ? ":blackboxed" : "";
     return `${path}/${source.id}/${blackBoxedPart}`;
   };
 
   onExpand = (item: Item, expandedState: Set<string>) => {
-    this.props.setExpandedState(expandedState);
+    this.props.setExpandedState(this.props.thread, expandedState);
   };
 
   onCollapse = (item: Item, expandedState: Set<string>) => {
-    this.props.setExpandedState(expandedState);
+    this.props.setExpandedState(this.props.thread, expandedState);
   };
 
   onKeyDown = (e: KeyboardEvent) => {
-    const { focusedItem } = this.state;
+    const { focused } = this.props;
 
-    if (e.keyCode === 13 && focusedItem) {
-      this.selectItem(focusedItem);
+    if (e.keyCode === 13 && focused) {
+      this.selectItem(focused);
     }
   };
 
   isEmpty() {
     const { sourceTree } = this.state;
     return sourceTree.contents.length === 0;
   }
 
@@ -262,46 +267,47 @@ class SourcesTree extends Component<Prop
     const { debuggeeUrl, projectRoot } = this.props;
 
     return (
       <SourcesTreeItem
         item={item}
         depth={depth}
         focused={focused}
         expanded={expanded}
-        focusItem={this.focusItem}
+        focusItem={this.onFocus}
         selectItem={this.selectItem}
         source={this.getSource(item)}
         debuggeeUrl={debuggeeUrl}
         projectRoot={projectRoot}
         setExpanded={setExpanded}
       />
     );
   };
 
   renderTree() {
-    const { expanded } = this.props;
+    const { expanded, focused } = this.props;
     const { highlightItems, listItems, parentMap } = this.state;
 
     const treeProps = {
       autoExpandAll: false,
       autoExpandDepth: expanded ? 0 : 1,
       expanded,
+      focused,
       getChildren: (item: $Shape<TreeDirectory>) =>
         nodeHasChildren(item) ? item.contents : [],
       getParent: (item: $Shape<TreeNode>) => parentMap.get(item),
       getPath: this.getPath,
       getRoots: this.getRoots,
       highlightItems,
       itemHeight: 21,
       key: this.isEmpty() ? "empty" : "full",
       listItems,
       onCollapse: this.onCollapse,
       onExpand: this.onExpand,
-      onFocus: this.focusItem,
+      onFocus: this.onFocus,
       renderItem: this.renderItem,
       preventBlur: true
     };
 
     return <ManagedTree {...treeProps} />;
   }
 
   renderPane(...children) {
@@ -314,17 +320,17 @@ class SourcesTree extends Component<Prop
           "sources-list-custom-root": projectRoot
         })}
       >
         {children}
       </div>
     );
   }
 
-  render() {
+  renderContents() {
     const { projectRoot } = this.props;
 
     if (this.isEmpty()) {
       if (projectRoot) {
         return this.renderPane(
           this.renderProjectRootHeader(),
           this.renderEmptyElement(L10N.getStr("sources.noSourcesAvailableRoot"))
         );
@@ -337,41 +343,66 @@ class SourcesTree extends Component<Prop
 
     return this.renderPane(
       this.renderProjectRootHeader(),
       <div key="tree" className="sources-list" onKeyDown={this.onKeyDown}>
         {this.renderTree()}
       </div>
     );
   }
+
+  render() {
+    if (this.props.workerDisplayName) {
+      return (
+        <div>
+          {this.props.workerDisplayName}
+          {this.renderContents()}
+        </div>
+      );
+    }
+    return this.renderContents();
+  }
 }
 
-function getSourceForTree(state: AppState, source: ?Source): ?Source | null {
+function getSourceForTree(
+  state: AppState,
+  source: ?Source,
+  thread: ?string
+): ?Source {
   if (!source || !source.isPrettyPrinted) {
     return source;
   }
 
-  return getGeneratedSourceByURL(state, getRawSourceURL(source.url));
+  const candidate = getGeneratedSourceByURL(state, getRawSourceURL(source.url));
+
+  if (!thread || !candidate || candidate.thread == thread) {
+    return candidate;
+  }
 }
 
-const mapStateToProps = state => {
+const mapStateToProps = (state, props) => {
   const selectedSource = getSelectedSource(state);
   const shownSource = getShownSource(state);
+  const focused = getFocusedSourceItem(state);
+  const thread = props.thread;
 
   return {
-    shownSource: getSourceForTree(state, shownSource),
-    selectedSource: getSourceForTree(state, selectedSource),
+    shownSource: getSourceForTree(state, shownSource, thread),
+    selectedSource: getSourceForTree(state, selectedSource, thread),
     debuggeeUrl: getDebuggeeUrl(state),
-    expanded: getExpandedState(state),
+    expanded: getExpandedState(state, props.thread),
+    focused: focused && focused.thread == props.thread ? focused.item : null,
     projectRoot: getProjectDirectoryRoot(state),
-    sources: getRelativeSources(state),
-    sourceCount: getSourceCount(state)
+    sources: getRelativeSourcesForThread(state, thread),
+    sourceCount: getSourceCount(state, props.thread),
+    workerDisplayName: getWorkerDisplayName(state, thread)
   };
 };
 
 export default connect(
   mapStateToProps,
   {
     selectSource: actions.selectSource,
     setExpandedState: actions.setExpandedState,
-    clearProjectDirectoryRoot: actions.clearProjectDirectoryRoot
+    clearProjectDirectoryRoot: actions.clearProjectDirectoryRoot,
+    focusItem: actions.focusItem
   }
 )(SourcesTree);
--- a/devtools/client/debugger/new/src/components/PrimaryPanes/index.js
+++ b/devtools/client/debugger/new/src/components/PrimaryPanes/index.js
@@ -5,42 +5,44 @@
 // @flow
 
 import React, { Component } from "react";
 import { connect } from "../../utils/connect";
 import { Tab, Tabs, TabList, TabPanels } from "react-aria-components/src/tabs";
 import { formatKeyShortcut } from "../../utils/text";
 import actions from "../../actions";
 import {
-  getSources,
+  getRelativeSources,
   getActiveSearch,
-  getSelectedPrimaryPaneTab
+  getSelectedPrimaryPaneTab,
+  getWorkerDisplayName
 } from "../../selectors";
 import { features, prefs } from "../../utils/prefs";
 import "./Sources.css";
 import classnames from "classnames";
 
 import Outline from "./Outline";
 import SourcesTree from "./SourcesTree";
 
-import type { SourcesMap } from "../../reducers/types";
+import type { SourcesMapByThread } from "../../reducers/types";
 import type { SelectedPrimaryPaneTabType } from "../../selectors";
 
 type State = {
   alphabetizeOutline: boolean
 };
 
 type Props = {
   selectedTab: SelectedPrimaryPaneTabType,
-  sources: SourcesMap,
+  sources: SourcesMapByThread,
   horizontal: boolean,
   sourceSearchOn: boolean,
   setPrimaryPaneTab: typeof actions.setPrimaryPaneTab,
   setActiveSearch: typeof actions.setActiveSearch,
-  closeActiveSearch: typeof actions.closeActiveSearch
+  closeActiveSearch: typeof actions.closeActiveSearch,
+  getWorkerDisplayName: string => string
 };
 
 class PrimaryPanes extends Component<Props, State> {
   constructor(props: Props) {
     super(props);
 
     this.state = {
       alphabetizeOutline: prefs.alphabetizeOutline
@@ -86,47 +88,61 @@ class PrimaryPanes extends Component<Pro
         className={classnames("tab outline-tab", { active: isOutline })}
         key="outline-tab"
       >
         {outline}
       </Tab>
     ];
   }
 
+  renderThreadSources() {
+    const threads = Object.getOwnPropertyNames(this.props.sources);
+    threads.sort(
+      (a, b) =>
+        this.props.getWorkerDisplayName(a) > this.props.getWorkerDisplayName(b)
+          ? 1
+          : -1
+    );
+    return threads.map(thread => <SourcesTree thread={thread} key={thread} />);
+  }
+
   render() {
     const { selectedTab } = this.props;
     const activeIndex = selectedTab === "sources" ? 0 : 1;
 
     return (
       <Tabs
         activeIndex={activeIndex}
         className="sources-panel"
         onActivateTab={this.onActivateTab}
       >
         <TabList className="source-outline-tabs">
           {this.renderOutlineTabs()}
         </TabList>
         <TabPanels className="source-outline-panel" hasFocusableContent>
-          <SourcesTree />
+          <div>{this.renderThreadSources()}</div>
           <Outline
             alphabetizeOutline={this.state.alphabetizeOutline}
             onAlphabetizeClick={this.onAlphabetizeClick}
           />
         </TabPanels>
       </Tabs>
     );
   }
 }
 
 const mapStateToProps = state => ({
   selectedTab: getSelectedPrimaryPaneTab(state),
-  sources: getSources(state),
-  sourceSearchOn: getActiveSearch(state) === "source"
+  sources: getRelativeSources(state),
+  sourceSearchOn: getActiveSearch(state) === "source",
+  getWorkerDisplayName: thread => getWorkerDisplayName(state, thread)
 });
 
-export default connect(
+const connector = connect(
   mapStateToProps,
   {
     setPrimaryPaneTab: actions.setPrimaryPaneTab,
     setActiveSearch: actions.setActiveSearch,
     closeActiveSearch: actions.closeActiveSearch
   }
-)(PrimaryPanes);
+);
+
+export default connector(PrimaryPanes);
--- a/devtools/client/debugger/new/src/components/QuickOpenModal.js
+++ b/devtools/client/debugger/new/src/components/QuickOpenModal.js
@@ -5,17 +5,17 @@
 // @flow
 import React, { Component } from "react";
 import { connect } from "../utils/connect";
 import fuzzyAldrin from "fuzzaldrin-plus";
 import { basename } from "../utils/path";
 
 import actions from "../actions";
 import {
-  getRelativeSources,
+  getRelativeSourcesList,
   getQuickOpenEnabled,
   getQuickOpenQuery,
   getQuickOpenType,
   getSelectedSource,
   getSymbols,
   getTabs,
   isSymbolsLoading
 } from "../selectors";
@@ -415,17 +415,17 @@ export class QuickOpenModal extends Comp
 }
 
 /* istanbul ignore next: ignoring testing of redux connection stuff */
 function mapStateToProps(state) {
   const selectedSource = getSelectedSource(state);
 
   return {
     enabled: getQuickOpenEnabled(state),
-    sources: formatSources(getRelativeSources(state), getTabs(state)),
+    sources: formatSources(getRelativeSourcesList(state), getTabs(state)),
     selectedSource,
     symbols: formatSymbols(getSymbols(state, selectedSource)),
     symbolsLoading: isSymbolsLoading(state, selectedSource),
     query: getQuickOpenQuery(state),
     searchType: getQuickOpenType(state),
     tabs: getTabs(state)
   };
 }
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/BreakpointHeading.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/BreakpointHeading.js
@@ -9,69 +9,80 @@ import actions from "../../../actions";
 import {
   getTruncatedFileName,
   getDisplayPath,
   getSourceQueryString,
   getFileURL
 } from "../../../utils/source";
 import {
   getHasSiblingOfSameName,
-  getBreakpointsForSource
+  getBreakpointsForSource,
+  getWorkerDisplayName
 } from "../../../selectors";
 
 import SourceIcon from "../../shared/SourceIcon";
 
 import type { Source, Breakpoint } from "../../../types";
 import showContextMenu from "./BreakpointHeadingsContextMenu";
 
 type Props = {
   sources: Source[],
   source: Source,
   hasSiblingOfSameName: boolean,
   breakpointsForSource: Breakpoint[],
   disableBreakpointsInSource: typeof actions.disableBreakpointsInSource,
   enableBreakpointsInSource: typeof actions.enableBreakpointsInSource,
   removeBreakpointsInSource: typeof actions.removeBreakpointsInSource,
-  selectSource: typeof actions.selectSource
+  selectSource: typeof actions.selectSource,
+  threadName: string
 };
 
 class BreakpointHeading extends PureComponent<Props> {
   onContextMenu = e => {
     showContextMenu({ ...this.props, contextMenuEvent: e });
   };
 
   render() {
-    const { sources, source, hasSiblingOfSameName, selectSource } = this.props;
+    const {
+      sources,
+      source,
+      hasSiblingOfSameName,
+      selectSource,
+      threadName
+    } = this.props;
 
     const path = getDisplayPath(source, sources);
     const query = hasSiblingOfSameName ? getSourceQueryString(source) : "";
 
     return (
       <div
         className="breakpoint-heading"
         title={getFileURL(source, false)}
         onClick={() => selectSource(source.id)}
         onContextMenu={this.onContextMenu}
       >
         <SourceIcon
           source={source}
           shouldHide={icon => ["file", "javascript"].includes(icon)}
         />
         <div className="filename">
-          {getTruncatedFileName(source, query)}
+          {threadName +
+            (threadName ? ": " : "") +
+            getTruncatedFileName(source, query)}
           {path && <span>{`../${path}/..`}</span>}
         </div>
       </div>
     );
   }
 }
 
 const mapStateToProps = (state, { source }) => ({
   hasSiblingOfSameName: getHasSiblingOfSameName(state, source),
-  breakpointsForSource: getBreakpointsForSource(state, source.id)
+  breakpointsForSource: getBreakpointsForSource(state, source.id),
+  threadName: getWorkerDisplayName(state, source.thread)
 });
 
 export default connect(
   mapStateToProps,
   {
     selectSource: actions.selectSource,
     enableBreakpointsInSource: actions.enableBreakpointsInSource,
     disableBreakpointsInSource: actions.disableBreakpointsInSource,
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Workers.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Workers.js
@@ -8,17 +8,18 @@ import type { List } from "immutable";
 
 import "./Workers.css";
 
 import actions from "../../actions";
 import {
   getMainThread,
   getCurrentThread,
   threadIsPaused,
-  getWorkers
+  getWorkers,
+  getWorkerDisplayName
 } from "../../selectors";
 import { basename } from "../../utils/path";
 import { features } from "../../utils/prefs";
 import type { Worker } from "../../types";
 import AccessibleImage from "../shared/AccessibleImage";
 import classnames from "classnames";
 
 type Props = {
@@ -40,17 +41,21 @@ export class Workers extends Component<P
           className={classnames(
             "worker",
             worker.actor == currentThread && "selected"
           )}
           key={worker.actor}
           onClick={() => this.props.selectThread(worker.actor)}
         >
           <img className="domain" />
-          {(worker.url ? basename(worker.url) : "Main Thread") +
+          {(worker.url
+            ? `${this.props.getWorkerDisplayName(worker.actor)}: ${basename(
+                worker.url
+              )}`
+            : "Main Thread") +
             (this.props.threadIsPaused(worker.actor) ? " PAUSED" : "")}
         </div>
       ));
     }
     const { openWorkerToolbox } = this.props;
     return workers.map(worker => (
       <div
         className="worker"
@@ -76,16 +81,17 @@ export class Workers extends Component<P
           : this.renderNoWorkersPlaceholder()}
       </div>
     );
   }
 }
 
 const mapStateToProps = state => ({
   workers: getWorkers(state),
+  getWorkerDisplayName: thread => getWorkerDisplayName(state, thread),
   mainThread: getMainThread(state),
   currentThread: getCurrentThread(state),
   threadIsPaused: thread => threadIsPaused(state, thread)
 });
 
 export default connect(
   mapStateToProps,
   {
--- a/devtools/client/debugger/new/src/components/shared/ManagedTree.js
+++ b/devtools/client/debugger/new/src/components/shared/ManagedTree.js
@@ -19,56 +19,54 @@ type Props = {
   autoExpandDepth: number,
   getChildren: Object => Object[],
   getPath: (Object, index?: number) => string,
   getParent: Item => any,
   getRoots: () => any,
   highlightItems?: Array<Item>,
   itemHeight: number,
   listItems?: Array<Item>,
-  onFocus?: (item: any) => void,
+  onFocus: (item: any) => void,
   onExpand?: (item: Item, expanded: Set<string>) => void,
   onCollapse?: (item: Item, expanded: Set<string>) => void,
   renderItem: any,
-  disabledFocus?: boolean,
   focused?: any,
   expanded?: any
 };
 
 type State = {
-  expanded: any,
-  focusedItem: ?Item
+  expanded: any
 };
 
 class ManagedTree extends Component<Props, State> {
   constructor(props: Props) {
     super(props);
     this.state = {
       expanded: props.expanded || new Set(),
       focusedItem: null
     };
   }
 
+  static defaultProps = {
+    onFocus: () => {}
+  };
+
   componentWillReceiveProps(nextProps: Props) {
-    const { listItems, highlightItems, focused } = this.props;
+    const { listItems, highlightItems } = this.props;
     if (nextProps.listItems && nextProps.listItems != listItems) {
       this.expandListItems(nextProps.listItems);
     }
 
     if (
       nextProps.highlightItems &&
       nextProps.highlightItems != highlightItems &&
       nextProps.highlightItems.length
     ) {
       this.highlightItem(nextProps.highlightItems);
     }
-
-    if (nextProps.focused && nextProps.focused !== focused) {
-      this.focusItem(nextProps.focused);
-    }
   }
 
   setExpanded = (
     item: Item,
     isExpanded: boolean,
     shouldIncludeChildren: boolean
   ) => {
     const expandItem = i => {
@@ -104,63 +102,53 @@ class ManagedTree extends Component<Prop
     } else if (!isExpanded && this.props.onCollapse) {
       this.props.onCollapse(item, expanded);
     }
   };
 
   expandListItems(listItems: Array<Item>) {
     const { expanded } = this.state;
     listItems.forEach(item => expanded.add(this.props.getPath(item)));
-    this.focusItem(listItems[0]);
+    this.props.onFocus(listItems[0]);
     this.setState({ expanded });
   }
 
   highlightItem(highlightItems: Array<Item>) {
     const { expanded } = this.state;
     // This file is visible, so we highlight it.
     if (expanded.has(this.props.getPath(highlightItems[0]))) {
-      this.focusItem(highlightItems[0]);
+      this.props.onFocus(highlightItems[0]);
     } else {
       // Look at folders starting from the top-level until finds a
       // closed folder and highlights this folder
       const index = highlightItems
         .reverse()
         .findIndex(
           item =>
             !expanded.has(this.props.getPath(item)) && item.name !== "root"
         );
 
       if (highlightItems[index]) {
-        this.focusItem(highlightItems[index]);
+        this.props.onFocus(highlightItems[index]);
       }
     }
   }
 
-  focusItem = (item: Item) => {
-    if (!this.props.disabledFocus && this.state.focusedItem !== item) {
-      this.setState({ focusedItem: item });
-
-      if (this.props.onFocus) {
-        this.props.onFocus(item);
-      }
-    }
-  };
-
   render() {
-    const { expanded, focusedItem } = this.state;
+    const { expanded } = this.state;
     return (
       <div className="managed-tree">
         <Tree
           {...this.props}
           isExpanded={item => expanded.has(this.props.getPath(item))}
-          focused={focusedItem}
+          focused={this.props.focused}
           getKey={this.props.getPath}
           onExpand={item => this.setExpanded(item, true, false)}
           onCollapse={item => this.setExpanded(item, false, false)}
-          onFocus={this.focusItem}
+          onFocus={this.props.onFocus}
           renderItem={(...args) =>
             this.props.renderItem(...args, {
               setExpanded: this.setExpanded
             })
           }
         />
       </div>
     );
--- a/devtools/client/debugger/new/src/reducers/debuggee.js
+++ b/devtools/client/debugger/new/src/reducers/debuggee.js
@@ -34,13 +34,20 @@ export default function debuggee(
       return state.set("workers", List(action.workers));
     default:
       return state;
   }
 }
 
 export const getWorkers = (state: OuterState) => state.debuggee.workers;
 
-type OuterState = { debuggee: DebuggeeState };
+export const getWorkerDisplayName = (state: OuterState, thread: string) => {
+  let index = 1;
+  for (const { actor } of state.debuggee.workers) {
+    if (actor == thread) {
+      return `Worker #${index}`;
+    }
+    index++;
+  }
+  return "";
+};
 
-export function getWorker(state: OuterState, url: string) {
-  return getWorkers(state).find(value => url);
-}
+type OuterState = { debuggee: DebuggeeState };
--- a/devtools/client/debugger/new/src/reducers/source-tree.js
+++ b/devtools/client/debugger/new/src/reducers/source-tree.js
@@ -7,22 +7,22 @@
 /**
  * Source tree reducer
  * @module reducers/source-tree
  */
 
 import type { SourceTreeAction } from "../actions/types";
 
 export type SourceTreeState = {
-  expanded: Set<string> | null
+  expanded: { [string]: Set<string> }
 };
 
 export function InitialState(): SourceTreeState {
   return {
-    expanded: null
+    expanded: {}
   };
 }
 
 export default function update(
   state: SourceTreeState = InitialState(),
   action: SourceTreeAction
 ): SourceTreeState {
   switch (action.type) {
@@ -31,19 +31,19 @@ export default function update(
   }
 
   return state;
 }
 
 function updateExpanded(state, action) {
   return {
     ...state,
-    expanded: new Set(action.expanded)
+    expanded: { ...state.expanded, [action.thread]: new Set(action.expanded) }
   };
 }
 
 type OuterState = {
   sourceTree: SourceTreeState
 };
 
-export function getExpandedState(state: OuterState) {
-  return state.sourceTree.expanded;
+export function getExpandedState(state: OuterState, thread: string) {
+  return state.sourceTree.expanded[thread];
 }
--- a/devtools/client/debugger/new/src/reducers/sources.js
+++ b/devtools/client/debugger/new/src/reducers/sources.js
@@ -17,40 +17,43 @@ import {
   isGenerated,
   isOriginal as isOriginalSource
 } from "../utils/source";
 import { originalToGeneratedId } from "devtools-source-map";
 import { prefs } from "../utils/prefs";
 
 import type { Source, SourceId, SourceLocation } from "../types";
 import type { PendingSelectedLocation, Selector } from "./types";
-import type { Action, DonePromiseAction } from "../actions/types";
+import type { Action, DonePromiseAction, FocusItem } from "../actions/types";
 import type { LoadSourceAction } from "../actions/types/SourceAction";
 
 export type SourcesMap = { [string]: Source };
+export type SourcesMapByThread = { [string]: SourcesMap };
 
 type UrlsMap = { [string]: SourceId[] };
 
 export type SourcesState = {
   sources: SourcesMap,
   urls: UrlsMap,
-  relativeSources: SourcesMap,
+  relativeSources: SourcesMapByThread,
   pendingSelectedLocation?: PendingSelectedLocation,
   selectedLocation: ?SourceLocation,
-  projectDirectoryRoot: string
+  projectDirectoryRoot: string,
+  focusedItem: ?FocusItem
 };
 
 export function initialSourcesState(): SourcesState {
   return {
     sources: {},
     urls: {},
     relativeSources: {},
     selectedLocation: undefined,
     pendingSelectedLocation: prefs.pendingSelectedLocation,
-    projectDirectoryRoot: prefs.projectDirectoryRoot
+    projectDirectoryRoot: prefs.projectDirectoryRoot,
+    focusedItem: null
   };
 }
 
 export function createSource(source: Object): Source {
   return {
     id: undefined,
     url: undefined,
     sourceMapURL: undefined,
@@ -132,30 +135,33 @@ function update(
         const { id, url } = action.source;
         const { isBlackBoxed } = ((action: any): DonePromiseAction).value;
         updateBlackBoxList(url, isBlackBoxed);
         return updateSources(state, [{ id, isBlackBoxed }]);
       }
       break;
 
     case "SET_PROJECT_DIRECTORY_ROOT":
-      return recalculateRelativeSources(state, action.url);
+      return updateProjectDirectoryRoot(state, action.url);
 
     case "NAVIGATE":
       const source =
         state.selectedLocation &&
         state.sources[state.selectedLocation.sourceId];
 
       const url = source && source.url;
 
       if (!url) {
         return initialSourcesState();
       }
 
       return { ...initialSourcesState(), url };
+
+    case "SET_FOCUSED_SOURCE_ITEM":
+      return { ...state, focusedItem: action.item };
   }
 
   return state;
 }
 
 function getTextPropsFromAction(action) {
   const { sourceId } = action;
 
@@ -185,20 +191,25 @@ function setSourceTextProps(state, actio
   const source = getTextPropsFromAction(action);
   if (!source) {
     return state;
   }
   return updateSources(state, [source]);
 }
 
 function updateSources(state, sources) {
+  const relativeSources = { ...state.relativeSources };
+  for (const thread in relativeSources) {
+    relativeSources[thread] = { ...relativeSources[thread] };
+  }
+
   state = {
     ...state,
     sources: { ...state.sources },
-    relativeSources: { ...state.relativeSources },
+    relativeSources,
     urls: { ...state.urls }
   };
 
   return sources.reduce(
     (newState, source) => updateSource(newState, source),
     state
   );
 }
@@ -225,38 +236,42 @@ function updateSource(state: SourcesStat
     updatedSource,
     state.projectDirectoryRoot
   );
 
   return state;
 }
 
 function updateRelativeSource(
-  relativeSources: SourcesMap,
+  relativeSources: SourcesMapByThread,
   source: Source,
   root: string
-): SourcesMap {
+): SourcesMapByThread {
   if (!underRoot(source, root)) {
     return relativeSources;
   }
 
   const relativeSource: Source = ({
     ...source,
     relativeUrl: getRelativeUrl(source, root)
   }: any);
 
-  relativeSources[source.id] = relativeSource;
+  if (!relativeSources[source.thread]) {
+    relativeSources[source.thread] = {};
+  }
+
+  relativeSources[source.thread][source.id] = relativeSource;
 
   return relativeSources;
 }
 
-function recalculateRelativeSources(state: SourcesState, root: string) {
+function updateProjectDirectoryRoot(state: SourcesState, root: string) {
   prefs.projectDirectoryRoot = root;
 
-  const relativeSources = (Object.values(state.sources): any).reduce(
+  const relativeSources = getSourceList({ sources: state }).reduce(
     (sources, source: Source) => updateRelativeSource(sources, source, root),
     {}
   );
 
   return {
     ...state,
     projectDirectoryRoot: root,
     relativeSources
@@ -303,28 +318,30 @@ export function getSourceFromId(state: O
 
 export function getOriginalSourceByURL(
   state: OuterState,
   url: string
 ): ?Source {
   return getOriginalSourceByUrlInSources(
     getSources(state),
     getUrls(state),
-    url
+    url,
+    ""
   );
 }
 
 export function getGeneratedSourceByURL(
   state: OuterState,
   url: string
 ): ?Source {
   return getGeneratedSourceByUrlInSources(
     getSources(state),
     getUrls(state),
-    url
+    url,
+    ""
   );
 }
 
 export function getSpecificSourceByURL(
   state: OuterState,
   url: string,
   isOriginal: boolean
 ): ?Source {
@@ -376,50 +393,52 @@ export function getPrettySource(state: O
 
   return getSpecificSourceByURL(state, getPrettySourceURL(source.url), true);
 }
 
 export function hasPrettySource(state: OuterState, id: string) {
   return !!getPrettySource(state, id);
 }
 
-export function getOriginalSourceByUrlInSources(
+function getSourceHelper(
+  original: boolean,
   sources: SourcesMap,
   urls: UrlsMap,
-  url: string
+  url: string,
+  thread: string = ""
 ) {
   const foundSources = getSourcesByUrlInSources(sources, urls, url);
   if (!foundSources) {
     return null;
   }
 
-  return foundSources.find(source => isOriginalSource(source) == true);
+  return foundSources.find(
+    source =>
+      isOriginalSource(source) == original &&
+      (!thread || source.thread == thread)
+  );
 }
-export function getGeneratedSourceByUrlInSources(
-  sources: SourcesMap,
-  urls: UrlsMap,
-  url: string
-) {
-  const foundSources = getSourcesByUrlInSources(sources, urls, url);
-  if (!foundSources) {
-    return null;
-  }
+
+export const getOriginalSourceByUrlInSources = getSourceHelper.bind(null, true);
 
-  return foundSources.find(source => isOriginalSource(source) == false);
-}
+export const getGeneratedSourceByUrlInSources = getSourceHelper.bind(
+  null,
+  false
+);
 
 export function getSpecificSourceByUrlInSources(
   sources: SourcesMap,
   urls: UrlsMap,
   url: string,
-  isOriginal: boolean
+  isOriginal: boolean,
+  thread: string
 ) {
   return isOriginal
-    ? getOriginalSourceByUrlInSources(sources, urls, url)
-    : getGeneratedSourceByUrlInSources(sources, urls, url);
+    ? getOriginalSourceByUrlInSources(sources, urls, url, thread)
+    : getGeneratedSourceByUrlInSources(sources, urls, url, thread);
 }
 
 export function getSourceByUrlInSources(
   sources: SourcesMap,
   urls: UrlsMap,
   url: string
 ) {
   const foundSources = getSourcesByUrlInSources(sources, urls, url);
@@ -472,24 +491,32 @@ export function getSourceInSources(sourc
 export function getSources(state: OuterState) {
   return state.sources.sources;
 }
 
 export function getUrls(state: OuterState) {
   return state.sources.urls;
 }
 
-export function getSourceList(state: OuterState): Source[] {
-  return (Object.values(getSources(state)): any);
+export function getSourceList(state: OuterState, thread?: string): Source[] {
+  const sourceList = (Object.values(getSources(state)): any);
+  return !thread
+    ? sourceList
+    : sourceList.filter(source => source.thread == thread);
 }
 
-export const getSourceCount: Selector<number> = createSelector(
-  getSources,
-  sources => Object.keys(sources).length
-);
+export function getRelativeSourcesList(state: OuterState): Source[] {
+  return ((Object.values(getRelativeSources(state)): any).flatMap(
+    Object.values
+  ): any);
+}
+
+export function getSourceCount(state: OuterState, thread?: string) {
+  return getSourceList(state, thread).length;
+}
 
 export const getSelectedLocation: Selector<?SourceLocation> = createSelector(
   getSourcesState,
   sources => sources.selectedLocation
 );
 
 export const getSelectedSource: Selector<?Source> = createSelector(
   getSelectedLocation,
@@ -502,13 +529,24 @@ export const getSelectedSource: Selector
     return sources[selectedLocation.sourceId];
   }
 );
 
 export function getProjectDirectoryRoot(state: OuterState): string {
   return state.sources.projectDirectoryRoot;
 }
 
-export function getRelativeSources(state: OuterState): SourcesMap {
+export function getRelativeSources(state: OuterState): SourcesMapByThread {
   return state.sources.relativeSources;
 }
 
+export function getRelativeSourcesForThread(
+  state: OuterState,
+  thread: string
+): SourcesMap {
+  return getRelativeSources(state)[thread];
+}
+
+export function getFocusedSourceItem(state: OuterState): ?FocusItem {
+  return state.sources.focusedItem;
+}
+
 export default update;
--- a/devtools/client/debugger/new/src/reducers/tabs.js
+++ b/devtools/client/debugger/new/src/reducers/tabs.js
@@ -26,22 +26,32 @@ import type { Action } from "../actions/
 import type { SourcesState } from "./sources";
 import type { Source } from "../types";
 import type { Selector } from "./types";
 
 export type Tab = {
   url: string,
   framework?: string | null,
   isOriginal: boolean,
-  sourceId?: string
+  sourceId?: string,
+  thread: string
 };
 export type TabList = Tab[];
 
-function isSimilarTab(tab: Tab, url: string, isOriginal: boolean) {
-  return tab.url === url && tab.isOriginal === isOriginal;
+function isSimilarTab(
+  tab: Tab,
+  url: string,
+  isOriginal: boolean,
+  thread: string
+) {
+  return (
+    tab.url === url &&
+    tab.isOriginal === isOriginal &&
+    (!thread || !tab.thread || thread == tab.thread)
+  );
 }
 
 function update(state: TabList = [], action: Action): TabList {
   switch (action.type) {
     case "ADD_TAB":
     case "UPDATE_TAB":
       return updateTabList(state, action);
 
@@ -58,17 +68,20 @@ function update(state: TabList = [], act
   }
 }
 
 export function removeSourceFromTabList(
   tabs: TabList,
   source: Source
 ): TabList {
   return tabs.filter(
-    tab => tab.url !== source.url || tab.isOriginal != isOriginalId(source.id)
+    tab =>
+      tab.url !== source.url ||
+      tab.isOriginal != isOriginalId(source.id) ||
+      (tab.thread && tab.thread !== source.thread)
   );
 }
 
 export function removeSourcesFromTabList(tabs: TabList, sources: Source[]) {
   return sources.reduce(
     (t, source) => removeSourceFromTabList(t, source),
     tabs
   );
@@ -76,26 +89,26 @@ export function removeSourcesFromTabList
 
 /**
  * Adds the new source to the tab list if it is not already there
  * @memberof reducers/tabs
  * @static
  */
 function updateTabList(
   tabs: TabList,
-  { url, framework = null, sourceId, isOriginal = false }
+  { url, framework = null, sourceId, isOriginal = false, thread = "" }
 ) {
   // Set currentIndex to -1 for URL-less tabs so that they aren't
   // filtered by isSimilarTab
   const currentIndex = url
-    ? tabs.findIndex(tab => isSimilarTab(tab, url, isOriginal))
+    ? tabs.findIndex(tab => isSimilarTab(tab, url, isOriginal, thread))
     : -1;
 
   if (currentIndex === -1) {
-    tabs = [{ url, framework, sourceId, isOriginal }, ...tabs];
+    tabs = [{ url, framework, sourceId, isOriginal, thread }, ...tabs];
   } else if (framework) {
     tabs[currentIndex].framework = framework;
   }
 
   asyncStore.tabs = persistTabs(tabs);
   return tabs;
 }
 
@@ -133,17 +146,22 @@ export function getNewSelectedSourceId(
   }
 
   const selectedTab = getSource(state, selectedLocation.sourceId);
   if (!selectedTab) {
     return "";
   }
 
   const matchingTab = availableTabs.find(tab =>
-    isSimilarTab(tab, selectedTab.url, isOriginalId(selectedLocation.sourceId))
+    isSimilarTab(
+      tab,
+      selectedTab.url,
+      isOriginalId(selectedLocation.sourceId),
+      selectedTab.thread
+    )
   );
 
   if (matchingTab) {
     const sources = state.sources.sources;
     if (!sources) {
       return "";
     }
 
@@ -166,17 +184,18 @@ export function getNewSelectedSourceId(
   const newSelectedTabIndex = Math.min(leftNeighborIndex, lastAvailbleTabIndex);
   const availableTab = availableTabs[newSelectedTabIndex];
 
   if (availableTab) {
     const tabSource = getSpecificSourceByUrlInSources(
       getSources(state),
       getUrls(state),
       availableTab.url,
-      availableTab.isOriginal
+      availableTab.isOriginal,
+      availableTab.thread
     );
 
     if (tabSource) {
       return tabSource.id;
     }
   }
 
   return "";
@@ -212,16 +231,17 @@ export const getSourcesForTabs: Selector
 );
 
 function getTabWithOrWithoutUrl(tab, sources, urls) {
   if (tab.url) {
     return getSpecificSourceByUrlInSources(
       sources,
       urls,
       tab.url,
-      tab.isOriginal
+      tab.isOriginal,
+      tab.thread
     );
   }
 
   return tab.sourceId ? sources[tab.sourceId] : null;
 }
 
 export default update;
--- a/devtools/client/debugger/new/src/utils/quick-open.js
+++ b/devtools/client/debugger/new/src/utils/quick-open.js
@@ -11,22 +11,25 @@ import {
   getSourceClassnames,
   getSourceQueryString
 } from "./source";
 
 import type { Location as BabelLocation } from "@babel/types";
 import type { Symbols } from "../reducers/ast";
 import type { QuickOpenType } from "../reducers/quick-open";
 import type { TabList } from "../reducers/tabs";
+import type { SourcesMapByThread } from "../reducers/types";
 import type { Source } from "../types";
 import type {
   SymbolDeclaration,
   IdentifierDeclaration
 } from "../workers/parser";
 
+import { flatten } from "lodash";
+
 export const MODIFIERS = {
   "@": "functions",
   "#": "variables",
   ":": "goto",
   "?": "shortcuts"
 };
 
 export function parseQuickOpenQuery(query: string): QuickOpenType {
@@ -132,18 +135,16 @@ export function formatShortcutResults():
       value: L10N.getStr("gotoLineModal.title"),
       title: `: ${L10N.getStr("gotoLineModal.placeholder")}`,
       id: ":"
     }
   ];
 }
 
 export function formatSources(
-  sources: { [string]: Source },
+  sources: Source[],
   tabs: TabList
 ): Array<QuickOpenResult> {
-  const sourceList: Source[] = (Object.values(sources): any);
-
-  return sourceList
+  return sources
     .filter(source => !isPretty(source))
     .filter(({ relativeUrl }) => !!relativeUrl)
     .map(source => formatSourcesForList(source, tabs));
 }
--- a/devtools/client/debugger/new/src/utils/sources-tree/createTree.js
+++ b/devtools/client/debugger/new/src/utils/sources-tree/createTree.js
@@ -27,12 +27,11 @@ export function createTree({ sources, de
     addToTree(uncollapsedTree, source, debuggeeHost, projectRoot);
   }
 
   const sourceTree = collapseTree((uncollapsedTree: TreeDirectory));
 
   return {
     uncollapsedTree,
     sourceTree,
-    parentMap: createParentMap(sourceTree),
-    focusedItem: null
+    parentMap: createParentMap(sourceTree)
   };
 }
--- a/devtools/client/debugger/new/src/utils/sources-tree/updateTree.js
+++ b/devtools/client/debugger/new/src/utils/sources-tree/updateTree.js
@@ -1,99 +1,55 @@
 /* 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/>. */
 
 // @flow
 
 import { addToTree } from "./addToTree";
 import { collapseTree } from "./collapseTree";
-import { createParentMap, isSource, partIsFile } from "./utils";
+import { createParentMap } from "./utils";
 import { difference } from "lodash";
-import {
-  getDomain,
-  findNodeInContents,
-  createTreeNodeMatcher
-} from "./treeOrder";
+import { getDomain } from "./treeOrder";
 import type { SourcesMap } from "../../reducers/types";
-import type { TreeDirectory, TreeNode } from "./types";
+import type { TreeDirectory } from "./types";
 
 function newSourcesSet(newSources, prevSources) {
   const newSourceIds = difference(
     Object.keys(newSources),
     Object.keys(prevSources)
   );
   const uniqSources = newSourceIds.map(id => newSources[id]);
   return uniqSources;
 }
 
-function findFocusedItemInTree(
-  newSourceTree: TreeDirectory,
-  focusedItem: TreeNode,
-  debuggeeHost: ?string
-): ?TreeNode {
-  const parts = focusedItem.path.split("/").filter(p => p !== "");
-  let path = "";
-
-  return parts.reduce((subTree, part, index) => {
-    if (subTree === undefined || subTree === null) {
-      return null;
-    } else if (isSource(subTree)) {
-      return subTree;
-    }
-
-    path = path ? `${path}/${part}` : part;
-    const { index: childIndex } = findNodeInContents(
-      subTree,
-      createTreeNodeMatcher(
-        part,
-        !partIsFile(index, parts, focusedItem),
-        debuggeeHost
-      )
-    );
-
-    return subTree.contents[childIndex];
-  }, newSourceTree);
-}
-
 type Params = {
   newSources: SourcesMap,
   prevSources: SourcesMap,
   uncollapsedTree: TreeDirectory,
   sourceTree: TreeDirectory,
   debuggeeUrl: string,
-  projectRoot: string,
-  focusedItem: ?TreeNode
+  projectRoot: string
 };
 
 export function updateTree({
   newSources,
   prevSources,
   debuggeeUrl,
   projectRoot,
   uncollapsedTree,
-  sourceTree,
-  focusedItem
+  sourceTree
 }: Params) {
   const newSet = newSourcesSet(newSources, prevSources);
   const debuggeeHost = getDomain(debuggeeUrl);
 
   for (const source of newSet) {
     addToTree(uncollapsedTree, source, debuggeeHost, projectRoot);
   }
 
   const newSourceTree = collapseTree(uncollapsedTree);
 
-  if (focusedItem) {
-    focusedItem = findFocusedItemInTree(
-      newSourceTree,
-      focusedItem,
-      debuggeeHost
-    );
-  }
-
   return {
     uncollapsedTree,
     sourceTree: newSourceTree,
-    parentMap: createParentMap(newSourceTree),
-    focusedItem
+    parentMap: createParentMap(newSourceTree)
   };
 }
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -172,17 +172,17 @@
 #include "mozilla/dom/HTMLTextAreaElement.h"
 #include "mozilla/dom/MediaSource.h"
 
 #include "mozAutoDocUpdate.h"
 #include "nsGlobalWindow.h"
 #include "mozilla/Encoding.h"
 #include "nsDOMNavigationTiming.h"
 
-#include "nsSMILAnimationController.h"
+#include "mozilla/SMILAnimationController.h"
 #include "imgIContainer.h"
 #include "nsSVGUtils.h"
 
 #include "nsRefreshDriver.h"
 
 // FOR CSP (autogenerated by xpidl)
 #include "nsIContentSecurityPolicy.h"
 #include "mozilla/dom/nsCSPContext.h"
@@ -6124,24 +6124,24 @@ nsIDocument* nsIDocument::RequestExterna
       aURI, aReferrer, aReferrerPolicy, aRequestingNode, this, aPendingLoad);
 }
 
 void nsIDocument::EnumerateExternalResources(nsSubDocEnumFunc aCallback,
                                              void* aData) {
   mExternalResourceMap.EnumerateResources(aCallback, aData);
 }
 
-nsSMILAnimationController* nsIDocument::GetAnimationController() {
+SMILAnimationController* nsIDocument::GetAnimationController() {
   // We create the animation controller lazily because most documents won't want
   // one and only SVG documents and the like will call this
   if (mAnimationController) return mAnimationController;
   // Refuse to create an Animation Controller for data documents.
   if (mLoadedAsData || mLoadedAsInteractiveData) return nullptr;
 
-  mAnimationController = new nsSMILAnimationController(this);
+  mAnimationController = new SMILAnimationController(this);
 
   // If there's a presContext then check the animation mode and pause if
   // necessary.
   nsPresContext* context = GetPresContext();
   if (mAnimationController && context &&
       context->ImageAnimationMode() == imgIContainer::kDontAnimMode) {
     mAnimationController->Pause(nsSMILTimeContainer::PAUSE_USERPREF);
   }
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -107,17 +107,16 @@ class nsISecurityConsoleMessage;
 class nsIStreamListener;
 class nsIStructuredCloneContainer;
 class nsIURI;
 class nsIVariant;
 class nsViewManager;
 class nsPresContext;
 class nsRange;
 class nsSimpleContentList;
-class nsSMILAnimationController;
 class nsTextNode;
 class nsUnblockOnloadEvent;
 class nsWindowSizes;
 class nsDOMCaretPosition;
 class nsViewportInfo;
 class nsIGlobalObject;
 class nsIXULWindow;
 
@@ -127,16 +126,17 @@ class CSSStyleSheet;
 class Encoding;
 class ErrorResult;
 class EventStates;
 class EventListenerManager;
 class FullscreenExit;
 class FullscreenRequest;
 class PendingAnimationTracker;
 class ServoStyleSet;
+class SMILAnimationController;
 template <typename>
 class OwningNonNull;
 struct URLExtraData;
 
 namespace css {
 class Loader;
 class ImageLoader;
 class Rule;
@@ -2403,17 +2403,17 @@ class nsIDocument : public nsINode,
   // will have a non-null return value.
   bool HasAnimationController() { return !!mAnimationController; }
 
   // Getter for this document's SMIL Animation Controller. Performs lazy
   // initialization, if this document supports animation and if
   // mAnimationController isn't yet initialized.
   //
   // If HasAnimationController is true, this is guaranteed to return non-null.
-  nsSMILAnimationController* GetAnimationController();
+  mozilla::SMILAnimationController* GetAnimationController();
 
   // Gets the tracker for animations that are waiting to start.
   // Returns nullptr if there is no pending animation tracker for this document
   // which will be the case if there have never been any CSS animations or
   // transitions on elements in the document.
   mozilla::PendingAnimationTracker* GetPendingAnimationTracker() {
     return mPendingAnimationTracker;
   }
@@ -3641,17 +3641,17 @@ class nsIDocument : public nsINode,
 
   typedef mozilla::SegmentedVector<nsCOMPtr<mozilla::dom::Link>, kSegmentSize,
                                    InfallibleAllocPolicy>
       LinksToUpdateList;
 
   LinksToUpdateList mLinksToUpdate;
 
   // SMIL Animation Controller, lazily-initialized in GetAnimationController
-  RefPtr<nsSMILAnimationController> mAnimationController;
+  RefPtr<mozilla::SMILAnimationController> mAnimationController;
 
   // Table of element properties for this document.
   nsPropertyTable mPropertyTable;
 
   // Our cached .children collection
   nsCOMPtr<nsIHTMLCollection> mChildrenCollection;
 
   // Various DOM lists
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -767,22 +767,22 @@ DOMInterfaces = {
 },
 
 'StyleSheet': {
     'nativeType': 'mozilla::StyleSheet',
     'headerFile': 'mozilla/StyleSheetInlines.h',
 },
 
 'SVGAnimatedLengthList': {
-    'nativeType': 'mozilla::DOMSVGAnimatedLengthList',
+    'nativeType': 'mozilla::dom::DOMSVGAnimatedLengthList',
     'headerFile': 'DOMSVGAnimatedLengthList.h',
 },
 
 'SVGAnimatedNumberList': {
-    'nativeType': 'mozilla::DOMSVGAnimatedNumberList',
+    'nativeType': 'mozilla::dom::DOMSVGAnimatedNumberList',
     'headerFile': 'DOMSVGAnimatedNumberList.h'
 },
 
 'SVGAnimatedTransformList': {
     'nativeType': 'mozilla::dom::DOMSVGAnimatedTransformList',
     'headerFile': 'DOMSVGAnimatedTransformList.h'
 },
 
@@ -819,36 +819,36 @@ DOMInterfaces = {
     'concrete': False,
 },
 
 'SVGGradientElement': {
     'concrete': False,
 },
 
 'SVGLength': {
-    'nativeType': 'mozilla::DOMSVGLength',
+    'nativeType': 'mozilla::dom::DOMSVGLength',
     'headerFile': 'DOMSVGLength.h'
 },
 
 'SVGLengthList': {
-    'nativeType': 'mozilla::DOMSVGLengthList',
+    'nativeType': 'mozilla::dom::DOMSVGLengthList',
     'headerFile': 'DOMSVGLengthList.h'
 },
 
 'SVGLinearGradientElement': {
     'headerFile': 'mozilla/dom/SVGGradientElement.h',
 },
 
 'SVGNumber': {
-    'nativeType': 'mozilla::DOMSVGNumber',
+    'nativeType': 'mozilla::dom::DOMSVGNumber',
     'headerFile': 'DOMSVGNumber.h',
 },
 
 'SVGNumberList': {
-    'nativeType': 'mozilla::DOMSVGNumberList',
+    'nativeType': 'mozilla::dom::DOMSVGNumberList',
     'headerFile': 'DOMSVGNumberList.h'
 },
 
 'SVGPathSeg': {
     'nativeType': 'mozilla::DOMSVGPathSeg',
     'headerFile': 'DOMSVGPathSeg.h',
     'concrete': False,
 },
@@ -980,16 +980,18 @@ DOMInterfaces = {
     'concrete': False
 },
 
 'SVGTextPositioningElement': {
     'concrete': False
 },
 
 'SVGTransform': {
+    'nativeType': 'mozilla::dom::DOMSVGTransform',
+    'headerFile': 'DOMSVGTransform.h',
     'binaryNames': {
         "matrix": "GetMatrix"
     }
 },
 
 'SVGTransformList': {
     'nativeType': 'mozilla::DOMSVGTransformList',
     'headerFile': 'DOMSVGTransformList.h'
rename from dom/smil/nsSMILAnimationController.cpp
rename to dom/smil/SMILAnimationController.cpp
--- a/dom/smil/nsSMILAnimationController.cpp
+++ b/dom/smil/SMILAnimationController.cpp
@@ -1,15 +1,15 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "nsSMILAnimationController.h"
+#include "mozilla/SMILAnimationController.h"
 
 #include <algorithm>
 
 #include "mozilla/AutoRestore.h"
 #include "mozilla/RestyleManager.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/SVGAnimationElement.h"
 #include "nsContentUtils.h"
@@ -17,26 +17,27 @@
 #include "nsIDocument.h"
 #include "nsIPresShell.h"
 #include "nsIPresShellInlines.h"
 #include "nsITimer.h"
 #include "nsSMILCompositor.h"
 #include "nsSMILCSSProperty.h"
 #include "nsSMILTimedElement.h"
 
-using namespace mozilla;
 using namespace mozilla::dom;
 
+namespace mozilla {
+
 //----------------------------------------------------------------------
-// nsSMILAnimationController implementation
+// SMILAnimationController implementation
 
 //----------------------------------------------------------------------
 // ctors, dtors, factory methods
 
-nsSMILAnimationController::nsSMILAnimationController(nsIDocument* aDoc)
+SMILAnimationController::SMILAnimationController(nsIDocument* aDoc)
     : mAvgTimeBetweenSamples(0),
       mResampleNeeded(false),
       mDeferredStartSampling(false),
       mRunningSample(false),
       mRegisteredWithRefreshDriver(false),
       mMightHavePendingStyleUpdates(false),
       mDocument(aDoc) {
   MOZ_ASSERT(aDoc, "need a non-null document");
@@ -47,73 +48,73 @@ nsSMILAnimationController::nsSMILAnimati
   } else {
     mStartTime = mozilla::TimeStamp::Now();
   }
   mCurrentSampleTime = mStartTime;
 
   Begin();
 }
 
-nsSMILAnimationController::~nsSMILAnimationController() {
+SMILAnimationController::~SMILAnimationController() {
   NS_ASSERTION(mAnimationElementTable.Count() == 0,
                "Animation controller shouldn't be tracking any animation"
                " elements when it dies");
   NS_ASSERTION(!mRegisteredWithRefreshDriver,
                "Leaving stale entry in refresh driver's observer list");
 }
 
-void nsSMILAnimationController::Disconnect() {
+void SMILAnimationController::Disconnect() {
   MOZ_ASSERT(mDocument, "disconnecting when we weren't connected...?");
   MOZ_ASSERT(mRefCnt.get() == 1,
              "Expecting to disconnect when doc is sole remaining owner");
   NS_ASSERTION(mPauseState & nsSMILTimeContainer::PAUSE_PAGEHIDE,
                "Expecting to be paused for pagehide before disconnect");
 
   StopSampling(GetRefreshDriver());
 
   mDocument = nullptr;  // (raw pointer)
 }
 
 //----------------------------------------------------------------------
 // nsSMILTimeContainer methods:
 
-void nsSMILAnimationController::Pause(uint32_t aType) {
+void SMILAnimationController::Pause(uint32_t aType) {
   nsSMILTimeContainer::Pause(aType);
 
   if (mPauseState) {
     mDeferredStartSampling = false;
     StopSampling(GetRefreshDriver());
   }
 }
 
-void nsSMILAnimationController::Resume(uint32_t aType) {
+void SMILAnimationController::Resume(uint32_t aType) {
   bool wasPaused = (mPauseState != 0);
   // Update mCurrentSampleTime so that calls to GetParentTime--used for
   // calculating parent offsets--are accurate
   mCurrentSampleTime = mozilla::TimeStamp::Now();
 
   nsSMILTimeContainer::Resume(aType);
 
   if (wasPaused && !mPauseState && mChildContainerTable.Count()) {
     MaybeStartSampling(GetRefreshDriver());
     Sample();  // Run the first sample manually
   }
 }
 
-nsSMILTime nsSMILAnimationController::GetParentTime() const {
+nsSMILTime SMILAnimationController::GetParentTime() const {
   return (nsSMILTime)(mCurrentSampleTime - mStartTime).ToMilliseconds();
 }
 
 //----------------------------------------------------------------------
 // nsARefreshObserver methods:
-NS_IMPL_ADDREF(nsSMILAnimationController)
-NS_IMPL_RELEASE(nsSMILAnimationController)
+NS_IMPL_ADDREF(SMILAnimationController)
+NS_IMPL_RELEASE(SMILAnimationController)
 
 // nsRefreshDriver Callback function
-void nsSMILAnimationController::WillRefresh(mozilla::TimeStamp aTime) {
+void SMILAnimationController::WillRefresh(mozilla::TimeStamp aTime) {
   // Although we never expect aTime to go backwards, when we initialise the
   // animation controller, if we can't get hold of a refresh driver we
   // initialise mCurrentSampleTime to Now(). It may be possible that after
   // doing so we get sampled by a refresh driver whose most recent refresh time
   // predates when we were initialised, so to be safe we make sure to take the
   // most recent time here.
   aTime = std::max(mCurrentSampleTime, aTime);
 
@@ -153,113 +154,113 @@ void nsSMILAnimationController::WillRefr
   mCurrentSampleTime = aTime;
 
   Sample();
 }
 
 //----------------------------------------------------------------------
 // Animation element registration methods:
 
-void nsSMILAnimationController::RegisterAnimationElement(
+void SMILAnimationController::RegisterAnimationElement(
     SVGAnimationElement* aAnimationElement) {
   mAnimationElementTable.PutEntry(aAnimationElement);
   if (mDeferredStartSampling) {
     mDeferredStartSampling = false;
     if (mChildContainerTable.Count()) {
       // mAnimationElementTable was empty, but now we've added its 1st element
       MOZ_ASSERT(mAnimationElementTable.Count() == 1,
                  "we shouldn't have deferred sampling if we already had "
                  "animations registered");
       StartSampling(GetRefreshDriver());
       Sample();  // Run the first sample manually
     }  // else, don't sample until a time container is registered (via AddChild)
   }
 }
 
-void nsSMILAnimationController::UnregisterAnimationElement(
+void SMILAnimationController::UnregisterAnimationElement(
     SVGAnimationElement* aAnimationElement) {
   mAnimationElementTable.RemoveEntry(aAnimationElement);
 }
 
 //----------------------------------------------------------------------
 // Page show/hide
 
-void nsSMILAnimationController::OnPageShow() {
+void SMILAnimationController::OnPageShow() {
   Resume(nsSMILTimeContainer::PAUSE_PAGEHIDE);
 }
 
-void nsSMILAnimationController::OnPageHide() {
+void SMILAnimationController::OnPageHide() {
   Pause(nsSMILTimeContainer::PAUSE_PAGEHIDE);
 }
 
 //----------------------------------------------------------------------
 // Cycle-collection support
 
-void nsSMILAnimationController::Traverse(
+void SMILAnimationController::Traverse(
     nsCycleCollectionTraversalCallback* aCallback) {
   // Traverse last compositor table
   if (mLastCompositorTable) {
     for (auto iter = mLastCompositorTable->Iter(); !iter.Done(); iter.Next()) {
       nsSMILCompositor* compositor = iter.Get();
       compositor->Traverse(aCallback);
     }
   }
 }
 
-void nsSMILAnimationController::Unlink() { mLastCompositorTable = nullptr; }
+void SMILAnimationController::Unlink() { mLastCompositorTable = nullptr; }
 
 //----------------------------------------------------------------------
 // Refresh driver lifecycle related methods
 
-void nsSMILAnimationController::NotifyRefreshDriverCreated(
+void SMILAnimationController::NotifyRefreshDriverCreated(
     nsRefreshDriver* aRefreshDriver) {
   if (!mPauseState) {
     MaybeStartSampling(aRefreshDriver);
   }
 }
 
-void nsSMILAnimationController::NotifyRefreshDriverDestroying(
+void SMILAnimationController::NotifyRefreshDriverDestroying(
     nsRefreshDriver* aRefreshDriver) {
   if (!mPauseState && !mDeferredStartSampling) {
     StopSampling(aRefreshDriver);
   }
 }
 
 //----------------------------------------------------------------------
 // Timer-related implementation helpers
 
-void nsSMILAnimationController::StartSampling(nsRefreshDriver* aRefreshDriver) {
+void SMILAnimationController::StartSampling(nsRefreshDriver* aRefreshDriver) {
   NS_ASSERTION(mPauseState == 0, "Starting sampling but controller is paused");
   NS_ASSERTION(!mDeferredStartSampling,
                "Started sampling but the deferred start flag is still set");
   if (aRefreshDriver) {
     MOZ_ASSERT(!mRegisteredWithRefreshDriver,
                "Redundantly registering with refresh driver");
     MOZ_ASSERT(!GetRefreshDriver() || aRefreshDriver == GetRefreshDriver(),
                "Starting sampling with wrong refresh driver");
     // We're effectively resuming from a pause so update our current sample time
     // or else it will confuse our "average time between samples" calculations.
     mCurrentSampleTime = mozilla::TimeStamp::Now();
     aRefreshDriver->AddRefreshObserver(this, FlushType::Style);
     mRegisteredWithRefreshDriver = true;
   }
 }
 
-void nsSMILAnimationController::StopSampling(nsRefreshDriver* aRefreshDriver) {
+void SMILAnimationController::StopSampling(nsRefreshDriver* aRefreshDriver) {
   if (aRefreshDriver && mRegisteredWithRefreshDriver) {
     // NOTE: The document might already have been detached from its PresContext
     // (and RefreshDriver), which would make GetRefreshDriver() return null.
     MOZ_ASSERT(!GetRefreshDriver() || aRefreshDriver == GetRefreshDriver(),
                "Stopping sampling with wrong refresh driver");
     aRefreshDriver->RemoveRefreshObserver(this, FlushType::Style);
     mRegisteredWithRefreshDriver = false;
   }
 }
 
-void nsSMILAnimationController::MaybeStartSampling(
+void SMILAnimationController::MaybeStartSampling(
     nsRefreshDriver* aRefreshDriver) {
   if (mDeferredStartSampling) {
     // We've received earlier 'MaybeStartSampling' calls, and we're
     // deferring until we get a registered animation.
     return;
   }
 
   if (mAnimationElementTable.Count()) {
@@ -267,21 +268,21 @@ void nsSMILAnimationController::MaybeSta
   } else {
     mDeferredStartSampling = true;
   }
 }
 
 //----------------------------------------------------------------------
 // Sample-related methods and callbacks
 
-void nsSMILAnimationController::DoSample() {
+void SMILAnimationController::DoSample() {
   DoSample(true);  // Skip unchanged time containers
 }
 
-void nsSMILAnimationController::DoSample(bool aSkipUnchangedContainers) {
+void SMILAnimationController::DoSample(bool aSkipUnchangedContainers) {
   if (!mDocument) {
     NS_ERROR("Shouldn't be sampling after document has disconnected");
     return;
   }
   if (mRunningSample) {
     NS_ERROR("Shouldn't be recursively sampling");
     return;
   }
@@ -415,17 +416,17 @@ void nsSMILAnimationController::DoSample
 
   // Update last compositor table
   mLastCompositorTable = currentCompositorTable.forget();
   mMightHavePendingStyleUpdates = mightHavePendingStyleUpdates;
 
   NS_ASSERTION(!mResampleNeeded, "Resample dirty flag set during sample!");
 }
 
-void nsSMILAnimationController::RewindElements() {
+void SMILAnimationController::RewindElements() {
   bool rewindNeeded = false;
   for (auto iter = mChildContainerTable.Iter(); !iter.Done(); iter.Next()) {
     nsSMILTimeContainer* container = iter.Get()->GetKey();
     if (container->NeedsRewind()) {
       rewindNeeded = true;
       break;
     }
   }
@@ -440,17 +441,17 @@ void nsSMILAnimationController::RewindEl
     }
   }
 
   for (auto iter = mChildContainerTable.Iter(); !iter.Done(); iter.Next()) {
     iter.Get()->GetKey()->ClearNeedsRewind();
   }
 }
 
-void nsSMILAnimationController::DoMilestoneSamples() {
+void SMILAnimationController::DoMilestoneSamples() {
   // We need to sample the timing model but because SMIL operates independently
   // of the frame-rate, we can get one sample at t=0s and the next at t=10min.
   //
   // In between those two sample times a whole string of significant events
   // might be expected to take place: events firing, new interdependencies
   // between animations resolved and dissolved, etc.
   //
   // Furthermore, at any given time, we want to sample all the intervals that
@@ -532,17 +533,17 @@ void nsSMILAnimationController::DoMilest
         elem->TimedElement().SampleEndAt(containerTime);
       } else {
         elem->TimedElement().SampleAt(containerTime);
       }
     }
   }
 }
 
-/*static*/ void nsSMILAnimationController::SampleTimedElement(
+/*static*/ void SMILAnimationController::SampleTimedElement(
     SVGAnimationElement* aElement, TimeContainerHashtable* aActiveContainers) {
   nsSMILTimeContainer* timeContainer = aElement->GetTimeContainer();
   if (!timeContainer) return;
 
   // We'd like to call timeContainer->NeedsSample() here and skip all timed
   // elements that belong to paused time containers that don't need a sample,
   // but that doesn't work because we've already called Sample() on all the time
   // containers so the paused ones don't need a sample any more and they'll
@@ -555,17 +556,17 @@ void nsSMILAnimationController::DoMilest
 
   nsSMILTime containerTime = timeContainer->GetCurrentTimeAsSMILTime();
 
   MOZ_ASSERT(!timeContainer->IsSeeking(),
              "Doing a regular sample but the time container is still seeking");
   aElement->TimedElement().SampleAt(containerTime);
 }
 
-/*static*/ void nsSMILAnimationController::AddAnimationToCompositorTable(
+/*static*/ void SMILAnimationController::AddAnimationToCompositorTable(
     SVGAnimationElement* aElement, nsSMILCompositorTable* aCompositorTable,
     bool& aStyleFlushNeeded) {
   // Add a compositor to the hash table if there's not already one there
   nsSMILTargetIdentifier key;
   if (!GetTargetIdentifierForAnimation(aElement, key))
     // Something's wrong/missing about animation's target; skip this animation
     return;
 
@@ -603,17 +604,17 @@ static inline bool IsTransformAttribute(
          (aAttributeName == nsGkAtoms::transform ||
           aAttributeName == nsGkAtoms::patternTransform ||
           aAttributeName == nsGkAtoms::gradientTransform);
 }
 
 // Helper function that, given a SVGAnimationElement, looks up its target
 // element & target attribute and populates a nsSMILTargetIdentifier
 // for this target.
-/*static*/ bool nsSMILAnimationController::GetTargetIdentifierForAnimation(
+/*static*/ bool SMILAnimationController::GetTargetIdentifierForAnimation(
     SVGAnimationElement* aAnimElem, nsSMILTargetIdentifier& aResult) {
   // Look up target (animated) element
   Element* targetElem = aAnimElem->GetTargetElementContent();
   if (!targetElem)
     // Animation has no target elem -- skip it.
     return false;
 
   // Look up target (animated) attribute
@@ -635,21 +636,21 @@ static inline bool IsTransformAttribute(
   // Construct the key
   aResult.mElement = targetElem;
   aResult.mAttributeName = attributeName;
   aResult.mAttributeNamespaceID = attributeNamespaceID;
 
   return true;
 }
 
-bool nsSMILAnimationController::PreTraverse() {
+bool SMILAnimationController::PreTraverse() {
   return PreTraverseInSubtree(nullptr);
 }
 
-bool nsSMILAnimationController::PreTraverseInSubtree(Element* aRoot) {
+bool SMILAnimationController::PreTraverseInSubtree(Element* aRoot) {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!mMightHavePendingStyleUpdates) {
     return false;
   }
 
   nsPresContext* context = mDocument->GetPresContext();
   if (!context) {
@@ -687,44 +688,47 @@ bool nsSMILAnimationController::PreTrave
   }
 
   return foundElementsNeedingRestyle;
 }
 
 //----------------------------------------------------------------------
 // Add/remove child time containers
 
-nsresult nsSMILAnimationController::AddChild(nsSMILTimeContainer& aChild) {
+nsresult SMILAnimationController::AddChild(nsSMILTimeContainer& aChild) {
   TimeContainerPtrKey* key = mChildContainerTable.PutEntry(&aChild);
   NS_ENSURE_TRUE(key, NS_ERROR_OUT_OF_MEMORY);
 
   if (!mPauseState && mChildContainerTable.Count() == 1) {
     MaybeStartSampling(GetRefreshDriver());
     Sample();  // Run the first sample manually
   }
 
   return NS_OK;
 }
 
-void nsSMILAnimationController::RemoveChild(nsSMILTimeContainer& aChild) {
+void SMILAnimationController::RemoveChild(nsSMILTimeContainer& aChild) {
   mChildContainerTable.RemoveEntry(&aChild);
 
   if (!mPauseState && mChildContainerTable.Count() == 0) {
     StopSampling(GetRefreshDriver());
   }
 }
 
 // Helper method
-nsRefreshDriver* nsSMILAnimationController::GetRefreshDriver() {
+nsRefreshDriver* SMILAnimationController::GetRefreshDriver() {
   if (!mDocument) {
     NS_ERROR("Requesting refresh driver after document has disconnected!");
     return nullptr;
   }
 
   nsPresContext* context = mDocument->GetPresContext();
   return context ? context->RefreshDriver() : nullptr;
 }
 
-void nsSMILAnimationController::FlagDocumentNeedsFlush() {
+void SMILAnimationController::FlagDocumentNeedsFlush() {
   if (nsIPresShell* shell = mDocument->GetShell()) {
     shell->SetNeedStyleFlush();
   }
 }
+
+}  // namespace mozilla
+
rename from dom/smil/nsSMILAnimationController.h
rename to dom/smil/SMILAnimationController.h
--- a/dom/smil/nsSMILAnimationController.h
+++ b/dom/smil/SMILAnimationController.h
@@ -22,35 +22,34 @@
 struct nsSMILTargetIdentifier;
 class nsIDocument;
 
 namespace mozilla {
 namespace dom {
 class Element;
 class SVGAnimationElement;
 }  // namespace dom
-}  // namespace mozilla
 
 //----------------------------------------------------------------------
-// nsSMILAnimationController
+// SMILAnimationController
 //
 // The animation controller maintains the animation timer and determines the
 // sample times and sample rate for all SMIL animations in a document. There is
 // at most one animation controller per nsDocument so that frame-rate tuning can
 // be performed at a document-level.
 //
 // The animation controller can contain many child time containers (timed
 // document root objects) which may correspond to SVG document fragments within
 // a compound document. These time containers can be paused individually or
 // here, at the document level.
 //
-class nsSMILAnimationController final : public nsSMILTimeContainer,
+class SMILAnimationController final : public nsSMILTimeContainer,
                                         public nsARefreshObserver {
  public:
-  explicit nsSMILAnimationController(nsIDocument* aDoc);
+  explicit SMILAnimationController(nsIDocument* aDoc);
 
   // Clears mDocument pointer. (Called by our nsIDocument when it's going away)
   void Disconnect();
 
   // nsSMILContainer
   virtual void Pause(uint32_t aType) override;
   virtual void Resume(uint32_t aType) override;
   virtual nsSMILTime GetParentTime() const override;
@@ -107,17 +106,17 @@ class nsSMILAnimationController final : 
   bool MightHavePendingStyleUpdates() const {
     return mMightHavePendingStyleUpdates;
   }
 
   bool PreTraverse();
   bool PreTraverseInSubtree(mozilla::dom::Element* aRoot);
 
  protected:
-  ~nsSMILAnimationController();
+  ~SMILAnimationController();
 
   // Typedefs
   typedef nsPtrHashKey<nsSMILTimeContainer> TimeContainerPtrKey;
   typedef nsTHashtable<TimeContainerPtrKey> TimeContainerHashtable;
   typedef nsPtrHashKey<mozilla::dom::SVGAnimationElement>
       AnimationElementPtrKey;
   typedef nsTHashtable<AnimationElementPtrKey> AnimationElementHashtable;
 
@@ -202,9 +201,11 @@ class nsSMILAnimationController final : 
 
   // Contains compositors used in our last sample.  We keep this around
   // so we can detect when an element/attribute used to be animated,
   // but isn't anymore for some reason. (e.g. if its <animate> element is
   // removed or retargeted)
   nsAutoPtr<nsSMILCompositorTable> mLastCompositorTable;
 };
 
+}  // namespace mozilla
+
 #endif  // NS_SMILANIMATIONCONTROLLER_H_
--- a/dom/smil/SMILBoolType.h
+++ b/dom/smil/SMILBoolType.h
@@ -3,30 +3,30 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_SMILBOOLTYPE_H_
 #define MOZILLA_SMILBOOLTYPE_H_
 
 #include "mozilla/Attributes.h"
-#include "nsISMILType.h"
+#include "SMILType.h"
 
 namespace mozilla {
 
-class SMILBoolType : public nsISMILType {
+class SMILBoolType : public SMILType {
  public:
   // Singleton for nsSMILValue objects to hold onto.
   static SMILBoolType* Singleton() {
     static SMILBoolType sSingleton;
     return &sSingleton;
   }
 
  protected:
-  // nsISMILType Methods
+  // SMILType Methods
   // -------------------
   virtual void Init(nsSMILValue& aValue) const override;
   virtual void Destroy(nsSMILValue& aValue) const override;
   virtual nsresult Assign(nsSMILValue& aDest,
                           const nsSMILValue& aSrc) const override;
   virtual nsresult Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd,
                        uint32_t aCount) const override;
   virtual bool IsEqual(const nsSMILValue& aLeft,
--- a/dom/smil/SMILCSSValueType.h
+++ b/dom/smil/SMILCSSValueType.h
@@ -4,41 +4,41 @@
  * 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/. */
 
 /* representation of a value for a SMIL-animated CSS property */
 
 #ifndef NS_SMILCSSVALUETYPE_H_
 #define NS_SMILCSSVALUETYPE_H_
 
-#include "nsISMILType.h"
+#include "SMILType.h"
 #include "nsCSSPropertyID.h"
 #include "nsStringFwd.h"
 #include "mozilla/Attributes.h"
 
 namespace mozilla {
 struct AnimationValue;
 class DeclarationBlock;
 namespace dom {
 class Element;
 }  // namespace dom
 
 /*
  * SMILCSSValueType: Represents a SMIL-animated CSS value.
  */
-class SMILCSSValueType : public nsISMILType {
+class SMILCSSValueType : public SMILType {
  public:
   typedef mozilla::dom::Element Element;
   typedef mozilla::AnimationValue AnimationValue;
 
   // Singleton for nsSMILValue objects to hold onto.
   static SMILCSSValueType sSingleton;
 
  protected:
-  // nsISMILType Methods
+  // SMILType Methods
   // -------------------
   void Init(nsSMILValue& aValue) const override;
   void Destroy(nsSMILValue&) const override;
   nsresult Assign(nsSMILValue& aDest, const nsSMILValue& aSrc) const override;
   bool IsEqual(const nsSMILValue& aLeft,
                const nsSMILValue& aRight) const override;
   nsresult Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd,
                uint32_t aCount) const override;
--- a/dom/smil/SMILEnumType.h
+++ b/dom/smil/SMILEnumType.h
@@ -3,30 +3,30 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_SMILENUMTYPE_H_
 #define MOZILLA_SMILENUMTYPE_H_
 
 #include "mozilla/Attributes.h"
-#include "nsISMILType.h"
+#include "SMILType.h"
 
 namespace mozilla {
 
-class SMILEnumType : public nsISMILType {
+class SMILEnumType : public SMILType {
  public:
   // Singleton for nsSMILValue objects to hold onto.
   static SMILEnumType* Singleton() {
     static SMILEnumType sSingleton;
     return &sSingleton;
   }
 
  protected:
-  // nsISMILType Methods
+  // SMILType Methods
   // -------------------
   virtual void Init(nsSMILValue& aValue) const override;
   virtual void Destroy(nsSMILValue& aValue) const override;
   virtual nsresult Assign(nsSMILValue& aDest,
                           const nsSMILValue& aSrc) const override;
   virtual bool IsEqual(const nsSMILValue& aLeft,
                        const nsSMILValue& aRight) const override;
   virtual nsresult Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd,
--- a/dom/smil/SMILFloatType.h
+++ b/dom/smil/SMILFloatType.h
@@ -3,30 +3,30 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef NS_SMILFLOATTYPE_H_
 #define NS_SMILFLOATTYPE_H_
 
 #include "mozilla/Attributes.h"
-#include "nsISMILType.h"
+#include "SMILType.h"
 
 namespace mozilla {
 
-class SMILFloatType : public nsISMILType {
+class SMILFloatType : public SMILType {
  public:
   // Singleton for nsSMILValue objects to hold onto.
   static SMILFloatType* Singleton() {
     static SMILFloatType sSingleton;
     return &sSingleton;
   }
 
  protected:
-  // nsISMILType Methods
+  // SMILType Methods
   // -------------------
   virtual void Init(nsSMILValue& aValue) const override;
   virtual void Destroy(nsSMILValue& aValue) const override;
   virtual nsresult Assign(nsSMILValue& aDest,
                           const nsSMILValue& aSrc) const override;
   virtual bool IsEqual(const nsSMILValue& aLeft,
                        const nsSMILValue& aRight) const override;
   virtual nsresult Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd,
--- a/dom/smil/SMILIntegerType.h
+++ b/dom/smil/SMILIntegerType.h
@@ -3,21 +3,21 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_SMILINTEGERTYPE_H_
 #define MOZILLA_SMILINTEGERTYPE_H_
 
 #include "mozilla/Attributes.h"
-#include "nsISMILType.h"
+#include "SMILType.h"
 
 namespace mozilla {
 
-class SMILIntegerType : public nsISMILType {
+class SMILIntegerType : public SMILType {
  public:
   virtual void Init(nsSMILValue& aValue) const override;
   virtual void Destroy(nsSMILValue& aValue) const override;
   virtual nsresult Assign(nsSMILValue& aDest,
                           const nsSMILValue& aSrc) const override;
   virtual bool IsEqual(const nsSMILValue& aLeft,
                        const nsSMILValue& aRight) const override;
   virtual nsresult Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd,
--- a/dom/smil/SMILNullType.h
+++ b/dom/smil/SMILNullType.h
@@ -3,27 +3,27 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef NS_SMILNULLTYPE_H_
 #define NS_SMILNULLTYPE_H_
 
 #include "mozilla/Attributes.h"
-#include "nsISMILType.h"
+#include "SMILType.h"
 
 namespace mozilla {
 
-class SMILNullType : public nsISMILType {
+class SMILNullType : public SMILType {
  public:
   // Singleton for nsSMILValue objects to hold onto.
   static SMILNullType* Singleton();
 
  protected:
-  // nsISMILType Methods
+  // SMILType Methods
   // -------------------
   virtual void Init(nsSMILValue& aValue) const override {}
   virtual void Destroy(nsSMILValue& aValue) const override {}
   virtual nsresult Assign(nsSMILValue& aDest,
                           const nsSMILValue& aSrc) const override;
 
   // The remaining methods should never be called, so although they're very
   // simple they don't need to be inline.
--- a/dom/smil/SMILStringType.h
+++ b/dom/smil/SMILStringType.h
@@ -3,30 +3,30 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_SMILSTRINGTYPE_H_
 #define MOZILLA_SMILSTRINGTYPE_H_
 
 #include "mozilla/Attributes.h"
-#include "nsISMILType.h"
+#include "SMILType.h"
 
 namespace mozilla {
 
-class SMILStringType : public nsISMILType {
+class SMILStringType : public SMILType {
  public:
   // Singleton for nsSMILValue objects to hold onto.
   static SMILStringType* Singleton() {
     static SMILStringType sSingleton;
     return &sSingleton;
   }
 
  protected:
-  // nsISMILType Methods
+  // SMILType Methods
   // -------------------
   virtual void Init(nsSMILValue& aValue) const override;
   virtual void Destroy(nsSMILValue& aValue) const override;
   virtual nsresult Assign(nsSMILValue& aDest,
                           const nsSMILValue& aSrc) const override;
   virtual bool IsEqual(const nsSMILValue& aLeft,
                        const nsSMILValue& aRight) const override;
   virtual nsresult Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd,
rename from dom/smil/nsISMILType.h
rename to dom/smil/SMILType.h
--- a/dom/smil/nsISMILType.h
+++ b/dom/smil/SMILType.h
@@ -7,18 +7,20 @@
 #ifndef NS_ISMILTYPE_H_
 #define NS_ISMILTYPE_H_
 
 #include "mozilla/Attributes.h"
 #include "nscore.h"
 
 class nsSMILValue;
 
+namespace mozilla {
+
 //////////////////////////////////////////////////////////////////////////////
-// nsISMILType: Interface for defining the basic operations needed for animating
+// SMILType: Interface for defining the basic operations needed for animating
 // a particular kind of data (e.g. lengths, colors, transformation matrices).
 //
 // This interface is never used directly but always through an nsSMILValue that
 // bundles together a pointer to a concrete implementation of this interface and
 // the data upon which it should operate.
 //
 // We keep the data and type separate rather than just providing different
 // subclasses of nsSMILValue. This is so that sizeof(nsSMILValue) is the same
@@ -40,21 +42,21 @@ class nsSMILValue;
 // | -- Assign?          |     X         |    X        |    X             |
 // | -- Add?             |     -         |    X?       |    X             |
 // | -- SandwichAdd?     |     -         |    -?       |    X             |
 // | -- ComputeDistance? |     -         |    -        |    X?            |
 // | -- Interpolate?     |     -         |    X        |    X             |
 // +---------------------+---------------+-------------+------------------+
 //
 
-class nsISMILType {
+class SMILType {
   /**
    * Only give the nsSMILValue class access to this interface.
    */
-  friend class nsSMILValue;
+  friend class ::nsSMILValue;
 
  protected:
   /**
    * Initialises aValue and sets it to some identity value such that adding
    * aValue to another value of the same type has no effect.
    *
    * @pre  aValue.IsNull()
    * @post aValue.mType == this
@@ -79,17 +81,17 @@ class nsISMILType {
    *          underlying type of the specified object differs.
    *
    * @pre aDest.mType == aSrc.mType == this
    */
   virtual nsresult Assign(nsSMILValue& aDest,
                           const nsSMILValue& aSrc) const = 0;
 
   /**
-   * Test two nsSMILValue objects (of this nsISMILType) for equality.
+   * Test two nsSMILValue objects (of this SMILType) for equality.
    *
    * A return value of true represents a guarantee that aLeft and aRight are
    * equal. (That is, they would behave identically if passed to the methods
    * Add, SandwichAdd, ComputeDistance, and Interpolate).
    *
    * A return value of false simply indicates that we make no guarantee
    * about equality.
    *
@@ -202,9 +204,11 @@ class nsISMILType {
    *
    * @pre aStartVal.mType == aEndVal.mType == aResult.mType == this
    */
   virtual nsresult Interpolate(const nsSMILValue& aStartVal,
                                const nsSMILValue& aEndVal, double aUnitDistance,
                                nsSMILValue& aResult) const = 0;
 };
 
+}  // namespace mozilla
+
 #endif  // NS_ISMILTYPE_H_
--- a/dom/smil/moz.build
+++ b/dom/smil/moz.build
@@ -6,18 +6,16 @@
 
 with Files("**"):
     BUG_COMPONENT = ("Core", "SVG")
 
 MOCHITEST_MANIFESTS += ['test/mochitest.ini']
 
 EXPORTS += [
     'nsISMILAttr.h',
-    'nsISMILType.h',
-    'nsSMILAnimationController.h',
     'nsSMILAnimationFunction.h',
     'nsSMILCompositorTable.h',
     'nsSMILCSSProperty.h',
     'nsSMILInstanceTime.h',
     'nsSMILInterval.h',
     'nsSMILKeySpline.h',
     'nsSMILMilestone.h',
     'nsSMILRepeatCount.h',
@@ -27,38 +25,43 @@ EXPORTS += [
     'nsSMILTimedElement.h',
     'nsSMILTimeValue.h',
     'nsSMILTimeValueSpec.h',
     'nsSMILTimeValueSpecParams.h',
     'nsSMILTypes.h',
     'nsSMILValue.h',
     'SMILCSSValueType.h',
     'SMILNullType.h',
+    'SMILType.h',
+]
+
+EXPORTS.mozilla += [
+    'SMILAnimationController.h',
 ]
 
 EXPORTS.mozilla.dom += [
     'TimeEvent.h',
 ]
 
 UNIFIED_SOURCES += [
-    'nsSMILAnimationController.cpp',
     'nsSMILAnimationFunction.cpp',
     'nsSMILCompositor.cpp',
     'nsSMILCSSProperty.cpp',
     'nsSMILInstanceTime.cpp',
     'nsSMILInterval.cpp',
     'nsSMILKeySpline.cpp',
     'nsSMILParserUtils.cpp',
     'nsSMILRepeatCount.cpp',
     'nsSMILSetAnimationFunction.cpp',
     'nsSMILTimeContainer.cpp',
     'nsSMILTimedElement.cpp',
     'nsSMILTimeValue.cpp',
     'nsSMILTimeValueSpec.cpp',
     'nsSMILValue.cpp',
+    'SMILAnimationController.cpp',
     'SMILBoolType.cpp',
     'SMILCSSValueType.cpp',
     'SMILEnumType.cpp',
     'SMILFloatType.cpp',
     'SMILIntegerType.cpp',
     'SMILNullType.cpp',
     'SMILStringType.cpp',
     'TimeEvent.cpp',
--- a/dom/smil/nsSMILCompositor.h
+++ b/dom/smil/nsSMILCompositor.h
@@ -108,15 +108,15 @@ class nsSMILCompositor : public PLDHashE
 
   // Member data for detecting when we need to force-recompose
   // ---------------------------------------------------------
   // Flag for tracking whether we need to compose. Initialized to false, but
   // gets flipped to true if we detect that something has changed.
   bool mForceCompositing;
 
   // Cached base value, so we can detect & force-recompose when it changes
-  // from one sample to the next. (nsSMILAnimationController moves this
+  // from one sample to the next. (SMILAnimationController moves this
   // forward from the previous sample's compositor by calling
   // StealCachedBaseValue.)
   nsSMILValue mCachedBaseValue;
 };
 
 #endif  // NS_SMILCOMPOSITOR_H_
--- a/dom/smil/nsSMILCompositorTable.h
+++ b/dom/smil/nsSMILCompositorTable.h
@@ -8,16 +8,16 @@
 #define NS_SMILCOMPOSITORTABLE_H_
 
 #include "nsTHashtable.h"
 
 //----------------------------------------------------------------------
 // nsSMILCompositorTable : A hashmap of nsSMILCompositors
 //
 // This is just a forward-declaration because it is included in
-// nsSMILAnimationController which is used in nsDocument. We don't want to
+// SMILAnimationController which is used in nsDocument. We don't want to
 // expose all of nsSMILCompositor or otherwise any changes to it will mean the
 // whole world will need to be rebuilt.
 
 class nsSMILCompositor;
 typedef nsTHashtable<nsSMILCompositor> nsSMILCompositorTable;
 
 #endif  // NS_SMILCOMPOSITORTABLE_H_
--- a/dom/smil/nsSMILTargetIdentifier.h
+++ b/dom/smil/nsSMILTargetIdentifier.h
@@ -9,21 +9,21 @@
 
 #include "mozilla/dom/Element.h"
 
 /**
  * Struct: nsSMILTargetIdentifier
  *
  * Tuple of: { Animated Element, Attribute Name }
  *
- * Used in nsSMILAnimationController as hash key for mapping an animation
+ * Used in SMILAnimationController as hash key for mapping an animation
  * target to the nsSMILCompositor for that target.
  *
  * NOTE: Need a nsRefPtr for the element & attribute name, because
- * nsSMILAnimationController retain its hash table for one sample into the
+ * SMILAnimationController retain its hash table for one sample into the
  * future, and we need to make sure their target isn't deleted in that time.
  */
 
 struct nsSMILTargetIdentifier {
   nsSMILTargetIdentifier()
       : mElement(nullptr),
         mAttributeName(nullptr),
         mAttributeNamespaceID(kNameSpaceID_Unknown) {}
--- a/dom/smil/nsSMILValue.cpp
+++ b/dom/smil/nsSMILValue.cpp
@@ -6,17 +6,17 @@
 
 #include "nsSMILValue.h"
 #include "nsDebug.h"
 #include <string.h>
 
 //----------------------------------------------------------------------
 // Public methods
 
-nsSMILValue::nsSMILValue(const nsISMILType* aType)
+nsSMILValue::nsSMILValue(const SMILType* aType)
     : mType(SMILNullType::Singleton()) {
   mU.mBool = false;
   if (!aType) {
     NS_ERROR("Trying to construct nsSMILValue with null mType pointer");
     return;
   }
 
   InitAndCheckPostcondition(aType);
@@ -115,26 +115,26 @@ nsresult nsSMILValue::Interpolate(const 
   }
 
   return mType->Interpolate(*this, aEndVal, aUnitDistance, aResult);
 }
 
 //----------------------------------------------------------------------
 // Helper methods
 
-// Wrappers for nsISMILType::Init & ::Destroy that verify their postconditions
-void nsSMILValue::InitAndCheckPostcondition(const nsISMILType* aNewType) {
+// Wrappers for SMILType::Init & ::Destroy that verify their postconditions
+void nsSMILValue::InitAndCheckPostcondition(const SMILType* aNewType) {
   aNewType->Init(*this);
   MOZ_ASSERT(mType == aNewType,
              "Post-condition of Init failed. nsSMILValue is invalid");
 }
 
 void nsSMILValue::DestroyAndCheckPostcondition() {
   mType->Destroy(*this);
   MOZ_ASSERT(IsNull(),
              "Post-condition of Destroy failed. "
              "nsSMILValue not null after destroying");
 }
 
-void nsSMILValue::DestroyAndReinit(const nsISMILType* aNewType) {
+void nsSMILValue::DestroyAndReinit(const SMILType* aNewType) {
   DestroyAndCheckPostcondition();
   InitAndCheckPostcondition(aNewType);
 }
--- a/dom/smil/nsSMILValue.h
+++ b/dom/smil/nsSMILValue.h
@@ -2,46 +2,47 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef NS_SMILVALUE_H_
 #define NS_SMILVALUE_H_
 
-#include "nsISMILType.h"
+#include "SMILType.h"
 #include "SMILNullType.h"
 
 /**
  * Although objects of this type are generally only created on the stack and
  * only exist during the taking of a new time sample, that's not always the
  * case. The nsSMILValue objects obtained from attributes' base values are
  * cached so that the SMIL engine can make certain optimizations during a
  * sample if the base value has not changed since the last sample (potentially
  * avoiding recomposing). These nsSMILValue objects typically live much longer
  * than a single sample.
  */
 class nsSMILValue {
  public:
   typedef mozilla::SMILNullType SMILNullType;
+  typedef mozilla::SMILType SMILType;
 
   nsSMILValue() : mU(), mType(SMILNullType::Singleton()) {}
-  explicit nsSMILValue(const nsISMILType* aType);
+  explicit nsSMILValue(const SMILType* aType);
   nsSMILValue(const nsSMILValue& aVal);
 
   ~nsSMILValue() { mType->Destroy(*this); }
 
   const nsSMILValue& operator=(const nsSMILValue& aVal);
 
   // Move constructor / reassignment operator:
   nsSMILValue(nsSMILValue&& aVal);
   nsSMILValue& operator=(nsSMILValue&& aVal);
 
   // Equality operators. These are allowed to be conservative (return false
-  // more than you'd expect) - see comment above nsISMILType::IsEqual.
+  // more than you'd expect) - see comment above SMILType::IsEqual.
   bool operator==(const nsSMILValue& aVal) const;
   bool operator!=(const nsSMILValue& aVal) const { return !(*this == aVal); }
 
   bool IsNull() const { return (mType == SMILNullType::Singleton()); }
 
   nsresult Add(const nsSMILValue& aValueToAdd, uint32_t aCount = 1);
   nsresult SandwichAdd(const nsSMILValue& aValueToAdd);
   nsresult ComputeDistance(const nsSMILValue& aTo, double& aDistance) const;
@@ -57,17 +58,17 @@ class nsSMILValue {
       float mAngle;
       uint16_t mUnit;
       uint16_t mOrientType;
     } mOrient;
     int32_t mIntPair[2];
     float mNumberPair[2];
     void* mPtr;
   } mU;
-  const nsISMILType* mType;
+  const SMILType* mType;
 
  protected:
-  void InitAndCheckPostcondition(const nsISMILType* aNewType);
+  void InitAndCheckPostcondition(const SMILType* aNewType);
   void DestroyAndCheckPostcondition();
-  void DestroyAndReinit(const nsISMILType* aNewType);
+  void DestroyAndReinit(const SMILType* aNewType);
 };
 
 #endif  // NS_SMILVALUE_H_
--- a/dom/svg/DOMSVGAnimatedLengthList.cpp
+++ b/dom/svg/DOMSVGAnimatedLengthList.cpp
@@ -10,16 +10,17 @@
 #include "SVGElement.h"
 #include "nsCOMPtr.h"
 #include "nsSVGAttrTearoffTable.h"
 #include "mozilla/dom/SVGAnimatedLengthListBinding.h"
 
 // See the architecture comment in this file's header.
 
 namespace mozilla {
+namespace dom {
 
 static inline nsSVGAttrTearoffTable<SVGAnimatedLengthList,
                                     DOMSVGAnimatedLengthList>&
 SVGAnimatedLengthListTearoffTable() {
   static nsSVGAttrTearoffTable<SVGAnimatedLengthList, DOMSVGAnimatedLengthList>
       sSVGAnimatedLengthListTearoffTable;
   return sSVGAnimatedLengthListTearoffTable;
 }
@@ -118,9 +119,10 @@ bool DOMSVGAnimatedLengthList::IsAnimati
 SVGAnimatedLengthList& DOMSVGAnimatedLengthList::InternalAList() {
   return *mElement->GetAnimatedLengthList(mAttrEnum);
 }
 
 const SVGAnimatedLengthList& DOMSVGAnimatedLengthList::InternalAList() const {
   return *mElement->GetAnimatedLengthList(mAttrEnum);
 }
 
+}  // namespace dom
 }  // namespace mozilla
--- a/dom/svg/DOMSVGAnimatedLengthList.h
+++ b/dom/svg/DOMSVGAnimatedLengthList.h
@@ -11,16 +11,19 @@
 #include "nsCycleCollectionParticipant.h"
 #include "SVGElement.h"
 #include "mozilla/Attributes.h"
 
 namespace mozilla {
 
 class SVGAnimatedLengthList;
 class SVGLengthList;
+
+namespace dom {
+
 class DOMSVGLengthList;
 
 /**
  * Class DOMSVGAnimatedLengthList
  *
  * This class is used to create the DOM tearoff objects that wrap internal
  * SVGAnimatedLengthList objects. We have this internal-DOM split because DOM
  * classes are relatively heavy-weight objects with non-optimal interfaces for
@@ -190,11 +193,12 @@ class DOMSVGAnimatedLengthList final : p
   // Strong ref to our element to keep it alive. We hold this not only for
   // ourself, but also for our base/animVal and all of their items.
   RefPtr<dom::SVGElement> mElement;
 
   uint8_t mAttrEnum;
   uint8_t mAxis;
 };
 
+}  // namespace dom
 }  // namespace mozilla
 
 #endif  // MOZILLA_DOMSVGANIMATEDLENGTHLIST_H__
--- a/dom/svg/DOMSVGAnimatedNumberList.cpp
+++ b/dom/svg/DOMSVGAnimatedNumberList.cpp
@@ -10,16 +10,17 @@
 #include "SVGElement.h"
 #include "nsCOMPtr.h"
 #include "nsSVGAttrTearoffTable.h"
 #include "mozilla/dom/SVGAnimatedNumberListBinding.h"
 
 // See the architecture comment in this file's header.
 
 namespace mozilla {
+namespace dom {
 
 static inline nsSVGAttrTearoffTable<SVGAnimatedNumberList,
                                     DOMSVGAnimatedNumberList>&
 SVGAnimatedNumberListTearoffTable() {
   static nsSVGAttrTearoffTable<SVGAnimatedNumberList, DOMSVGAnimatedNumberList>
       sSVGAnimatedNumberListTearoffTable;
   return sSVGAnimatedNumberListTearoffTable;
 }
@@ -124,9 +125,10 @@ bool DOMSVGAnimatedNumberList::IsAnimati
 SVGAnimatedNumberList& DOMSVGAnimatedNumberList::InternalAList() {
   return *mElement->GetAnimatedNumberList(mAttrEnum);
 }
 
 const SVGAnimatedNumberList& DOMSVGAnimatedNumberList::InternalAList() const {
   return *mElement->GetAnimatedNumberList(mAttrEnum);
 }
 
+}  // namespace dom
 }  // namespace mozilla
--- a/dom/svg/DOMSVGAnimatedNumberList.h
+++ b/dom/svg/DOMSVGAnimatedNumberList.h
@@ -10,20 +10,23 @@
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "SVGElement.h"
 #include "nsWrapperCache.h"
 #include "mozilla/Attributes.h"
 
 namespace mozilla {
 
-class DOMSVGNumberList;
 class SVGAnimatedNumberList;
 class SVGNumberList;
 
+namespace dom {
+
+class DOMSVGNumberList;
+
 /**
  * Class DOMSVGAnimatedNumberList
  *
  * This class is used to create the DOM tearoff objects that wrap internal
  * SVGAnimatedNumberList objects.
  *
  * See the architecture comment in DOMSVGAnimatedLengthList.h (that's
  * LENGTH list). The comment for that class largly applies to this one too
@@ -119,11 +122,12 @@ class DOMSVGAnimatedNumberList final : p
 
   // Strong ref to our element to keep it alive. We hold this not only for
   // ourself, but also for our base/animVal and all of their items.
   RefPtr<dom::SVGElement> mElement;
 
   uint8_t mAttrEnum;
 };
 
+}  // namespace dom
 }  // namespace mozilla
 
 #endif  // MOZILLA_DOMSVGANIMATEDNUMBERLIST_H__
--- a/dom/svg/DOMSVGLength.cpp
+++ b/dom/svg/DOMSVGLength.cpp
@@ -16,17 +16,17 @@
 #include "mozilla/dom/SVGLengthBinding.h"
 #include "mozilla/FloatingPoint.h"
 #include "nsSVGAttrTearoffTable.h"
 
 // See the architecture comment in DOMSVGAnimatedLengthList.h.
 
 namespace mozilla {
 
-using namespace dom;
+namespace dom {
 
 static nsSVGAttrTearoffTable<nsSVGLength2, DOMSVGLength>
     sBaseSVGLengthTearOffTable, sAnimSVGLengthTearOffTable;
 
 // We could use NS_IMPL_CYCLE_COLLECTION(, except that in Unlink() we need to
 // clear our list's weak ref to us to be safe. (The other option would be to
 // not unlink and rely on the breaking of the other edges in the cycle, as
 // NS_SVG_VAL_IMPL_CYCLE_COLLECTION does.)
@@ -49,17 +49,17 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(DOM
   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMSVGLength)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMSVGLength)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMSVGLength)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
-  NS_INTERFACE_MAP_ENTRY(mozilla::DOMSVGLength)  // pseudo-interface
+  NS_INTERFACE_MAP_ENTRY(DOMSVGLength)  // pseudo-interface
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 //----------------------------------------------------------------------
 // Helper class: AutoChangeLengthNotifier
 // Stack-based helper class to pair calls to WillChangeLengthList and
 // DidChangeLengthList.
 class MOZ_RAII AutoChangeLengthNotifier {
@@ -457,9 +457,10 @@ SVGLength& DOMSVGLength::InternalItem() 
 #ifdef DEBUG
 bool DOMSVGLength::IndexIsValid() {
   SVGAnimatedLengthList* alist = Element()->GetAnimatedLengthList(mAttrEnum);
   return (mIsAnimValItem && mListIndex < alist->GetAnimValue().Length()) ||
          (!mIsAnimValItem && mListIndex < alist->GetBaseValue().Length());
 }
 #endif
 
+}  // namespace dom
 }  // namespace mozilla
--- a/dom/svg/DOMSVGLength.h
+++ b/dom/svg/DOMSVGLength.h
@@ -30,17 +30,16 @@
 #define MOZ_SVG_LIST_INDEX_BIT_COUNT 22  // supports > 4 million list items
 
 namespace mozilla {
 
 class ErrorResult;
 
 namespace dom {
 class SVGElement;
-}
 
 /**
  * Class DOMSVGLength
  *
  * This class creates the DOM objects that wrap internal SVGLength objects that
  * are in an SVGLengthList. It is also used to create the objects returned by
  * SVGSVGElement.createSVGLength().
  *
@@ -230,13 +229,14 @@ class DOMSVGLength final : public nsISup
 
   // The following members are only used when we have an nsSVGLength2
   nsSVGLength2* mVal;  // kept alive because it belongs to mSVGElement
   RefPtr<dom::SVGElement> mSVGElement;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(DOMSVGLength, MOZILLA_DOMSVGLENGTH_IID)
 
+}  // namespace dom
 }  // namespace mozilla
 
 #undef MOZ_SVG_LIST_INDEX_BIT_COUNT
 
 #endif  // MOZILLA_DOMSVGLENGTH_H__
--- a/dom/svg/DOMSVGLengthList.cpp
+++ b/dom/svg/DOMSVGLengthList.cpp
@@ -14,32 +14,33 @@
 #include "mozilla/dom/SVGLengthListBinding.h"
 #include <algorithm>
 
 // See the comment in this file's header.
 
 // local helper functions
 namespace {
 
-using mozilla::DOMSVGLength;
+using mozilla::dom::DOMSVGLength;
 
 void UpdateListIndicesFromIndex(FallibleTArray<DOMSVGLength*>& aItemsArray,
                                 uint32_t aStartingIndex) {
   uint32_t length = aItemsArray.Length();
 
   for (uint32_t i = aStartingIndex; i < length; ++i) {
     if (aItemsArray[i]) {
       aItemsArray[i]->UpdateListIndex(i);
     }
   }
 }
 
 }  // namespace
 
 namespace mozilla {
+namespace dom {
 
 // We could use NS_IMPL_CYCLE_COLLECTION(, except that in Unlink() we need to
 // clear our DOMSVGAnimatedLengthList's weak ref to us to be safe. (The other
 // option would be to not unlink and rely on the breaking of the other edges in
 // the cycle, as NS_SVG_VAL_IMPL_CYCLE_COLLECTION does.)
 NS_IMPL_CYCLE_COLLECTION_CLASS(DOMSVGLengthList)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMSVGLengthList)
@@ -385,9 +386,10 @@ void DOMSVGLengthList::MaybeRemoveItemFr
   if (animVal->mItems[aIndex]) {
     animVal->mItems[aIndex]->RemovingFromList();
   }
   animVal->mItems.RemoveElementAt(aIndex);
 
   UpdateListIndicesFromIndex(animVal->mItems, aIndex);
 }
 
+}  // namespace dom
 }  // namespace mozilla
--- a/dom/svg/DOMSVGLengthList.h
+++ b/dom/svg/DOMSVGLengthList.h
@@ -12,21 +12,19 @@
 #include "nsDebug.h"
 #include "nsTArray.h"
 #include "SVGLengthList.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/ErrorResult.h"
 
 namespace mozilla {
 
+namespace dom {
 class DOMSVGLength;
-
-namespace dom {
 class SVGElement;
-}
 
 /**
  * Class DOMSVGLengthList
  *
  * This class is used to create the DOM tearoff objects that wrap internal
  * SVGLengthList objects.
  *
  * See the architecture comment in DOMSVGAnimatedLengthList.h.
@@ -156,11 +154,12 @@ class DOMSVGLengthList final : public ns
 
   // Weak refs to our DOMSVGLength items. The items are friends and take care
   // of clearing our pointer to them when they die.
   FallibleTArray<DOMSVGLength*> mItems;
 
   RefPtr<DOMSVGAnimatedLengthList> mAList;
 };
 
+}  // namespace dom
 }  // namespace mozilla
 
 #endif  // MOZILLA_DOMSVGLENGTHLIST_H__
--- a/dom/svg/DOMSVGNumber.cpp
+++ b/dom/svg/DOMSVGNumber.cpp
@@ -11,16 +11,17 @@
 #include "SVGElement.h"
 #include "nsError.h"
 #include "nsContentUtils.h"  // for NS_ENSURE_FINITE
 #include "mozilla/dom/SVGNumberBinding.h"
 
 // See the architecture comment in DOMSVGAnimatedNumberList.h.
 
 namespace mozilla {
+namespace dom {
 
 // We could use NS_IMPL_CYCLE_COLLECTION(, except that in Unlink() we need to
 // clear our list's weak ref to us to be safe. (The other option would be to
 // not unlink and rely on the breaking of the other edges in the cycle, as
 // NS_SVG_VAL_IMPL_CYCLE_COLLECTION does.)
 NS_IMPL_CYCLE_COLLECTION_CLASS(DOMSVGNumber)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMSVGNumber)
@@ -162,12 +163,13 @@ bool DOMSVGNumber::IndexIsValid() {
   SVGAnimatedNumberList* alist = Element()->GetAnimatedNumberList(mAttrEnum);
   return (mIsAnimValItem && mListIndex < alist->GetAnimValue().Length()) ||
          (!mIsAnimValItem && mListIndex < alist->GetBaseValue().Length());
 }
 #endif
 
 JSObject* DOMSVGNumber::WrapObject(JSContext* aCx,
                                    JS::Handle<JSObject*> aGivenProto) {
-  return dom::SVGNumber_Binding::Wrap(aCx, this, aGivenProto);
+  return SVGNumber_Binding::Wrap(aCx, this, aGivenProto);
 }
 
+}  // namespace dom
 }  // namespace mozilla
--- a/dom/svg/DOMSVGNumber.h
+++ b/dom/svg/DOMSVGNumber.h
@@ -15,17 +15,16 @@
 #include "nsWrapperCache.h"
 
 #define MOZ_SVG_LIST_INDEX_BIT_COUNT 27  // supports > 134 million list items
 
 namespace mozilla {
 
 namespace dom {
 class SVGElement;
-}
 
 /**
  * Class DOMSVGNumber
  *
  * This class creates the DOM objects that wrap internal SVGNumber objects that
  * are in an SVGNumberList. It is also used to create the objects returned by
  * SVGSVGElement.createSVGNumber().
  *
@@ -147,13 +146,14 @@ class DOMSVGNumber final : public nsISup
   uint32_t mListIndex : MOZ_SVG_LIST_INDEX_BIT_COUNT;
   uint32_t mAttrEnum : 4;  // supports up to 16 attributes
   uint32_t mIsAnimValItem : 1;
 
   // The following member is only used when we're not in a list:
   float mValue;
 };
 
+}  // namespace dom
 }  // namespace mozilla
 
 #undef MOZ_SVG_LIST_INDEX_BIT_COUNT
 
 #endif  // MOZILLA_DOMSVGNUMBER_H__
--- a/dom/svg/DOMSVGNumberList.cpp
+++ b/dom/svg/DOMSVGNumberList.cpp
@@ -14,32 +14,33 @@
 #include "mozilla/dom/SVGNumberListBinding.h"
 #include <algorithm>
 
 // See the comment in this file's header.
 
 // local helper functions
 namespace {
 
-using mozilla::DOMSVGNumber;
+using mozilla::dom::DOMSVGNumber;
 
 void UpdateListIndicesFromIndex(FallibleTArray<DOMSVGNumber*>& aItemsArray,
                                 uint32_t aStartingIndex) {
   uint32_t length = aItemsArray.Length();
 
   for (uint32_t i = aStartingIndex; i < length; ++i) {
     if (aItemsArray[i]) {
       aItemsArray[i]->UpdateListIndex(i);
     }
   }
 }
 
 }  // namespace
 
 namespace mozilla {
+namespace dom {
 
 // We could use NS_IMPL_CYCLE_COLLECTION(, except that in Unlink() we need to
 // clear our DOMSVGAnimatedNumberList's weak ref to us to be safe. (The other
 // option would be to not unlink and rely on the breaking of the other edges in
 // the cycle, as NS_SVG_VAL_IMPL_CYCLE_COLLECTION does.)
 NS_IMPL_CYCLE_COLLECTION_CLASS(DOMSVGNumberList)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMSVGNumberList)
@@ -363,9 +364,10 @@ void DOMSVGNumberList::MaybeRemoveItemFr
   if (animVal->mItems[aIndex]) {
     animVal->mItems[aIndex]->RemovingFromList();
   }
   animVal->mItems.RemoveElementAt(aIndex);
 
   UpdateListIndicesFromIndex(animVal->mItems, aIndex);
 }
 
+}  // namespace dom
 }  // namespace mozilla
--- a/dom/svg/DOMSVGNumberList.h
+++ b/dom/svg/DOMSVGNumberList.h
@@ -12,21 +12,19 @@
 #include "nsDebug.h"
 #include "nsTArray.h"
 #include "SVGNumberList.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/ErrorResult.h"
 
 namespace mozilla {
 
+namespace dom {
 class DOMSVGNumber;
-
-namespace dom {
 class SVGElement;
-}
 
 /**
  * Class DOMSVGNumberList
  *
  * This class is used to create the DOM tearoff objects that wrap internal
  * SVGNumberList objects.
  *
  * See the architecture comment in DOMSVGAnimatedNumberList.h.
@@ -154,11 +152,12 @@ class DOMSVGNumberList final : public ns
 
   // Weak refs to our DOMSVGNumber items. The items are friends and take care
   // of clearing our pointer to them when they die.
   FallibleTArray<DOMSVGNumber*> mItems;
 
   RefPtr<DOMSVGAnimatedNumberList> mAList;
 };
 
+}  // namespace dom
 }  // namespace mozilla
 
 #endif  // MOZILLA_DOMSVGNUMBERLIST_H__
rename from dom/svg/SVGTransform.cpp
rename to dom/svg/DOMSVGTransform.cpp
--- a/dom/svg/SVGTransform.cpp
+++ b/dom/svg/DOMSVGTransform.cpp
@@ -1,17 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "SVGTransform.h"
+#include "DOMSVGTransform.h"
 
-#include "mozilla/dom/SVGTransform.h"
 #include "mozilla/dom/SVGMatrix.h"
 #include "mozilla/dom/SVGTransformBinding.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/FloatingPoint.h"
 #include "nsError.h"
 #include "SVGAnimatedTransformList.h"
 #include "nsSVGAttrTearoffTable.h"
 
@@ -19,64 +18,66 @@ namespace {
 const double kRadPerDegree = 2.0 * M_PI / 360.0;
 }  // namespace
 
 namespace mozilla {
 namespace dom {
 
 using namespace SVGTransform_Binding;
 
-static nsSVGAttrTearoffTable<SVGTransform, SVGMatrix>& SVGMatrixTearoffTable() {
-  static nsSVGAttrTearoffTable<SVGTransform, SVGMatrix> sSVGMatrixTearoffTable;
+static nsSVGAttrTearoffTable<DOMSVGTransform, SVGMatrix>&
+SVGMatrixTearoffTable() {
+  static nsSVGAttrTearoffTable<DOMSVGTransform, SVGMatrix>
+      sSVGMatrixTearoffTable;
   return sSVGMatrixTearoffTable;
 }
 
 //----------------------------------------------------------------------
 
 // We could use NS_IMPL_CYCLE_COLLECTION(, except that in Unlink() we need to
 // clear our list's weak ref to us to be safe. (The other option would be to
 // not unlink and rely on the breaking of the other edges in the cycle, as
 // NS_SVG_VAL_IMPL_CYCLE_COLLECTION does.)
-NS_IMPL_CYCLE_COLLECTION_CLASS(SVGTransform)
+NS_IMPL_CYCLE_COLLECTION_CLASS(DOMSVGTransform)
 
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(SVGTransform)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMSVGTransform)
   // We may not belong to a list, so we must null check tmp->mList.
   if (tmp->mList) {
     tmp->mList->mItems[tmp->mListIndex] = nullptr;
   }
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mList)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(SVGTransform)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMSVGTransform)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mList)
   SVGMatrix* matrix = SVGMatrixTearoffTable().GetTearoff(tmp);
   CycleCollectionNoteChild(cb, matrix, "matrix");
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
-NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(SVGTransform)
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(DOMSVGTransform)
   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
-NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(SVGTransform, AddRef)
-NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(SVGTransform, Release)
+NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(DOMSVGTransform, AddRef)
+NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(DOMSVGTransform, Release)
 
-JSObject* SVGTransform::WrapObject(JSContext* aCx,
-                                   JS::Handle<JSObject*> aGivenProto) {
+JSObject* DOMSVGTransform::WrapObject(JSContext* aCx,
+                                      JS::Handle<JSObject*> aGivenProto) {
   return SVGTransform_Binding::Wrap(aCx, this, aGivenProto);
 }
 
 //----------------------------------------------------------------------
 // Helper class: AutoChangeTransformNotifier
 // Stack-based helper class to pair calls to WillChangeTransformList
 // and DidChangeTransformList.
 class MOZ_RAII AutoChangeTransformNotifier {
  public:
   explicit AutoChangeTransformNotifier(
-      SVGTransform* aTransform MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+      DOMSVGTransform* aTransform MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : mTransform(aTransform) {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     MOZ_ASSERT(mTransform, "Expecting non-null transform");
     if (mTransform->HasOwner()) {
       mEmptyOrOldValue = mTransform->Element()->WillChangeTransformList();
     }
   }
 
@@ -87,122 +88,123 @@ class MOZ_RAII AutoChangeTransformNotifi
       // script, potentially removing mTransform from its list.
       if (mTransform->mList && mTransform->mList->IsAnimating()) {
         mTransform->Element()->AnimationNeedsResample();
       }
     }
   }
 
  private:
-  SVGTransform* const mTransform;
+  DOMSVGTransform* const mTransform;
   nsAttrValue mEmptyOrOldValue;
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 //----------------------------------------------------------------------
 // Ctors:
 
-SVGTransform::SVGTransform(DOMSVGTransformList* aList, uint32_t aListIndex,
-                           bool aIsAnimValItem)
+DOMSVGTransform::DOMSVGTransform(DOMSVGTransformList* aList,
+                                 uint32_t aListIndex, bool aIsAnimValItem)
     : mList(aList),
       mListIndex(aListIndex),
       mIsAnimValItem(aIsAnimValItem),
       mTransform(nullptr) {
   // These shifts are in sync with the members in the header.
   MOZ_ASSERT(aList && aListIndex <= MaxListIndex(), "bad arg");
 
   MOZ_ASSERT(IndexIsValid(), "Bad index for DOMSVGNumber!");
 }
 
-SVGTransform::SVGTransform()
+DOMSVGTransform::DOMSVGTransform()
     : mList(nullptr),
       mListIndex(0),
       mIsAnimValItem(false),
       mTransform(new nsSVGTransform())  // Default ctor for objects not in a
                                         // list initialises to matrix type with
                                         // identity matrix
 {}
 
-SVGTransform::SVGTransform(const gfxMatrix& aMatrix)
+DOMSVGTransform::DOMSVGTransform(const gfxMatrix& aMatrix)
     : mList(nullptr),
       mListIndex(0),
       mIsAnimValItem(false),
       mTransform(new nsSVGTransform(aMatrix)) {}
 
-SVGTransform::SVGTransform(const nsSVGTransform& aTransform)
+DOMSVGTransform::DOMSVGTransform(const nsSVGTransform& aTransform)
     : mList(nullptr),
       mListIndex(0),
       mIsAnimValItem(false),
       mTransform(new nsSVGTransform(aTransform)) {}
 
-SVGTransform::~SVGTransform() {
+DOMSVGTransform::~DOMSVGTransform() {
   SVGMatrix* matrix = SVGMatrixTearoffTable().GetTearoff(this);
   if (matrix) {
     SVGMatrixTearoffTable().RemoveTearoff(this);
     NS_RELEASE(matrix);
   }
   // Our mList's weak ref to us must be nulled out when we die. If GC has
   // unlinked us using the cycle collector code, then that has already
   // happened, and mList is null.
   if (mList) {
     mList->mItems[mListIndex] = nullptr;
   }
 }
 
-uint16_t SVGTransform::Type() const { return Transform().Type(); }
+uint16_t DOMSVGTransform::Type() const { return Transform().Type(); }
 
-SVGMatrix* SVGTransform::GetMatrix() {
+SVGMatrix* DOMSVGTransform::GetMatrix() {
   SVGMatrix* wrapper = SVGMatrixTearoffTable().GetTearoff(this);
   if (!wrapper) {
     NS_ADDREF(wrapper = new SVGMatrix(*this));
     SVGMatrixTearoffTable().AddTearoff(this, wrapper);
   }
   return wrapper;
 }
 
-float SVGTransform::Angle() const { return Transform().Angle(); }
+float DOMSVGTransform::Angle() const { return Transform().Angle(); }
 
-void SVGTransform::SetMatrix(SVGMatrix& aMatrix, ErrorResult& rv) {
+void DOMSVGTransform::SetMatrix(SVGMatrix& aMatrix, ErrorResult& rv) {
   if (mIsAnimValItem) {
     rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
     return;
   }
   SetMatrix(aMatrix.GetMatrix());
 }
 
-void SVGTransform::SetTranslate(float tx, float ty, ErrorResult& rv) {
+void DOMSVGTransform::SetTranslate(float tx, float ty, ErrorResult& rv) {
   if (mIsAnimValItem) {
     rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
     return;
   }
 
   if (Transform().Type() == SVG_TRANSFORM_TRANSLATE && Matrixgfx()._31 == tx &&
       Matrixgfx()._32 == ty) {
     return;
   }
 
   AutoChangeTransformNotifier notifier(this);
   Transform().SetTranslate(tx, ty);
 }
 
-void SVGTransform::SetScale(float sx, float sy, ErrorResult& rv) {
+void DOMSVGTransform::SetScale(float sx, float sy, ErrorResult& rv) {
   if (mIsAnimValItem) {
     rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
     return;
   }
 
   if (Transform().Type() == SVG_TRANSFORM_SCALE && Matrixgfx()._11 == sx &&
       Matrixgfx()._22 == sy) {
     return;
   }
   AutoChangeTransformNotifier notifier(this);
   Transform().SetScale(sx, sy);
 }
 
-void SVGTransform::SetRotate(float angle, float cx, float cy, ErrorResult& rv) {
+void DOMSVGTransform::SetRotate(float angle, float cx, float cy,
+                                ErrorResult& rv) {
   if (mIsAnimValItem) {
     rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
     return;
   }
 
   if (Transform().Type() == SVG_TRANSFORM_ROTATE) {
     float currentCx, currentCy;
     Transform().GetRotationOrigin(currentCx, currentCy);
@@ -210,17 +212,17 @@ void SVGTransform::SetRotate(float angle
       return;
     }
   }
 
   AutoChangeTransformNotifier notifier(this);
   Transform().SetRotate(angle, cx, cy);
 }
 
-void SVGTransform::SetSkewX(float angle, ErrorResult& rv) {
+void DOMSVGTransform::SetSkewX(float angle, ErrorResult& rv) {
   if (mIsAnimValItem) {
     rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
     return;
   }
 
   if (Transform().Type() == SVG_TRANSFORM_SKEWX &&
       Transform().Angle() == angle) {
     return;
@@ -231,17 +233,17 @@ void SVGTransform::SetSkewX(float angle,
     return;
   }
 
   AutoChangeTransformNotifier notifier(this);
   DebugOnly<nsresult> result = Transform().SetSkewX(angle);
   MOZ_ASSERT(NS_SUCCEEDED(result), "SetSkewX unexpectedly failed");
 }
 
-void SVGTransform::SetSkewY(float angle, ErrorResult& rv) {
+void DOMSVGTransform::SetSkewY(float angle, ErrorResult& rv) {
   if (mIsAnimValItem) {
     rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
     return;
   }
 
   if (Transform().Type() == SVG_TRANSFORM_SKEWY &&
       Transform().Angle() == angle) {
     return;
@@ -255,59 +257,60 @@ void SVGTransform::SetSkewY(float angle,
   AutoChangeTransformNotifier notifier(this);
   DebugOnly<nsresult> result = Transform().SetSkewY(angle);
   MOZ_ASSERT(NS_SUCCEEDED(result), "SetSkewY unexpectedly failed");
 }
 
 //----------------------------------------------------------------------
 // List management methods:
 
-void SVGTransform::InsertingIntoList(DOMSVGTransformList* aList,
-                                     uint32_t aListIndex, bool aIsAnimValItem) {
+void DOMSVGTransform::InsertingIntoList(DOMSVGTransformList* aList,
+                                        uint32_t aListIndex,
+                                        bool aIsAnimValItem) {
   MOZ_ASSERT(!HasOwner(), "Inserting item that is already in a list");
 
   mList = aList;
   mListIndex = aListIndex;
   mIsAnimValItem = aIsAnimValItem;
   mTransform = nullptr;
 
   MOZ_ASSERT(IndexIsValid(), "Bad index for DOMSVGLength!");
 }
 
-void SVGTransform::RemovingFromList() {
+void DOMSVGTransform::RemovingFromList() {
   MOZ_ASSERT(!mTransform,
              "Item in list also has another non-list value associated with it");
 
   mTransform = new nsSVGTransform(InternalItem());
   mList = nullptr;
   mIsAnimValItem = false;
 }
 
-nsSVGTransform& SVGTransform::InternalItem() {
+nsSVGTransform& DOMSVGTransform::InternalItem() {
   SVGAnimatedTransformList* alist = Element()->GetAnimatedTransformList();
   return mIsAnimValItem && alist->mAnimVal ? (*alist->mAnimVal)[mListIndex]
                                            : alist->mBaseVal[mListIndex];
 }
 
-const nsSVGTransform& SVGTransform::InternalItem() const {
-  return const_cast<SVGTransform*>(this)->InternalItem();
+const nsSVGTransform& DOMSVGTransform::InternalItem() const {
+  return const_cast<DOMSVGTransform*>(this)->InternalItem();
 }
 
 #ifdef DEBUG
-bool SVGTransform::IndexIsValid() {
+bool DOMSVGTransform::IndexIsValid() {
   SVGAnimatedTransformList* alist = Element()->GetAnimatedTransformList();
   return (mIsAnimValItem && mListIndex < alist->GetAnimValue().Length()) ||
          (!mIsAnimValItem && mListIndex < alist->GetBaseValue().Length());
 }
 #endif  // DEBUG
 
 //----------------------------------------------------------------------
 // Interface for SVGMatrix's use
 
-void SVGTransform::SetMatrix(const gfxMatrix& aMatrix) {
+void DOMSVGTransform::SetMatrix(const gfxMatrix& aMatrix) {
   MOZ_ASSERT(!mIsAnimValItem, "Attempting to modify read-only transform");
 
   if (Transform().Type() == SVG_TRANSFORM_MATRIX &&
       nsSVGTransform::MatricesEqual(Matrixgfx(), aMatrix)) {
     return;
   }
 
   AutoChangeTransformNotifier notifier(this);
rename from dom/svg/SVGTransform.h
rename to dom/svg/DOMSVGTransform.h
--- a/dom/svg/SVGTransform.h
+++ b/dom/svg/DOMSVGTransform.h
@@ -23,51 +23,51 @@ namespace mozilla {
 namespace dom {
 
 class SVGElement;
 class SVGMatrix;
 
 /**
  * DOM wrapper for an SVG transform. See DOMSVGLength.h.
  */
-class SVGTransform final : public nsWrapperCache {
+class DOMSVGTransform final : public nsWrapperCache {
   friend class AutoChangeTransformNotifier;
 
  public:
-  NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(SVGTransform)
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(SVGTransform)
+  NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(DOMSVGTransform)
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(DOMSVGTransform)
 
   /**
-   * Generic ctor for SVGTransform objects that are created for an attribute.
+   * Generic ctor for DOMSVGTransform objects that are created for an attribute.
    */
-  SVGTransform(DOMSVGTransformList* aList, uint32_t aListIndex,
-               bool aIsAnimValItem);
+  DOMSVGTransform(DOMSVGTransformList* aList, uint32_t aListIndex,
+                  bool aIsAnimValItem);
 
   /**
    * Ctors for creating the objects returned by:
    *   SVGSVGElement.createSVGTransform(),
    *   SVGSVGElement.createSVGTransformFromMatrix(in SVGMatrix matrix),
    *   SVGTransformList.createSVGTransformFromMatrix(in SVGMatrix matrix)
    * which do not initially belong to an attribute.
    */
-  explicit SVGTransform();
-  explicit SVGTransform(const gfxMatrix& aMatrix);
+  explicit DOMSVGTransform();
+  explicit DOMSVGTransform(const gfxMatrix& aMatrix);
 
   /**
    * Ctor for creating an unowned copy. Used with Clone().
    */
-  explicit SVGTransform(const nsSVGTransform& aMatrix);
+  explicit DOMSVGTransform(const nsSVGTransform& aMatrix);
 
   /**
    * Create an unowned copy of an owned transform. The caller is responsible for
    * the first AddRef().
    */
-  SVGTransform* Clone() {
+  DOMSVGTransform* Clone() {
     NS_ASSERTION(mList, "unexpected caller");
-    return new SVGTransform(InternalItem());
+    return new DOMSVGTransform(InternalItem());
   }
 
   bool IsInList() const { return !!mList; }
 
   /**
    * In future, if this class is used for non-list transforms, this will be
    * different to IsInList().
    */
@@ -113,17 +113,17 @@ class SVGTransform final : public nsWrap
   void SetMatrix(dom::SVGMatrix& matrix, ErrorResult& rv);
   void SetTranslate(float tx, float ty, ErrorResult& rv);
   void SetScale(float sx, float sy, ErrorResult& rv);
   void SetRotate(float angle, float cx, float cy, ErrorResult& rv);
   void SetSkewX(float angle, ErrorResult& rv);
   void SetSkewY(float angle, ErrorResult& rv);
 
  protected:
-  ~SVGTransform();
+  ~DOMSVGTransform();
 
   // Interface for SVGMatrix's use
   friend class dom::SVGMatrix;
   bool IsAnimVal() const { return mIsAnimValItem; }
   const gfxMatrix& Matrixgfx() const { return Transform().GetMatrix(); }
   void SetMatrix(const gfxMatrix& aMatrix);
 
  private:
--- a/dom/svg/DOMSVGTransformList.cpp
+++ b/dom/svg/DOMSVGTransformList.cpp
@@ -1,29 +1,29 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "DOMSVGTransformList.h"
 
+#include "mozilla/dom/DOMSVGTransform.h"
 #include "mozilla/dom/SVGElement.h"
-#include "mozilla/dom/SVGTransform.h"
 #include "mozilla/dom/SVGMatrix.h"
 #include "mozilla/dom/SVGTransformListBinding.h"
 #include "SVGAnimatedTransformList.h"
 #include "nsError.h"
 #include <algorithm>
 
 // local helper functions
 namespace {
 
 void UpdateListIndicesFromIndex(
-    FallibleTArray<mozilla::dom::SVGTransform*>& aItemsArray,
+    FallibleTArray<mozilla::dom::DOMSVGTransform*>& aItemsArray,
     uint32_t aStartingIndex) {
   uint32_t length = aItemsArray.Length();
 
   for (uint32_t i = aStartingIndex; i < length; ++i) {
     if (aItemsArray[i]) {
       aItemsArray[i]->UpdateListIndex(i);
     }
   }
@@ -100,20 +100,20 @@ class MOZ_RAII AutoChangeTransformListNo
   DOMSVGTransformList* const mTransformList;
   nsAttrValue mEmptyOrOldValue;
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 void DOMSVGTransformList::InternalListLengthWillChange(uint32_t aNewLength) {
   uint32_t oldLength = mItems.Length();
 
-  if (aNewLength > SVGTransform::MaxListIndex()) {
+  if (aNewLength > DOMSVGTransform::MaxListIndex()) {
     // It's safe to get out of sync with our internal list as long as we have
     // FEWER items than it does.
-    aNewLength = SVGTransform::MaxListIndex();
+    aNewLength = DOMSVGTransform::MaxListIndex();
   }
 
   RefPtr<DOMSVGTransformList> kungFuDeathGrip;
   if (aNewLength < oldLength) {
     // RemovingFromList() might clear last reference to |this|.
     // Retain a temporary reference to keep from dying before returning.
     kungFuDeathGrip = this;
   }
@@ -149,86 +149,86 @@ void DOMSVGTransformList::Clear(ErrorRes
   if (IsAnimValList()) {
     error.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
     return;
   }
 
   if (LengthNoFlush() > 0) {
     AutoChangeTransformListNotifier notifier(this);
     // Notify any existing DOM items of removal *before* truncating the lists
-    // so that they can find their SVGTransform internal counterparts and copy
-    // their values. This also notifies the animVal list:
+    // so that they can find their DOMSVGTransform internal counterparts and
+    // copy their values. This also notifies the animVal list:
     mAList->InternalBaseValListWillChangeLengthTo(0);
 
     mItems.Clear();
     InternalList().Clear();
   }
 }
 
-already_AddRefed<SVGTransform> DOMSVGTransformList::Initialize(
-    SVGTransform& newItem, ErrorResult& error) {
+already_AddRefed<DOMSVGTransform> DOMSVGTransformList::Initialize(
+    DOMSVGTransform& newItem, ErrorResult& error) {
   if (IsAnimValList()) {
     error.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
     return nullptr;
   }
 
   // If newItem is already in a list we should insert a clone of newItem, and
   // for consistency, this should happen even if *this* is the list that
   // newItem is currently in. Note that in the case of newItem being in this
   // list, the Clear() call before the InsertItemBefore() call would remove it
   // from this list, and so the InsertItemBefore() call would not insert a
   // clone of newItem, it would actually insert newItem. To prevent that from
   // happening we have to do the clone here, if necessary.
 
-  RefPtr<SVGTransform> domItem = &newItem;
+  RefPtr<DOMSVGTransform> domItem = &newItem;
   if (domItem->HasOwner()) {
     domItem = newItem.Clone();
   }
 
   Clear(error);
   MOZ_ASSERT(!error.Failed(), "How could this fail?");
   return InsertItemBefore(*domItem, 0, error);
 }
 
-already_AddRefed<SVGTransform> DOMSVGTransformList::GetItem(
+already_AddRefed<DOMSVGTransform> DOMSVGTransformList::GetItem(
     uint32_t index, ErrorResult& error) {
   bool found;
-  RefPtr<SVGTransform> item = IndexedGetter(index, found, error);
+  RefPtr<DOMSVGTransform> item = IndexedGetter(index, found, error);
   if (!found) {
     error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
   }
   return item.forget();
 }
 
-already_AddRefed<SVGTransform> DOMSVGTransformList::IndexedGetter(
+already_AddRefed<DOMSVGTransform> DOMSVGTransformList::IndexedGetter(
     uint32_t index, bool& found, ErrorResult& error) {
   if (IsAnimValList()) {
     Element()->FlushAnimations();
   }
   found = index < LengthNoFlush();
   if (found) {
     return GetItemAt(index);
   }
   return nullptr;
 }
 
-already_AddRefed<SVGTransform> DOMSVGTransformList::InsertItemBefore(
-    SVGTransform& newItem, uint32_t index, ErrorResult& error) {
+already_AddRefed<DOMSVGTransform> DOMSVGTransformList::InsertItemBefore(
+    DOMSVGTransform& newItem, uint32_t index, ErrorResult& error) {
   if (IsAnimValList()) {
     error.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
     return nullptr;
   }
 
   index = std::min(index, LengthNoFlush());
-  if (index >= SVGTransform::MaxListIndex()) {
+  if (index >= DOMSVGTransform::MaxListIndex()) {
     error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     return nullptr;
   }
 
-  RefPtr<SVGTransform> domItem = &newItem;
+  RefPtr<DOMSVGTransform> domItem = &newItem;
   if (newItem.HasOwner()) {
     domItem = newItem.Clone();  // must do this before changing anything!
   }
 
   // Ensure we have enough memory so we can avoid complex error handling below:
   if (!mItems.SetCapacity(mItems.Length() + 1, fallible) ||
       !InternalList().SetCapacity(InternalList().Length() + 1)) {
     error.Throw(NS_ERROR_OUT_OF_MEMORY);
@@ -254,29 +254,29 @@ already_AddRefed<SVGTransform> DOMSVGTra
   // data from InternalList() itself!:
   domItem->InsertingIntoList(this, index, IsAnimValList());
 
   UpdateListIndicesFromIndex(mItems, index + 1);
 
   return domItem.forget();
 }
 
-already_AddRefed<SVGTransform> DOMSVGTransformList::ReplaceItem(
-    SVGTransform& newItem, uint32_t index, ErrorResult& error) {
+already_AddRefed<DOMSVGTransform> DOMSVGTransformList::ReplaceItem(
+    DOMSVGTransform& newItem, uint32_t index, ErrorResult& error) {
   if (IsAnimValList()) {
     error.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
     return nullptr;
   }
 
   if (index >= LengthNoFlush()) {
     error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     return nullptr;
   }
 
-  RefPtr<SVGTransform> domItem = &newItem;
+  RefPtr<DOMSVGTransform> domItem = &newItem;
   if (newItem.HasOwner()) {
     domItem = newItem.Clone();  // must do this before changing anything!
   }
 
   AutoChangeTransformListNotifier notifier(this);
   if (mItems[index]) {
     // Notify any existing DOM item of removal *before* modifying the lists so
     // that the DOM item can copy the *old* value at its index:
@@ -288,17 +288,17 @@ already_AddRefed<SVGTransform> DOMSVGTra
 
   // This MUST come after the ToSVGPoint() call, otherwise that call
   // would end up reading bad data from InternalList()!
   domItem->InsertingIntoList(this, index, IsAnimValList());
 
   return domItem.forget();
 }
 
-already_AddRefed<SVGTransform> DOMSVGTransformList::RemoveItem(
+already_AddRefed<DOMSVGTransform> DOMSVGTransformList::RemoveItem(
     uint32_t index, ErrorResult& error) {
   if (IsAnimValList()) {
     error.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
     return nullptr;
   }
 
   if (index >= LengthNoFlush()) {
     error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
@@ -307,37 +307,37 @@ already_AddRefed<SVGTransform> DOMSVGTra
 
   AutoChangeTransformListNotifier notifier(this);
   // Now that we know we're removing, keep animVal list in sync as necessary.
   // Do this *before* touching InternalList() so the removed item can get its
   // internal value.
   MaybeRemoveItemFromAnimValListAt(index);
 
   // We have to return the removed item, so get it, creating it if necessary:
-  RefPtr<SVGTransform> result = GetItemAt(index);
+  RefPtr<DOMSVGTransform> result = GetItemAt(index);
 
   // Notify the DOM item of removal *before* modifying the lists so that the
   // DOM item can copy its *old* value:
   result->RemovingFromList();
 
   InternalList().RemoveItem(index);
   mItems.RemoveElementAt(index);
 
   UpdateListIndicesFromIndex(mItems, index);
 
   return result.forget();
 }
 
-already_AddRefed<SVGTransform>
+already_AddRefed<DOMSVGTransform>
 DOMSVGTransformList::CreateSVGTransformFromMatrix(dom::SVGMatrix& matrix) {
-  RefPtr<SVGTransform> result = new SVGTransform(matrix.GetMatrix());
+  RefPtr<DOMSVGTransform> result = new DOMSVGTransform(matrix.GetMatrix());
   return result.forget();
 }
 
-already_AddRefed<SVGTransform> DOMSVGTransformList::Consolidate(
+already_AddRefed<DOMSVGTransform> DOMSVGTransformList::Consolidate(
     ErrorResult& error) {
   if (IsAnimValList()) {
     error.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
     return nullptr;
   }
 
   if (LengthNoFlush() == 0) {
     return nullptr;
@@ -351,30 +351,31 @@ already_AddRefed<SVGTransform> DOMSVGTra
   // First calculate our matrix
   gfxMatrix mx = InternalList().GetConsolidationMatrix();
 
   // Then orphan the existing items
   Clear(error);
   MOZ_ASSERT(!error.Failed(), "How could this fail?");
 
   // And append the new transform
-  RefPtr<SVGTransform> transform = new SVGTransform(mx);
+  RefPtr<DOMSVGTransform> transform = new DOMSVGTransform(mx);
   return InsertItemBefore(*transform, LengthNoFlush(), error);
 }
 
 //----------------------------------------------------------------------
 // Implementation helpers:
 
-already_AddRefed<SVGTransform> DOMSVGTransformList::GetItemAt(uint32_t aIndex) {
+already_AddRefed<DOMSVGTransform> DOMSVGTransformList::GetItemAt(
+    uint32_t aIndex) {
   MOZ_ASSERT(aIndex < mItems.Length());
 
   if (!mItems[aIndex]) {
-    mItems[aIndex] = new SVGTransform(this, aIndex, IsAnimValList());
+    mItems[aIndex] = new DOMSVGTransform(this, aIndex, IsAnimValList());
   }
-  RefPtr<SVGTransform> result = mItems[aIndex];
+  RefPtr<DOMSVGTransform> result = mItems[aIndex];
   return result.forget();
 }
 
 void DOMSVGTransformList::MaybeInsertNullInAnimValListAt(uint32_t aIndex) {
   MOZ_ASSERT(!IsAnimValList(), "call from baseVal to animVal");
 
   if (!AnimListMirrorsBaseList()) {
     return;
--- a/dom/svg/DOMSVGTransformList.h
+++ b/dom/svg/DOMSVGTransformList.h
@@ -15,30 +15,30 @@
 #include "mozilla/Attributes.h"
 #include "mozilla/ErrorResult.h"
 
 namespace mozilla {
 
 namespace dom {
 class SVGElement;
 class SVGMatrix;
-class SVGTransform;
+class DOMSVGTransform;
 }  // namespace dom
 
 /**
  * Class DOMSVGTransformList
  *
  * This class is used to create the DOM tearoff objects that wrap internal
  * SVGTransformList objects.
  *
  * See the architecture comment in DOMSVGAnimatedTransformList.h.
  */
 class DOMSVGTransformList final : public nsISupports, public nsWrapperCache {
   friend class AutoChangeTransformListNotifier;
-  friend class dom::SVGTransform;
+  friend class dom::DOMSVGTransform;
 
   ~DOMSVGTransformList() {
     // Our mAList's weak ref to us must be nulled out when we die. If GC has
     // unlinked us using the cycle collector code, then that has already
     // happened, and mAList is null.
     if (mAList) {
       (IsAnimValList() ? mAList->mAnimVal : mAList->mBaseVal) = nullptr;
     }
@@ -91,36 +91,36 @@ class DOMSVGTransformList final : public
 
   uint32_t NumberOfItems() const {
     if (IsAnimValList()) {
       Element()->FlushAnimations();
     }
     return LengthNoFlush();
   }
   void Clear(ErrorResult& error);
-  already_AddRefed<dom::SVGTransform> Initialize(dom::SVGTransform& newItem,
+  already_AddRefed<dom::DOMSVGTransform> Initialize(
+      dom::DOMSVGTransform& newItem, ErrorResult& error);
+  already_AddRefed<dom::DOMSVGTransform> GetItem(uint32_t index,
                                                  ErrorResult& error);
-  already_AddRefed<dom::SVGTransform> GetItem(uint32_t index,
-                                              ErrorResult& error);
-  already_AddRefed<dom::SVGTransform> IndexedGetter(uint32_t index, bool& found,
+  already_AddRefed<dom::DOMSVGTransform> IndexedGetter(uint32_t index,
+                                                       bool& found,
+                                                       ErrorResult& error);
+  already_AddRefed<dom::DOMSVGTransform> InsertItemBefore(
+      dom::DOMSVGTransform& newItem, uint32_t index, ErrorResult& error);
+  already_AddRefed<dom::DOMSVGTransform> ReplaceItem(
+      dom::DOMSVGTransform& newItem, uint32_t index, ErrorResult& error);
+  already_AddRefed<dom::DOMSVGTransform> RemoveItem(uint32_t index,
                                                     ErrorResult& error);
-  already_AddRefed<dom::SVGTransform> InsertItemBefore(
-      dom::SVGTransform& newItem, uint32_t index, ErrorResult& error);
-  already_AddRefed<dom::SVGTransform> ReplaceItem(dom::SVGTransform& newItem,
-                                                  uint32_t index,
-                                                  ErrorResult& error);
-  already_AddRefed<dom::SVGTransform> RemoveItem(uint32_t index,
-                                                 ErrorResult& error);
-  already_AddRefed<dom::SVGTransform> AppendItem(dom::SVGTransform& newItem,
-                                                 ErrorResult& error) {
+  already_AddRefed<dom::DOMSVGTransform> AppendItem(
+      dom::DOMSVGTransform& newItem, ErrorResult& error) {
     return InsertItemBefore(newItem, LengthNoFlush(), error);
   }
-  already_AddRefed<dom::SVGTransform> CreateSVGTransformFromMatrix(
+  already_AddRefed<dom::DOMSVGTransform> CreateSVGTransformFromMatrix(
       dom::SVGMatrix& matrix);
-  already_AddRefed<dom::SVGTransform> Consolidate(ErrorResult& error);
+  already_AddRefed<dom::DOMSVGTransform> Consolidate(ErrorResult& error);
   uint32_t Length() const { return NumberOfItems(); }
 
  private:
   dom::SVGElement* Element() const { return mAList->mElement; }
 
   /// Used to determine if this list is the baseVal or animVal list.
   bool IsAnimValList() const {
     MOZ_ASSERT(this == mAList->mBaseVal || this == mAList->mAnimVal,
@@ -133,24 +133,24 @@ class DOMSVGTransformList final : public
    *
    * To simplify the code we just have this one method for obtaining both
    * baseVal and animVal internal lists. This means that animVal lists don't
    * get const protection, but our setter methods guard against changing
    * animVal lists.
    */
   SVGTransformList& InternalList() const;
 
-  /// Returns the SVGTransform at aIndex, creating it if necessary.
-  already_AddRefed<dom::SVGTransform> GetItemAt(uint32_t aIndex);
+  /// Returns the DOMSVGTransform at aIndex, creating it if necessary.
+  already_AddRefed<dom::DOMSVGTransform> GetItemAt(uint32_t aIndex);
 
   void MaybeInsertNullInAnimValListAt(uint32_t aIndex);
   void MaybeRemoveItemFromAnimValListAt(uint32_t aIndex);
 
-  // Weak refs to our SVGTransform items. The items are friends and take care
+  // Weak refs to our DOMSVGTransform items. The items are friends and take care
   // of clearing our pointer to them when they die.
-  FallibleTArray<dom::SVGTransform*> mItems;
+  FallibleTArray<dom::DOMSVGTransform*> mItems;
 
   RefPtr<dom::DOMSVGAnimatedTransformList> mAList;
 };
 
 }  // namespace mozilla
 
 #endif  // MOZILLA_DOMSVGTRANSFORMLIST_H__
--- a/dom/svg/SVGAnimatedLength.h
+++ b/dom/svg/SVGAnimatedLength.h
@@ -9,19 +9,19 @@
 
 #include "mozilla/Attributes.h"
 #include "SVGElement.h"
 
 class nsSVGLength2;
 
 namespace mozilla {
 
-class DOMSVGLength;
+namespace dom {
 
-namespace dom {
+class DOMSVGLength;
 
 class SVGAnimatedLength final : public nsWrapperCache {
  public:
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(SVGAnimatedLength)
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(SVGAnimatedLength)
 
   SVGAnimatedLength(nsSVGLength2* aVal, SVGElement* aSVGElement)
       : mVal(aVal), mSVGElement(aSVGElement) {}
--- a/dom/svg/SVGAnimatedLengthList.h
+++ b/dom/svg/SVGAnimatedLengthList.h
@@ -33,18 +33,18 @@ class SVGElement;
  *
  * Except where noted otherwise, this class' methods take care of keeping the
  * appropriate DOM wrappers in sync (see the comment in
  * DOMSVGAnimatedLengthList::InternalBaseValListWillChangeTo) so that their
  * consumers don't need to concern themselves with that.
  */
 class SVGAnimatedLengthList {
   // friends so that they can get write access to mBaseVal
-  friend class DOMSVGLength;
-  friend class DOMSVGLengthList;
+  friend class dom::DOMSVGLength;
+  friend class dom::DOMSVGLengthList;
 
  public:
   SVGAnimatedLengthList() {}
 
   /**
    * Because it's so important that mBaseVal and its DOMSVGLengthList wrapper
    * (if any) be kept in sync (see the comment in
    * DOMSVGAnimatedLengthList::InternalBaseValListWillChangeTo), this method
--- a/dom/svg/SVGAnimatedNumberList.h
+++ b/dom/svg/SVGAnimatedNumberList.h
@@ -33,18 +33,18 @@ class SVGElement;
  *
  * Except where noted otherwise, this class' methods take care of keeping the
  * appropriate DOM wrappers in sync (see the comment in
  * DOMSVGAnimatedNumberList::InternalBaseValListWillChangeTo) so that their
  * consumers don't need to concern themselves with that.
  */
 class SVGAnimatedNumberList {
   // friends so that they can get write access to mBaseVal
-  friend class DOMSVGNumber;
-  friend class DOMSVGNumberList;
+  friend class dom::DOMSVGNumber;
+  friend class dom::DOMSVGNumberList;
 
  public:
   SVGAnimatedNumberList() : mIsBaseSet(false) {}
 
   /**
    * Because it's so important that mBaseVal and its DOMSVGNumberList wrapper
    * (if any) be kept in sync (see the comment in
    * DOMSVGAnimatedNumberList::InternalBaseValListWillChangeTo), this method
--- a/dom/svg/SVGAnimatedTransformList.h
+++ b/dom/svg/SVGAnimatedTransformList.h
@@ -16,17 +16,17 @@
 class nsAtom;
 class nsSMILValue;
 
 namespace mozilla {
 
 namespace dom {
 class SVGAnimationElement;
 class SVGElement;
-class SVGTransform;
+class DOMSVGTransform;
 }  // namespace dom
 
 /**
  * Class SVGAnimatedTransformList
  *
  * This class is very different to the SVG DOM interface of the same name found
  * in the SVG specification. This is a lightweight internal class - see
  * DOMSVGAnimatedTransformList for the heavier DOM class that wraps instances of
@@ -35,17 +35,17 @@ class SVGTransform;
  *
  * Except where noted otherwise, this class' methods take care of keeping the
  * appropriate DOM wrappers in sync (see the comment in
  * DOMSVGAnimatedTransformList::InternalBaseValListWillChangeTo) so that their
  * consumers don't need to concern themselves with that.
  */
 class SVGAnimatedTransformList {
   // friends so that they can get write access to mBaseVal
-  friend class dom::SVGTransform;
+  friend class dom::DOMSVGTransform;
   friend class DOMSVGTransformList;
 
  public:
   SVGAnimatedTransformList()
       : mIsAttrSet(false), mRequiresFrameReconstruction(true) {}
 
   /**
    * Because it's so important that mBaseVal and its DOMSVGTransformList wrapper
--- a/dom/svg/SVGAnimationElement.cpp
+++ b/dom/svg/SVGAnimationElement.cpp
@@ -3,17 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/SVGAnimationElement.h"
 #include "mozilla/dom/SVGSVGElement.h"
 #include "mozilla/dom/ElementInlines.h"
 #include "nsSMILTimeContainer.h"
-#include "nsSMILAnimationController.h"
+#include "SMILAnimationController.h"
 #include "nsSMILAnimationFunction.h"
 #include "nsContentUtils.h"
 #include "nsIContentInlines.h"
 #include "nsIURI.h"
 #include "prtime.h"
 
 namespace mozilla {
 namespace dom {
@@ -141,17 +141,17 @@ nsresult SVGAnimationElement::BindToTree
   MOZ_ASSERT(!mHrefTarget.get(),
              "Shouldn't have href-target yet (or it should've been cleared)");
   nsresult rv =
       SVGAnimationElementBase::BindToTree(aDocument, aParent, aBindingParent);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Add myself to the animation controller's master set of animation elements.
   if (nsIDocument* doc = GetComposedDoc()) {
-    nsSMILAnimationController* controller = doc->GetAnimationController();
+    SMILAnimationController* controller = doc->GetAnimationController();
     if (controller) {
       controller->RegisterAnimationElement(this);
     }
     const nsAttrValue* href =
         HasAttr(kNameSpaceID_None, nsGkAtoms::href)
             ? mAttrs.GetAttr(nsGkAtoms::href, kNameSpaceID_None)
             : mAttrs.GetAttr(nsGkAtoms::href, kNameSpaceID_XLink);
     if (href) {
@@ -165,17 +165,17 @@ nsresult SVGAnimationElement::BindToTree
   }
 
   AnimationNeedsResample();
 
   return NS_OK;
 }
 
 void SVGAnimationElement::UnbindFromTree(bool aDeep, bool aNullParent) {
-  nsSMILAnimationController* controller = OwnerDoc()->GetAnimationController();
+  SMILAnimationController* controller = OwnerDoc()->GetAnimationController();
   if (controller) {
     controller->UnregisterAnimationElement(this);
   }
 
   mHrefTarget.Unlink();
   mTimedElement.DissolveReferences();
 
   AnimationNeedsResample();
--- a/dom/svg/SVGElement.cpp
+++ b/dom/svg/SVGElement.cpp
@@ -16,30 +16,30 @@
 #include "mozilla/dom/SVGLengthBinding.h"
 #include "mozilla/dom/SVGSVGElement.h"
 #include "mozilla/dom/SVGTests.h"
 #include "mozilla/dom/SVGUnitTypesBinding.h"
 #include "mozilla/DeclarationBlock.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/InternalMutationEvent.h"
 #include "mozilla/RestyleManager.h"
+#include "mozilla/SMILAnimationController.h"
 #include "mozilla/Unused.h"
 #include "mozAutoDocUpdate.h"
 #include "nsAttrValueOrString.h"
 #include "nsCSSProps.h"
 #include "nsContentUtils.h"
 #include "nsICSSDeclaration.h"
 #include "nsIContentInlines.h"
 #include "nsIDocument.h"
 #include "nsError.h"
 #include "nsGkAtoms.h"
 #include "nsIPresShell.h"
 #include "nsIFrame.h"
 #include "nsLayoutUtils.h"
-#include "nsSMILAnimationController.h"
 #include "nsSVGAngle.h"
 #include "nsSVGBoolean.h"
 #include "nsSVGEnum.h"
 #include "nsSVGInteger.h"
 #include "nsSVGIntegerPair.h"
 #include "nsSVGLength2.h"
 #include "nsSVGNumber2.h"
 #include "nsSVGNumberPair.h"
--- a/dom/svg/SVGFEColorMatrixElement.h
+++ b/dom/svg/SVGFEColorMatrixElement.h
@@ -10,19 +10,19 @@
 #include "nsSVGEnum.h"
 #include "SVGFilters.h"
 #include "SVGAnimatedNumberList.h"
 
 nsresult NS_NewSVGFEColorMatrixElement(
     nsIContent** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 
 namespace mozilla {
-class DOMSVGAnimatedNumberList;
+namespace dom {
 
-namespace dom {
+class DOMSVGAnimatedNumberList;
 
 typedef SVGFE SVGFEColorMatrixElementBase;
 
 class SVGFEColorMatrixElement : public SVGFEColorMatrixElementBase {
   friend nsresult(::NS_NewSVGFEColorMatrixElement(
       nsIContent** aResult,
       already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo));
 
--- a/dom/svg/SVGFEConvolveMatrixElement.h
+++ b/dom/svg/SVGFEConvolveMatrixElement.h
@@ -15,19 +15,19 @@
 #include "nsSVGNumber2.h"
 #include "nsSVGString.h"
 #include "SVGAnimatedNumberList.h"
 
 nsresult NS_NewSVGFEConvolveMatrixElement(
     nsIContent** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 
 namespace mozilla {
-class DOMSVGAnimatedNumberList;
 
 namespace dom {
+class DOMSVGAnimatedNumberList;
 class SVGAnimatedBoolean;
 
 typedef SVGFE SVGFEConvolveMatrixElementBase;
 
 class SVGFEConvolveMatrixElement : public SVGFEConvolveMatrixElementBase {
   friend nsresult(::NS_NewSVGFEConvolveMatrixElement(
       nsIContent** aResult,
       already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo));
--- a/dom/svg/SVGIntegerPairSMILType.h
+++ b/dom/svg/SVGIntegerPairSMILType.h
@@ -3,32 +3,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/. */
 
 #ifndef MOZILLA_SVGINTEGERPAIRSMILTYPE_H_
 #define MOZILLA_SVGINTEGERPAIRSMILTYPE_H_
 
 #include "mozilla/Attributes.h"
-#include "nsISMILType.h"
+#include "SMILType.h"
 
 class nsSMILValue;
 
 namespace mozilla {
 
-class SVGIntegerPairSMILType : public nsISMILType {
+class SVGIntegerPairSMILType : public SMILType {
  public:
   // Singleton for nsSMILValue objects to hold onto.
   static SVGIntegerPairSMILType* Singleton() {
     static SVGIntegerPairSMILType sSingleton;
     return &sSingleton;
   }
 
  protected:
-  // nsISMILType Methods
+  // SMILType Methods
   // -------------------
   virtual void Init(nsSMILValue& aValue) const override;
   virtual void Destroy(nsSMILValue&) const override;
   virtual nsresult Assign(nsSMILValue& aDest,
                           const nsSMILValue& aSrc) const override;
   virtual bool IsEqual(const nsSMILValue& aLeft,
                        const nsSMILValue& aRight) const override;
   virtual nsresult Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd,
--- a/dom/svg/SVGLengthList.h
+++ b/dom/svg/SVGLengthList.h
@@ -14,29 +14,34 @@
 #include "nsIWeakReferenceUtils.h"
 #include "SVGElement.h"
 #include "nsTArray.h"
 #include "SVGLength.h"
 #include "mozilla/dom/SVGLengthBinding.h"
 
 namespace mozilla {
 
+namespace dom {
+class DOMSVGLength;
+class DOMSVGLengthList;
+}  // namespace dom
+
 /**
  * ATTENTION! WARNING! WATCH OUT!!
  *
  * Consumers that modify objects of this type absolutely MUST keep the DOM
  * wrappers for those lists (if any) in sync!! That's why this class is so
  * locked down.
  *
  * The DOM wrapper class for this class is DOMSVGLengthList.
  */
 class SVGLengthList {
+  friend class dom::DOMSVGLength;
+  friend class dom::DOMSVGLengthList;
   friend class SVGAnimatedLengthList;
-  friend class DOMSVGLengthList;
-  friend class DOMSVGLength;
 
  public:
   SVGLengthList() {}
   ~SVGLengthList() {}
 
   // Only methods that don't make/permit modification to this list are public.
   // Only our friend classes can access methods that may change us.
 
--- a/dom/svg/SVGLengthListSMILType.h
+++ b/dom/svg/SVGLengthListSMILType.h
@@ -3,34 +3,34 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_SVGLENGTHLISTSMILTYPE_H_
 #define MOZILLA_SVGLENGTHLISTSMILTYPE_H_
 
 #include "mozilla/Attributes.h"
-#include "nsISMILType.h"
+#include "SMILType.h"
 
 class nsSMILValue;
 
 namespace mozilla {
 
 ////////////////////////////////////////////////////////////////////////
 // SVGLengthListSMILType
 //
 // Operations for animating an SVGLengthList.
 //
-class SVGLengthListSMILType : public nsISMILType {
+class SVGLengthListSMILType : public SMILType {
  public:
   // Singleton for nsSMILValue objects to hold onto.
   static SVGLengthListSMILType sSingleton;
 
  protected:
-  // nsISMILType Methods
+  // SMILType Methods
   // -------------------
 
   /**
    * When this method initializes the SVGLengthListAndInfo for its nsSMILValue
    * argument, it has to blindly set its mCanZeroPadList to true despite
    * the fact that some attributes can't be zero-padded. (See the explaination
    * that follows.) SVGAnimatedLengthList::SMILAnimatedLengthList's
    * GetBaseValue() and ValueFromString() methods then override this for the
--- a/dom/svg/SVGMatrix.cpp
+++ b/dom/svg/SVGMatrix.cpp
@@ -15,17 +15,17 @@ const double radPerDegree = 2.0 * M_PI /
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(SVGMatrix, mTransform)
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(SVGMatrix, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(SVGMatrix, Release)
 
-SVGTransform* SVGMatrix::GetParentObject() const { return mTransform; }
+DOMSVGTransform* SVGMatrix::GetParentObject() const { return mTransform; }
 
 JSObject* SVGMatrix::WrapObject(JSContext* aCx,
                                 JS::Handle<JSObject*> aGivenProto) {
   return SVGMatrix_Binding::Wrap(aCx, this, aGivenProto);
 }
 
 void SVGMatrix::SetA(float aA, ErrorResult& rv) {
   if (IsAnimVal()) {
--- a/dom/svg/SVGMatrix.h
+++ b/dom/svg/SVGMatrix.h
@@ -32,17 +32,17 @@
  * Pre-multiplying may look wrong if you're only familiar with the SVG
  * convention, but in that case hopefully the above explanation clears things
  * up.
  */
 
 #ifndef mozilla_dom_SVGMatrix_h
 #define mozilla_dom_SVGMatrix_h
 
-#include "mozilla/dom/SVGTransform.h"
+#include "mozilla/dom/DOMSVGTransform.h"
 #include "gfxMatrix.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsWrapperCache.h"
 #include "mozilla/Attributes.h"
 
 namespace mozilla {
 namespace dom {
 
@@ -50,34 +50,34 @@ namespace dom {
  * DOM wrapper for an SVG matrix.
  */
 class SVGMatrix final : public nsWrapperCache {
  public:
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(SVGMatrix)
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(SVGMatrix)
 
   /**
-   * Ctor for SVGMatrix objects that belong to a SVGTransform.
+   * Ctor for SVGMatrix objects that belong to a DOMSVGTransform.
    */
-  explicit SVGMatrix(SVGTransform& aTransform) : mTransform(&aTransform) {}
+  explicit SVGMatrix(DOMSVGTransform& aTransform) : mTransform(&aTransform) {}
 
   /**
-   * Ctors for SVGMatrix objects created independently of a SVGTransform.
+   * Ctors for SVGMatrix objects created independently of a DOMSVGTransform.
    */
   // Default ctor for gfxMatrix will produce identity mx
   SVGMatrix() {}
 
   explicit SVGMatrix(const gfxMatrix& aMatrix) : mMatrix(aMatrix) {}
 
   const gfxMatrix& GetMatrix() const {
     return mTransform ? mTransform->Matrixgfx() : mMatrix;
   }
 
   // WebIDL
-  SVGTransform* GetParentObject() const;
+  DOMSVGTransform* GetParentObject() const;
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) override;
 
   float A() const { return static_cast<float>(GetMatrix()._11); }
   void SetA(float aA, ErrorResult& rv);
   float B() const { return static_cast<float>(GetMatrix()._12); }
   void SetB(float aB, ErrorResult& rv);
   float C() const { return static_cast<float>(GetMatrix()._21); }
@@ -112,19 +112,20 @@ class SVGMatrix final : public nsWrapper
       mMatrix = aMatrix;
     }
   }
 
   bool IsAnimVal() const {
     return mTransform ? mTransform->IsAnimVal() : false;
   }
 
-  RefPtr<SVGTransform> mTransform;
+  RefPtr<DOMSVGTransform> mTransform;
 
   // Typically we operate on the matrix data accessed via mTransform but for
-  // matrices that exist independently of an SVGTransform we use mMatrix below.
+  // matrices that exist independently of an DOMSVGTransform we use mMatrix
+  // below.
   gfxMatrix mMatrix;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // mozilla_dom_SVGMatrix_h
--- a/dom/svg/SVGMotionSMILType.h
+++ b/dom/svg/SVGMotionSMILType.h
@@ -1,52 +1,52 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-/* implementation of nsISMILType for use by <animateMotion> element */
+/* implementation of SMILType for use by <animateMotion> element */
 
 #ifndef MOZILLA_SVGMOTIONSMILTYPE_H_
 #define MOZILLA_SVGMOTIONSMILTYPE_H_
 
 #include "mozilla/gfx/2D.h"
 #include "mozilla/Attributes.h"
-#include "nsISMILType.h"
+#include "SMILType.h"
 
 class nsSMILValue;
 
 namespace mozilla {
 
 /**
  * MotionRotateType: Enum to indicate the type of our "rotate" attribute.
  */
 enum RotateType {
   eRotateType_Explicit,    // for e.g. rotate="45"/"45deg"/"0.785rad"
   eRotateType_Auto,        // for rotate="auto"
   eRotateType_AutoReverse  // for rotate="auto-reverse"
 };
 
 /**
- * SVGMotionSMILType: Implements the nsISMILType interface for SMIL animations
+ * SVGMotionSMILType: Implements the SMILType interface for SMIL animations
  * from <animateMotion>.
  *
  * NOTE: Even though there's technically no "motion" attribute, we behave in
  * many ways as if there were, for simplicity.
  */
-class SVGMotionSMILType : public nsISMILType {
+class SVGMotionSMILType : public SMILType {
   typedef mozilla::gfx::Path Path;
 
  public:
   // Singleton for nsSMILValue objects to hold onto.
   static SVGMotionSMILType sSingleton;
 
  protected:
-  // nsISMILType Methods
+  // SMILType Methods
   // -------------------
   virtual void Init(nsSMILValue& aValue) const override;
   virtual void Destroy(nsSMILValue& aValue) const override;
   virtual nsresult Assign(nsSMILValue& aDest,
                           const nsSMILValue& aSrc) const override;
   virtual bool IsEqual(const nsSMILValue& aLeft,
                        const nsSMILValue& aRight) const override;
   virtual nsresult Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd,
--- a/dom/svg/SVGNumberList.h
+++ b/dom/svg/SVGNumberList.h
@@ -12,29 +12,34 @@
 #include "nsIContent.h"
 #include "nsINode.h"
 #include "nsIWeakReferenceUtils.h"
 #include "SVGElement.h"
 #include "nsTArray.h"
 
 namespace mozilla {
 
+namespace dom {
+class DOMSVGNumber;
+class DOMSVGNumberList;
+}  // namespace dom
+
 /**
  * ATTENTION! WARNING! WATCH OUT!!
  *
  * Consumers that modify objects of this type absolutely MUST keep the DOM
  * wrappers for those lists (if any) in sync!! That's why this class is so
  * locked down.
  *
  * The DOM wrapper class for this class is DOMSVGNumberList.
  */
 class SVGNumberList {
+  friend class dom::DOMSVGNumber;
+  friend class dom::DOMSVGNumberList;
   friend class SVGAnimatedNumberList;
-  friend class DOMSVGNumberList;
-  friend class DOMSVGNumber;
 
  public:
   SVGNumberList() {}
   ~SVGNumberList() {}
 
   // Only methods that don't make/permit modification to this list are public.
   // Only our friend classes can access methods that may change us.
 
--- a/dom/svg/SVGNumberListSMILType.h
+++ b/dom/svg/SVGNumberListSMILType.h
@@ -3,34 +3,34 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_SVGNUMBERLISTSMILTYPE_H_
 #define MOZILLA_SVGNUMBERLISTSMILTYPE_H_
 
 #include "mozilla/Attributes.h"
-#include "nsISMILType.h"
+#include "SMILType.h"
 
 class nsSMILValue;
 
 namespace mozilla {
 
 ////////////////////////////////////////////////////////////////////////
 // SVGNumberListSMILType
 //
 // Operations for animating an SVGNumberList.
 //
-class SVGNumberListSMILType : public nsISMILType {
+class SVGNumberListSMILType : public SMILType {
  public:
   // Singleton for nsSMILValue objects to hold onto.
   static SVGNumberListSMILType sSingleton;
 
  protected:
-  // nsISMILType Methods
+  // SMILType Methods
   // -------------------
 
   virtual void Init(nsSMILValue& aValue) const override;
 
   virtual void Destroy(nsSMILValue& aValue) const override;
   virtual nsresult Assign(nsSMILValue& aDest,
                           const nsSMILValue& aSrc) const override;
   virtual bool IsEqual(const nsSMILValue& aLeft,
--- a/dom/svg/SVGNumberPairSMILType.h
+++ b/dom/svg/SVGNumberPairSMILType.h
@@ -3,29 +3,29 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_SVGNUMBERPAIRSMILTYPE_H_
 #define MOZILLA_SVGNUMBERPAIRSMILTYPE_H_
 
 #include "mozilla/Attributes.h"
-#include "nsISMILType.h"
+#include "SMILType.h"
 
 class nsSMILValue;
 
 namespace mozilla {
 
-class SVGNumberPairSMILType : public nsISMILType {
+class SVGNumberPairSMILType : public SMILType {
  public:
   // Singleton for nsSMILValue objects to hold onto.
   static SVGNumberPairSMILType sSingleton;
 
  protected:
-  // nsISMILType Methods
+  // SMILType Methods
   // -------------------
   virtual void Init(nsSMILValue& aValue) const override;
   virtual void Destroy(nsSMILValue&) const override;
   virtual nsresult Assign(nsSMILValue& aDest,
                           const nsSMILValue& aSrc) const override;
   virtual bool IsEqual(const nsSMILValue& aLeft,
                        const nsSMILValue& aRight) const override;
   virtual nsresult Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd,
--- a/dom/svg/SVGOrientSMILType.h
+++ b/dom/svg/SVGOrientSMILType.h
@@ -3,22 +3,22 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_SVGORIENTSMILTYPE_H_
 #define MOZILLA_SVGORIENTSMILTYPE_H_
 
 #include "mozilla/Attributes.h"
-#include "nsISMILType.h"
+#include "SMILType.h"
 
 class nsSMILValue;
 
 /**
- * This nsISMILType class is a special case for the 'orient' attribute on SVG's
+ * This SMILType class is a special case for the 'orient' attribute on SVG's
  * 'marker' element.
  *
  *   orient = "auto | auto-start-reverse | <angle>"
  *
  * Unusually, this attribute doesn't have just a single corresponding DOM
  * property, but rather is split into two properties: 'orientType' (of type
  * SVGAnimatedEnumeration) and 'orientAngle' (of type SVGAnimatedAngle). If
  * 'orientType.animVal' is SVG_MARKER_ORIENT_ANGLE, then
@@ -29,23 +29,23 @@ class nsSMILValue;
  * SVG_MARKER_ORIENT_AUTO_START_REVERSE constant value for orientType to use;
  * instead, if the attribute is set to "auto-start-reverse",
  * SVG_MARKER_ORIENT_UNKNOWN is used.  Internally, however, we do use a
  * constant with this name.
  */
 
 namespace mozilla {
 
-class SVGOrientSMILType : public nsISMILType {
+class SVGOrientSMILType : public SMILType {
  public:
   // Singleton for nsSMILValue objects to hold onto.
   static SVGOrientSMILType sSingleton;
 
  protected:
-  // nsISMILType Methods
+  // SMILType Methods
   // -------------------
   virtual void Init(nsSMILValue& aValue) const override;
   virtual void Destroy(nsSMILValue&) const override;
   virtual nsresult Assign(nsSMILValue& aDest,
                           const nsSMILValue& aSrc) const override;
   virtual bool IsEqual(const nsSMILValue& aLeft,
                        const nsSMILValue& aRight) const override;
   virtual nsresult Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd,
--- a/dom/svg/SVGPathSegListSMILType.h
+++ b/dom/svg/SVGPathSegListSMILType.h
@@ -3,37 +3,37 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_SVGPATHSEGLISTSMILTYPE_H_
 #define MOZILLA_SVGPATHSEGLISTSMILTYPE_H_
 
 #include "mozilla/Attributes.h"
-#include "nsISMILType.h"
+#include "SMILType.h"
 
 class nsSMILValue;
 
 namespace mozilla {
 
 ////////////////////////////////////////////////////////////////////////
 // SVGPathSegListSMILType
 //
 // Operations for animating an SVGPathData.
 //
-class SVGPathSegListSMILType : public nsISMILType {
+class SVGPathSegListSMILType : public SMILType {
  public:
   // Singleton for nsSMILValue objects to hold onto.
   static SVGPathSegListSMILType* Singleton() {
     static SVGPathSegListSMILType sSingleton;
     return &sSingleton;
   }
 
  protected:
-  // nsISMILType Methods
+  // SMILType Methods
   // -------------------
 
   virtual void Init(nsSMILValue& aValue) const override;
 
   virtual void Destroy(nsSMILValue& aValue) const override;
   virtual nsresult Assign(nsSMILValue& aDest,
                           const nsSMILValue& aSrc) const override;
   virtual bool IsEqual(const nsSMILValue& aLeft,
--- a/dom/svg/SVGPointListSMILType.h
+++ b/dom/svg/SVGPointListSMILType.h
@@ -3,34 +3,34 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_SVGPOINTLISTSMILTYPE_H_
 #define MOZILLA_SVGPOINTLISTSMILTYPE_H_
 
 #include "mozilla/Attributes.h"
-#include "nsISMILType.h"
+#include "SMILType.h"
 
 class nsSMILValue;
 
 namespace mozilla {
 
 ////////////////////////////////////////////////////////////////////////
 // SVGPointListSMILType
 //
 // Operations for animating an SVGPointList.
 //
-class SVGPointListSMILType : public nsISMILType {
+class SVGPointListSMILType : public SMILType {
  public:
   // Singleton for nsSMILValue objects to hold onto.
   static SVGPointListSMILType sSingleton;
 
  protected:
-  // nsISMILType Methods
+  // SMILType Methods
   // -------------------
 
   virtual void Init(nsSMILValue& aValue) const override;
 
   virtual void Destroy(nsSMILValue& aValue) const override;
   virtual nsresult Assign(nsSMILValue& aDest,
                           const nsSMILValue& aSrc) const override;
   virtual bool IsEqual(const nsSMILValue& aLeft,
--- a/dom/svg/SVGSVGElement.cpp
+++ b/dom/svg/SVGSVGElement.cpp
@@ -7,25 +7,25 @@
 #include "mozilla/dom/SVGSVGElement.h"
 
 #include "mozilla/ContentEvents.h"
 #include "mozilla/dom/SVGSVGElementBinding.h"
 #include "mozilla/dom/SVGMatrix.h"
 #include "mozilla/dom/SVGRect.h"
 #include "mozilla/dom/SVGViewElement.h"
 #include "mozilla/EventDispatcher.h"
+#include "mozilla/SMILAnimationController.h"
 
 #include "DOMSVGLength.h"
 #include "DOMSVGNumber.h"
 #include "DOMSVGPoint.h"
 #include "nsFrameSelection.h"
 #include "nsLayoutStylesheetCache.h"
 #include "nsIFrame.h"
 #include "nsISVGSVGFrame.h"
-#include "nsSMILAnimationController.h"
 #include "nsSMILTimeContainer.h"
 #include "nsSVGAngle.h"
 #include "SVGAngle.h"
 #include "nsSVGDisplayableFrame.h"
 #include "nsSVGUtils.h"
 
 NS_IMPL_NS_NEW_SVG_ELEMENT_CHECK_PARSER(SVG)
 
@@ -278,24 +278,24 @@ already_AddRefed<SVGMatrix> SVGSVGElemen
   RefPtr<SVGMatrix> matrix = new SVGMatrix();
   return matrix.forget();
 }
 
 already_AddRefed<SVGIRect> SVGSVGElement::CreateSVGRect() {
   return NS_NewSVGRect(this);
 }
 
-already_AddRefed<SVGTransform> SVGSVGElement::CreateSVGTransform() {
-  RefPtr<SVGTransform> transform = new SVGTransform();
+already_AddRefed<DOMSVGTransform> SVGSVGElement::CreateSVGTransform() {
+  RefPtr<DOMSVGTransform> transform = new DOMSVGTransform();
   return transform.forget();
 }
 
-already_AddRefed<SVGTransform> SVGSVGElement::CreateSVGTransformFromMatrix(
+already_AddRefed<DOMSVGTransform> SVGSVGElement::CreateSVGTransformFromMatrix(
     SVGMatrix& matrix) {
-  RefPtr<SVGTransform> transform = new SVGTransform(matrix.GetMatrix());
+  RefPtr<DOMSVGTransform> transform = new DOMSVGTransform(matrix.GetMatrix());
   return transform.forget();
 }
 
 //----------------------------------------------------------------------
 // helper method for implementing SetCurrentScale/Translate
 
 void SVGSVGElement::SetCurrentScaleTranslate(float s, float x, float y) {
   if (s == mCurrentScale && x == mCurrentTranslate.GetX() &&
@@ -375,17 +375,17 @@ nsSMILTimeContainer* SVGSVGElement::GetT
   }
   // invalid structure
   return nullptr;
 }
 //----------------------------------------------------------------------
 // SVGElement
 nsresult SVGSVGElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                                    nsIContent* aBindingParent) {
-  nsSMILAnimationController* smilController = nullptr;
+  SMILAnimationController* smilController = nullptr;
 
   if (aDocument) {
     smilController = aDocument->GetAnimationController();
     if (smilController) {
       // SMIL is enabled in this document
       if (WillBeOutermostSVG(aParent, aBindingParent)) {
         // We'll be the outermost <svg> element.  We'll need a time container.
         if (!mTimedDocumentRoot) {
--- a/dom/svg/SVGSVGElement.h
+++ b/dom/svg/SVGSVGElement.h
@@ -14,20 +14,20 @@ nsresult NS_NewSVGSVGElement(
     mozilla::dom::FromParser aFromParser);
 
 class nsSMILTimeContainer;
 
 namespace mozilla {
 class AutoSVGViewHandler;
 class SVGFragmentIdentifier;
 class EventChainPreVisitor;
+
+namespace dom {
 class DOMSVGLength;
 class DOMSVGNumber;
-
-namespace dom {
 class SVGAngle;
 class SVGMatrix;
 class SVGIRect;
 class SVGSVGElement;
 
 // Stores svgView arguments of SVG fragment identifiers.
 class SVGView {
  public:
@@ -132,18 +132,18 @@ class SVGSVGElement final : public SVGSV
   void SetCurrentTime(float seconds);
   void DeselectAll();
   already_AddRefed<DOMSVGNumber> CreateSVGNumber();
   already_AddRefed<DOMSVGLength> CreateSVGLength();
   already_AddRefed<SVGAngle> CreateSVGAngle();
   already_AddRefed<nsISVGPoint> CreateSVGPoint();
   already_AddRefed<SVGMatrix> CreateSVGMatrix();
   already_AddRefed<SVGIRect> CreateSVGRect();
-  already_AddRefed<SVGTransform> CreateSVGTransform();
-  already_AddRefed<SVGTransform> CreateSVGTransformFromMatrix(
+  already_AddRefed<DOMSVGTransform> CreateSVGTransform();
+  already_AddRefed<DOMSVGTransform> CreateSVGTransformFromMatrix(
       SVGMatrix& matrix);
   using nsINode::GetElementById;  // This does what we want
   uint16_t ZoomAndPan();
   void SetZoomAndPan(uint16_t aZoomAndPan, ErrorResult& rv);
 
   // SVGElement overrides
 
   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
--- a/dom/svg/SVGTextPositioningElement.h
+++ b/dom/svg/SVGTextPositioningElement.h
@@ -8,20 +8,20 @@
 #define mozilla_dom_SVGTextPositioningElement_h
 
 #include "mozilla/dom/SVGTextContentElement.h"
 #include "SVGAnimatedLengthList.h"
 #include "SVGAnimatedNumberList.h"
 
 namespace mozilla {
 class SVGAnimatedLengthList;
+
+namespace dom {
 class DOMSVGAnimatedLengthList;
 class DOMSVGAnimatedNumberList;
-
-namespace dom {
 typedef SVGTextContentElement SVGTextPositioningElementBase;
 
 class SVGTextPositioningElement : public SVGTextPositioningElementBase {
  public:
   // WebIDL
   already_AddRefed<DOMSVGAnimatedLengthList> X();
   already_AddRefed<DOMSVGAnimatedLengthList> Y();
   already_AddRefed<DOMSVGAnimatedLengthList> Dx();
--- a/dom/svg/SVGTransformList.h
+++ b/dom/svg/SVGTransformList.h
@@ -9,32 +9,32 @@
 
 #include "gfxMatrix.h"
 #include "nsSVGTransform.h"
 #include "nsTArray.h"
 
 namespace mozilla {
 
 namespace dom {
-class SVGTransform;
+class DOMSVGTransform;
 }  // namespace dom
 
 /**
  * ATTENTION! WARNING! WATCH OUT!!
  *
  * Consumers that modify objects of this type absolutely MUST keep the DOM
  * wrappers for those lists (if any) in sync!! That's why this class is so
  * locked down.
  *
  * The DOM wrapper class for this class is DOMSVGTransformList.
  */
 class SVGTransformList {
   friend class SVGAnimatedTransformList;
   friend class DOMSVGTransformList;
-  friend class dom::SVGTransform;
+  friend class dom::DOMSVGTransform;
 
  public:
   SVGTransformList() {}
   ~SVGTransformList() {}
 
   // Only methods that don't make/permit modification to this list are public.
   // Only our friend classes can access methods that may change us.
 
--- a/dom/svg/SVGTransformListSMILType.h
+++ b/dom/svg/SVGTransformListSMILType.h
@@ -3,17 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef SVGTRANSFORMLISTSMILTYPE_H_
 #define SVGTRANSFORMLISTSMILTYPE_H_
 
 #include "mozilla/Attributes.h"
-#include "nsISMILType.h"
+#include "SMILType.h"
 #include "nsTArray.h"
 
 class nsSMILValue;
 
 namespace mozilla {
 
 class nsSVGTransform;
 class SVGTransformList;
@@ -73,26 +73,26 @@ class SVGTransformSMILData;
 //     <animateTransform type="skewX" additive="sum".../>
 //   </circle>
 //
 // Similar conditions hold for Interpolate() which in cases such as to-animation
 // may have use a start-value the base value of the target attribute (which as
 // we have seen above can contain 0..n elements) whilst the end-value comes from
 // the <animateTransform> and so can only hold 1 transform.
 //
-class SVGTransformListSMILType : public nsISMILType {
+class SVGTransformListSMILType : public SMILType {
  public:
   // Singleton for nsSMILValue objects to hold onto.
   static SVGTransformListSMILType* Singleton() {
     static SVGTransformListSMILType sSingleton;
     return &sSingleton;
   }
 
  protected:
-  // nsISMILType Methods
+  // SMILType Methods
   // -------------------
   virtual void Init(nsSMILValue& aValue) const override;
   virtual void Destroy(nsSMILValue& aValue) const override;
   virtual nsresult Assign(nsSMILValue& aDest,
                           const nsSMILValue& aSrc) const override;
   virtual bool IsEqual(const nsSMILValue& aLeft,
                        const nsSMILValue& aRight) const override;
   virtual nsresult Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd,
--- a/dom/svg/SVGViewBoxSMILType.h
+++ b/dom/svg/SVGViewBoxSMILType.h
@@ -3,29 +3,29 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_SVGVIEWBOXSMILTYPE_H_
 #define MOZILLA_SVGVIEWBOXSMILTYPE_H_
 
 #include "mozilla/Attributes.h"
-#include "nsISMILType.h"
+#include "SMILType.h"
 
 class nsSMILValue;
 
 namespace mozilla {
 
-class SVGViewBoxSMILType : public nsISMILType {
+class SVGViewBoxSMILType : public SMILType {
  public:
   // Singleton for nsSMILValue objects to hold onto.
   static SVGViewBoxSMILType sSingleton;
 
  protected:
-  // nsISMILType Methods
+  // SMILType Methods
   // -------------------
   virtual void Init(nsSMILValue& aValue) const override;
   virtual void Destroy(nsSMILValue&) const override;
   virtual nsresult Assign(nsSMILValue& aDest,
                           const nsSMILValue& aSrc) const override;
   virtual bool IsEqual(const nsSMILValue& aLeft,
                        const nsSMILValue& aRight) const override;
   virtual nsresult Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd,
--- a/dom/svg/moz.build
+++ b/dom/svg/moz.build
@@ -15,16 +15,17 @@ EXPORTS += [
     'SVGAttrValueWrapper.h',
     'SVGContentUtils.h',
     'SVGPreserveAspectRatio.h',
     'SVGStringList.h',
     'SVGTagList.h',
 ]
 
 EXPORTS.mozilla.dom += [
+    'DOMSVGTransform.h',
     'SVGAElement.h',
     'SVGAngle.h',
     'SVGAnimatedAngle.h',
     'SVGAnimatedBoolean.h',
     'SVGAnimatedEnumeration.h',
     'SVGAnimatedInteger.h',
     'SVGAnimatedLength.h',
     'SVGAnimatedNumber.h',
@@ -94,17 +95,16 @@ EXPORTS.mozilla.dom += [
     'SVGSwitchElement.h',
     'SVGSymbolElement.h',
     'SVGTests.h',
     'SVGTextContentElement.h',
     'SVGTextElement.h',
     'SVGTextPathElement.h',
     'SVGTextPositioningElement.h',
     'SVGTitleElement.h',
-    'SVGTransform.h',
     'SVGTransformableElement.h',
     'SVGTSpanElement.h',
     'SVGUseElement.h',
     'SVGViewElement.h',
     'SVGViewportElement.h',
 ]
 
 UNIFIED_SOURCES += [
@@ -115,16 +115,17 @@ UNIFIED_SOURCES += [
     'DOMSVGLengthList.cpp',
     'DOMSVGNumber.cpp',
     'DOMSVGNumberList.cpp',
     'DOMSVGPathSeg.cpp',
     'DOMSVGPathSegList.cpp',
     'DOMSVGPoint.cpp',
     'DOMSVGPointList.cpp',
     'DOMSVGStringList.cpp',
+    'DOMSVGTransform.cpp',
     'DOMSVGTransformList.cpp',
     'nsISVGPoint.cpp',
     'nsSVGAngle.cpp',
     'nsSVGBoolean.cpp',
     'nsSVGClass.cpp',
     'nsSVGEnum.cpp',
     'nsSVGFeatures.cpp',
     'nsSVGInteger.cpp',
@@ -237,17 +238,16 @@ UNIFIED_SOURCES += [
     'SVGSwitchElement.cpp',
     'SVGSymbolElement.cpp',
     'SVGTests.cpp',
     'SVGTextContentElement.cpp',
     'SVGTextElement.cpp',
     'SVGTextPathElement.cpp',
     'SVGTextPositioningElement.cpp',
     'SVGTitleElement.cpp',
-    'SVGTransform.cpp',
     'SVGTransformableElement.cpp',
     'SVGTransformList.cpp',
     'SVGTransformListParser.cpp',
     'SVGTransformListSMILType.cpp',
     'SVGTSpanElement.cpp',
     'SVGUseElement.cpp',
     'SVGViewBoxSMILType.cpp',
     'SVGViewElement.cpp',
--- a/dom/svg/nsSVGLength2.h
+++ b/dom/svg/nsSVGLength2.h
@@ -18,18 +18,18 @@
 #include "SVGElement.h"
 #include "SVGContentUtils.h"
 #include "mozilla/gfx/Rect.h"
 
 class nsIFrame;
 class nsSMILValue;
 
 namespace mozilla {
+namespace dom {
 class DOMSVGLength;
-namespace dom {
 class SVGAnimatedLength;
 class SVGAnimationElement;
 class SVGViewportElement;
 }  // namespace dom
 }  // namespace mozilla
 
 namespace mozilla {
 namespace dom {
@@ -80,17 +80,17 @@ class NonSVGFrameUserSpaceMetrics : publ
   nsIFrame* mFrame;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 class nsSVGLength2 {
   friend class mozilla::dom::SVGAnimatedLength;
-  friend class mozilla::DOMSVGLength;
+  friend class mozilla::dom::DOMSVGLength;
   typedef mozilla::dom::UserSpaceMetrics UserSpaceMetrics;
   typedef mozilla::dom::SVGElement SVGElement;
   typedef mozilla::dom::SVGViewportElement SVGViewportElement;
   typedef mozilla::SVGContentUtils SVGContentUtils;
 
  public:
   void Init(uint8_t aCtxType = mozilla::SVGContentUtils::XY,
             uint8_t aAttrEnum = 0xff, float aValue = 0,
@@ -186,19 +186,19 @@ class nsSVGLength2 {
   nsresult SetBaseValue(float aValue, SVGElement* aSVGElement, bool aDoSetAttr);
   void SetBaseValueInSpecifiedUnits(float aValue, SVGElement* aSVGElement,
                                     bool aDoSetAttr);
   nsresult SetAnimValue(float aValue, SVGElement* aSVGElement);
   void SetAnimValueInSpecifiedUnits(float aValue, SVGElement* aSVGElement);
   nsresult NewValueSpecifiedUnits(uint16_t aUnitType, float aValue,
                                   SVGElement* aSVGElement);
   nsresult ConvertToSpecifiedUnits(uint16_t aUnitType, SVGElement* aSVGElement);
-  nsresult ToDOMBaseVal(mozilla::DOMSVGLength** aResult,
+  nsresult ToDOMBaseVal(mozilla::dom::DOMSVGLength** aResult,
                         SVGElement* aSVGElement);
-  nsresult ToDOMAnimVal(mozilla::DOMSVGLength** aResult,
+  nsresult ToDOMAnimVal(mozilla::dom::DOMSVGLength** aResult,
                         SVGElement* aSVGElement);
 
  public:
   struct SMILLength : public nsISMILAttr {
    public:
     SMILLength(nsSVGLength2* aVal, SVGElement* aSVGElement)
         : mVal(aVal), mSVGElement(aSVGElement) {}
 
--- a/dom/svg/nsSVGTransform.h
+++ b/dom/svg/nsSVGTransform.h
@@ -10,17 +10,17 @@
 #include "gfxMatrix.h"
 #include "mozilla/dom/SVGTransformBinding.h"
 #include "mozilla/gfx/Matrix.h"
 #include "nsDebug.h"
 
 namespace mozilla {
 
 /*
- * The DOM wrapper class for this class is DOMSVGTransformMatrix.
+ * The DOM wrapper class for this class is DOMSVGTransform.
  */
 class nsSVGTransform {
  public:
   // Default ctor initialises to matrix type with identity matrix
   nsSVGTransform()
       : mMatrix()  // Initialises to identity
         ,
         mAngle(0.f),
--- a/gfx/thebes/gfxSVGGlyphs.cpp
+++ b/gfx/thebes/gfxSVGGlyphs.cpp
@@ -20,20 +20,20 @@
 #include "nsStreamUtils.h"
 #include "nsIPrincipal.h"
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/FontTableURIProtocolHandler.h"
 #include "mozilla/dom/SVGDocument.h"
 #include "mozilla/LoadInfo.h"
 #include "mozilla/NullPrincipal.h"
+#include "mozilla/SMILAnimationController.h"
 #include "nsSVGUtils.h"
 #include "nsContentUtils.h"
 #include "gfxFont.h"
-#include "nsSMILAnimationController.h"
 #include "gfxContext.h"
 #include "harfbuzz/hb.h"
 #include "mozilla/dom/ImageTracker.h"
 
 #define SVG_CONTENT_TYPE NS_LITERAL_CSTRING("image/svg+xml")
 #define UTF8_CHARSET NS_LITERAL_CSTRING("utf-8")
 
 using namespace mozilla;
--- a/image/SVGDocumentWrapper.cpp
+++ b/image/SVGDocumentWrapper.cpp
@@ -16,17 +16,17 @@
 #include "nsIObserverService.h"
 #include "nsIParser.h"
 #include "nsIPresShell.h"
 #include "nsIRequest.h"
 #include "nsIStreamListener.h"
 #include "nsIXMLContentSink.h"
 #include "nsNetCID.h"
 #include "nsComponentManagerUtils.h"
-#include "nsSMILAnimationController.h"
+#include "mozilla/SMILAnimationController.h"
 #include "nsServiceManagerUtils.h"
 #include "mozilla/dom/SVGSVGElement.h"
 #include "SVGObserverUtils.h"
 #include "mozilla/dom/SVGAnimatedLength.h"
 #include "nsMimeTypes.h"
 #include "DOMSVGLength.h"
 #include "nsDocument.h"
 #include "mozilla/dom/ImageTracker.h"
@@ -124,34 +124,34 @@ void SVGDocumentWrapper::StartAnimation(
   // Can be called for animated images during shutdown, after we've
   // already Observe()'d XPCOM shutdown and cleared out our mViewer pointer.
   if (!mViewer) {
     return;
   }
 
   nsIDocument* doc = mViewer->GetDocument();
   if (doc) {
-    nsSMILAnimationController* controller = doc->GetAnimationController();
+    SMILAnimationController* controller = doc->GetAnimationController();
     if (controller) {
       controller->Resume(nsSMILTimeContainer::PAUSE_IMAGE);
     }
     doc->ImageTracker()->SetAnimatingState(true);
   }
 }
 
 void SVGDocumentWrapper::StopAnimation() {
   // Can be called for animated images during shutdown, after we've
   // already Observe()'d XPCOM shutdown and cleared out our mViewer pointer.
   if (!mViewer) {
     return;
   }
 
   nsIDocument* doc = mViewer->GetDocument();
   if (doc) {
-    nsSMILAnimationController* controller = doc->GetAnimationController();
+    SMILAnimationController* controller = doc->GetAnimationController();
     if (controller) {
       controller->Pause(nsSMILTimeContainer::PAUSE_IMAGE);
     }
     doc->ImageTracker()->SetAnimatingState(false);
   }
 }
 
 void SVGDocumentWrapper::ResetAnimation() {
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -102,17 +102,17 @@
 #include "nsPIDOMWindow.h"
 #include "nsFocusManager.h"
 #include "nsIObjectFrame.h"
 #include "nsIObjectLoadingContent.h"
 #include "nsNetUtil.h"
 #include "nsThreadUtils.h"
 #include "nsStyleSheetService.h"
 #include "gfxUtils.h"
-#include "nsSMILAnimationController.h"
+#include "mozilla/SMILAnimationController.h"
 #include "SVGContentUtils.h"
 #include "SVGObserverUtils.h"
 #include "SVGFragmentIdentifier.h"
 #include "nsFrameSelection.h"
 
 #include "mozilla/dom/Performance.h"
 #include "nsRefreshDriver.h"
 #include "nsDOMNavigationTiming.h"
@@ -1030,17 +1030,17 @@ void PresShell::Init(nsIDocument* aDocum
 
     mReflowCountMgr->SetDumpFrameCounts(dumpFrameCounts);
     mReflowCountMgr->SetDumpFrameByFrameCounts(dumpFrameByFrameCounts);
     mReflowCountMgr->SetPaintFrameCounts(paintFrameCounts);
   }
 #endif
 
   if (mDocument->HasAnimationController()) {
-    nsSMILAnimationController* animCtrl = mDocument->GetAnimationController();
+    SMILAnimationController* animCtrl = mDocument->GetAnimationController();
     animCtrl->NotifyRefreshDriverCreated(GetPresContext()->RefreshDriver());
   }
 
   for (DocumentTimeline* timeline : mDocument->Timelines()) {
     timeline->NotifyRefreshDriverCreated(GetPresContext()->RefreshDriver());
   }
 
   // Get our activeness from the docShell.
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -50,17 +50,17 @@
 #include "nsTransitionManager.h"
 #include "nsAnimationManager.h"
 #include "CounterStyleManager.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/dom/Element.h"
 #include "nsIMessageManager.h"
 #include "mozilla/dom/HTMLBodyElement.h"
 #include "mozilla/dom/MediaQueryList.h"
-#include "nsSMILAnimationController.h"
+#include "mozilla/SMILAnimationController.h"
 #include "mozilla/css/ImageLoader.h"
 #include "mozilla/dom/PBrowserParent.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/TabParent.h"
 #include "nsRefreshDriver.h"
 #include "Layers.h"
 #include "LayerUserData.h"
 #include "ClientLayerManager.h"
@@ -1087,17 +1087,17 @@ void nsPresContext::SetImgAnimations(nsI
        childContent = childContent->GetNextSibling()) {
     SetImgAnimations(childContent, aMode);
   }
 }
 
 void nsPresContext::SetSMILAnimations(nsIDocument* aDoc, uint16_t aNewMode,
                                       uint16_t aOldMode) {
   if (aDoc->HasAnimationController()) {
-    nsSMILAnimationController* controller = aDoc->GetAnimationController();
+    SMILAnimationController* controller = aDoc->GetAnimationController();
     switch (aNewMode) {
       case imgIContainer::kNormalAnimMode:
       case imgIContainer::kLoopOnceAnimMode:
         if (aOldMode == imgIContainer::kDontAnimMode)
           controller->Resume(nsSMILTimeContainer::PAUSE_USERPREF);
         break;
 
       case imgIContainer::kDontAnimMode:
--- a/layout/style/ServoStyleSet.cpp
+++ b/layout/style/ServoStyleSet.cpp
@@ -12,33 +12,33 @@
 #include "mozilla/DocumentStyleRootIterator.h"
 #include "mozilla/EffectCompositor.h"
 #include "mozilla/IntegerRange.h"
 #include "mozilla/LookAndFeel.h"
 #include "mozilla/ServoBindings.h"
 #include "mozilla/RestyleManager.h"
 #include "mozilla/ServoStyleRuleMap.h"
 #include "mozilla/ServoTypes.h"
+#include "mozilla/SMILAnimationController.h"
 #include "mozilla/StyleAnimationValue.h"
 #include "mozilla/css/Loader.h"
 #include "mozilla/dom/AnonymousContent.h"
 #include "mozilla/dom/ChildIterator.h"
 #include "mozilla/dom/FontFaceSet.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/ElementInlines.h"
 #include "nsCSSAnonBoxes.h"
 #include "nsCSSFrameConstructor.h"
 #include "nsCSSPseudoElements.h"
 #include "nsDeviceContext.h"
 #include "nsHTMLStyleSheet.h"
 #include "nsIAnonymousContentCreator.h"
 #include "nsIDocumentInlines.h"
 #include "nsMediaFeatures.h"
 #include "nsPrintfCString.h"
-#include "nsSMILAnimationController.h"
 #include "nsXBLPrototypeBinding.h"
 #include "gfxUserFontSet.h"
 #include "nsBindingManager.h"
 #include "nsWindowSizes.h"
 #include "GeckoProfiler.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
@@ -404,17 +404,17 @@ void ServoStyleSet::PreTraverseSync() {
   presContext->CacheAllLangs();
 }
 
 void ServoStyleSet::PreTraverse(ServoTraversalFlags aFlags, Element* aRoot) {
   PreTraverseSync();
 
   // Process animation stuff that we should avoid doing during the parallel
   // traversal.
-  nsSMILAnimationController* smilController =
+  SMILAnimationController* smilController =
       mDocument->HasAnimationController() ? mDocument->GetAnimationController()
                                           : nullptr;
 
   MOZ_ASSERT(GetPresContext());
   if (aRoot) {
     GetPresContext()->EffectCompositor()->PreTraverseInSubtree(aFlags, aRoot);
     if (smilController) {
       smilController->PreTraverseInSubtree(aRoot);
--- a/mobile/android/mach_commands.py
+++ b/mobile/android/mach_commands.py
@@ -45,19 +45,23 @@ def REMOVED(cls):
     See https://developer.mozilla.org/en-US/docs/Simple_Firefox_for_Android_build#Developing_Firefox_for_Android_in_Android_Studio_or_IDEA_IntelliJ.  # NOQA: E501
     """
     return False
 
 
 @CommandProvider
 class MachCommands(MachCommandBase):
     def _root_url(self, artifactdir=None, objdir=None):
+        """Generate a publicly-accessible URL for the tasks's artifacts, or an objdir path"""
         if 'TASK_ID' in os.environ and 'RUN_ID' in os.environ:
-            return 'https://queue.taskcluster.net/v1/task/{}/runs/{}/artifacts/{}'.format(
-                os.environ['TASK_ID'], os.environ['RUN_ID'], artifactdir)
+            import taskcluster_urls
+            from taskgraph.util.taskcluster import get_root_url
+            return taskcluster_urls.api(
+                get_root_url(), 'queue', 'v1', 'task/{}/runs/{}/artifacts/{}'.format(
+                    os.environ['TASK_ID'], os.environ['RUN_ID'], artifactdir))
         else:
             return os.path.join(self.topobjdir, objdir)
 
     @Command('android', category='devenv',
              description='Run Android-specific commands.',
              conditions=[conditions.is_android])
     def android(self):
         pass
--- a/python/mozrelease/mozrelease/buglist_creator.py
+++ b/python/mozrelease/mozrelease/buglist_creator.py
@@ -213,16 +213,17 @@ Task group: [{task_group_id}](https://to
         subject_prefix = "[mobile] "
     if product in {"firefox", "devedition"}:
         subject_prefix = "[desktop] "
 
     subject = '{} Build of {} {} build {}'.format(subject_prefix, product, version, build_number)
 
     notify_options = {}
     if 'TASKCLUSTER_PROXY_URL' in os.environ:
+        # Until bug 1460015 is finished, use the old baseUrl style of proxy URL
         base_url = os.environ['TASKCLUSTER_PROXY_URL'].rstrip('/')
         notify_options['baseUrl'] = '{}/notify/v1'.format(base_url)
     notify = Notify(notify_options)
     for address in addresses:
         notify.email({
             'address': address,
             'subject': subject,
             'content': content,
--- a/taskcluster/ci/l10n/kind.yml
+++ b/taskcluster/ci/l10n/kind.yml
@@ -103,37 +103,37 @@ job-template:
                 win32: windows2012-32/opt
                 win64: windows2012-64/opt
                 android-api-16: android-4-0-armv7-api16/opt
     env:
         by-build-platform:
             linux.*:    # linux64 and 32 get same treatment here
                 EN_US_PACKAGE_NAME: target.tar.bz2
                 EN_US_BINARY_URL:
-                    task-reference: https://queue.taskcluster.net/v1/task/<build-signing>/artifacts/{artifact_prefix}
+                    artifact-reference: <build-signing/{artifact_prefix}>
                 MAR_TOOLS_URL:
-                    task-reference: https://queue.taskcluster.net/v1/task/<build>/artifacts/{artifact_prefix}/host/bin
+                    artifact-reference: <build/{artifact_prefix}/host/bin>
             macosx64:
                 EN_US_PACKAGE_NAME: target.dmg
                 EN_US_BINARY_URL:
-                    task-reference: https://queue.taskcluster.net/v1/task/<repackage>/artifacts/{artifact_prefix}
+                    artifact-reference: <repackage/{artifact_prefix}>
                 MAR_TOOLS_URL:
-                    task-reference: https://queue.taskcluster.net/v1/task/<build>/artifacts/{artifact_prefix}/host/bin
+                    artifact-reference: <build/{artifact_prefix}/host/bin>
             win.*:
                 EN_US_PACKAGE_NAME: target.zip
                 EN_US_BINARY_URL:
-                    task-reference: https://queue.taskcluster.net/v1/task/<build-signing>/artifacts/{artifact_prefix}
+                    artifact-reference: <build-signing/{artifact_prefix}>
                 EN_US_INSTALLER_BINARY_URL:
-                    task-reference: https://queue.taskcluster.net/v1/task/<repackage-signing>/artifacts/{artifact_prefix}
+                    artifact-reference: <repackage-signing/{artifact_prefix}>
                 MAR_TOOLS_URL:
-                    task-reference: https://queue.taskcluster.net/v1/task/<build>/artifacts/{artifact_prefix}/host/bin
+                    artifact-reference: <build/{artifact_prefix}/host/bin>
             android-api-16:
                 EN_US_PACKAGE_NAME: target.apk
                 EN_US_BINARY_URL:
-                    task-reference: https://queue.taskcluster.net/v1/task/<build>/artifacts/{artifact_prefix}
+                    artifact-reference: <build/{artifact_prefix}>
     mozharness:
         config:
             by-build-platform:
                 linux:
                     - single_locale/{project}.py
                     - single_locale/linux32.py
                     - single_locale/tc_common.py
                     - single_locale/tc_linux32.py
--- a/taskcluster/ci/nightly-l10n/kind.yml
+++ b/taskcluster/ci/nightly-l10n/kind.yml
@@ -132,37 +132,37 @@ job-template:
                 win32-devedition-nightly: windows2012-32-devedition/opt
                 win64-devedition-nightly: windows2012-64-devedition/opt
                 android-api-16-nightly: android-4-0-armv7-api16/opt
     env:
         by-build-platform:
             linux.*:    # linux64 and 32 get same treatment here
                 EN_US_PACKAGE_NAME: target.tar.bz2
                 EN_US_BINARY_URL:
-                    task-reference: https://queue.taskcluster.net/v1/task/<build-signing>/artifacts/{artifact_prefix}
+                    artifact-reference: <build-signing/{artifact_prefix}>
                 MAR_TOOLS_URL:
-                    task-reference: https://queue.taskcluster.net/v1/task/<build>/artifacts/{artifact_prefix}/host/bin
+                    artifact-reference: <build/{artifact_prefix}/host/bin>
             macosx64.*:
                 EN_US_PACKAGE_NAME: target.dmg
                 EN_US_BINARY_URL:
-                    task-reference: https://queue.taskcluster.net/v1/task/<repackage>/artifacts/{artifact_prefix}
+                    artifact-reference: <repackage/{artifact_prefix}>
                 MAR_TOOLS_URL:
-                    task-reference: https://queue.taskcluster.net/v1/task/<build>/artifacts/{artifact_prefix}/host/bin
+                    artifact-reference: <build/{artifact_prefix}/host/bin>
             win.*:
                 EN_US_PACKAGE_NAME: target.zip
                 EN_US_BINARY_URL:
-                    task-reference: https://queue.taskcluster.net/v1/task/<build-signing>/artifacts/{artifact_prefix}
+                    artifact-reference: <build-signing/{artifact_prefix}>
                 EN_US_INSTALLER_BINARY_URL:
-                    task-reference: https://queue.taskcluster.net/v1/task/<repackage-signing>/artifacts/{artifact_prefix}
+                    artifact-reference: <repackage-signing/{artifact_prefix}>
                 MAR_TOOLS_URL:
-                    task-reference: https://queue.taskcluster.net/v1/task/<build>/artifacts/{artifact_prefix}/host/bin
+                    artifact-reference: <build/{artifact_prefix}/host/bin>
             android-api-16-nightly:
                 EN_US_PACKAGE_NAME: target.apk
                 EN_US_BINARY_URL:
-                    task-reference: https://queue.taskcluster.net/v1/task/<build>/artifacts/{artifact_prefix}/en-US
+                    artifact-reference: <build/{artifact_prefix}/en-US>
     mozharness:
         config:
             by-build-platform:
                 linux-nightly:
                     - single_locale/{project}.py
                     - single_locale/linux32.py
                     - single_locale/tc_common.py
                     - single_locale/tc_linux32.py
--- a/taskcluster/ci/upload-symbols/kind.yml
+++ b/taskcluster/ci/upload-symbols/kind.yml
@@ -19,17 +19,16 @@ not-for-build-platforms:
 
 job-template:
     description: Upload Symbols
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
         docker-image: {in-tree: "lint"}
         max-run-time: 900
         env:
-            ARTIFACT_TASKID: {"task-reference": "<build>"}
             # {level} gets replaced in the upload_symbols transform
             SYMBOL_SECRET: "project/releng/gecko/build/level-{level}/gecko-symbol-upload"
     run:
         using: mach
-        mach: python toolkit/crashreporter/tools/upload_symbols.py https://queue.taskcluster.net/v1/task/${ARTIFACT_TASKID}/artifacts/public/build/target.crashreporter-symbols-full.zip
+        mach: {artifact-reference: "python toolkit/crashreporter/tools/upload_symbols.py <build/public/build/target.crashreporter-symbols-full.zip>"}
         sparse-profile: upload-symbols
     scopes:
         - secrets:get:project/releng/gecko/build/level-{level}/gecko-symbol-upload
--- a/taskcluster/docker/debian-base/Dockerfile
+++ b/taskcluster/docker/debian-base/Dockerfile
@@ -44,18 +44,19 @@ RUN for s in debian_$DIST debian_$DIST-u
 RUN apt-get update && \
     apt-get install \
       apt-transport-https \
       ca-certificates
 
 COPY setup_packages.sh /usr/local/sbin/
 COPY cloud-mirror-workaround.sh /usr/local/sbin/
 
+# %ARG TASKCLUSTER_ROOT_URL
 # %ARG DOCKER_IMAGE_PACKAGES
-RUN /usr/local/sbin/setup_packages.sh $DOCKER_IMAGE_PACKAGES && \
+RUN /usr/local/sbin/setup_packages.sh $TASKCLUSTER_ROOT_URL $DOCKER_IMAGE_PACKAGES && \
     echo 'dir::bin::methods::https "/usr/local/sbin/cloud-mirror-workaround.sh";' > /etc/apt/apt.conf.d/99cloud-mirror-workaround && \
     apt-get update && \
     apt-get install \
       git \
       less \
       make \
       mercurial \
       patch \
--- a/taskcluster/docker/debian-base/setup_packages.sh
+++ b/taskcluster/docker/debian-base/setup_packages.sh
@@ -1,5 +1,17 @@
 #!/bin/sh
 
+TASKCLUSTER_ROOT_URL=$1
+shift
+
+# duplicate the functionality of taskcluster-lib-urls, but in bash..
+if [ "$TASKCLUSTER_ROOT_URL" = "https://taskcluster.net" ]; then
+    queue_base='https://queue.taskcluster.net/v1'
+else
+    queue_base="$TASKCLUSTER_ROOT_URL/api/queue/v1"
+fi
+
+
 for task in "$@"; do
-  echo "deb [trusted=yes] https://queue.taskcluster.net/v1/task/$task/artifacts/public/build/ debian/" > "/etc/apt/sources.list.d/99$task.list"
+  echo "adding package source $queue_base/task/$task/artifacts/public/build/"
+  echo "deb [trusted=yes] $queue_base/task/$task/artifacts/public/build/ debian/" > "/etc/apt/sources.list.d/99$task.list"
 done
--- a/taskcluster/docker/debian7-build/Dockerfile
+++ b/taskcluster/docker/debian7-build/Dockerfile
@@ -3,18 +3,19 @@ FROM $DOCKER_IMAGE_PARENT
 MAINTAINER Mike Hommey <mhommey@mozilla.com>
 
 VOLUME /builds/worker/checkouts
 VOLUME /builds/worker/workspace
 VOLUME /builds/worker/tooltool-cache
 
 ENV XZ_OPT=-T0
 
+# %ARG TASKCLUSTER_ROOT_URL
 # %ARG DOCKER_IMAGE_PACKAGES
-RUN /usr/local/sbin/setup_packages.sh $DOCKER_IMAGE_PACKAGES
+RUN /usr/local/sbin/setup_packages.sh $TASKCLUSTER_ROOT_URL $DOCKER_IMAGE_PACKAGES
 
 # %ARG ARCH
 RUN dpkg --add-architecture $ARCH
 
 # Ideally, we wouldn't need gcc-multilib and the extra linux-libc-dev,
 # but the latter is required to make the former installable, and the former
 # because of bug 1409276.
 # We exclude /usr/share/doc/*/changelog.Debian* files because they might differ
--- a/taskcluster/docker/debian7-mozjs-rust-build/Dockerfile
+++ b/taskcluster/docker/debian7-mozjs-rust-build/Dockerfile
@@ -1,12 +1,13 @@
 # %ARG DOCKER_IMAGE_PARENT
 FROM $DOCKER_IMAGE_PARENT
 MAINTAINER Mike Hommey <mhommey@mozilla.com>
 
 VOLUME /builds/worker/checkouts
 VOLUME /builds/worker/workspace
 VOLUME /builds/worker/tooltool-cache
 
+# %ARG TASKCLUSTER_ROOT_URL
 # %ARG DOCKER_IMAGE_PACKAGES
-RUN /usr/local/sbin/setup_packages.sh $DOCKER_IMAGE_PACKAGES && \
+RUN /usr/local/sbin/setup_packages.sh $TASKCLUSTER_ROOT_URL $DOCKER_IMAGE_PACKAGES && \
     apt-get update && \
     apt-get install cmake
--- a/taskcluster/docker/diffoscope/get_and_diffoscope
+++ b/taskcluster/docker/diffoscope/get_and_diffoscope
@@ -6,32 +6,39 @@ set -x
 cd /builds/worker
 
 mkdir a b
 
 # Until https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=879010 is
 # implemented, it's better to first manually extract the data.
 # Plus dmg files are not supported yet.
 
+# duplicate the functionality of taskcluster-lib-urls, but in bash..
+if [ "$TASKCLUSTER_ROOT_URL" = "https://taskcluster.net" ]; then
+    queue_base='https://queue.taskcluster.net/v1'
+else
+    queue_base="$TASKCLUSTER_ROOT_URL/api/queue/v1"
+fi
+
 case "$ORIG_URL" in
 */target.zip|*/target.apk)
 	curl -sL "$ORIG_URL" > a.zip
 	curl -sL "$NEW_URL" > b.zip
 	unzip -d a a.zip
 	unzip -d b b.zip
 	;;
 */target.tar.bz2)
 	curl -sL "$ORIG_URL" | tar -C a -jxf -
 	curl -sL "$NEW_URL" | tar -C b -jxf -
 	;;
 */target.dmg)
 	# We don't have mach available to call mach artifact toolchain.
 	# This is the trivial equivalent for those toolchains we use here.
 	for t in $MOZ_TOOLCHAINS; do
-		curl -sL https://queue.taskcluster.net/v1/task/${t#*@}/artifacts/${t%@*} | tar -Jxf -
+		curl -sL $queue_base/task/${t#*@}/artifacts/${t%@*} | tar -Jxf -
 	done
 	for tool in lipo otool; do
 		ln -s /builds/worker/cctools/bin/x86_64-apple-darwin*-$tool bin/$tool
 	done
 	export PATH=$PATH:/builds/worker/bin
 	curl -sL "$ORIG_URL" > a.dmg
 	curl -sL "$NEW_URL" > b.dmg
 	for i in a b; do
--- a/taskcluster/docker/funsize-update-generator/runme.sh
+++ b/taskcluster/docker/funsize-update-generator/runme.sh
@@ -4,35 +4,42 @@ set -xe
 
 test "$TASK_ID"
 test "$SHA1_SIGNING_CERT"
 test "$SHA384_SIGNING_CERT"
 
 ARTIFACTS_DIR="/home/worker/artifacts"
 mkdir -p "$ARTIFACTS_DIR"
 
-curl --location --retry 10 --retry-delay 10 -o /home/worker/task.json \
-    "https://queue.taskcluster.net/v1/task/$TASK_ID"
+# duplicate the functionality of taskcluster-lib-urls, but in bash..
+if [ "$TASKCLUSTER_ROOT_URL" = "https://taskcluster.net" ]; then
+    queue_base='https://queue.taskcluster.net/v1'
+else
+    queue_base="$TASKCLUSTER_ROOT_URL/api/queue/v1"
+fi
+
+curl --location --retry 10 --retry-delay 10 -o /home/worker/task.json "$queue_base/task/$TASK_ID"
 
 # auth:aws-s3:read-write:tc-gp-private-1d-us-east-1/releng/mbsdiff-cache/
 # -> bucket of tc-gp-private-1d-us-east-1, path of releng/mbsdiff-cache/
 # Trailing slash is important, due to prefix permissions in S3.
 S3_BUCKET_AND_PATH=$(jq -r '.scopes[] | select(contains ("auth:aws-s3"))' /home/worker/task.json | awk -F: '{print $4}')
 
 # Will be empty if there's no scope for AWS S3.
 if [ -n "${S3_BUCKET_AND_PATH}" ] && getent hosts taskcluster
 then
   # Does this parse as we expect?
   S3_PATH=${S3_BUCKET_AND_PATH#*/}
   AWS_BUCKET_NAME=${S3_BUCKET_AND_PATH%/${S3_PATH}*}
   test "${S3_PATH}"
   test "${AWS_BUCKET_NAME}"
 
   set +x  # Don't echo these.
-  secret_url="taskcluster/auth/v1/aws/s3/read-write/${AWS_BUCKET_NAME}/${S3_PATH}"
+  # Until bug 1460015 is finished, use baseUrl-style proxy URLs
+  secret_url="${TASKCLUSTER_PROXY_URL}/auth/v1/aws/s3/read-write/${AWS_BUCKET_NAME}/${S3_PATH}"
   AUTH=$(curl "${secret_url}")
   AWS_ACCESS_KEY_ID=$(echo "${AUTH}" | jq -r '.credentials.accessKeyId')
   AWS_SECRET_ACCESS_KEY=$(echo "${AUTH}" | jq -r '.credentials.secretAccessKey')
   AWS_SESSION_TOKEN=$(echo "${AUTH}" | jq -r '.credentials.sessionToken')
   export AWS_ACCESS_KEY_ID
   export AWS_SECRET_ACCESS_KEY
   export AWS_SESSION_TOKEN
   AUTH=
--- a/taskcluster/docker/funsize-update-generator/scripts/funsize.py
+++ b/taskcluster/docker/funsize-update-generator/scripts/funsize.py
@@ -30,26 +30,29 @@ from datadog import initialize, ThreadSt
 
 log = logging.getLogger(__name__)
 
 # Create this even when not sending metrics, so the context manager
 # statements work.
 ddstats = ThreadStats(namespace='releng.releases.partials')
 
 
+ROOT_URL = os.environ['TASKCLUSTER_ROOT_URL']
+QUEUE_PREFIX = ("https://queue.taskcluster.net/"
+                if ROOT_URL == 'https://taskcluster.net'
+                else ROOT_URL + '/api/queue/')
 ALLOWED_URL_PREFIXES = (
     "http://download.cdn.mozilla.net/pub/mozilla.org/firefox/nightly/",
     "http://download.cdn.mozilla.net/pub/firefox/nightly/",
     "https://mozilla-nightly-updates.s3.amazonaws.com",
-    "https://queue.taskcluster.net/",
     "http://ftp.mozilla.org/",
     "http://download.mozilla.org/",
     "https://archive.mozilla.org/",
     "http://archive.mozilla.org/",
-    "https://queue.taskcluster.net/v1/task/",
+    QUEUE_PREFIX,
 )
 STAGING_URL_PREFIXES = (
     "http://ftp.stage.mozaws.net/",
 )
 
 DEFAULT_FILENAME_TEMPLATE = "{appName}-{branch}-{version}-{platform}-" \
                             "{locale}-{from_buildid}-{to_buildid}.partial.mar"
 
--- a/taskcluster/docker/periodic-updates/runme.sh
+++ b/taskcluster/docker/periodic-updates/runme.sh
@@ -46,28 +46,35 @@ if [ ! -z "${DO_SUFFIX_LIST}" ]
 then
   PARAMS="${PARAMS} --suffix-list"
 fi
 
 
 export ARTIFACTS_DIR="/home/worker/artifacts"
 mkdir -p "$ARTIFACTS_DIR"
 
+# duplicate the functionality of taskcluster-lib-urls, but in bash..
+if [ "$TASKCLUSTER_ROOT_URL" = "https://taskcluster.net" ]; then
+    queue_base='https://queue.taskcluster.net/v1'
+else
+    queue_base="$TASKCLUSTER_ROOT_URL/api/queue/v1"
+fi
+
 # Get Arcanist API token
 
 if [ -n "${TASK_ID}" ]
 then
-  curl --location --retry 10 --retry-delay 10 -o /home/worker/task.json \
-    "https://queue.taskcluster.net/v1/task/$TASK_ID"
+  curl --location --retry 10 --retry-delay 10 -o /home/worker/task.json "$queue_base/task/$TASK_ID"
   ARC_SECRET=$(jq -r '.scopes[] | select(contains ("arc-phabricator-token"))' /home/worker/task.json | awk -F: '{print $3}')
 fi
 if [ -n "${ARC_SECRET}" ] && getent hosts taskcluster
 then
   set +x # Don't echo these
-  secrets_url="http://taskcluster/secrets/v1/secret/${ARC_SECRET}"
+  # Until bug 1460015 is finished, use baseUrl-style proxy URLs
+  secrets_url="${TASKCLUSTER_PROXY_URL}/secrets/v1/secret/${ARC_SECRET}"
   SECRET=$(curl "${secrets_url}")
   TOKEN=$(echo "${SECRET}" | jq -r '.secret.token')
 elif [ -n "${ARC_TOKEN}" ] # Allow for local testing.
 then
   TOKEN="${ARC_TOKEN}"
 fi
 
 if [ -n "${TOKEN}" ]
--- a/taskcluster/docker/periodic-updates/scripts/periodic_file_updates.sh
+++ b/taskcluster/docker/periodic-updates/scripts/periodic_file_updates.sh
@@ -90,16 +90,25 @@ SUFFIX_LIST_UPDATED=false
 ARTIFACTS_DIR="${ARTIFACTS_DIR:-.}"
 # Defaults
 HSTS_DIFF_ARTIFACT="${ARTIFACTS_DIR}/${HSTS_DIFF_ARTIFACT:-"nsSTSPreloadList.diff"}"
 HPKP_DIFF_ARTIFACT="${ARTIFACTS_DIR}/${HPKP_DIFF_ARTIFACT:-"StaticHPKPins.h.diff"}"
 BLOCKLIST_DIFF_ARTIFACT="${ARTIFACTS_DIR}/${BLOCKLIST_DIFF_ARTIFACT:-"blocklist.diff"}"
 REMOTE_SETTINGS_DIFF_ARTIFACT="${ARTIFACTS_DIR}/${REMOTE_SETTINGS_DIFF_ARTIFACT:-"remote-settings.diff"}"
 SUFFIX_LIST_DIFF_ARTIFACT="${ARTIFACTS_DIR}/${SUFFIX_LIST_DIFF_ARTIFACT:-"effective_tld_names.diff"}"
 
+# duplicate the functionality of taskcluster-lib-urls, but in bash..
+if [ "$TASKCLUSTER_ROOT_URL" = "https://taskcluster.net" ]; then
+    queue_base='https://queue.taskcluster.net/v1'
+    index_base='https://index.taskcluster.net/v1'
+else
+    queue_base="$TASKCLUSTER_ROOT_URL/api/queue/v1"
+    index_base="$TASKCLUSTER_ROOT_URL/api/index/v1"
+fi
+
 # Get the current in-tree version for a code branch.
 function get_version {
   VERSION_REPO=$1
   VERSION_FILE='version.txt'
 
   # TODO bypass temporary file
 
   cd "${BASEDIR}"
@@ -141,40 +150,40 @@ function download_shared_artifacts_from_
 }
 
 function download_shared_artifacts_from_tc {
   cd "${BASEDIR}"
   TASKID_FILE="taskId.json"
 
   # Download everything we need to run js with xpcshell
   echo "INFO: Downloading all the necessary pieces from the taskcluster index..."
-  TASKID_URL="https://index.taskcluster.net/v1/task/gecko.v2.${REPODIR}.latest.${PRODUCT}.linux64-opt"
+  TASKID_URL="$index_base/task/gecko.v2.${REPODIR}.latest.${PRODUCT}.linux64-opt"
   if [ "${USE_MC}" == "true" ]; then
-    TASKID_URL="https://index.taskcluster.net/v1/task/gecko.v2.mozilla-central.latest.${PRODUCT}.linux64-opt"
+    TASKID_URL="$index_base/task/gecko.v2.mozilla-central.latest.${PRODUCT}.linux64-opt"
   fi
-  ${WGET} -O ${TASKID_FILE} ${TASKID_URL}
+  ${WGET} -O ${TASKID_FILE} "${TASKID_URL}"
   INDEX_TASK_ID="$($JQ -r '.taskId' ${TASKID_FILE})"
   if [ -z "${INDEX_TASK_ID}" ]; then
     echo "Failed to look up taskId at ${TASKID_URL}"
     exit 22
   else
     echo "INFO: Got taskId of $INDEX_TASK_ID"
   fi
 
   TASKSTATUS_FILE="taskstatus.json"
-  STATUS_URL="https://queue.taskcluster.net/v1/task/${INDEX_TASK_ID}/status"
+  STATUS_URL="$queue_base/task/${INDEX_TASK_ID}/status"
   ${WGET} -O "${TASKSTATUS_FILE}" "${STATUS_URL}"
   LAST_RUN_INDEX=$(($(jq '.status.runs | length' ${TASKSTATUS_FILE}) - 1))
   echo "INFO: Examining run number ${LAST_RUN_INDEX}"
 
-  BROWSER_ARCHIVE_URL="https://queue.taskcluster.net/v1/task/${INDEX_TASK_ID}/runs/${LAST_RUN_INDEX}/artifacts/public/build/${BROWSER_ARCHIVE}"
+  BROWSER_ARCHIVE_URL="$queue_base/task/${INDEX_TASK_ID}/runs/${LAST_RUN_INDEX}/artifacts/public/build/${BROWSER_ARCHIVE}"
   echo "INFO: ${WGET} ${BROWSER_ARCHIVE_URL}"
   ${WGET} "${BROWSER_ARCHIVE_URL}"
 
-  TESTS_ARCHIVE_URL="https://queue.taskcluster.net/v1/task/${INDEX_TASK_ID}/runs/${LAST_RUN_INDEX}/artifacts/public/build/${TESTS_ARCHIVE}"
+  TESTS_ARCHIVE_URL="$queue_base/task/${INDEX_TASK_ID}/runs/${LAST_RUN_INDEX}/artifacts/public/build/${TESTS_ARCHIVE}"
   echo "INFO: ${WGET} ${TESTS_ARCHIVE_URL}"
   ${WGET} "${TESTS_ARCHIVE_URL}"
 }
 
 function unpack_artifacts {
   cd "${BASEDIR}"
   if [ ! -f "${BROWSER_ARCHIVE}" ]; then
     echo "Downloaded file '${BROWSER_ARCHIVE}' not found in directory '$(pwd)'." >&2
--- a/taskcluster/docker/pipfile-updates/runme.sh
+++ b/taskcluster/docker/pipfile-updates/runme.sh
@@ -14,28 +14,35 @@ test "${PIPFILE_DIRECTORY}"
 PIP_ARG="-2"
 if [ -n "${PYTHON3}" ]; then
   PIP_ARG="-3"
 fi
 
 export ARTIFACTS_DIR="/home/worker/artifacts"
 mkdir -p "$ARTIFACTS_DIR"
 
+# duplicate the functionality of taskcluster-lib-urls, but in bash..
+if [ "$TASKCLUSTER_ROOT_URL" = "https://taskcluster.net" ]; then
+    queue_base='https://queue.taskcluster.net/v1'
+else
+    queue_base="$TASKCLUSTER_ROOT_URL/api/queue/v1"
+fi
+
 # Get Arcanist API token
 
 if [ -n "${TASK_ID}" ]
 then
-  curl --location --retry 10 --retry-delay 10 -o /home/worker/task.json \
-    "https://queue.taskcluster.net/v1/task/$TASK_ID"
+  curl --location --retry 10 --retry-delay 10 -o /home/worker/task.json "$queue_base/task/$TASK_ID"
   ARC_SECRET=$(jq -r '.scopes[] | select(contains ("arc-phabricator-token"))' /home/worker/task.json | awk -F: '{print $3}')
 fi
 if [ -n "${ARC_SECRET}" ] && getent hosts taskcluster
 then
   set +x # Don't echo these
-  secrets_url="http://taskcluster/secrets/v1/secret/${ARC_SECRET}"
+  # Until bug 1460015 is finished, use the old, baseUrl-style proxy URLs
+  secrets_url="${TASKCLUSTER_PROXY_URL}/secrets/v1/secret/${ARC_SECRET}"
   SECRET=$(curl "${secrets_url}")
   TOKEN=$(echo "${SECRET}" | jq -r '.secret.token')
 elif [ -n "${ARC_TOKEN}" ] # Allow for local testing.
 then
   TOKEN="${ARC_TOKEN}"
 fi
 
 if [ -n "${TOKEN}" ]
--- a/taskcluster/docker/toolchain-build/Dockerfile
+++ b/taskcluster/docker/toolchain-build/Dockerfile
@@ -4,17 +4,18 @@ MAINTAINER Mike Hommey <mhommey@mozilla.
 
 VOLUME /builds/worker/checkouts
 VOLUME /builds/worker/workspace
 VOLUME /builds/worker/tooltool-cache
 
 ENV XZ_OPT=-T0
 
 # %ARG DOCKER_IMAGE_PACKAGES
-RUN /usr/local/sbin/setup_packages.sh $DOCKER_IMAGE_PACKAGES
+# %ARG TASKCLUSTER_ROOT_URL
+RUN /usr/local/sbin/setup_packages.sh $TASKCLUSTER_ROOT_URL $DOCKER_IMAGE_PACKAGES
 
 RUN apt-get update && \
     apt-get install \
       autoconf \
       automake \
       bison \
       build-essential \
       curl \
--- a/taskcluster/docs/optimization-process.rst
+++ b/taskcluster/docs/optimization-process.rst
@@ -65,11 +65,11 @@ simultaneously rewrites all dependencies
 To do so, it assigns a taskId to each retained task and uses the replacement
 taskId for all replaced tasks.
 
 The result is an optimized taskgraph with tasks named by taskId instead of
 label. At this phase, the edges in the task graph diverge from the
 ``task.dependencies`` attributes, as the latter may contain dependencies
 outside of the taskgraph (for replacement tasks).
 
-As a side-effect, this phase also expands all ``{"task-reference": ".."}``
-objects within the task definitions.
+As a side-effect, this phase also expands all ``{"task-reference": ".."}`` and
+``{"artifact-reference": ".."}`` objects within the task definitions.
 
--- a/taskcluster/docs/partials.rst
+++ b/taskcluster/docs/partials.rst
@@ -26,17 +26,17 @@ The 'extra' section of the task definiti
 the 'funsize' key. In here is a list of partials that this specific task will
 generate, and each entry includes the earlier (or 'from') version, and the most
 recent (or 'to') version, which for most releases will likely be a taskcluster
 artifact.
 
 .. code-block:: json
 
     {
-       "to_mar": "https://queue.taskcluster.net/v1/task/EWtBFqVuT-WqG3tGLxWhmA/artifacts/public/build/ach/target.complete.mar",
+       "to_mar": "https://tc.net/api/queue/v1/task/EWtBFqVuT-WqG3tGLxWhmA/artifacts/public/build/ach/target.complete.mar",
        "product": "Firefox",
        "dest_mar": "target-60.0b8.partial.mar",
        "locale": "ach",
        "from_mar": "http://archive.mozilla.org/pub/firefox/candidates/60.0b8-candidates/build1/update/linux-i686/ach/firefox-60.0b8.complete.mar",
        "update_number": 2,
        "platform": "linux32",
        "previousVersion": "60.0b8",
        "previousBuildNumber": "1",
--- a/taskcluster/docs/taskgraph.rst
+++ b/taskcluster/docs/taskgraph.rst
@@ -171,16 +171,21 @@ using simple parameterized values, as fo
 
 ``{"task-reference": "string containing <dep-name>"}``
     The task definition may contain "task references" of this form.  These will
     be replaced during the optimization step, with the appropriate taskId for
     the named dependency substituted for ``<dep-name>`` in the string.
     Multiple labels may be substituted in a single string, and ``<<>`` can be
     used to escape a literal ``<``.
 
+``{"artifact-reference": "..<dep-name/artifact/name>.."}``
+    Similar to a ``task-reference``, but this substitutes a URL to the queue's
+    ``getLatestArtifact`` API method (for which a GET will redirect to the
+    artifact itself).
+
 .. _taskgraph-graph-config:
 
 Graph Configuration
 -------------------
 
 There are several configuration settings that are pertain to the entire
 taskgraph. These are specified in :file:`config.yml` at the root of the
 taskgraph configuration (typically :file:`taskcluster/ci/`). The available
--- a/taskcluster/docs/transforms.rst
+++ b/taskcluster/docs/transforms.rst
@@ -197,18 +197,19 @@ Signing Descriptions
 Signing kinds are passed a single dependent job (from its kind dependency) to act
 on.
 
 The transforms in ``taskcluster/taskgraph/transforms/signing.py`` implement
 this common functionality.  They expect a "signing description", and produce a
 task definition.  The schema for a signing description is defined at the top of
 ``signing.py``, with copious comments.
 
-In particular you define a set of upstream artifact urls (that point at the dependent
-task) and can optionally provide a dependent name (defaults to build) for use in
-task-reference. You also need to provide the signing formats to use.
+In particular you define a set of upstream artifact urls (that point at the
+dependent task) and can optionally provide a dependent name (defaults to build)
+for use in ``task-reference``/``artifact-reference``. You also need to provide
+the signing formats to use.
 
 More Detail
 -----------
 
 The source files provide lots of additional detail, both in the code itself and
 in the comments and docstrings.  For the next level of detail beyond this file,
 consult the transform source under ``taskcluster/taskgraph/transforms``.
--- a/taskcluster/scripts/misc/build-libdmg-hfsplus.sh
+++ b/taskcluster/scripts/misc/build-libdmg-hfsplus.sh
@@ -26,14 +26,21 @@ git checkout $LIBDMG_REV
 git archive --prefix=libdmg-hfsplus/ ${LIBDMG_REV} | xz > $UPLOAD_DIR/libdmg-hfsplus.tar.xz
 cmake -DOPENSSL_USE_STATIC_LIBS=1 .
 make -j$(getconf _NPROCESSORS_ONLN)
 
 # We only need the dmg and hfsplus tools.
 strip dmg/dmg hfs/hfsplus
 cp dmg/dmg hfs/hfsplus $STAGE
 
+# duplicate the functionality of taskcluster-lib-urls, but in bash..
+if [ "$TASKCLUSTER_ROOT_URL" = "https://taskcluster.net" ]; then
+    queue_base='https://queue.taskcluster.net/v1'
+else
+    queue_base="$TASKCLUSTER_ROOT_URL/api/queue/v1"
+fi
+
 cat >$STAGE/README<<EOF
 Built from ${LIBDMG_REPOSITORY} rev `git rev-parse ${LIBDMG_REV}`.
 Source is available as a taskcluster artifact:
-https://queue.taskcluster.net/v1/task/$TASK_ID/artifacts/public/libdmg-hfsplus.tar.xz
+$queue_base/task/$TASK_ID/artifacts/public/libdmg-hfsplus.tar.xz
 EOF
 tar cf - -C $WORKSPACE `basename $STAGE` | xz > $UPLOAD_DIR/dmg.tar.xz
--- a/taskcluster/scripts/misc/fetch-content
+++ b/taskcluster/scripts/misc/fetch-content
@@ -21,21 +21,16 @@ import time
 import urllib.request
 
 try:
     import zstandard
 except ImportError:
     zstandard = None
 
 
-PUBLIC_ARTIFACT_URL = ('https://queue.taskcluster.net/v1/task/{task}/artifacts/'
-                       '{artifact}')
-PRIVATE_ARTIFACT_URL = ('http://taskcluster/queue/v1/task/{task}/artifacts/'
-                        '{artifact}')
-
 CONCURRENCY = multiprocessing.cpu_count()
 
 
 def log(msg):
     print(msg, file=sys.stderr)
     sys.stderr.flush()
 
 
@@ -372,30 +367,45 @@ def command_static_url(args):
         try:
             dest.unlink()
         except FileNotFoundError:
             pass
 
         raise
 
 
+def api(root_url, service, version, path):
+    # taskcluster-lib-urls is not available when this script runs, so
+    # simulate its behavior:
+    if root_url == 'https://taskcluster.net':
+        return 'https://{service}.taskcluster.net/{version}/{path}'.format(
+                service=service, version=version, path=path)
+    return 'https://{root_url}/api/{service}/{version}/{path}'.format(
+            root_url=root_url, service=service, version=version, path=path)
+
+
 def command_task_artifacts(args):
     fetches = json.loads(os.environ['MOZ_FETCHES'])
     downloads = []
     for fetch in fetches:
         extdir = pathlib.Path(args.dest)
         if 'dest' in fetch:
             extdir = extdir.joinpath(fetch['dest'])
         extdir.mkdir(parents=True, exist_ok=True)
+        root_url = os.environ['TASKCLUSTER_ROOT_URL']
         if fetch['artifact'].startswith('public/'):
-            url = PUBLIC_ARTIFACT_URL.format(task=fetch['task'],
-                                             artifact=fetch['artifact'])
+            path = 'task/{task}/artifacts/{artifact}'.format(
+                    task=fetch['task'], artifact=fetch['artifact'])
+            url = api(root_url, 'queue', 'v1', path)
         else:
-            url = PRIVATE_ARTIFACT_URL.format(task=fetch['task'],
-                                              artifact=fetch['artifact'])
+            # Until bug 1460015 is finished, use the old baseUrl style proxy URLs
+            url = ('{proxy_url}/queue/v1/task/{task}/artifacts/{artifact}').format(
+                    proxy_url=os.environ['TASKCLUSTER_PROXY_URL'],
+                    task=fetch['task'],
+                    artifact=fetch['artifact'])
         downloads.append((url, extdir, fetch['extract']))
 
     fetch_urls(downloads)
 
 
 def main():
     parser = argparse.ArgumentParser()
     subparsers = parser.add_subparsers(title='sub commands')
--- a/taskcluster/taskgraph/actions/cancel_all.py
+++ b/taskcluster/taskgraph/actions/cancel_all.py
@@ -5,58 +5,39 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 import concurrent.futures as futures
 import logging
 import os
 
-from taskgraph.util.taskcluster import get_session, cancel_task
+from taskgraph.util.taskcluster import list_task_group, cancel_task
 from .registry import register_callback_action
 
 # the maximum number of parallel cancelTask calls to make
 CONCURRENCY = 50
 
-base_url = 'https://queue.taskcluster.net/v1/{}'
-
 logger = logging.getLogger(__name__)
 
 
-def list_group(task_group_id, session):
-    params = {}
-    while True:
-        url = base_url.format('task-group/{}/list'.format(task_group_id))
-        response = session.get(url, stream=True, params=params)
-        response.raise_for_status()
-        response = response.json()
-        for task in [t['status'] for t in response['tasks']]:
-            if task['state'] in ['running', 'pending', 'unscheduled']:
-                yield task['taskId']
-        if response.get('continuationToken'):
-            params = {'continuationToken': response.get('continuationToken')}
-        else:
-            break
-
-
 @register_callback_action(
     title='Cancel All',
     name='cancel-all',
     kind='hook',
     generic=True,
     symbol='cAll',
     description=(
         'Cancel all running and pending tasks created by the decision task '
         'this action task is associated with.'
     ),
     order=400,
     context=[]
 )
 def cancel_all_action(parameters, graph_config, input, task_group_id, task_id, task):
-    session = get_session()
     own_task_id = os.environ.get('TASK_ID', '')
     with futures.ThreadPoolExecutor(CONCURRENCY) as e:
         cancels_jobs = [
             e.submit(cancel_task, t, use_proxy=True)
-            for t in list_group(task_group_id, session) if t != own_task_id
+            for t in list_task_group(task_group_id) if t != own_task_id
         ]
         for job in cancels_jobs:
             job.result()
--- a/taskcluster/taskgraph/actions/create_interactive.py
+++ b/taskcluster/taskgraph/actions/create_interactive.py
@@ -3,22 +3,26 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 import logging
 import re
+import taskcluster_urls
 
 from .util import (
     create_tasks,
     fetch_graph_and_labels
 )
-from taskgraph.util.taskcluster import send_email
+from taskgraph.util.taskcluster import (
+    send_email,
+    get_root_url,
+)
 from .registry import register_callback_action
 
 logger = logging.getLogger(__name__)
 
 EMAIL_SUBJECT = 'Your Interactive Task for {label}'
 EMAIL_CONTENT = '''\
 As you requested, Firefox CI has created an interactive task to run {label}
 on revision {revision} in {repo}. Click the button below to connect to the
@@ -148,17 +152,17 @@ def create_interactive_action(parameters
 
     if input and 'notify' in input:
         email = input['notify']
         # no point sending to a noreply address!
         if email == 'noreply@noreply.mozilla.org':
             return
 
         info = {
-            'url': 'https://tools.taskcluster.net/tasks/{}/connect'.format(taskId),
+            'url': taskcluster_urls.ui(get_root_url(), 'tasks/{}/connect'.format(taskId)),
             'label': label,
             'revision': parameters['head_rev'],
             'repo': parameters['head_repository'],
         }
         send_email(
             email,
             subject=EMAIL_SUBJECT.format(**info),
             content=EMAIL_CONTENT.format(**info),
--- a/taskcluster/taskgraph/morph.py
+++ b/taskcluster/taskgraph/morph.py
@@ -6,20 +6,21 @@
 Graph morphs are modifications to task-graphs that take place *after* the
 optimization phase.
 
 These graph morphs are largely invisible to developers running `./mach`
 locally, so they should be limited to changes that do not modify the meaning of
 the graph.
 """
 
-# Note that the translation of `{'task-reference': '..'}` is handled in the
-# optimization phase (since optimization involves dealing with taskIds
-# directly).  Similarly, `{'relative-datestamp': '..'}` is handled at the last
-# possible moment during task creation.
+# Note that the translation of `{'task-reference': '..'}` and
+# `artifact-reference` are handled in the optimization phase (since
+# optimization involves dealing with taskIds directly).  Similarly,
+# `{'relative-datestamp': '..'}` is handled at the last possible moment during
+# task creation.
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 import logging
 import os
 import re
 
 import jsone
--- a/taskcluster/taskgraph/test/test_util_docker.py
+++ b/taskcluster/taskgraph/test/test_util_docker.py
@@ -5,24 +5,27 @@
 from __future__ import absolute_import, print_function, unicode_literals
 
 import os
 import shutil
 import stat
 import tarfile
 import tempfile
 import unittest
+import mock
+import taskcluster_urls as liburls
 
 from taskgraph.util import docker
 from mozunit import main, MockedOpen
 
 
 MODE_STANDARD = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH
 
 
+@mock.patch.dict('os.environ', {'TASKCLUSTER_ROOT_URL': liburls.test_root_url()})
 class TestDocker(unittest.TestCase):
 
     def test_generate_context_hash(self):
         tmpdir = tempfile.mkdtemp()
         old_GECKO = docker.GECKO
         docker.GECKO = tmpdir
         try:
             os.makedirs(os.path.join(tmpdir, 'docker', 'my-image'))
--- a/taskcluster/taskgraph/test/test_util_parameterization.py
+++ b/taskcluster/taskgraph/test/test_util_parameterization.py
@@ -1,16 +1,18 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 import unittest
 import datetime
+import mock
+import os
 
 from mozunit import main
 from taskgraph.util.parameterization import (
     resolve_timestamps,
     resolve_task_references,
 )
 
 
@@ -83,10 +85,51 @@ class TestTaskRefs(unittest.TestCase):
         "resolve_task_references raises a KeyError on reference to an invalid task"
         self.assertRaisesRegexp(
             KeyError,
             "task 'subject' has no dependency named 'no-such'",
             lambda: resolve_task_references('subject', {'task-reference': '<no-such>'}, {})
         )
 
 
+class TestArtifactRefs(unittest.TestCase):
+
+    def do(self, input, output):
+        taskid_for_edge_name = {'edge%d' % n: 'tid%d' % n for n in range(1, 4)}
+        with mock.patch.dict(os.environ, {'TASKCLUSTER_ROOT_URL': 'https://tc-tests.localhost'}):
+            self.assertEqual(resolve_task_references('subject', input, taskid_for_edge_name),
+                             output)
+
+    def test_in_list(self):
+        "resolve_task_references resolves artifact references in a list"
+        self.do(
+            {'in-a-list': [
+                'stuff', {'artifact-reference': '<edge1/public/foo/bar>'}]},
+            {'in-a-list': [
+                'stuff', 'https://tc-tests.localhost/api/queue/v1'
+                '/task/tid1/artifacts/public/foo/bar']})
+
+    def test_in_dict(self):
+        "resolve_task_references resolves artifact references in a dict"
+        self.do(
+            {'in-a-dict':
+                {'stuff': {'artifact-reference': '<edge2/public/bar/foo>'}}},
+            {'in-a-dict':
+                {'stuff': 'https://tc-tests.localhost/api/queue/v1'
+                    '/task/tid2/artifacts/public/bar/foo'}})
+
+    def test_in_string(self):
+        "resolve_task_references resolves artifact references embedded in a string"
+        self.do(
+            {'stuff': {'artifact-reference': '<edge1/public/filename> and <edge2/public/bar>'}},
+            {'stuff': 'https://tc-tests.localhost/api/queue/v1'
+                '/task/tid1/artifacts/public/filename and '
+                'https://tc-tests.localhost/api/queue/v1/task/tid2/artifacts/public/bar'})
+
+    def test_invalid(self):
+        "resolve_task_references ignores badly-formatted artifact references"
+        for inv in ['<edge1>', 'edge1/foo>', '<edge1>/foo', '<edge1>foo']:
+            resolved = resolve_task_references('subject', {'artifact-reference': inv}, {})
+            self.assertEqual(resolved, inv)
+
+
 if __name__ == '__main__':
     main()
--- a/taskcluster/taskgraph/transforms/balrog_submit.py
+++ b/taskcluster/taskgraph/transforms/balrog_submit.py
@@ -12,28 +12,23 @@ from taskgraph.transforms.base import Tr
 from taskgraph.util.attributes import copy_attributes_from_dependent_job
 from taskgraph.util.schema import (
     optionally_keyed_by, resolve_keyed_by,
 )
 from taskgraph.util.scriptworker import (
     get_balrog_server_scope, get_worker_type_for_scope
 )
 from taskgraph.transforms.task import task_description_schema
-from voluptuous import Any, Required, Optional
+from voluptuous import Optional
 
 
 # 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()}
 
-# shortcut for a string where task references are allowed
-taskref_or_string = Any(
-    basestring,
-    {Required('task-reference'): basestring})
-
 balrog_description_schema = schema.extend({
     # unique label to describe this balrog task, defaults to balrog-{dep.label}
     Optional('label'): basestring,
 
 
     Optional(
         'update-no-wnp',
         description="Whether the parallel `-No-WNP` blob should be updated as well.",
--- a/taskcluster/taskgraph/transforms/beetmover.py
+++ b/taskcluster/taskgraph/transforms/beetmover.py
@@ -2,17 +2,17 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 """
 Transform the beetmover task into an actual task description.
 """
 
 from __future__ import absolute_import, print_function, unicode_literals
 
-from voluptuous import Any, Optional, Required
+from voluptuous import Optional, Required
 
 from taskgraph.loader.single_dep import schema
 from taskgraph.transforms.base import TransformSequence
 from taskgraph.transforms.task import task_description_schema
 from taskgraph.util.attributes import copy_attributes_from_dependent_job
 from taskgraph.util.scriptworker import (generate_beetmover_artifact_map,
                                          generate_beetmover_upstream_artifacts,
                                          get_beetmover_bucket_scope,
@@ -114,21 +114,16 @@ UPSTREAM_SOURCE_ARTIFACTS = [
 ]
 
 # 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(
-    basestring,
-    {Required('task-reference'): basestring})
-
 beetmover_description_schema = schema.extend({
     # depname is used in taskref's to identify the taskID of the unsigned things
     Required('depname', default='build'): basestring,
 
     # unique label to describe this beetmover task, defaults to {dep.label}-beetmover
     Optional('label'): basestring,
 
     # treeherder is allowed here to override any defaults we use for beetmover.  See
--- a/taskcluster/taskgraph/transforms/beetmover_checksums.py
+++ b/taskcluster/taskgraph/transforms/beetmover_checksums.py
@@ -13,27 +13,23 @@ from taskgraph.transforms.base import Tr
 from taskgraph.transforms.beetmover import craft_release_properties
 from taskgraph.util.attributes import copy_attributes_from_dependent_job
 from taskgraph.util.scriptworker import (generate_beetmover_artifact_map,
                                          generate_beetmover_upstream_artifacts,
                                          get_beetmover_action_scope,
                                          get_beetmover_bucket_scope,
                                          get_worker_type_for_scope,
                                          should_use_artifact_map)
-from voluptuous import Any, Optional, Required
+from voluptuous import Optional, Required
 from taskgraph.transforms.task import task_description_schema
 
 # 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()}
 
-taskref_or_string = Any(
-    basestring,
-    {Required('task-reference'): basestring})
-
 beetmover_checksums_description_schema = schema.extend({
     Required('depname', default='build'): basestring,
     Required('attributes'): {basestring: object},
     Optional('label'): basestring,
     Optional('treeherder'): task_description_schema['treeherder'],
     Optional('locale'): basestring,
     Optional('shipping-phase'): task_description_schema['shipping-phase'],
     Optional('shipping-product'): task_description_schema['shipping-product'],
--- a/taskcluster/taskgraph/transforms/beetmover_emefree_checksums.py
+++ b/taskcluster/taskgraph/transforms/beetmover_emefree_checksums.py
@@ -7,26 +7,22 @@ Transform release-beetmover-source-check
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 from taskgraph.loader.single_dep import schema
 from taskgraph.transforms.base import TransformSequence
 from taskgraph.transforms.beetmover import craft_release_properties
 from taskgraph.util.attributes import copy_attributes_from_dependent_job
 from taskgraph.transforms.task import task_description_schema
-from voluptuous import Any, Required, Optional
+from voluptuous import Required, Optional
 
 # 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()}
 
-taskref_or_string = Any(
-    basestring,
-    {Required('task-reference'): basestring})
-
 beetmover_checksums_description_schema = schema.extend({
     Required('depname', default='build'): basestring,
     Optional('label'): basestring,
     Optional('extra'): object,
     Optional('shipping-phase'): task_description_schema['shipping-phase'],
     Optional('shipping-product'): task_description_schema['shipping-product'],
 })
 
--- a/taskcluster/taskgraph/transforms/beetmover_langpack_checksums.py
+++ b/taskcluster/taskgraph/transforms/beetmover_langpack_checksums.py
@@ -10,26 +10,22 @@ from __future__ import absolute_import, 
 from taskgraph.loader.single_dep import schema
 from taskgraph.transforms.base import TransformSequence
 from taskgraph.transforms.beetmover import craft_release_properties
 from taskgraph.util.attributes import copy_attributes_from_dependent_job
 from taskgraph.util.scriptworker import (get_beetmover_bucket_scope,
                                          get_beetmover_action_scope,
                                          get_worker_type_for_scope)
 from taskgraph.transforms.task import task_description_schema
-from voluptuous import Any, Required, Optional
+from voluptuous import Required, Optional
 
 # 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()}
 
-taskref_or_string = Any(
-    basestring,
-    {Required('task-reference'): basestring})
-
 beetmover_checksums_description_schema = schema.extend({
     Required('depname', default='build'): basestring,
     Optional('label'): basestring,
     Optional('treeherder'): task_description_schema['treeherder'],
     Optional('locale'): basestring,
     Optional('shipping-phase'): task_description_schema['shipping-phase'],
     Optional('shipping-product'): task_description_schema['shipping-product'],
 })
--- a/taskcluster/taskgraph/transforms/beetmover_push_to_release.py
+++ b/taskcluster/taskgraph/transforms/beetmover_push_to_release.py
@@ -3,35 +3,34 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 """
 Transform the beetmover-push-to-release task into a task description.
 """
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 from taskgraph.transforms.base import TransformSequence
-from taskgraph.util.schema import Schema
+from taskgraph.util.schema import (
+    Schema,
+    taskref_or_string,
+)
 from taskgraph.util.scriptworker import (
     get_beetmover_bucket_scope, add_scope_prefix,
     get_worker_type_for_scope,
 )
 from taskgraph.transforms.job import job_description_schema
 from taskgraph.transforms.task import task_description_schema
-from voluptuous import Any, Required, Optional
+from voluptuous import Required, Optional
 
 # 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()}
 job_description_schema = {str(k): v for k, v in job_description_schema.schema.iteritems()}
 
 
-taskref_or_string = Any(
-    basestring,
-    {Required('task-reference'): basestring})
-
 beetmover_push_to_release_description_schema = Schema({
     Required('name'): basestring,
     Required('product'): basestring,
     Required('treeherder-platform'): basestring,
     Optional('attributes'): {basestring: object},
     Optional('job-from'): task_description_schema['job-from'],
     Optional('run'): {basestring: object},
     Optional('run-on-projects'): task_description_schema['run-on-projects'],
--- a/taskcluster/taskgraph/transforms/beetmover_repackage.py
+++ b/taskcluster/taskgraph/transforms/beetmover_repackage.py
@@ -14,17 +14,17 @@ from taskgraph.util.attributes import co
 from taskgraph.util.partials import (get_balrog_platform_name,
                                      get_partials_artifacts,
                                      get_partials_artifact_map)
 from taskgraph.util.scriptworker import (get_beetmover_bucket_scope,
                                          get_beetmover_action_scope,
                                          get_worker_type_for_scope)
 from taskgraph.util.taskcluster import get_artifact_prefix
 from taskgraph.transforms.task import task_description_schema
-from voluptuous import Any, Required, Optional
+from voluptuous import Required, Optional
 
 import logging
 import re
 
 logger = logging.getLogger(__name__)
 
 
 def _compile_regex_mapping(mapping):
@@ -134,21 +134,16 @@ UPSTREAM_ARTIFACT_SIGNED_MAR_PATHS = [
     'target.complete.mar',
     'target.bz2.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()}
 
-# shortcut for a string where task references are allowed
-taskref_or_string = Any(
-    basestring,
-    {Required('task-reference'): basestring})
-
 beetmover_description_schema = schema.extend({
     # depname is used in taskref's to identify the taskID of the unsigned things
     Required('depname', default='build'): basestring,
 
     # 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
--- a/taskcluster/taskgraph/transforms/beetmover_repackage_partner.py
+++ b/taskcluster/taskgraph/transforms/beetmover_repackage_partner.py
@@ -34,21 +34,16 @@ import logging
 
 logger = logging.getLogger(__name__)
 
 
 # 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()}
 
-# shortcut for a string where task references are allowed
-taskref_or_string = Any(
-    basestring,
-    {Required('task-reference'): basestring})
-
 beetmover_description_schema = schema.extend({
     # depname is used in taskref's to identify the taskID of the unsigned things
     Required('depname', default='build'): basestring,
 
     # unique label to describe this beetmover task, defaults to {dep.label}-beetmover
     Optional('label'): basestring,
 
     Required('partner-bucket-scope'): optionally_keyed_by('release-level', basestring),
--- a/taskcluster/taskgraph/transforms/beetmover_source_checksums.py
+++ b/taskcluster/taskgraph/transforms/beetmover_source_checksums.py
@@ -13,26 +13,22 @@ from taskgraph.transforms.beetmover impo
 from taskgraph.util.attributes import copy_attributes_from_dependent_job
 from taskgraph.util.scriptworker import (generate_beetmover_artifact_map,
                                          generate_beetmover_upstream_artifacts,
                                          get_beetmover_bucket_scope,
                                          get_beetmover_action_scope,
                                          get_worker_type_for_scope,
                                          should_use_artifact_map)
 from taskgraph.transforms.task import task_description_schema
-from voluptuous import Any, Required, Optional
+from voluptuous import Required, Optional
 
 # 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()}
 
-taskref_or_string = Any(
-    basestring,
-    {Required('task-reference'): basestring})
-
 beetmover_checksums_description_schema = schema.extend({
     Required('depname', default='build'): basestring,
     Optional('label'): basestring,
     Optional('treeherder'): task_description_schema['treeherder'],
     Optional('locale'): basestring,
     Optional('shipping-phase'): task_description_schema['shipping-phase'],
     Optional('shipping-product'): task_description_schema['shipping-product'],
     Optional('attributes'): task_description_schema['attributes'],
--- a/taskcluster/taskgraph/transforms/checksums_signing.py
+++ b/taskcluster/taskgraph/transforms/checksums_signing.py
@@ -11,26 +11,22 @@ from taskgraph.loader.single_dep import 
 from taskgraph.transforms.base import TransformSequence
 from taskgraph.util.attributes import copy_attributes_from_dependent_job
 from taskgraph.util.scriptworker import (
     get_signing_cert_scope,
     get_worker_type_for_scope,
     add_scope_prefix,
 )
 from taskgraph.transforms.task import task_description_schema
-from voluptuous import Any, Required, Optional
+from voluptuous import Required, Optional
 
 # 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()}
 
-taskref_or_string = Any(
-    basestring,
-    {Required('task-reference'): basestring})
-
 checksums_signing_description_schema = schema.extend({
     Required('depname', default='beetmover'): 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'],
 })
 
--- a/taskcluster/taskgraph/transforms/diffoscope.py
+++ b/taskcluster/taskgraph/transforms/diffoscope.py
@@ -7,17 +7,17 @@ defined in kind.yml
 """
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 from taskgraph.transforms.base import TransformSequence
 from taskgraph.util.schema import (
     Schema,
 )
-from taskgraph.util.taskcluster import get_artifact_path, get_artifact_url
+from taskgraph.util.taskcluster import get_artifact_path
 from voluptuous import (
     Any,
     Optional,
     Required,
 )
 
 index_or_string = Any(
     basestring,
@@ -59,17 +59,17 @@ def fill_template(config, tasks):
 
         deps = {}
         urls = {}
         previous_artifact = None
         for k in ('original', 'new'):
             value = task[k]
             if isinstance(value, basestring):
                 deps[k] = value
-                task_id = '<{}>'.format(k)
+                dep_name = k
                 os_hint = value
             else:
                 index = value['index-search']
                 if index not in dummy_tasks:
                     dummy_tasks[index] = {
                         'label': 'index-search-' + index,
                         'description': index,
                         'worker-type': 'invalid/always-optimized',
@@ -77,34 +77,36 @@ def fill_template(config, tasks):
                             'using': 'always-optimized',
                         },
                         'optimization': {
                             'index-search': [index],
                         }
                     }
                     yield dummy_tasks[index]
                 deps[index] = 'index-search-' + index
-                task_id = '<{}>'.format(index)
+                dep_name = index
                 os_hint = index.split('.')[-1]
             if 'linux' in os_hint:
                 artifact = 'target.tar.bz2'
             elif 'macosx' in os_hint:
                 artifact = 'target.dmg'
             elif 'android' in os_hint:
                 artifact = 'target.apk'
             elif 'win' in os_hint:
                 artifact = 'target.zip'
             else:
                 raise Exception(
                     'Cannot figure out the OS for {!r}'.format(value))
             if previous_artifact is not None and previous_artifact != artifact:
                 raise Exception(
                     'Cannot compare builds from different OSes')
-            url = get_artifact_url(task_id, get_artifact_path(task, artifact))
-            urls[k] = {'task-reference': url}
+            urls[k] = {
+                'artifact-reference': '<{}/{}>'.format(
+                    dep_name, get_artifact_path(task, artifact)),
+            }
             previous_artifact = artifact
 
         taskdesc = {
             'label': 'diff-' + name,
             'description': name,
             'treeherder': {
                 'symbol': task['symbol'],
                 'platform': 'diff/opt',
--- a/taskcluster/taskgraph/transforms/docker_image.py
+++ b/taskcluster/taskgraph/transforms/docker_image.py
@@ -10,16 +10,17 @@ import re
 from collections import deque
 import taskgraph
 from taskgraph.transforms.base import TransformSequence
 from taskgraph.transforms.task import _run_task_suffix
 from .. import GECKO
 from taskgraph.util.docker import (
     generate_context_hash,
 )
+from taskgraph.util.taskcluster import get_root_url
 from taskgraph.util.schema import (
     Schema,
 )
 from voluptuous import (
     Optional,
     Required,
 )
 
@@ -106,16 +107,18 @@ def fill_template(config, tasks):
         # task-reference value, see further below). We add the package routes
         # containing a hash to get the overall docker image hash, so changes
         # to packages will be reflected in the docker image hash.
         args['DOCKER_IMAGE_PACKAGES'] = ' '.join('<{}>'.format(p)
                                                  for p in packages)
         if parent:
             args['DOCKER_IMAGE_PARENT'] = '{}:{}'.format(parent, context_hashes[parent])
 
+        args['TASKCLUSTER_ROOT_URL'] = get_root_url()
+
         if not taskgraph.fast:
             context_path = os.path.join('taskcluster', 'docker', definition)
             context_hash = generate_context_hash(
                 GECKO, context_path, image_name, args)
         else:
             context_hash = '0'*40
         digest_data = [context_hash]
         context_hashes[image_name] = context_hash
--- a/taskcluster/taskgraph/transforms/final_verify.py
+++ b/taskcluster/taskgraph/transforms/final_verify.py
@@ -4,40 +4,37 @@
 """
 Transform the beetmover task into an actual task description.
 """
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 from taskgraph.transforms.base import TransformSequence
 from taskgraph.util.schema import resolve_keyed_by
-from taskgraph.util.taskcluster import get_taskcluster_artifact_prefix
 
 transforms = TransformSequence()
 
 
 @transforms.add
 def add_command(config, tasks):
     for task in tasks:
         if not task["worker"].get("env"):
             task["worker"]["env"] = {}
 
         final_verify_configs = []
         for upstream in task.get("dependencies", {}).keys():
             if 'update-verify-config' in upstream:
                 final_verify_configs.append(
-                    "{}update-verify.cfg".format(
-                        get_taskcluster_artifact_prefix(task, "<{}>".format(upstream))
-                    )
+                    "<{}/public/build/update-verify.cfg>".format(upstream),
                 )
         task['run'] = {
             'using': 'run-task',
             'command': {
-                'task-reference': 'cd /builds/worker/checkouts/gecko && '
-                                  'tools/update-verify/release/final-verification.sh '
-                                  + ' '.join(final_verify_configs),
+                'artifact-reference': 'cd /builds/worker/checkouts/gecko && '
+                                      'tools/update-verify/release/final-verification.sh '
+                                      + ' '.join(final_verify_configs),
             },
             'sparse-profile': 'update-verify',
         }
         for thing in ("BUILD_TOOLS_REPO",):
             thing = "worker.env.{}".format(thing)
             resolve_keyed_by(task, thing, thing, **config.params)
         yield task
--- a/taskcluster/taskgraph/transforms/job/debian_package.py
+++ b/taskcluster/taskgraph/transforms/job/debian_package.py
@@ -4,24 +4,26 @@
 """
 Support for running spidermonkey jobs via dedicated scripts
 """
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 import os
 import re
+import taskcluster_urls
 
 from taskgraph.util.schema import Schema
 from voluptuous import Any, Optional, Required
 
 from taskgraph.transforms.job import run_job_using
 from taskgraph.transforms.job.common import add_artifacts
 
 from taskgraph.util.hash import hash_path
+from taskgraph.util.taskcluster import get_root_url
 from taskgraph import GECKO
 import taskgraph
 
 DSC_PACKAGE_RE = re.compile('.*(?=_)')
 SOURCE_PACKAGE_RE = re.compile('.*(?=[-_]\d)')
 
 source_definition = {
     Required('url'): basestring,
@@ -147,16 +149,18 @@ def docker_worker_debian_package(config,
         )
     if 'patch' not in run and 'pre-build-command' not in run:
         adjust += ('debchange -l ".{prefix}moz" --distribution "{dist}"'
                    ' "Mozilla backport for {dist}." < /dev/null && ').format(
             prefix=name.split('-', 1)[0],
             dist=run['dist'],
         )
 
+    queue_url = taskcluster_urls.api(get_root_url(), 'queue', 'v1', '')
+
     # We can't depend on docker images (since docker images depend on packages),
     # so we inline the whole script here.
     worker['command'] = [
         'sh',
         '-x',
         '-c',
         # Fill /etc/apt/sources.list with the relevant snapshot repository.
         'echo "deb http://snapshot.debian.org/archive/debian'
@@ -166,18 +170,17 @@ def docker_worker_debian_package(config,
         'echo "deb http://snapshot.debian.org/archive/debian'
         '/{snapshot}/ {dist}-backports main" >> /etc/apt/sources.list && '
         'echo "deb http://snapshot.debian.org/archive/debian-security'
         '/{snapshot}/ {dist}/updates main" >> /etc/apt/sources.list && '
         'apt-get update -o Acquire::Check-Valid-Until=false -q && '
         # Add sources for packages coming from other package tasks.
         'apt-get install -yyq apt-transport-https ca-certificates && '
         'for task in $PACKAGES; do '
-        '  echo "deb [trusted=yes] https://queue.taskcluster.net/v1/task'
-        '/$task/artifacts/public/build/ debian/" '
+        '  echo "deb [trusted=yes] {queue_url}task/$task/artifacts/public/build/ debian/" '
         '>> /etc/apt/sources.list; '
         'done && '
         # Install the base utilities required to build debian packages.
         'apt-get update -o Acquire::Check-Valid-Until=false -q && '
         'apt-get install -yyq {base_deps} && '
         'cd /tmp && '
         # Get, validate and extract the package source.
         'dget -d -u {src_url} && '
@@ -193,16 +196,17 @@ def docker_worker_debian_package(config,
         # Copy the artifacts
         'mkdir -p {artifacts}/debian && '
         'dcmd cp ../{package}_*.changes {artifacts}/debian/ && '
         'cd {artifacts} && '
         # Make the artifacts directory usable as an APT repository.
         'apt-ftparchive sources debian | gzip -c9 > debian/Sources.gz && '
         'apt-ftparchive packages debian | gzip -c9 > debian/Packages.gz'
         .format(
+            queue_url=queue_url,
             package=package,
             snapshot=run['snapshot'],
             dist=run['dist'],
             src_url=src_url,
             src_file=src_file,
             src_sha256=src_sha256,
             unpack=unpack,
             adjust=adjust,
--- a/taskcluster/taskgraph/transforms/job/mach.py
+++ b/taskcluster/taskgraph/transforms/job/mach.py
@@ -3,24 +3,27 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 """
 Support for running mach tasks (via run-task)
 """
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 from taskgraph.transforms.job import run_job_using, configure_taskdesc_for_run
-from taskgraph.util.schema import Schema
+from taskgraph.util.schema import (
+    Schema,
+    taskref_or_string,
+)
 from voluptuous import Required, Optional, Any
 
 mach_schema = Schema({
     Required('using'): 'mach',
 
     # The mach command (omitting `./mach`) to run
-    Required('mach'): basestring,
+    Required('mach'): taskref_or_string,
 
     # The sparse checkout profile to use. Value is the filename relative to the
     # directory where sparse profiles are defined (build/sparse-profiles/).
     Optional('sparse-profile'): Any(basestring, None),
 
     # if true, perform a checkout of a comm-central based branch inside the
     # gecko checkout
     Required('comm-checkout'): bool,
@@ -36,13 +39,21 @@ defaults = {
 
 
 @run_job_using("docker-worker", "mach", schema=mach_schema, defaults=defaults)
 @run_job_using("native-engine", "mach", schema=mach_schema, defaults=defaults)
 @run_job_using("generic-worker", "mach", schema=mach_schema, defaults=defaults)
 def configure_mach(config, job, taskdesc):
     run = job['run']
 
+    command_prefix = 'cd $GECKO_PATH && ./mach '
+    mach = run['mach']
+    if isinstance(mach, dict):
+        ref, pattern = next(iter(mach.items()))
+        command = {ref: command_prefix + pattern}
+    else:
+        command = command_prefix + mach
+
     # defer to the run_task implementation
-    run['command'] = 'cd $GECKO_PATH && ./mach {mach}'.format(**run)
+    run['command'] = command
     run['using'] = 'run-task'
     del run['mach']
     configure_taskdesc_for_run(config, job, taskdesc, job['worker']['implementation'])
--- a/taskcluster/taskgraph/transforms/l10n.py
+++ b/taskcluster/taskgraph/transforms/l10n.py
@@ -13,16 +13,17 @@ import json
 from mozbuild.chunkify import chunkify
 from taskgraph.loader.multi_dep import schema
 from taskgraph.transforms.base import (
     TransformSequence,
 )
 from taskgraph.util.schema import (
     optionally_keyed_by,
     resolve_keyed_by,
+    taskref_or_string,
 )
 from taskgraph.util.attributes import copy_attributes_from_dependent_job
 from taskgraph.util.taskcluster import get_artifact_prefix
 from taskgraph.util.treeherder import add_suffix
 from taskgraph.transforms.job import job_description_schema
 from taskgraph.transforms.task import task_description_schema
 from voluptuous import (
     Any,
@@ -30,21 +31,16 @@ from voluptuous import (
     Required,
 )
 
 
 def _by_platform(arg):
     return optionally_keyed_by('build-platform', arg)
 
 
-# shortcut for a string where task references are allowed
-taskref_or_string = Any(
-    basestring,
-    {Required('task-reference'): basestring})
-
 # Voluptuous uses marker objects as dictionary *keys*, but they are not
 # comparable, so we cast all of the keys back to regular strings
 job_description_schema = {str(k): v for k, v in job_description_schema.schema.iteritems()}
 task_description_schema = {str(k): v for k, v in task_description_schema.schema.iteritems()}
 
 l10n_description_schema = schema.extend({
     # Name for this job, inferred from the dependent job before validation
     Required('name'): basestring,
--- a/taskcluster/taskgraph/transforms/partials.py
+++ b/taskcluster/taskgraph/transforms/partials.py
@@ -4,18 +4,18 @@
 """
 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
 from taskgraph.util.partials import get_balrog_platform_name, get_builds
-from taskgraph.util.taskcluster import get_taskcluster_artifact_prefix, get_artifact_prefix
 from taskgraph.util.platforms import architecture
+from taskgraph.util.taskcluster import get_artifact_prefix
 
 import logging
 logger = logging.getLogger(__name__)
 
 transforms = TransformSequence()
 
 
 def _generate_task_output_files(job, filenames, locale=None):
@@ -72,29 +72,30 @@ def make_task_description(config, jobs):
         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)
-
         extra = {'funsize': {'partials': list()}}
         update_number = 1
-        artifact_path = "{}{}".format(
-            get_taskcluster_artifact_prefix(dep_job, dep_task_ref, locale=locale),
-            'target.complete.mar'
+
+        locale_suffix = ''
+        if locale:
+            locale_suffix = '{}/'.format(locale)
+        artifact_path = "<{}/{}/{}target.complete.mar>".format(
+            dependent_kind, get_artifact_prefix(dep_job), locale_suffix,
         )
         for build in sorted(builds):
             partial_info = {
                 'locale': build_locale,
                 'from_mar': builds[build]['mar_url'],
-                'to_mar': {'task-reference': artifact_path},
+                'to_mar': {'artifact-reference': artifact_path},
                 'platform': get_balrog_platform_name(dep_th_platform),
                 'branch': config.params['project'],
                 'update_number': update_number,
                 'dest_mar': build,
             }
             if 'product' in builds[build]:
                 partial_info['product'] = builds[build]['product']
             if 'previousVersion' in builds[build]:
--- a/taskcluster/taskgraph/transforms/repackage.py
+++ b/taskcluster/taskgraph/transforms/repackage.py
@@ -15,28 +15,23 @@ from taskgraph.util.attributes import co
 from taskgraph.util.schema import (
     optionally_keyed_by,
     resolve_keyed_by,
 )
 from taskgraph.util.taskcluster import get_artifact_prefix
 from taskgraph.util.platforms import archive_format, executable_extension, architecture
 from taskgraph.util.workertypes import worker_type_implementation
 from taskgraph.transforms.job import job_description_schema
-from voluptuous import Any, Required, Optional
+from voluptuous import Required, Optional
 
 # Voluptuous uses marker objects as dictionary *keys*, but they are not
 # comparable, so we cast all of the keys back to regular strings
 job_description_schema = {str(k): v for k, v in job_description_schema.schema.iteritems()}
 
 
-# shortcut for a string where task references are allowed
-taskref_or_string = Any(
-    basestring,
-    {Required('task-reference'): basestring})
-
 packaging_description_schema = schema.extend({
     # depname is used in taskref's to identify the taskID of the signed things
     Required('depname', default='build'): basestring,
 
     # unique label to describe this repackaging task
     Optional('label'): basestring,
 
     # treeherder is allowed here to override any defaults we use for repackaging.  See
--- a/taskcluster/taskgraph/transforms/repackage_partner.py
+++ b/taskcluster/taskgraph/transforms/repackage_partner.py
@@ -17,32 +17,27 @@ from taskgraph.util.schema import (
     resolve_keyed_by,
 )
 from taskgraph.util.taskcluster import get_artifact_prefix
 from taskgraph.util.partners import check_if_partners_enabled
 from taskgraph.util.platforms import archive_format, executable_extension
 from taskgraph.util.workertypes import worker_type_implementation
 from taskgraph.transforms.task import task_description_schema
 from taskgraph.transforms.repackage import PACKAGE_FORMATS
-from voluptuous import Any, Required, Optional
+from voluptuous import Required, Optional
 
 # 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()}
 
 
 def _by_platform(arg):
     return optionally_keyed_by('build-platform', arg)
 
 
-# shortcut for a string where task references are allowed
-taskref_or_string = Any(
-    basestring,
-    {Required('task-reference'): basestring})
-
 packaging_description_schema = schema.extend({
     # depname is used in taskref's to identify the taskID of the signed things
     Required('depname', default='build'): basestring,
 
     # unique label to describe this repackaging task
     Optional('label'): basestring,
 
     # Routes specific to this task, if defined
--- a/taskcluster/taskgraph/transforms/signing.py
+++ b/taskcluster/taskgraph/transforms/signing.py
@@ -5,36 +5,32 @@
 Transform the signing task into an actual task description.
 """
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 from taskgraph.loader.single_dep import schema
 from taskgraph.transforms.base import TransformSequence
 from taskgraph.util.attributes import copy_attributes_from_dependent_job
+from taskgraph.util.schema import taskref_or_string
 from taskgraph.util.scriptworker import (
     add_scope_prefix,
     get_signing_cert_scope_per_platform,
     get_worker_type_for_scope,
 )
 from taskgraph.transforms.task import task_description_schema
-from voluptuous import Any, Required, Optional
+from voluptuous import Required, Optional
 
 
 # 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(
-    basestring,
-    {Required('task-reference'): basestring})
-
 signing_description_schema = schema.extend({
     # Artifacts from dep task to sign - Sync with taskgraph/transforms/task.py
     # because this is passed directly into the signingscript worker
     Required('upstream-artifacts'): [{
         # taskId of the task with the artifact
         Required('taskId'): taskref_or_string,
 
         # type of signing task (for CoT)
--- a/taskcluster/taskgraph/transforms/source_checksums_signing.py
+++ b/taskcluster/taskgraph/transforms/source_checksums_signing.py
@@ -11,26 +11,22 @@ from taskgraph.loader.single_dep import 
 from taskgraph.transforms.base import TransformSequence
 from taskgraph.util.attributes import copy_attributes_from_dependent_job
 from taskgraph.util.scriptworker import (
     get_signing_cert_scope,
     get_worker_type_for_scope,
     add_scope_prefix,
 )
 from taskgraph.transforms.task import task_description_schema
-from voluptuous import Any, Required, Optional
+from voluptuous import Required, Optional
 
 # 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()}
 
-taskref_or_string = Any(
-    basestring,
-    {Required('task-reference'): basestring})
-
 checksums_signing_description_schema = schema.extend({
     Required('depname', default='beetmover'): 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'],
 })
 
--- a/taskcluster/taskgraph/transforms/source_test.py
+++ b/taskcluster/taskgraph/transforms/source_test.py
@@ -22,18 +22,16 @@ from taskgraph.util.treeherder import jo
 from voluptuous import (
     Any,
     Extra,
     Optional,
     Required,
     Schema,
 )
 
-ARTIFACT_URL = 'https://queue.taskcluster.net/v1/task/{}/artifacts/{}'
-
 job_description_schema = {str(k): v for k, v in job_description_schema.schema.iteritems()}
 
 source_test_description_schema = Schema({
     # most fields are passed directly through as job fields, and are not
     # repeated here
     Extra: object,
 
     # The platform on which this task runs.  This will be used to set up attributes
--- a/taskcluster/taskgraph/transforms/task.py
+++ b/taskcluster/taskgraph/transforms/task.py
@@ -16,22 +16,24 @@ import re
 import time
 from copy import deepcopy
 
 from mozbuild.util import memoize
 from taskgraph.util.attributes import TRUNK_PROJECTS
 from taskgraph.util.hash import hash_path
 from taskgraph.util.treeherder import split_symbol
 from taskgraph.transforms.base import TransformSequence
+from taskgraph.util.taskcluster import get_root_url
 from taskgraph.util.schema import (
     validate_schema,
     Schema,
     optionally_keyed_by,
     resolve_keyed_by,
     OptimizationSchema,
+    taskref_or_string,
 )
 from taskgraph.util.scriptworker import (
     BALROG_ACTIONS,
     get_release_config,
     add_scope_prefix,
 )
 from taskgraph.util.signed_artifacts import get_signed_artifacts
 from voluptuous import Any, Required, Optional, Extra
@@ -42,22 +44,16 @@ RUN_TASK = os.path.join(GECKO, 'taskclus
 
 
 @memoize
 def _run_task_suffix():
     """String to append to cache names under control of run-task."""
     return hash_path(RUN_TASK)[0:20]
 
 
-# shortcut for a string where task references are allowed
-taskref_or_string = Any(
-    basestring,
-    {Required('task-reference'): basestring},
-)
-
 # A task description is a general description of a TaskCluster task
 task_description_schema = Schema({
     # the label for this task
     Required('label'): basestring,
 
     # description of the task (for metadata)
     Required('description'): basestring,
 
@@ -494,16 +490,21 @@ def build_docker_worker_payload(config, 
             image = {
                 "path": "public/image.tar.zst",
                 "namespace": image['indexed'],
                 "type": "indexed-image",
             }
         else:
             raise Exception("unknown docker image type")
 
+    # propagate our TASKCLUSTER_ROOT_URL to the task; note that this will soon
+    # be provided directly by the worker, making this redundant:
+    # https://bugzilla.mozilla.org/show_bug.cgi?id=1460015
+    worker['env']['TASKCLUSTER_ROOT_URL'] = get_root_url()
+
     features = {}
 
     if worker.get('relengapi-proxy'):
         features['relengAPIProxy'] = True
 
     if worker.get('taskcluster-proxy'):
         features['taskclusterProxy'] = True
         worker['env']['TASKCLUSTER_PROXY_URL'] = 'http://taskcluster/'
@@ -526,16 +527,21 @@ def build_docker_worker_payload(config, 
                 level=config.params['level'])
         )
         worker['env']['USE_SCCACHE'] = '1'
         # Disable sccache idle shutdown.
         worker['env']['SCCACHE_IDLE_TIMEOUT'] = '0'
     else:
         worker['env']['SCCACHE_DISABLE'] = '1'
 
+    # this will soon be provided directly by the worker:
+    # https://bugzilla.mozilla.org/show_bug.cgi?id=1460015
+    if features.get('taskclusterProxy'):
+        worker['env']['TASKCLUSTER_PROXY_URL'] = 'http://taskcluster'
+
     capabilities = {}
 
     for lo in 'audio', 'video':
         if worker.get('loopback-' + lo):
             capitalized = 'loopback' + lo.capitalize()
             devices = capabilities.setdefault('devices', {})
             devices[capitalized] = True
             task_def['scopes'].append('docker-worker:capability:device:' + capitalized)
@@ -758,16 +764,21 @@ def build_generic_worker_payload(config,
 
     task_def['payload'] = {
         'command': worker['command'],
         'maxRunTime': worker['max-run-time'],
     }
 
     env = worker.get('env', {})
 
+    # propagate our TASKCLUSTER_ROOT_URL to the task; note that this will soon
+    # be provided directly by the worker, making this redundant:
+    # https://bugzilla.mozilla.org/show_bug.cgi?id=1460015
+    env['TASKCLUSTER_ROOT_URL'] = get_root_url()
+
     if task.get('needs-sccache'):
         env['USE_SCCACHE'] = '1'
         # Disable sccache idle shutdown.
         env['SCCACHE_IDLE_TIMEOUT'] = '0'
     else:
         env['SCCACHE_DISABLE'] = '1'
 
     if env:
@@ -812,17 +823,19 @@ def build_generic_worker_payload(config,
 
     features = {}
 
     if worker.get('chain-of-trust'):
         features['chainOfTrust'] = True
 
     if worker.get('taskcluster-proxy'):
         features['taskclusterProxy'] = True
-        worker['env']['TASKCLUSTER_PROXY_URL'] = 'http://taskcluster/'
+        # this will soon be provided directly by the worker:
+        # https://bugzilla.mozilla.org/show_bug.cgi?id=1460015
+        worker['env']['TASKCLUSTER_PROXY_URL'] = 'http://taskcluster'
 
     if worker.get('run-as-administrator', False):
         features['runAsAdministrator'] = True
 
     if features:
         task_def['payload']['features'] = features
 
     # coalesce / superseding
@@ -1310,16 +1323,21 @@ def build_always_optimized_payload(confi
         # type=directory)
         Required('name'): basestring,
     }],
     # Wether any artifacts are assigned to this worker
     Optional('skip-artifacts'): bool,
 })
 def build_macosx_engine_payload(config, task, task_def):
     worker = task['worker']
+
+    # propagate our TASKCLUSTER_ROOT_URL to the task; note that this will soon
+    # be provided directly by the worker, making this redundant
+    worker.setdefault('env', {})['TASKCLUSTER_ROOT_URL'] = get_root_url()
+
     artifacts = map(lambda artifact: {
         'name': artifact['name'],
         'path': artifact['path'],
         'type': artifact['type'],
         'expires': task_def['expires'],
     }, worker.get('artifacts', []))
 
     task_def['payload'] = {
--- a/taskcluster/taskgraph/util/docker.py
+++ b/taskcluster/taskgraph/util/docker.py
@@ -203,28 +203,28 @@ def create_context_tar(topsrcdir, contex
         return stream_context_tar(topsrcdir, context_dir, fh, prefix, args)
 
 
 def stream_context_tar(topsrcdir, context_dir, out_file, prefix, args=None):
     """Like create_context_tar, but streams the tar file to the `out_file` file
     object."""
     archive_files = {}
     replace = []
+    content = []
 
     context_dir = os.path.join(topsrcdir, context_dir)
 
     for root, dirs, files in os.walk(context_dir):
         for f in files:
             source_path = os.path.join(root, f)
             rel = source_path[len(context_dir) + 1:]
             archive_path = os.path.join(prefix, rel)
             archive_files[archive_path] = source_path
 
     # Parse Dockerfile for special syntax of extra files to include.
-    content = []
     with open(os.path.join(context_dir, 'Dockerfile'), 'rb') as fh:
         for line in fh:
             if line.startswith('# %ARG'):
                 p = line[len('# %ARG '):].strip()
                 if not args or p not in args:
                     raise Exception('missing argument: {}'.format(p))
                 replace.append((re.compile(r'\${}\b'.format(p)),
                                 args[p].encode('ascii')))
--- a/taskcluster/taskgraph/util/parameterization.py
+++ b/taskcluster/taskgraph/util/parameterization.py
@@ -2,47 +2,75 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 import re
 
 from taskgraph.util.time import json_time_from_now
+from taskgraph.util.taskcluster import get_artifact_url
 
 TASK_REFERENCE_PATTERN = re.compile('<([^>]+)>')
+ARTIFACT_REFERENCE_PATTERN = re.compile('<([^/]+)/([^>]+)>')
 
 
-def _recurse(val, param_name, param_fn):
-    param_keys = [param_name]
-
+def _recurse(val, param_fns):
     def recurse(val):
         if isinstance(val, list):
             return [recurse(v) for v in val]
         elif isinstance(val, dict):
-            if val.keys() == param_keys:
-                return param_fn(val[param_name])
-            else:
-                return {k: recurse(v) for k, v in val.iteritems()}
+            if len(val) == 1:
+                for param_key, param_fn in param_fns.items():
+                    if val.keys() == [param_key]:
+                        return param_fn(val[param_key])
+            return {k: recurse(v) for k, v in val.iteritems()}
         else:
             return val
     return recurse(val)
 
 
 def resolve_timestamps(now, task_def):
     """Resolve all instances of `{'relative-datestamp': '..'}` in the given task definition"""
-    return _recurse(task_def, 'relative-datestamp', lambda v: json_time_from_now(v, now))
+    return _recurse(task_def, {
+        'relative-datestamp': lambda v: json_time_from_now(v, now),
+    })
 
 
 def resolve_task_references(label, task_def, dependencies):
-    """Resolve all instances of `{'task-reference': '..<..>..'}` in the given task
-    definition, using the given dependencies"""
-    def repl(match):
-        key = match.group(1)
-        try:
-            return dependencies[key]
-        except KeyError:
-            # handle escaping '<'
-            if key == '<':
-                return key
-            raise KeyError("task '{}' has no dependency named '{}'".format(label, key))
+    """Resolve all instances of
+      {'task-reference': '..<..>..'}
+    and
+      {'artifact-reference`: '..<dependency/artifact/path>..'}
+    in the given task definition, using the given dependencies"""
+
+    def task_reference(val):
+        def repl(match):
+            key = match.group(1)
+            try:
+                return dependencies[key]
+            except KeyError:
+                # handle escaping '<'
+                if key == '<':
+                    return key
+                raise KeyError("task '{}' has no dependency named '{}'".format(label, key))
+
+        return TASK_REFERENCE_PATTERN.sub(repl, val)
 
-    return _recurse(task_def, 'task-reference', lambda v: TASK_REFERENCE_PATTERN.sub(repl, v))
+    def artifact_reference(val):
+        def repl(match):
+            dependency, artifact_name = match.group(1, 2)
+
+            try:
+                task_id = dependencies[dependency]
+            except KeyError:
+                raise KeyError("task '{}' has no dependency named '{}'".format(label, dependency))
+
+            assert artifact_name.startswith('public/'), \
+                "artifact-reference only supports public artifacts, not `{}`".format(artifact_name)
+            return get_artifact_url(task_id, artifact_name)
+
+        return ARTIFACT_REFERENCE_PATTERN.sub(repl, val)
+
+    return _recurse(task_def, {
+        'task-reference': task_reference,
+        'artifact-reference': artifact_reference,
+    })
--- a/taskcluster/taskgraph/util/schema.py
+++ b/taskcluster/taskgraph/util/schema.py
@@ -226,8 +226,15 @@ OptimizationSchema = voluptuous.Any(
     {'seta': None},
     # skip this task if none of the given file patterns match
     {'skip-unless-changed': [basestring]},
     # skip this task if unless the change files' SCHEDULES contains any of these components
     {'skip-unless-schedules': list(schedules.ALL_COMPONENTS)},
     # skip if SETA or skip-unless-schedules says to
     {'skip-unless-schedules-or-seta': list(schedules.ALL_COMPONENTS)},
 )
+
+# shortcut for a string where task references are allowed
+taskref_or_string = voluptuous.Any(
+    basestring,
+    {voluptuous.Required('task-reference'): basestring},
+    {voluptuous.Required('artifact-reference'): basestring},
+)
--- a/taskcluster/taskgraph/util/taskcluster.py
+++ b/taskcluster/taskgraph/util/taskcluster.py
@@ -1,51 +1,68 @@
 # -*- coding: utf-8 -*-
 
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from __future__ import absolute_import, print_function, unicode_literals
 
+import os
 import datetime
 import functools
 import yaml
 import requests
 import logging
+import taskcluster_urls as liburls
 from mozbuild.util import memoize
 from requests.packages.urllib3.util.retry import Retry
 from requests.adapters import HTTPAdapter
 from taskgraph.task import Task
 
-_PUBLIC_TC_ARTIFACT_LOCATION = \
-    'https://queue.taskcluster.net/v1/task/{task_id}/artifacts/{artifact_prefix}/{postfix}'
-
-_PRIVATE_TC_ARTIFACT_LOCATION = \
-    'http://taskcluster/queue/v1/task/{task_id}/artifacts/{artifact_prefix}/{postfix}'
-
 logger = logging.getLogger(__name__)
 
 # this is set to true for `mach taskgraph action-callback --test`
 testing = False
 
+# Default rootUrl to use if none is given in the environment; this should point
+# to the production Taskcluster deployment used for CI.
+PRODUCTION_TASKCLUSTER_ROOT_URL = 'https://taskcluster.net'
+
+
+@memoize
+def get_root_url():
+    """Get the current TASKCLUSTER_ROOT_URL.  When running in a task, this must
+    come from $TASKCLUSTER_ROOT_URL; when run on the command line, we apply a
+    defualt that points to the production deployment of Taskcluster."""
+    if 'TASKCLUSTER_ROOT_URL' not in os.environ:
+        if 'TASK_ID' in os.environ:
+            raise RuntimeError('$TASKCLUSTER_ROOT_URL must be set when running in a task')
+        else:
+            logger.debug('Using default TASKCLUSTER_ROOT_URL (Firefox CI production)')
+            return PRODUCTION_TASKCLUSTER_ROOT_URL
+    logger.debug('Running in Taskcluster instance {}{}'.format(
+        os.environ['TASKCLUSTER_ROOT_URL'],
+        ' with taskcluster-proxy' if 'TASKCLUSTER_PROXY_URL' in os.environ else ''))
+    return os.environ['TASKCLUSTER_ROOT_URL']
+
 
 @memoize
 def get_session():
     session = requests.Session()
     retry = Retry(total=5, backoff_factor=0.1,
                   status_forcelist=[500, 502, 503, 504])
     session.mount('http://', HTTPAdapter(max_retries=retry))
     session.mount('https://', HTTPAdapter(max_retries=retry))
     return session
 
 
-def _do_request(url, **kwargs):
+def _do_request(url, force_get=False, **kwargs):
     session = get_session()
-    if kwargs:
+    if kwargs and not force_get:
         response = session.post(url, **kwargs)
     else:
         response = session.get(url, stream=True)
     if response.status_code >= 400:
         # Consume content before raise_for_status, so that the connection can be
         # reused.
         response.content
     response.raise_for_status()
@@ -58,27 +75,31 @@ def _handle_artifact(path, response):
     if path.endswith('.yml'):
         return yaml.safe_load(response.text)
     response.raw.read = functools.partial(response.raw.read,
                                           decode_content=True)
     return response.raw
 
 
 def get_artifact_url(task_id, path, use_proxy=False):
-    ARTIFACT_URL = 'https://queue.taskcluster.net/v1/task/{}/artifacts/{}'
+    artifact_tmpl = liburls.api(get_root_url(), 'queue', 'v1',
+                                'task/{}/artifacts/{}')
+    data = artifact_tmpl.format(task_id, path)
     if use_proxy:
         # Until Bug 1405889 is deployed, we can't download directly
         # from the taskcluster-proxy.  Work around by using the /bewit
         # endpoint instead.
-        data = ARTIFACT_URL.format(task_id, path)
         # The bewit URL is the body of a 303 redirect, which we don't
         # want to follow (which fetches a potentially large resource).
-        response = _do_request('http://taskcluster/bewit', data=data, allow_redirects=False)
+        response = _do_request(
+            os.environ['TASKCLUSTER_PROXY_URL'] + '/bewit',
+            data=data,
+            allow_redirects=False)
         return response.text
-    return ARTIFACT_URL.format(task_id, path)
+    return data
 
 
 def get_artifact(task_id, path, use_proxy=False):
     """
     Returns the artifact with the given path for the given task id.
 
     If the path ends with ".json" or ".yml", the content is deserialized as,
     respectively, json or yaml, and the corresponding python data (usually
@@ -104,20 +125,21 @@ def get_artifact_prefix(task):
 
 
 def get_artifact_path(task, path):
     return "{}/{}".format(get_artifact_prefix(task), path)
 
 
 def get_index_url(index_path, use_proxy=False, multiple=False):
     if use_proxy:
-        INDEX_URL = 'http://taskcluster/index/v1/task{}/{}'
+        # Until bug 1460015 is finished, use the old baseUrl style of proxy URL
+        index_tmpl = os.environ['TASKCLUSTER_PROXY_URL'] + '/index/v1/task{}/{}'
     else:
-        INDEX_URL = 'https://index.taskcluster.net/v1/task{}/{}'
-    return INDEX_URL.format('s' if multiple else '', index_path)
+        index_tmpl = liburls.api(get_root_url(), 'index', 'v1', 'task{}/{}')
+    return index_tmpl.format('s' if multiple else '', index_path)
 
 
 def find_task_id(index_path, use_proxy=False):
     try:
         response = _do_request(get_index_url(index_path, use_proxy))
     except requests.exceptions.HTTPError as e:
         if e.response.status_code == 404:
             raise KeyError("index path {} not found".format(index_path))
@@ -157,20 +179,21 @@ def list_tasks(index_path, use_proxy=Fal
 
 def parse_time(timestamp):
     """Turn a "JSON timestamp" as used in TC APIs into a datetime"""
     return datetime.datetime.strptime(timestamp, '%Y-%m-%dT%H:%M:%S.%fZ')
 
 
 def get_task_url(task_id, use_proxy=False):
     if use_proxy:
-        TASK_URL = 'http://taskcluster/queue/v1/task/{}'
+        # Until bug 1460015 is finished, use the old baseUrl style of proxy URL
+        task_tmpl = os.environ['TASKCLUSTER_PROXY_URL'] + '/queue/v1/task/{}'
     else:
-        TASK_URL = 'https://queue.taskcluster.net/v1/task/{}'
-    return TASK_URL.format(task_id)
+        task_tmpl = liburls.api(get_root_url(), 'queue', 'v1', 'task/{}')
+    return task_tmpl.format(task_id)
 
 
 def get_task_definition(task_id, use_proxy=False):
     response = _do_request(get_task_url(task_id, use_proxy))
     return response.json()
 
 
 def cancel_task(task_id, use_proxy=False):
@@ -200,58 +223,62 @@ def rerun_task(task_id):
         logger.info('Would have rerun {}.'.format(task_id))
     else:
         _do_request(get_task_url(task_id, use_proxy=True) + '/rerun', json={})
 
 
 def get_current_scopes():
     """Get the current scopes.  This only makes sense in a task with the Taskcluster
     proxy enabled, where it returns the actual scopes accorded to the task."""
-    resp = _do_request('http://taskcluster/auth/v1/scopes/current')
+    # Until bug 1460015 is finished, use the old baseUrl style of proxy URL
+    resp = _do_request(os.environ['TASKCLUSTER_PROXY_URL'] + '/auth/v1/scopes/current')
     return resp.json().get("scopes", [])
 
 
 def get_purge_cache_url(provisioner_id, worker_type, use_proxy=False):
     if use_proxy:
-        TASK_URL = 'http://taskcluster/purge-cache/v1/purge-cache/{}/{}'
+        # Until bug 1460015 is finished, use the old baseUrl style of proxy URL
+        url_tmpl = os.environ['TASKCLUSTER_PROXY_URL'] + '/purge-cache/v1/purge-cache/{}/{}'
     else:
-        TASK_URL = 'https://purge-cache.taskcluster.net/v1/purge-cache/{}/{}'
-    return TASK_URL.format(provisioner_id, worker_type)
+        url_tmpl = liburls.api(get_root_url(), 'purge-cache', 'v1', 'purge-cache/{}/{}')
+    return url_tmpl.format(provisioner_id, worker_type)
 
 
 def purge_cache(provisioner_id, worker_type, cache_name, use_proxy=False):
     """Requests a cache purge from the purge-caches service."""
     if testing:
         logger.info('Would have purged {}/{}/{}.'.format(provisioner_id, worker_type, cache_name))
     else:
         logger.info('Purging {}/{}/{}.'.format(provisioner_id, worker_type, cache_name))
         purge_cache_url = get_purge_cache_url(provisioner_id, worker_type, use_proxy)
         _do_request(purge_cache_url, json={'cacheName': cache_name})
 
 
-def get_taskcluster_artifact_prefix(task, task_id, postfix='', locale=None, force_private=False):
-    if locale:
-        postfix = '{}/{}'.format(locale, postfix)
-
-    artifact_prefix = get_artifact_prefix(task)
-    if artifact_prefix == 'public/build' and not force_private:
-        tmpl = _PUBLIC_TC_ARTIFACT_LOCATION
-    else:
-        tmpl = _PRIVATE_TC_ARTIFACT_LOCATION
-
-    return tmpl.format(
-        task_id=task_id, postfix=postfix, artifact_prefix=artifact_prefix
-    )
-
-
 def send_email(address, subject, content, link, use_proxy=False):
     """Sends an email using the notify service"""
     logger.info('Sending email to {}.'.format(address))
     if use_proxy:
-        url = 'http://taskcluster/notify/v1/email'
+        # Until bug 1460015 is finished, use the old baseUrl style of proxy URL
+        url = os.environ['TASKCLUSTER_PROXY_URL'] + '/notify/v1/email'
     else:
-        url = 'https://notify.taskcluster.net/v1/email'
+        url = liburls.api(get_root_url(), 'notify', 'v1', 'email')
     _do_request(url, json={
         'address': address,
         'subject': subject,
         'content': content,
         'link': link,
     })
+
+
+def list_task_group(task_group_id):
+    """Generate the tasks in a task group"""
+    params = {}
+    while True:
+        url = liburls.api(get_root_url(), 'queue', 'v1',
+                          'task-group/{}/list'.format(task_group_id))
+        resp = _do_request(url, force_get=True, params=params).json()
+        for task in [t['status'] for t in resp['tasks']]:
+            if task['state'] in ['running', 'pending', 'unscheduled']:
+                yield task['taskId']
+        if resp.get('continuationToken'):
+            params = {'continuationToken': resp.get('continuationToken')}
+        else:
+            break
--- a/testing/web-platform/meta/infrastructure/server/__dir__.ini
+++ b/testing/web-platform/meta/infrastructure/server/__dir__.ini
@@ -1,1 +1,2 @@
 lsan-allowed: [Alloc, CreateInner, MakeUnique, PLDHashTable::ChangeTable, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::SchedulerGroup::CreateEventTargetFor, mozilla::WeakPtr, mozilla::dom::WebSocket::ConstructorCommon, mozilla::dom::WebSocket::WebSocket, mozilla::net::BaseWebSocketChannel::InitLoadInfo, mozilla::net::WebSocketChannelChild::AsyncOpen, mozilla::net::WebSocketEventService::GetOrCreate, nsSupportsWeakReference::GetWeakReference, mozilla::net::nsStandardURL::TemplatedMutator]
+leak-threshold: [default:51200, tab:51200]
--- a/third_party/python/requirements.in
+++ b/third_party/python/requirements.in
@@ -8,10 +8,11 @@ pip-tools==3.0.0
 pipenv==2018.5.18
 psutil==5.4.3
 pytest==3.6.2
 python-hglib==2.4
 redo==2.0.2
 requests==2.9.1
 six==1.10.0
 taskcluster==4.0.1
+taskcluster-urls==11.0.0
 virtualenv==15.2.0
 voluptuous==0.11.5
--- a/third_party/python/requirements.txt
+++ b/third_party/python/requirements.txt
@@ -95,16 +95,20 @@ scandir==1.9.0 \
     --hash=sha256:f5c71e29b4e2af7ccdc03a020c626ede51da471173b4a6ad1e904f2b2e04b4bd \
     # via pathlib2
 six==1.10.0 \
     --hash=sha256:0ff78c403d9bccf5a425a6d31a12aa6b47f1c21ca4dc2573a7e2f32a97335eb1 \
     --hash=sha256:105f8d68616f8248e24bf0e9372ef04d3cc10104f1980f54d57b2ce73a5ad56a
 slugid==1.0.7 \
     --hash=sha256:6dab3c7eef0bb423fb54cb7752e0f466ddd0ee495b78b763be60e8a27f69e779 \
     # via taskcluster
+taskcluster-urls==11.0.0 \
+    --hash=sha256:18dcaa9c2412d34ff6c78faca33f0dd8f2384e3f00a98d5832c62d6d664741f0 \
+    --hash=sha256:2aceab7cf5b1948bc197f2e5e50c371aa48181ccd490b8bada00f1e3baf0c5cc \
+    --hash=sha256:74bd2110b5daaebcec5e1d287bf137b61cb8cf6b2d8f5f2b74183e32bc4e7c87
 taskcluster==4.0.1 \
     --hash=sha256:27256511044346ac71a495d3c636f2add95c102b9b09f90d6fb1ea3e9949d311 \
     --hash=sha256:99dd90bc1c566968868c8b07ede32f8e031cbccd52c7195a61e802679d461447 \
     --hash=sha256:d0360063c1a3fcaaa514bb31c03954ba573d2b671df40a2ecfdfd9339cc8e93e
 virtualenv-clone==0.3.0 \
     --hash=sha256:4507071d81013fd03ea9930ec26bc8648b997927a11fa80e8ee81198b57e0ac7 \
     --hash=sha256:b5cfe535d14dc68dfc1d1bb4ac1209ea28235b91156e2bba8e250d291c3fb4f8 \
     # via pipenv
new file mode 100644
--- /dev/null
+++ b/third_party/python/taskcluster-urls/LICENSE
@@ -0,0 +1,373 @@
+Mozilla Public License Version 2.0
+==================================
+
+1. Definitions
+--------------
+
+1.1. "Contributor"
+    means each individual or legal entity that creates, contributes to
+    the creation of, or owns Covered Software.
+
+1.2. "Contributor Version"
+    means the combination of the Contributions of others (if any) used
+    by a Contributor and that particular Contributor's Contribution.
+
+1.3. "Contribution"
+    means Covered Software of a particular Contributor.
+
+1.4. "Covered Software"
+    means Source Code Form to which the initial Contributor has attached
+    the notice in Exhibit A, the Executable Form of such Source Code
+    Form, and Modifications of such Source Code Form, in each case
+    including portions thereof.
+
+1.5. "Incompatible With Secondary Licenses"
+    means
+
+    (a) that the initial Contributor has attached the notice described
+        in Exhibit B to the Covered Software; or
+
+    (b) that the Covered Software was made available under the terms of
+        version 1.1 or earlier of the License, but not also under the
+        terms of a Secondary License.
+
+1.6. "Executable Form"
+    means any form of the work other than Source Code Form.
+
+1.7. "Larger Work"
+    means a work that combines Covered Software with other material, in
+    a separate file or files, that is not Covered Software.
+
+1.8. "License"
+    means this document.
+
+1.9. "Licensable"
+    means having the right to grant, to the maximum extent possible,
+    whether at the time of the initial grant or subsequently, any and
+    all of the rights conveyed by this License.
+
+1.10. "Modifications"
+    means any of the following:
+
+    (a) any file in Source Code Form that results from an addition to,
+        deletion from, or modification of the contents of Covered
+        Software; or
+
+    (b) any new file in Source Code Form that contains any Covered
+        Software.
+
+1.11. "Patent Claims" of a Contributor
+    means any patent claim(s), including without limitation, method,
+    process, and apparatus claims, in any patent Licensable by such
+    Contributor that would be infringed, but for the grant of the
+    License, by the making, using, selling, offering for sale, having
+    made, import, or transfer of either its Contributions or its
+    Contributor Version.
+
+1.12. "Secondary License"
+    means either the GNU General Public License, Version 2.0, the GNU
+    Lesser General Public License, Version 2.1, the GNU Affero General
+    Public License, Version 3.0, or any later versions of those
+    licenses.
+
+1.13. "Source Code Form"
+    means the form of the work preferred for making modifications.
+
+1.14. "You" (or "Your")
+    means an individual or a legal entity exercising rights under this
+    License. For legal entities, "You" includes any entity that
+    controls, is controlled by, or is under common control with You. For
+    purposes of this definition, "control" means (a) the power, direct
+    or indirect, to cause the direction or management of such entity,
+    whether by contract or otherwise, or (b) ownership of more than
+    fifty percent (50%) of the outstanding shares or beneficial
+    ownership of such entity.
+
+2. License Grants and Conditions
+--------------------------------
+
+2.1. Grants
+
+Each Contributor hereby grants You a world-wide, royalty-free,
+non-exclusive license:
+
+(a) under intellectual property rights (other than patent or trademark)
+    Licensable by such Contributor to use, reproduce, make available,
+    modify, display, perform, distribute, and otherwise exploit its
+    Contributions, either on an unmodified basis, with Modifications, or
+    as part of a Larger Work; and
+
+(b) under Patent Claims of such Contributor to make, use, sell, offer
+    for sale, have made, import, and otherwise transfer either its
+    Contributions or its Contributor Version.
+
+2.2. Effective Date
+
+The licenses granted in Section 2.1 with respect to any Contribution
+become effective for each Contribution on the date the Contributor first
+distributes such Contribution.
+
+2.3. Limitations on Grant Scope
+
+The licenses granted in this Section 2 are the only rights granted under
+this License. No additional rights or licenses will be implied from the
+distribution or licensing of Covered Software under this License.
+Notwithstanding Section 2.1(b) above, no patent license is granted by a
+Contributor:
+
+(a) for any code that a Contributor has removed from Covered Software;
+    or
+
+(b) for infringements caused by: (i) Your and any other third party's
+    modifications of Covered Software, or (ii) the combination of its
+    Contributions with other software (except as part of its Contributor
+    Version); or
+
+(c) under Patent Claims infringed by Covered Software in the absence of
+    its Contributions.
+
+This License does not grant any rights in the trademarks, service marks,
+or logos of any Contributor (except as may be necessary to comply with
+the notice requirements in Section 3.4).
+
+2.4. Subsequent Licenses
+
+No Contributor makes additional grants as a result of Your choice to
+distribute the Covered Software under a subsequent version of this
+License (see Section 10.2) or under the terms of a Secondary License (if
+permitted under the terms of Section 3.3).
+
+2.5. Representation
+
+Each Contributor represents that the Contributor believes its
+Contributions are its original creation(s) or it has sufficient rights
+to grant the rights to its Contributions conveyed by this License.
+
+2.6. Fair Use
+
+This License is not intended to limit any rights You have under
+applicable copyright doctrines of fair use, fair dealing, or other
+equivalents.
+
+2.7. Conditions
+
+Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
+in Section 2.1.
+
+3. Responsibilities
+-------------------
+
+3.1. Distribution of Source Form
+
+All distribution of Covered Software in Source Code Form, including any
+Modifications that You create or to which You contribute, must be under
+the terms of this License. You must inform recipients that the Source
+Code Form of the Covered Software is governed by the terms of this
+License, and how they can obtain a copy of this License. You may not
+attempt to alter or restrict the recipients' rights in the Source Code
+Form.
+
+3.2. Distribution of Executable Form
+
+If You distribute Covered Software in Executable Form then:
+
+(a) such Covered Software must also be made available in Source Code
+    Form, as described in Section 3.1, and You must inform recipients of
+    the Executable Form how they can obtain a copy of such Source Code
+    Form by reasonable means in a timely manner, at a charge no more
+    than the cost of distribution to the recipient; and
+
+(b) You may distribute such Executable Form under the terms of this
+    License, or sublicense it under different terms, provided that the
+    license for the Executable Form does not attempt to limit or alter
+    the recipients' rights in the Source Code Form under this License.
+
+3.3. Distribution of a Larger Work
+
+You may create and distribute a Larger Work under terms of Your choice,
+provided that You also comply with the requirements of this License for
+the Covered Software. If the Larger Work is a combination of Covered
+Software with a work governed by one or more Secondary Licenses, and the
+Covered Software is not Incompatible With Secondary Licenses, this
+License permits You to additionally distribute such Covered Software
+under the terms of such Secondary License(s), so that the recipient of
+the Larger Work may, at their option, further distribute the Covered
+Software under the terms of either this License or such Secondary
+License(s).
+
+3.4. Notices
+
+You may not remove or alter the substance of any license notices
+(including copyright notices, patent notices, disclaimers of warranty,
+or limitations of liability) contained within the Source Code Form of
+the Covered Software, except that You may alter any license notices to
+the extent required to remedy known factual inaccuracies.
+
+3.5. Application of Additional Terms
+
+You may choose to offer, and to charge a fee for, warranty, support,
+indemnity or liability obligations to one or more recipients of Covered
+Software. However, You may do so only on Your own behalf, and not on
+behalf of any Contributor. You must make it absolutely clear that any
+such warranty, support, indemnity, or liability obligation is offered by
+You alone, and You hereby agree to indemnify every Contributor for any
+liability incurred by such Contributor as a result of warranty, support,
+indemnity or liability terms You offer. You may include additional
+disclaimers of warranty and limitations of liability specific to any
+jurisdiction.
+
+4. Inability to Comply Due to Statute or Regulation
+---------------------------------------------------
+
+If it is impossible for You to comply with any of the terms of this
+License with respect to some or all of the Covered Software due to
+statute, judicial order, or regulation then You must: (a) comply with
+the terms of this License to the maximum extent possible; and (b)
+describe the limitations and the code they affect. Such description must
+be placed in a text file included with all distributions of the Covered
+Software under this License. Except to the extent prohibited by statute
+or regulation, such description must be sufficiently detailed for a
+recipient of ordinary skill to be able to understand it.
+
+5. Termination
+--------------
+
+5.1. The rights granted under this License will terminate automatically
+if You fail to comply with any of its terms. However, if You become
+compliant, then the rights granted under this License from a particular
+Contributor are reinstated (a) provisionally, unless and until such
+Contributor explicitly and finally terminates Your grants, and (b) on an
+ongoing basis, if such Contributor fails to notify You of the
+non-compliance by some reasonable means prior to 60 days after You have
+come back into compliance. Moreover, Your grants from a particular
+Contributor are reinstated on an ongoing basis if such Contributor
+notifies You of the non-compliance by some reasonable means, this is the
+first time You have received notice of non-compliance with this License
+from such Contributor, and You become compliant prior to 30 days after
+Your receipt of the notice.
+
+5.2. If You initiate litigation against any entity by asserting a patent
+infringement claim (excluding declaratory judgment actions,
+counter-claims, and cross-claims) alleging that a Contributor Version
+directly or indirectly infringes any patent, then the rights granted to
+You by any and all Contributors for the Covered Software under Section
+2.1 of this License shall terminate.
+
+5.3. In the event of termination under Sections 5.1 or 5.2 above, all
+end user license agreements (excluding distributors and resellers) which
+have been validly granted by You or Your distributors under this License
+prior to termination shall survive termination.
+
+************************************************************************
+*                                                                      *
+*  6. Disclaimer of Warranty                                           *
+*  -------------------------                                           *
+*                                                                      *
+*  Covered Software is provided under this License on an "as is"       *
+*  basis, without warranty of any kind, either expressed, implied, or  *
+*  statutory, including, without limitation, warranties that the       *
+*  Covered Software is free of defects, merchantable, fit for a        *
+*  particular purpose or non-infringing. The entire risk as to the     *
+*  quality and performance of the Covered Software is with You.        *
+*  Should any Covered Software prove defective in any respect, You     *
+*  (not any Contributor) assume the cost of any necessary servicing,   *
+*  repair, or correction. This disclaimer of warranty constitutes an   *
+*  essential part of this License. No use of any Covered Software is   *
+*  authorized under this License except under this disclaimer.         *
+*                                                                      *
+************************************************************************
+
+************************************************************************
+*                                                                      *
+*  7. Limitation of Liability                                          *
+*  --------------------------                                          *
+*                                                                      *
+*  Under no circumstances and under no legal theory, whether tort      *
+*  (including negligence), contract, or otherwise, shall any           *
+*  Contributor, or anyone who distributes Covered Software as          *
+*  permitted above, be liable to You for any direct, indirect,         *
+*  special, incidental, or consequential damages of any character      *
+*  including, without limitation, damages for lost profits, loss of    *
+*  goodwill, work stoppage, computer failure or malfunction, or any    *
+*  and all other commercial damages or losses, even if such party      *
+*  shall have been informed of the possibility of such damages. This   *
+*  limitation of liability shall not apply to liability for death or   *
+*  personal injury resulting from such party's negligence to the       *
+*  extent applicable law prohibits such limitation. Some               *
+*  jurisdictions do not allow the exclusion or limitation of           *
+*  incidental or consequential damages, so this exclusion and          *
+*  limitation may not apply to You.                                    *
+*                                                                      *
+************************************************************************
+
+8. Litigation
+-------------
+
+Any litigation relating to this License may be brought only in the
+courts of a jurisdiction where the defendant maintains its principal
+place of business and such litigation shall be governed by laws of that
+jurisdiction, without reference to its conflict-of-law provisions.
+Nothing in this Section shall prevent a party's ability to bring
+cross-claims or counter-claims.
+
+9. Miscellaneous
+----------------
+
+This License represents the complete agreement concerning the subject
+matter hereof. If any provision of this License is held to be
+unenforceable, such provision shall be reformed only to the extent
+necessary to make it enforceable. Any law or regulation which provides
+that the language of a contract shall be construed against the drafter
+shall not be used to construe this License against a Contributor.
+
+10. Versions of the License
+---------------------------
+
+10.1. New Versions
+
+Mozilla Foundation is the license steward. Except as provided in Section
+10.3, no one other than the license steward has the right to modify or
+publish new versions of this License. Each version will be given a
+distinguishing version number.
+
+10.2. Effect of New Versions
+
+You may distribute the Covered Software under the terms of the version
+of the License under which You originally received the Covered Software,
+or under the terms of any subsequent version published by the license
+steward.
+
+10.3. Modified Versions
+
+If you create software not governed by this License, and you want to
+create a new license for such software, you may create and use a
+modified version of this License if you rename the license and remove
+any references to the name of the license steward (except to note that
+such modified license differs from this License).
+
+10.4. Distributing Source Code Form that is Incompatible With Secondary
+Licenses
+
+If You choose to distribute Source Code Form that is Incompatible With
+Secondary Licenses under the terms of this version of the License, the
+notice described in Exhibit B of this License must be attached.
+
+Exhibit A - Source Code Form License Notice
+-------------------------------------------
+
+  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/.
+
+If it is not possible or desirable to put the notice in a particular
+file, then You may include the notice in a location (such as a LICENSE
+file in a relevant directory) where a recipient would be likely to look
+for such a notice.
+
+You may add additional accurate notices of copyright ownership.
+
+Exhibit B - "Incompatible With Secondary Licenses" Notice
+---------------------------------------------------------
+
+  This Source Code Form is "Incompatible With Secondary Licenses", as
+  defined by the Mozilla Public License, v. 2.0.
new file mode 100644
--- /dev/null
+++ b/third_party/python/taskcluster-urls/MANIFEST.in
@@ -0,0 +1,4 @@
+include LICENSE
+global-exclude *.py[co]
+include specification.yml
+include package.json
new file mode 100644
--- /dev/null
+++ b/third_party/python/taskcluster-urls/PKG-INFO
@@ -0,0 +1,253 @@
+Metadata-Version: 2.1
+Name: taskcluster-urls
+Version: 11.0.0
+Summary: Standardized url generator for taskcluster resources.
+Home-page: https://github.com/taskcluster/taskcluster-lib-urls
+Author: Brian Stack
+Author-email: bstack@mozilla.com
+License: MPL2
+Description: # Taskcluster URL Building Library
+        
+        [![License](https://img.shields.io/badge/license-MPL%202.0-orange.svg)](http://mozilla.org/MPL/2.0)
+        
+        A simple library to generate URLs for various Taskcluster resources across our various deployment methods.
+        
+        This serves as both a simple shim for projects that use JavaScript but also is the reference implementation for
+        how we define these paths.
+        
+        URLs are defined in the 'Taskcluster URL Format' document.
+        
+        Changelog
+        ---------
+        View the changelog on the [releases page](https://github.com/taskcluster/taskcluster-lib-urls/releases).
+        
+        Requirements
+        ------------
+        
+        This is tested on and should run on any of Node.js `{8, 10}`.
+        
+        JS Usage
+        --------
+        [![Node.js Build Status](https://travis-ci.org/taskcluster/taskcluster-lib-urls.svg?branch=master)](https://travis-ci.org/taskcluster/taskcluster-lib-urls)
+        [![npm](https://img.shields.io/npm/v/taskcluster-lib-urls.svg?maxAge=2592000)](https://www.npmjs.com/package/taskcluster-lib-urls)
+        
+        This package exports several methods for generating URLs conditionally based on
+        a root URL, as well as a few helper classes for generating URLs for a pre-determined
+        root URL:
+        
+        * `api(rootUrl, service, version, path)` -> `String`
+        * `apiReference(rootUrl, service, version)` -> `String`
+        * `docs(rootUrl, path)` -> `String`
+        * `exchangeReference(rootUrl, service, version)` -> `String`
+        * `schema(rootUrl, service, schema)` -> `String`
+        * `ui(rootUrl, path)` -> `String`
+        * `servicesManifest(rootUrl)` -> `String`
+        * `testRootUrl()` -> `String`
+        * `withRootUrl(rootUrl)` -> `Class` instance for above methods
+        
+        When the `rootUrl` is `https://taskcluster.net`, the generated URLs will be to the Heroku cluster. Otherwise they will follow the
+        [spec defined in this project](https://github.com/taskcluster/taskcluster-lib-urls/tree/master/docs/urls-spec.md).
+        
+        `testRootUrl()` is used to share a common fake `rootUrl` between various Taskcluster mocks in testing.
+        The URL does not resolve.
+        
+        ```js
+        // Specifying root URL every time:
+        const libUrls = require('taskcluster-lib-urls');
+        
+        libUrls.api(rootUrl, 'auth', 'v1', 'foo/bar');
+        libUrls.schema(rootUrl, 'auth', 'v1/foo.yml'); // Note that schema names have versions in them
+        libUrls.apiReference(rootUrl, 'auth', 'v1');
+        libUrls.exchangeReference(rootUrl, 'auth', 'v1');
+        libUrls.ui(rootUrl, 'foo/bar');
+        libUrls.servicesManifest(rootUrl);
+        libUrls.docs(rootUrl, 'foo/bar');
+        ```
+        
+        ```js
+        // Specifying root URL in advance:
+        const libUrls = require('taskcluster-lib-urls');
+        
+        const urls = libUrls.withRoot(rootUrl);
+        
+        urls.api('auth', 'v1', 'foo/bar');
+        urls.schema('auth', 'v1/foo.yml');
+        urls.apiReference('auth', 'v1');
+        urls.exchangeReference('auth', 'v1');
+        urls.ui('foo/bar');
+        urls.servicesManifest();
+        urls.docs('foo/bar');
+        ```
+        
+        If you would like, you can set this up via [taskcluster-lib-loader](https://github.com/taskcluster/taskcluster-lib-loader) as follows:
+        
+        ```js
+        {
+          libUrlss: {
+            require: ['cfg'],
+            setup: ({cfg}) => withRootUrl(cfg.rootURl),
+          },
+        }
+        ```
+        
+        Test with:
+        
+        ```
+        yarn install
+        yarn test
+        ```
+        
+        
+        Go Usage
+        --------
+        
+        [![GoDoc](https://godoc.org/github.com/taskcluster/taskcluster-lib-urls?status.svg)](https://godoc.org/github.com/taskcluster/taskcluster-lib-urls)
+        
+        The go package exports the following functions:
+        
+        ```go
+        func API(rootURL string, service string, version string, path string) string
+        func APIReference(rootURL string, service string, version string) string
+        func Docs(rootURL string, path string) string
+        func ExchangeReference(rootURL string, service string, version string) string
+        func Schema(rootURL string, service string, name string) string
+        func UI(rootURL string, path string) string
+        func ServicesManifest(rootURL string) string
+        ```
+        
+        Install with:
+        
+        ```
+        go install ./..
+        ```
+        
+        Test with:
+        
+        ```
+        go test -v ./...
+        ```
+        
+        Python Usage
+        ------------
+        
+        You can install the python client with `pip install taskcluster-urls`;
+        
+        ```python
+        import taskcluster_urls
+        
+        taskcluster_urls.api(root_url, 'auth', 'v1', 'foo/bar')
+        taskcluster_urls.schema(root_url, 'auth', 'v1/foo.yml') # Note that schema names have versions in them
+        taskcluster_urls.api_reference(root_url, 'auth', 'v1')
+        taskcluster_urls.exchange_reference(root_url, 'auth', 'v1')
+        taskcluster_urls.ui(root_url, 'foo/bar')
+        taskcluster_urls.servicesManifest(root_url)
+        taskcluster_urls.docs(root_url, 'foo/bar')
+        
+        And for testing,
+        ```python
+        taskcluster_urls.test_root_url()
+        ```
+        
+        Test with:
+        
+        ```
+        tox
+        ```
+        
+        Java Usage
+        ----------
+        
+        [![JavaDoc](https://img.shields.io/badge/javadoc-reference-blue.svg)](http://taskcluster.github.io/taskcluster-lib-urls/apidocs)
+        
+        In order to use this library from your maven project, simply include it as a project dependency:
+        
+        ```
+        <project>
+          ...
+          <dependencies>
+            ...
+            <dependency>
+              <groupId>org.mozilla.taskcluster</groupId>
+              <artifactId>taskcluster-lib-urls</artifactId>
+              <version>1.0.0</version>
+            </dependency>
+          </dependencies>
+        </project>
+        ```
+        
+        The taskcluster-lib-urls artifacts are now available from the [maven central repository](http://central.sonatype.org/):
+        
+        * [Search Results](http://search.maven.org/#search|gav|1|g%3A%22org.mozilla.taskcluster%22%20AND%20a%3A%22taskcluster-lib-urls%22)
+        * [Directory Listing](https://repo1.maven.org/maven2/org/mozilla/taskcluster/taskcluster-lib-urls/)
+        
+        To use the library, do as follows:
+        
+        ```java
+        import org.mozilla.taskcluster.urls.*;
+        
+        ...
+        
+            URLProvider urlProvider = URLs.provider("https://mytaskcluster.acme.org");
+        
+            String fooBarAPI        = urlProvider.api("auth", "v1", "foo/bar");
+            String fooSchema        = urlProvider.schema("auth", "v1/foo.yml"); // Note that schema names have versions in them
+            String authAPIRef       = urlProvider.apiReference("auth", "v1");
+            String authExchangesRef = urlProvider.exchangeReference("auth", "v1");
+            String uiFooBar         = urlProvider.ui("foo/bar");
+            String servicesManifest = urlProvider.servicesManifest();
+            String docsFooBar       = urlProvider.docs("foo/bar");
+        
+        ...
+        ```
+        
+        Install with:
+        
+        ```
+        mvn install
+        ```
+        
+        Test with:
+        
+        ```
+        mvn test
+        ```
+        
+        
+        Releasing
+        ---------
+        
+        New releases should be tested on Travis and Taskcluster to allow for all supported versions of various languages to be tested. Once satisfied that it works, new versions should be created with
+        `npm version` rather than by manually editing `package.json` and tags should be pushed to Github. 
+        
+        Make the Node release first, as Python's version depends on its `package.json`.  This follows the typical tag-and-push-to-publish approach:
+        
+        ```sh
+        $ npm version minor  # or patch, or major
+        $ git push upstream
+        ```
+        
+        Once that's done, build the Python sdists (only possible by the [maintainers on pypi](https://pypi.org/project/taskcluster-urls/#files)):
+        
+        ```sh
+        rm -rf dist/*
+        python setup.py sdist bdist_wheel
+        python3 setup.py bdist_wheel
+        pip install twine
+        twine upload dist/*
+        ```
+        
+        Make sure to update [the changelog](https://github.com/taskcluster/taskcluster-lib-urls/releases)!
+        
+        License
+        -------
+        
+        [Mozilla Public License Version 2.0](https://github.com/taskcluster/taskcluster-lib-urls/blob/master/LICENSE)
+        
+Platform: UNKNOWN
+Classifier: License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Description-Content-Type: text/markdown
new file mode 100644
--- /dev/null
+++ b/third_party/python/taskcluster-urls/README.md
@@ -0,0 +1,236 @@
+# Taskcluster URL Building Library
+
+[![License](https://img.shields.io/badge/license-MPL%202.0-orange.svg)](http://mozilla.org/MPL/2.0)
+
+A simple library to generate URLs for various Taskcluster resources across our various deployment methods.
+
+This serves as both a simple shim for projects that use JavaScript but also is the reference implementation for
+how we define these paths.
+
+URLs are defined in the 'Taskcluster URL Format' document.
+
+Changelog
+---------
+View the changelog on the [releases page](https://github.com/taskcluster/taskcluster-lib-urls/releases).
+
+Requirements
+------------
+
+This is tested on and should run on any of Node.js `{8, 10}`.
+
+JS Usage
+--------
+[![Node.js Build Status](https://travis-ci.org/taskcluster/taskcluster-lib-urls.svg?branch=master)](https://travis-ci.org/taskcluster/taskcluster-lib-urls)
+[![npm](https://img.shields.io/npm/v/taskcluster-lib-urls.svg?maxAge=2592000)](https://www.npmjs.com/package/taskcluster-lib-urls)
+
+This package exports several methods for generating URLs conditionally based on
+a root URL, as well as a few helper classes for generating URLs for a pre-determined
+root URL:
+
+* `api(rootUrl, service, version, path)` -> `String`
+* `apiReference(rootUrl, service, version)` -> `String`
+* `docs(rootUrl, path)` -> `String`
+* `exchangeReference(rootUrl, service, version)` -> `String`
+* `schema(rootUrl, service, schema)` -> `String`
+* `ui(rootUrl, path)` -> `String`
+* `servicesManifest(rootUrl)` -> `String`
+* `testRootUrl()` -> `String`
+* `withRootUrl(rootUrl)` -> `Class` instance for above methods
+
+When the `rootUrl` is `https://taskcluster.net`, the generated URLs will be to the Heroku cluster. Otherwise they will follow the
+[spec defined in this project](https://github.com/taskcluster/taskcluster-lib-urls/tree/master/docs/urls-spec.md).
+
+`testRootUrl()` is used to share a common fake `rootUrl` between various Taskcluster mocks in testing.
+The URL does not resolve.
+
+```js
+// Specifying root URL every time:
+const libUrls = require('taskcluster-lib-urls');
+
+libUrls.api(rootUrl, 'auth', 'v1', 'foo/bar');
+libUrls.schema(rootUrl, 'auth', 'v1/foo.yml'); // Note that schema names have versions in them
+libUrls.apiReference(rootUrl, 'auth', 'v1');
+libUrls.exchangeReference(rootUrl, 'auth', 'v1');
+libUrls.ui(rootUrl, 'foo/bar');
+libUrls.servicesManifest(rootUrl);
+libUrls.docs(rootUrl, 'foo/bar');
+```
+
+```js
+// Specifying root URL in advance:
+const libUrls = require('taskcluster-lib-urls');
+
+const urls = libUrls.withRoot(rootUrl);
+
+urls.api('auth', 'v1', 'foo/bar');
+urls.schema('auth', 'v1/foo.yml');
+urls.apiReference('auth', 'v1');
+urls.exchangeReference('auth', 'v1');
+urls.ui('foo/bar');
+urls.servicesManifest();
+urls.docs('foo/bar');
+```
+
+If you would like, you can set this up via [taskcluster-lib-loader](https://github.com/taskcluster/taskcluster-lib-loader) as follows:
+
+```js
+{
+  libUrlss: {
+    require: ['cfg'],
+    setup: ({cfg}) => withRootUrl(cfg.rootURl),
+  },
+}
+```
+
+Test with:
+
+```
+yarn install
+yarn test
+```
+
+
+Go Usage
+--------
+
+[![GoDoc](https://godoc.org/github.com/taskcluster/taskcluster-lib-urls?status.svg)](https://godoc.org/github.com/taskcluster/taskcluster-lib-urls)
+
+The go package exports the following functions:
+
+```go
+func API(rootURL string, service string, version string, path string) string
+func APIReference(rootURL string, service string, version string) string
+func Docs(rootURL string, path string) string
+func ExchangeReference(rootURL string, service string, version string) string
+func Schema(rootURL string, service string, name string) string
+func UI(rootURL string, path string) string
+func ServicesManifest(rootURL string) string
+```
+
+Install with:
+
+```
+go install ./..
+```
+
+Test with:
+
+```
+go test -v ./...
+```
+
+Python Usage
+------------
+
+You can install the python client with `pip install taskcluster-urls`;
+
+```python
+import taskcluster_urls
+
+taskcluster_urls.api(root_url, 'auth', 'v1', 'foo/bar')
+taskcluster_urls.schema(root_url, 'auth', 'v1/foo.yml') # Note that schema names have versions in them
+taskcluster_urls.api_reference(root_url, 'auth', 'v1')
+taskcluster_urls.exchange_reference(root_url, 'auth', 'v1')
+taskcluster_urls.ui(root_url, 'foo/bar')
+taskcluster_urls.servicesManifest(root_url)
+taskcluster_urls.docs(root_url, 'foo/bar')
+
+And for testing,
+```python
+taskcluster_urls.test_root_url()
+```
+
+Test with:
+
+```
+tox
+```
+
+Java Usage
+----------
+
+[![JavaDoc](https://img.shields.io/badge/javadoc-reference-blue.svg)](http://taskcluster.github.io/taskcluster-lib-urls/apidocs)
+
+In order to use this library from your maven project, simply include it as a project dependency:
+
+```
+<project>
+  ...
+  <dependencies>
+    ...
+    <dependency>
+      <groupId>org.mozilla.taskcluster</groupId>
+      <artifactId>taskcluster-lib-urls</artifactId>
+      <version>1.0.0</version>
+    </dependency>
+  </dependencies>
+</project>
+```
+
+The taskcluster-lib-urls artifacts are now available from the [maven central repository](http://central.sonatype.org/):
+
+* [Search Results](http://search.maven.org/#search|gav|1|g%3A%22org.mozilla.taskcluster%22%20AND%20a%3A%22taskcluster-lib-urls%22)
+* [Directory Listing](https://repo1.maven.org/maven2/org/mozilla/taskcluster/taskcluster-lib-urls/)
+
+To use the library, do as follows:
+
+```java
+import org.mozilla.taskcluster.urls.*;
+
+...
+
+    URLProvider urlProvider = URLs.provider("https://mytaskcluster.acme.org");
+
+    String fooBarAPI        = urlProvider.api("auth", "v1", "foo/bar");
+    String fooSchema        = urlProvider.schema("auth", "v1/foo.yml"); // Note that schema names have versions in them
+    String authAPIRef       = urlProvider.apiReference("auth", "v1");
+    String authExchangesRef = urlProvider.exchangeReference("auth", "v1");
+    String uiFooBar         = urlProvider.ui("foo/bar");
+    String servicesManifest = urlProvider.servicesManifest();
+    String docsFooBar       = urlProvider.docs("foo/bar");
+
+...
+```
+
+Install with:
+
+```
+mvn install
+```
+
+Test with:
+
+```
+mvn test
+```
+
+
+Releasing
+---------
+
+New releases should be tested on Travis and Taskcluster to allow for all supported versions of various languages to be tested. Once satisfied that it works, new versions should be created with
+`npm version` rather than by manually editing `package.json` and tags should be pushed to Github. 
+
+Make the Node release first, as Python's version depends on its `package.json`.  This follows the typical tag-and-push-to-publish approach:
+
+```sh
+$ npm version minor  # or patch, or major
+$ git push upstream
+```
+
+Once that's done, build the Python sdists (only possible by the [maintainers on pypi](https://pypi.org/project/taskcluster-urls/#files)):
+
+```sh
+rm -rf dist/*
+python setup.py sdist bdist_wheel
+python3 setup.py bdist_wheel
+pip install twine
+twine upload dist/*
+```
+
+Make sure to update [the changelog](https://github.com/taskcluster/taskcluster-lib-urls/releases)!
+
+License
+-------
+
+[Mozilla Public License Version 2.0](https://github.com/taskcluster/taskcluster-lib-urls/blob/master/LICENSE)
new file mode 100644
--- /dev/null
+++ b/third_party/python/taskcluster-urls/package.json
@@ -0,0 +1,25 @@
+{
+  "name": "taskcluster-lib-urls",
+  "version": "11.0.0",
+  "author": "Brian Stack <bstack@mozilla.com>",
+  "description": "Build urls for taskcluster resources.",
+  "license": "MPL-2.0",
+  "scripts": {
+    "lint": "eslint src/*.js test/*.js",
+    "pretest": "yarn lint",
+    "test": "mocha test/*_test.js"
+  },
+  "files": [
+    "src"
+  ],
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/taskcluster/taskcluster-lib-urls.git"
+  },
+  "main": "./src/index.js",
+  "devDependencies": {
+    "eslint-config-taskcluster": "^3.1.0",
+    "js-yaml": "^3.11.0",
+    "mocha": "^5.1.1"
+  }
+}
new file mode 100644
--- /dev/null
+++ b/third_party/python/taskcluster-urls/setup.cfg
@@ -0,0 +1,7 @@
+[tools:pytest]
+flake8-max-line-length = 120
+
+[egg_info]
+tag_build = 
+tag_date = 0
+
new file mode 100644
--- /dev/null
+++ b/third_party/python/taskcluster-urls/setup.py
@@ -0,0 +1,28 @@
+import json
+import os
+from setuptools import setup
+
+package_json = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'package.json')
+with open(package_json) as f:
+    version = json.load(f)['version']
+
+setup(
+    name='taskcluster-urls',
+    description='Standardized url generator for taskcluster resources.',
+    long_description=open(os.path.join(os.path.dirname(__file__), 'README.md')).read(),
+    long_description_content_type='text/markdown',
+    url='https://github.com/taskcluster/taskcluster-lib-urls',
+    version=version,
+    packages=['taskcluster_urls'],
+    author='Brian Stack',
+    author_email='bstack@mozilla.com',
+    license='MPL2',
+    classifiers=[
+        'License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)',
+        'Programming Language :: Python :: 2',
+        'Programming Language :: Python :: 2.7',
+        'Programming Language :: Python :: 3',
+        'Programming Language :: Python :: 3.5',
+        'Programming Language :: Python :: 3.6',
+    ],
+)
new file mode 100644
--- /dev/null
+++ b/third_party/python/taskcluster-urls/taskcluster_urls/__init__.py
@@ -0,0 +1,66 @@
+OLD_ROOT_URL = 'https://taskcluster.net'
+
+def api(root_url, service, version, path):
+    """Generate URL for path in a Taskcluster service."""
+    root_url = root_url.rstrip('/')
+    path = path.lstrip('/')
+    if root_url == OLD_ROOT_URL:
+        return 'https://{}.taskcluster.net/{}/{}'.format(service, version, path)
+    else:
+        return '{}/api/{}/{}/{}'.format(root_url, service, version, path)
+
+def api_reference(root_url, service, version):
+    """Generate URL for a Taskcluster api reference."""
+    root_url = root_url.rstrip('/')
+    if root_url == OLD_ROOT_URL:
+        return 'https://references.taskcluster.net/{}/{}/api.json'.format(service, version)
+    else:
+        return '{}/references/{}/{}/api.json'.format(root_url, service, version)
+
+def docs(root_url, path):
+    """Generate URL for path in the Taskcluster docs."""
+    root_url = root_url.rstrip('/')
+    path = path.lstrip('/')
+    if root_url == OLD_ROOT_URL:
+        return 'https://docs.taskcluster.net/{}'.format(path)
+    else:
+        return '{}/docs/{}'.format(root_url, path)
+
+def exchange_reference(root_url, service, version):
+    """Generate URL for a Taskcluster exchange reference."""
+    root_url = root_url.rstrip('/')
+    if root_url == OLD_ROOT_URL:
+        return 'https://references.taskcluster.net/{}/{}/exchanges.json'.format(service, version)
+    else:
+        return '{}/references/{}/{}/exchanges.json'.format(root_url, service, version)
+
+def schema(root_url, service, name):
+    """Generate URL for a schema in a Taskcluster service."""
+    root_url = root_url.rstrip('/')
+    name = name.lstrip('/')
+    if root_url == OLD_ROOT_URL:
+        return 'https://schemas.taskcluster.net/{}/{}'.format(service, name)
+    else:
+        return '{}/schemas/{}/{}'.format(root_url, service, name)
+
+def ui(root_url, path):
+    """Generate URL for a path in the Taskcluster ui."""
+    root_url = root_url.rstrip('/')
+    path = path.lstrip('/')
+    if root_url == OLD_ROOT_URL:
+        return 'https://tools.taskcluster.net/{}'.format(path)
+    else:
+        return '{}/{}'.format(root_url, path)
+
+def services_manifest(root_url):
+    """Returns a URL for the service manifest of a taskcluster deployment."""
+    root_url = root_url.rstrip('/')
+    if root_url == OLD_ROOT_URL:
+        return 'https://references.taskcluster.net/manifest.json'
+    else:
+        return '{}/references/manifest.json'.format(root_url)
+
+def test_root_url():
+    """Returns a standardized "testing" rootUrl that does not resolve but
+    is easily recognizable in test failures."""
+    return 'https://tc-tests.example.com'