Bug 1304647 - Measure the number of total URIs visited in a "session fragment", without any filtering. r=gijs, data-review=rweiss, a=gchang
authorAlessio Placitelli <alessio.placitelli@gmail.com>
Tue, 18 Oct 2016 01:58:00 +0200
changeset 356487 c445246757267ded8f1d71bfd4241b663193d3b2
parent 356486 d19d0c48a96f79cd7244a106a31df13ef4490a15
child 356488 078b673dcfc753e9a6ca3abbab97ed80f7456388
push id6570
push userraliiev@mozilla.com
push dateMon, 14 Nov 2016 12:26:13 +0000
treeherdermozilla-beta@f455459b2ae5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgijs, gchang
bugs1304647
milestone51.0a2
Bug 1304647 - Measure the number of total URIs visited in a "session fragment", without any filtering. r=gijs, data-review=rweiss, a=gchang MozReview-Commit-ID: 8L18SKeeM7F
browser/modules/BrowserUsageTelemetry.jsm
browser/modules/test/browser_UsageTelemetry.js
browser/modules/test/browser_UsageTelemetry_private_and_restore.js
toolkit/components/telemetry/Scalars.yaml
--- a/browser/modules/BrowserUsageTelemetry.jsm
+++ b/browser/modules/BrowserUsageTelemetry.jsm
@@ -26,16 +26,17 @@ const DOMWINDOW_OPENED_TOPIC = "domwindo
 
 // Probe names.
 const MAX_TAB_COUNT_SCALAR_NAME = "browser.engagement.max_concurrent_tab_count";
 const MAX_WINDOW_COUNT_SCALAR_NAME = "browser.engagement.max_concurrent_window_count";
 const TAB_OPEN_EVENT_COUNT_SCALAR_NAME = "browser.engagement.tab_open_event_count";
 const WINDOW_OPEN_EVENT_COUNT_SCALAR_NAME = "browser.engagement.window_open_event_count";
 const UNIQUE_DOMAINS_COUNT_SCALAR_NAME = "browser.engagement.unique_domains_count";
 const TOTAL_URI_COUNT_SCALAR_NAME = "browser.engagement.total_uri_count";
+const UNFILTERED_URI_COUNT_SCALAR_NAME = "browser.engagement.unfiltered_uri_count";
 
 // A list of known search origins.
 const KNOWN_SEARCH_SOURCES = [
   "abouthome",
   "contextmenu",
   "newtab",
   "searchbar",
   "urlbar",
@@ -73,23 +74,23 @@ function getSearchEngineId(engine) {
 }
 
 let URICountListener = {
   // A set containing the visited domains, see bug 1271310.
   _domainSet: new Set(),
   // A map to keep track of the URIs loaded from the restored tabs.
   _restoredURIsMap: new WeakMap(),
 
-  isValidURI(uri) {
+  isHttpURI(uri) {
     // Only consider http(s) schemas.
     return uri.schemeIs("http") || uri.schemeIs("https");
   },
 
   addRestoredURI(browser, uri) {
-    if (!this.isValidURI(uri)) {
+    if (!this.isHttpURI(uri)) {
       return;
     }
 
     this._restoredURIsMap.set(browser, uri.spec);
   },
 
   onLocationChange(browser, webProgress, request, uri, flags) {
     // Don't count this URI if it's an error page.
@@ -97,38 +98,61 @@ let URICountListener = {
       return;
     }
 
     // We only care about top level loads.
     if (!webProgress.isTopLevel) {
       return;
     }
 
-    if (!this.isValidURI(uri)) {
-      return;
-    }
-
     // The SessionStore sets the URI of a tab first, firing onLocationChange the
     // first time, then manages content loading using its scheduler. Once content
     // loads, we will hit onLocationChange again.
     // We can catch the first case by checking for null requests: be advised that
     // this can also happen when navigating page fragments, so account for it.
     if (!request &&
         !(flags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT)) {
       return;
     }
 
+    // Track URI loads, even if they're not http(s).
+    let uriSpec = null;
+    try {
+      uriSpec = uri.spec;
+    } catch (e) {
+      // If we have troubles parsing the spec, still count this as
+      // an unfiltered URI.
+      Services.telemetry.scalarAdd(UNFILTERED_URI_COUNT_SCALAR_NAME, 1);
+      return;
+    }
+
+
+    // Don't count about:blank and similar pages, as they would artificially
+    // inflate the counts.
+    if (browser.ownerDocument.defaultView.gInitialPages.includes(uriSpec)) {
+      return;
+    }
+
     // If the URI we're loading is in the _restoredURIsMap, then it comes from a
     // restored tab. If so, let's skip it and remove it from the map as we want to
     // count page refreshes.
-    if (this._restoredURIsMap.get(browser) === uri.spec) {
+    if (this._restoredURIsMap.get(browser) === uriSpec) {
       this._restoredURIsMap.delete(browser);
       return;
     }
 
+    // The URI wasn't from a restored tab. Count it among the unfiltered URIs.
+    // If this is an http(s) URI, this also gets counted by the "total_uri_count"
+    // probe.
+    Services.telemetry.scalarAdd(UNFILTERED_URI_COUNT_SCALAR_NAME, 1);
+
+    if (!this.isHttpURI(uri)) {
+      return;
+    }
+
     // Update the URI counts.
     Services.telemetry.scalarAdd(TOTAL_URI_COUNT_SCALAR_NAME, 1);
 
     // We only want to count the unique domains up to MAX_UNIQUE_VISITED_DOMAINS.
     if (this._domainSet.size == MAX_UNIQUE_VISITED_DOMAINS) {
       return;
     }
 
--- a/browser/modules/test/browser_UsageTelemetry.js
+++ b/browser/modules/test/browser_UsageTelemetry.js
@@ -1,16 +1,17 @@
 "use strict";
 
 const MAX_CONCURRENT_TABS = "browser.engagement.max_concurrent_tab_count";
 const TAB_EVENT_COUNT = "browser.engagement.tab_open_event_count";
 const MAX_CONCURRENT_WINDOWS = "browser.engagement.max_concurrent_window_count";
 const WINDOW_OPEN_COUNT = "browser.engagement.window_open_event_count";
 const TOTAL_URI_COUNT = "browser.engagement.total_uri_count";
 const UNIQUE_DOMAINS_COUNT = "browser.engagement.unique_domains_count";
+const UNFILTERED_URI_COUNT = "browser.engagement.unfiltered_uri_count";
 
 const TELEMETRY_SUBSESSION_TOPIC = "internal-telemetry-after-subsession-split";
 
 /**
  * Waits for the web progress listener associated with this tab to fire an
  * onLocationChange for a non-error page.
  *
  * @param {xul:browser} browser
@@ -54,191 +55,214 @@ let checkScalar = (scalars, scalarName, 
     return;
   }
   ok(!(scalarName in scalars), scalarName + " must not be reported.");
 };
 
 /**
  * Get a snapshot of the scalars and check them against the provided values.
  */
-let checkScalars = (maxTabs, tabOpenCount, maxWindows, windowsOpenCount, totalURIs, domainCount) => {
+let checkScalars = (countsObject) => {
   const scalars =
     Services.telemetry.snapshotScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN);
 
   // Check the expected values. Scalars that are never set must not be reported.
-  checkScalar(scalars, MAX_CONCURRENT_TABS, maxTabs,
+  checkScalar(scalars, MAX_CONCURRENT_TABS, countsObject.maxTabs,
               "The maximum tab count must match the expected value.");
-  checkScalar(scalars, TAB_EVENT_COUNT, tabOpenCount,
+  checkScalar(scalars, TAB_EVENT_COUNT, countsObject.tabOpenCount,
               "The number of open tab event count must match the expected value.");
-  checkScalar(scalars, MAX_CONCURRENT_WINDOWS, maxWindows,
+  checkScalar(scalars, MAX_CONCURRENT_WINDOWS, countsObject.maxWindows,
               "The maximum window count must match the expected value.");
-  checkScalar(scalars, WINDOW_OPEN_COUNT, windowsOpenCount,
+  checkScalar(scalars, WINDOW_OPEN_COUNT, countsObject.windowsOpenCount,
               "The number of window open event count must match the expected value.");
-  checkScalar(scalars, TOTAL_URI_COUNT, totalURIs,
+  checkScalar(scalars, TOTAL_URI_COUNT, countsObject.totalURIs,
               "The total URI count must match the expected value.");
-  checkScalar(scalars, UNIQUE_DOMAINS_COUNT, domainCount,
+  checkScalar(scalars, UNIQUE_DOMAINS_COUNT, countsObject.domainCount,
               "The unique domains count must match the expected value.");
+  checkScalar(scalars, UNFILTERED_URI_COUNT, countsObject.totalUnfilteredURIs,
+              "The unfiltered URI count must match the expected value.");
 };
 
 add_task(function* test_tabsAndWindows() {
   // Let's reset the counts.
   Services.telemetry.clearScalars();
 
   let openedTabs = [];
   let expectedTabOpenCount = 0;
   let expectedWinOpenCount = 0;
   let expectedMaxTabs = 0;
   let expectedMaxWins = 0;
 
   // Add a new tab and check that the count is right.
   openedTabs.push(yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank"));
   expectedTabOpenCount = 1;
   expectedMaxTabs = 2;
-  checkScalars(expectedMaxTabs, expectedTabOpenCount, expectedMaxWins, expectedWinOpenCount, 0, 0);
+  // This, and all the checks below, also check that initial pages (about:newtab, about:blank, ..)
+  // are not counted by the total_uri_count and the unfiltered_uri_count probes.
+  checkScalars({maxTabs: expectedMaxTabs, tabOpenCount: expectedTabOpenCount, maxWindows: expectedMaxWins,
+                windowsOpenCount: expectedWinOpenCount, totalURIs: 0, domainCount: 0,
+                totalUnfilteredURIs: 0});
 
   // Add two new tabs in the same window.
   openedTabs.push(yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank"));
   openedTabs.push(yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank"));
   expectedTabOpenCount += 2;
   expectedMaxTabs += 2;
-  checkScalars(expectedMaxTabs, expectedTabOpenCount, expectedMaxWins, expectedWinOpenCount, 0, 0);
+  checkScalars({maxTabs: expectedMaxTabs, tabOpenCount: expectedTabOpenCount, maxWindows: expectedMaxWins,
+                windowsOpenCount: expectedWinOpenCount, totalURIs: 0, domainCount: 0,
+                totalUnfilteredURIs: 0});
 
   // Add a new window and then some tabs in it. An empty new windows counts as a tab.
   let win = yield BrowserTestUtils.openNewBrowserWindow();
   openedTabs.push(yield BrowserTestUtils.openNewForegroundTab(win.gBrowser, "about:blank"));
   openedTabs.push(yield BrowserTestUtils.openNewForegroundTab(win.gBrowser, "about:blank"));
   openedTabs.push(yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank"));
   // The new window started with a new tab, so account for it.
   expectedTabOpenCount += 4;
   expectedWinOpenCount += 1;
   expectedMaxWins = 2;
   expectedMaxTabs += 4;
 
   // Remove a tab from the first window, the max shouldn't change.
   yield BrowserTestUtils.removeTab(openedTabs.pop());
-  checkScalars(expectedMaxTabs, expectedTabOpenCount, expectedMaxWins, expectedWinOpenCount, 0, 0);
+  checkScalars({maxTabs: expectedMaxTabs, tabOpenCount: expectedTabOpenCount, maxWindows: expectedMaxWins,
+                windowsOpenCount: expectedWinOpenCount, totalURIs: 0, domainCount: 0,
+                totalUnfilteredURIs: 0});
 
   // Remove all the extra windows and tabs.
   for (let tab of openedTabs) {
     yield BrowserTestUtils.removeTab(tab);
   }
   yield BrowserTestUtils.closeWindow(win);
 
   // Make sure all the scalars still have the expected values.
-  checkScalars(expectedMaxTabs, expectedTabOpenCount, expectedMaxWins, expectedWinOpenCount, 0, 0);
+  checkScalars({maxTabs: expectedMaxTabs, tabOpenCount: expectedTabOpenCount, maxWindows: expectedMaxWins,
+                windowsOpenCount: expectedWinOpenCount, totalURIs: 0, domainCount: 0,
+                totalUnfilteredURIs: 0});
 });
 
 add_task(function* test_subsessionSplit() {
   // Let's reset the counts.
   Services.telemetry.clearScalars();
 
   // Add a new window (that will have 4 tabs).
   let win = yield BrowserTestUtils.openNewBrowserWindow();
   let openedTabs = [];
   openedTabs.push(yield BrowserTestUtils.openNewForegroundTab(win.gBrowser, "about:blank"));
-  openedTabs.push(yield BrowserTestUtils.openNewForegroundTab(win.gBrowser, "about:blank"));
+  openedTabs.push(yield BrowserTestUtils.openNewForegroundTab(win.gBrowser, "about:mozilla"));
   openedTabs.push(yield BrowserTestUtils.openNewForegroundTab(win.gBrowser, "http://www.example.com"));
 
-  // Check that the scalars have the right values.
-  checkScalars(5 /*maxTabs*/, 4 /*tabOpen*/, 2 /*maxWins*/, 1 /*winOpen*/,
-               1 /* toalURIs */, 1 /* uniqueDomains */);
+  // Check that the scalars have the right values. We expect 2 unfiltered URI loads
+  // (about:mozilla and www.example.com, but no about:blank) and 1 URI totalURIs
+  // (only www.example.com).
+  checkScalars({maxTabs: 5, tabOpenCount: 4, maxWindows: 2, windowsOpenCount: 1,
+                totalURIs: 1, domainCount: 1, totalUnfilteredURIs: 2});
 
   // Remove a tab.
   yield BrowserTestUtils.removeTab(openedTabs.pop());
 
   // Simulate a subsession split by clearing the scalars (via |snapshotScalars|) and
   // notifying the subsession split topic.
   Services.telemetry.snapshotScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN,
                                      true /* clearScalars*/);
   Services.obs.notifyObservers(null, TELEMETRY_SUBSESSION_TOPIC, "");
 
   // After a subsession split, only the MAX_CONCURRENT_* scalars must be available
-  // and have the correct value. No tabs or windows were opened so other scalars
+  // and have the correct value. No tabs, windows or URIs were opened so other scalars
   // must not be reported.
-  checkScalars(4 /*maxTabs*/, 0 /*tabOpen*/, 2 /*maxWins*/, 0 /*winOpen*/,
-               0 /* toalURIs */, 0 /* uniqueDomains */);
+  checkScalars({maxTabs: 4, tabOpenCount: 0, maxWindows: 2, windowsOpenCount: 0,
+                totalURIs: 0, domainCount: 0, totalUnfilteredURIs: 0});
 
   // Remove all the extra windows and tabs.
   for (let tab of openedTabs) {
     yield BrowserTestUtils.removeTab(tab);
   }
   yield BrowserTestUtils.closeWindow(win);
 });
 
 add_task(function* test_URIAndDomainCounts() {
   // Let's reset the counts.
   Services.telemetry.clearScalars();
 
-  let checkCounts = (URICount, domainCount) => {
+  let checkCounts = (countsObject) => {
     // Get a snapshot of the scalars and then clear them.
     const scalars =
       Services.telemetry.snapshotScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN);
-    checkScalar(scalars, TOTAL_URI_COUNT, URICount,
+    checkScalar(scalars, TOTAL_URI_COUNT, countsObject.totalURIs,
                 "The URI scalar must contain the expected value.");
-    checkScalar(scalars, UNIQUE_DOMAINS_COUNT, domainCount,
+    checkScalar(scalars, UNIQUE_DOMAINS_COUNT, countsObject.domainCount,
                 "The unique domains scalar must contain the expected value.");
+    checkScalar(scalars, UNFILTERED_URI_COUNT, countsObject.totalUnfilteredURIs,
+                "The unfiltered URI scalar must contain the expected value.");
   };
 
   // Check that about:blank doesn't get counted in the URI total.
   let firstTab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
-  checkCounts(0, 0);
+  checkCounts({totalURIs: 0, domainCount: 0, totalUnfilteredURIs: 0});
 
   // Open a different page and check the counts.
   yield BrowserTestUtils.loadURI(firstTab.linkedBrowser, "http://example.com/");
   yield BrowserTestUtils.browserLoaded(firstTab.linkedBrowser);
-  checkCounts(1, 1);
+  checkCounts({totalURIs: 1, domainCount: 1, totalUnfilteredURIs: 1});
 
   // Activating a different tab must not increase the URI count.
   let secondTab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
   yield BrowserTestUtils.switchTab(gBrowser, firstTab);
-  checkCounts(1, 1);
+  checkCounts({totalURIs: 1, domainCount: 1, totalUnfilteredURIs: 1});
   yield BrowserTestUtils.removeTab(secondTab);
 
   // Open a new window and set the tab to a new address.
   let newWin = yield BrowserTestUtils.openNewBrowserWindow();
   yield BrowserTestUtils.loadURI(newWin.gBrowser.selectedBrowser, "http://example.com/");
   yield BrowserTestUtils.browserLoaded(newWin.gBrowser.selectedBrowser);
-  checkCounts(2, 1);
+  checkCounts({totalURIs: 2, domainCount: 1, totalUnfilteredURIs: 2});
 
   // We should not count AJAX requests.
   const XHR_URL = "http://example.com/r";
   yield ContentTask.spawn(newWin.gBrowser.selectedBrowser, XHR_URL, function(url) {
     return new Promise(resolve => {
       var xhr = new content.window.XMLHttpRequest();
       xhr.open("GET", url);
       xhr.onload = () => resolve();
       xhr.send();
     });
   });
-  checkCounts(2, 1);
+  checkCounts({totalURIs: 2, domainCount: 1, totalUnfilteredURIs: 2});
 
   // Check that we're counting page fragments.
   let loadingStopped = browserLocationChanged(newWin.gBrowser.selectedBrowser);
   yield BrowserTestUtils.loadURI(newWin.gBrowser.selectedBrowser, "http://example.com/#2");
   yield loadingStopped;
-  checkCounts(3, 1);
+  checkCounts({totalURIs: 3, domainCount: 1, totalUnfilteredURIs: 3});
 
-  // Check test.domain.com and some.domain.com are only counted once unique.
+  // Check that a different URI from the example.com domain doesn't increment the unique count.
   yield BrowserTestUtils.loadURI(newWin.gBrowser.selectedBrowser, "http://test1.example.com/");
   yield BrowserTestUtils.browserLoaded(newWin.gBrowser.selectedBrowser);
-  checkCounts(4, 1);
+  checkCounts({totalURIs: 4, domainCount: 1, totalUnfilteredURIs: 4});
 
   // Make sure that the unique domains counter is incrementing for a different domain.
   yield BrowserTestUtils.loadURI(newWin.gBrowser.selectedBrowser, "https://example.org/");
   yield BrowserTestUtils.browserLoaded(newWin.gBrowser.selectedBrowser);
-  checkCounts(5, 2);
+  checkCounts({totalURIs: 5, domainCount: 2, totalUnfilteredURIs: 5});
 
   // Check that we only account for top level loads (e.g. we don't count URIs from
   // embedded iframes).
   yield ContentTask.spawn(newWin.gBrowser.selectedBrowser, null, function* () {
     let doc = content.document;
     let iframe = doc.createElement("iframe");
     let promiseIframeLoaded = ContentTaskUtils.waitForEvent(iframe, "load", false);
     iframe.src = "https://example.org/test";
     doc.body.insertBefore(iframe, doc.body.firstChild);
     yield promiseIframeLoaded;
   });
-  checkCounts(5, 2);
+  checkCounts({totalURIs: 5, domainCount: 2, totalUnfilteredURIs: 5});
+
+  // Check that uncommon protocols get counted in the unfiltered URI probe.
+  const TEST_PAGE =
+    "data:text/html,<a id='target' href='%23par1'>Click me</a><a name='par1'>The paragraph.</a>";
+  yield BrowserTestUtils.loadURI(newWin.gBrowser.selectedBrowser, TEST_PAGE);
+  yield BrowserTestUtils.browserLoaded(newWin.gBrowser.selectedBrowser);
+  checkCounts({totalURIs: 5, domainCount: 2, totalUnfilteredURIs: 6});
 
   // Clean up.
   yield BrowserTestUtils.removeTab(firstTab);
   yield BrowserTestUtils.closeWindow(newWin);
 });
--- a/browser/modules/test/browser_UsageTelemetry_private_and_restore.js
+++ b/browser/modules/test/browser_UsageTelemetry_private_and_restore.js
@@ -1,15 +1,16 @@
 "use strict";
 
 const MAX_CONCURRENT_TABS = "browser.engagement.max_concurrent_tab_count";
 const TAB_EVENT_COUNT = "browser.engagement.tab_open_event_count";
 const MAX_CONCURRENT_WINDOWS = "browser.engagement.max_concurrent_window_count";
 const WINDOW_OPEN_COUNT = "browser.engagement.window_open_event_count";
 const TOTAL_URI_COUNT = "browser.engagement.total_uri_count";
+const UNFILTERED_URI_COUNT = "browser.engagement.unfiltered_uri_count";
 const UNIQUE_DOMAINS_COUNT = "browser.engagement.unique_domains_count";
 
 function promiseBrowserStateRestored() {
   return new Promise(resolve => {
      Services.obs.addObserver(function observer(aSubject, aTopic) {
        Services.obs.removeObserver(observer, "sessionstore-browser-state-restored");
        resolve();
      }, "sessionstore-browser-state-restored", false);
@@ -25,16 +26,17 @@ add_task(function* test_privateMode() {
   yield BrowserTestUtils.loadURI(privateWin.gBrowser.selectedBrowser, "http://example.com/");
   yield BrowserTestUtils.browserLoaded(privateWin.gBrowser.selectedBrowser);
 
   // Check that tab and window count is recorded.
   const scalars =
     Services.telemetry.snapshotScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN);
 
   ok(!(TOTAL_URI_COUNT in scalars), "We should not track URIs in private mode.");
+  ok(!(UNFILTERED_URI_COUNT in scalars), "We should not track URIs in private mode.");
   ok(!(UNIQUE_DOMAINS_COUNT in scalars), "We should not track unique domains in private mode.");
   is(scalars[TAB_EVENT_COUNT], 1, "The number of open tab event count must match the expected value.");
   is(scalars[MAX_CONCURRENT_TABS], 2, "The maximum tab count must match the expected value.");
   is(scalars[WINDOW_OPEN_COUNT], 1, "The number of window open event count must match the expected value.");
   is(scalars[MAX_CONCURRENT_WINDOWS], 2, "The maximum window count must match the expected value.");
 
   // Clean up.
   yield BrowserTestUtils.closeWindow(privateWin);
@@ -74,15 +76,16 @@ add_task(function* test_sessionRestore()
   SessionStore.setBrowserState(JSON.stringify(state));
   yield tabRestored;
 
   // Check that the URI is not recorded.
   const scalars =
     Services.telemetry.snapshotScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN);
 
   ok(!(TOTAL_URI_COUNT in scalars), "We should not track URIs from restored sessions.");
+  ok(!(UNFILTERED_URI_COUNT in scalars), "We should not track URIs from restored sessions.");
   ok(!(UNIQUE_DOMAINS_COUNT in scalars), "We should not track unique domains from restored sessions.");
 
   // Restore the original session and cleanup.
   let sessionRestored = promiseBrowserStateRestored();
   SessionStore.setBrowserState(JSON.stringify(state));
   yield sessionRestored;
 });
--- a/toolkit/components/telemetry/Scalars.yaml
+++ b/toolkit/components/telemetry/Scalars.yaml
@@ -184,16 +184,30 @@ browser.engagement:
       page reloads, after the session has been restored. This does not include background
       page requests and URIs from embedded pages or private browsing.
     expires: "55"
     kind: uint
     notification_emails:
       - rweiss@mozilla.com
     release_channel_collection: opt-out
 
+  unfiltered_uri_count:
+    bug_numbers:
+      - 1304647
+    description: >
+      The count of the total non-unique URIs visited in a subsession, not restricted to
+      a specific protocol, including page reloads and about:* pages (other than initial
+      pages such as about:blank, ...), after the session has been restored. This does
+      not include background page requests and URIs from embedded pages or private browsing.
+    expires: "55"
+    kind: uint
+    notification_emails:
+      - bcolloran@mozilla.com
+    release_channel_collection: opt-out
+
   unique_domains_count:
     bug_numbers:
       - 1271310
     description: >
       The count of the unique domains visited in a subsession, after the session
       has been restored. Subdomains under eTLD are aggregated after the first level
       (i.e. test.example.com and other.example.com are only counted once).
       This does not include background page requests and domains from embedded pages