diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js
index 1e9668c66c5c..95b590799d01 100644
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -6017,6 +6017,7 @@ var TabsProgressListener = {
) {
if (recordLoadTelemetry) {
TelemetryStopwatch.finish(histogram, aBrowser);
+ BrowserUtils.recordSiteOriginTelemetry(browserWindows());
}
}
} else if (
diff --git a/browser/modules/test/browser/browser.ini b/browser/modules/test/browser/browser.ini
index 9ac4e3fdcca2..4036cc7a3769 100644
--- a/browser/modules/test/browser/browser.ini
+++ b/browser/modules/test/browser/browser.ini
@@ -3,6 +3,7 @@ support-files =
head.js
prefs =
browser.newtabpage.activity-stream.improvesearch.handoffToAwesomebar=false
+ telemetry.number_of_site_origin.min_interval=0
[browser_BrowserWindowTracker.js]
[browser_ContentSearch.js]
@@ -37,7 +38,7 @@ skip-if = os != "win" || (os == "win" && bits == 64) # bug 1456807
[browser_UnsubmittedCrashHandler.js]
run-if = crashreporter
[browser_urlBar_zoom.js]
-skip-if = os == "mac" #Bug 1528429
+skip-if = os == "mac" #Bug 1528429
[browser_UsageTelemetry.js]
[browser_UsageTelemetry_domains.js]
[browser_UsageTelemetry_private_and_restore.js]
@@ -58,3 +59,6 @@ support-files =
[browser_UsageTelemetry_content.js]
[browser_UsageTelemetry_content_aboutHome.js]
[browser_UsageTelemetry_content_aboutRestartRequired.js]
+[browser_Telemetry_numberOfSiteOrigins.js]
+support-files =
+ contain_iframe.html
diff --git a/browser/modules/test/browser/browser_Telemetry_numberOfSiteOrigins.js b/browser/modules/test/browser/browser_Telemetry_numberOfSiteOrigins.js
new file mode 100644
index 000000000000..eb8de11f7840
--- /dev/null
+++ b/browser/modules/test/browser/browser_Telemetry_numberOfSiteOrigins.js
@@ -0,0 +1,55 @@
+/* eslint-disable mozilla/no-arbitrary-setTimeout */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * This file tests page reload key combination telemetry
+ */
+
+"use strict";
+
+ChromeUtils.defineModuleGetter(
+ this,
+ "TelemetryTestUtils",
+ "resource://testing-common/TelemetryTestUtils.jsm"
+);
+
+const gTestRoot = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "http://mochi.test:8888"
+);
+
+const { TimedPromise } = ChromeUtils.import(
+ "chrome://marionette/content/sync.js"
+);
+
+async function run_test(count) {
+ const histogram = TelemetryTestUtils.getAndClearHistogram(
+ "FX_NUMBER_OF_UNIQUE_SITE_ORIGINS_ALL_TABS"
+ );
+
+ let newTab = await BrowserTestUtils.openNewForegroundTab({
+ gBrowser,
+ opening: gTestRoot + "contain_iframe.html",
+ waitForStateStop: true,
+ });
+
+ await new Promise(resolve =>
+ setTimeout(function() {
+ window.requestIdleCallback(resolve);
+ }, 1000)
+ );
+
+ if (count < 2) {
+ await BrowserTestUtils.removeTab(newTab);
+ await run_test(count + 1);
+ } else {
+ TelemetryTestUtils.assertHistogram(histogram, 2, 1);
+ await BrowserTestUtils.removeTab(newTab);
+ }
+}
+
+add_task(async function test_telemetryMoreSiteOrigin() {
+ await run_test(1);
+});
diff --git a/browser/modules/test/browser/contain_iframe.html b/browser/modules/test/browser/contain_iframe.html
new file mode 100644
index 000000000000..67b2aa70983d
--- /dev/null
+++ b/browser/modules/test/browser/contain_iframe.html
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/docshell/base/CanonicalBrowsingContext.cpp b/docshell/base/CanonicalBrowsingContext.cpp
index b9272e9a783e..2992ccfddd90 100644
--- a/docshell/base/CanonicalBrowsingContext.cpp
+++ b/docshell/base/CanonicalBrowsingContext.cpp
@@ -207,6 +207,32 @@ void CanonicalBrowsingContext::NotifyMediaMutedChanged(bool aMuted) {
SetMuted(aMuted);
}
+uint32_t CanonicalBrowsingContext::CountSiteOrigins(
+ GlobalObject& aGlobal,
+ const Sequence>& aRoots) {
+ nsTHashtable uniqueSiteOrigins;
+
+ for (const auto& root : aRoots) {
+ root->PreOrderWalk([&](BrowsingContext* aContext) {
+ WindowGlobalParent* windowGlobalParent =
+ aContext->Canonical()->GetCurrentWindowGlobal();
+ if (windowGlobalParent) {
+ nsIPrincipal* documentPrincipal =
+ windowGlobalParent->DocumentPrincipal();
+
+ bool isContentPrincipal = documentPrincipal->GetIsContentPrincipal();
+ if (isContentPrincipal) {
+ nsCString siteOrigin;
+ documentPrincipal->GetSiteOrigin(siteOrigin);
+ uniqueSiteOrigins.PutEntry(siteOrigin);
+ }
+ }
+ });
+ }
+
+ return uniqueSiteOrigins.Count();
+}
+
void CanonicalBrowsingContext::UpdateMediaAction(MediaControlActions aAction) {
MediaActionHandler::UpdateMediaAction(this, aAction);
Group()->EachParent([&](ContentParent* aParent) {
diff --git a/docshell/base/CanonicalBrowsingContext.h b/docshell/base/CanonicalBrowsingContext.h
index 9abff280826d..7b7118629737 100644
--- a/docshell/base/CanonicalBrowsingContext.h
+++ b/docshell/base/CanonicalBrowsingContext.h
@@ -79,6 +79,12 @@ class CanonicalBrowsingContext final : public BrowsingContext {
// other top level windows in other processes.
void NotifyMediaMutedChanged(bool aMuted);
+ // Return the number of unique site origins by iterating all given BCs,
+ // including their subtrees.
+ static uint32_t CountSiteOrigins(
+ GlobalObject& aGlobal,
+ const Sequence>& aRoots);
+
// This function would update the media action for the current outer window
// and propogate the action to other browsing contexts in content processes.
void UpdateMediaAction(MediaControlActions aAction);
diff --git a/dom/chrome-webidl/BrowsingContext.webidl b/dom/chrome-webidl/BrowsingContext.webidl
index b8bd42c9fa8e..59b84db72602 100644
--- a/dom/chrome-webidl/BrowsingContext.webidl
+++ b/dom/chrome-webidl/BrowsingContext.webidl
@@ -74,6 +74,8 @@ interface CanonicalBrowsingContext : BrowsingContext {
void notifyStartDelayedAutoplayMedia();
void notifyMediaMutedChanged(boolean muted);
+ static unsigned long countSiteOrigins(sequence roots);
+
/**
* Loads a given URI. This will give priority to loading the requested URI
* in the object implementing this interface. If it can't be loaded here
diff --git a/mobile/android/modules/geckoview/GeckoViewProgress.jsm b/mobile/android/modules/geckoview/GeckoViewProgress.jsm
index ed6873e04b5d..d553c0161b47 100644
--- a/mobile/android/modules/geckoview/GeckoViewProgress.jsm
+++ b/mobile/android/modules/geckoview/GeckoViewProgress.jsm
@@ -28,6 +28,10 @@ XPCOMUtils.defineLazyServiceGetter(
"nsIIDNService"
);
+XPCOMUtils.defineLazyModuleGetters(this, {
+ BrowserUtils: "resource://gre/modules/BrowserUtils.jsm",
+});
+
var IdentityHandler = {
// The definitions below should be kept in sync with those in GeckoView.ProgressListener.SecurityInformation
// No trusted identity information. No site identity icon is shown.
@@ -270,6 +274,11 @@ class GeckoViewProgress extends GeckoViewModule {
};
this.eventDispatcher.sendRequest(message);
+
+ BrowserUtils.recordSiteOriginTelemetry(
+ Services.wm.getEnumerator("navigator:geckoview"),
+ true
+ );
}
}
diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml
index 62d96ad35def..d15a7e962dcd 100644
--- a/modules/libpref/init/StaticPrefList.yaml
+++ b/modules/libpref/init/StaticPrefList.yaml
@@ -7536,6 +7536,11 @@
value: false
mirror: always
+- name: telemetry.number_of_site_origin.min_interval
+ type: uint32_t
+ value: 300000
+ mirror: always
+
#---------------------------------------------------------------------------
# Prefs starting with "test."
#---------------------------------------------------------------------------
diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json
index d5c26d7933f9..05c962c10b47 100644
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -6880,6 +6880,18 @@
"releaseChannelCollection": "opt-out",
"description": "Diagnostic probe to aid in categorizing tab switch spinners. Records what most recently set the loadTimer to null if a spinner was displayed."
},
+ "FX_NUMBER_OF_UNIQUE_SITE_ORIGINS_ALL_TABS": {
+ "record_in_processes": ["main"],
+ "products": ["firefox", "geckoview_streaming"],
+ "expires_in_version": "never",
+ "kind": "exponential",
+ "high": 100,
+ "n_buckets": 50,
+ "description": "When a document is loaded, report the number of unique site origins across the browser(all tabs) if it has been at least 5 minutes since last time we collect this data",
+ "bug_numbers": [1589700],
+ "alert_emails": ["sefeng@mozilla.com", "perfteam@mozilla.com"],
+ "releaseChannelCollection": "opt-out"
+ },
"FX_TAB_SWITCH_REQUEST_TAB_WARMING_STATE": {
"record_in_processes": ["main"],
"products": ["firefox", "fennec", "geckoview"],
diff --git a/toolkit/components/telemetry/geckoview/streaming/metrics.yaml b/toolkit/components/telemetry/geckoview/streaming/metrics.yaml
index c1b56ebda4c5..54140c0f176a 100644
--- a/toolkit/components/telemetry/geckoview/streaming/metrics.yaml
+++ b/toolkit/components/telemetry/geckoview/streaming/metrics.yaml
@@ -78,6 +78,31 @@ geckoview:
- esawin@mozilla.com
expires: never
+ document_site_origins:
+ type: custom_distribution
+ description: >
+ When a document is loaded, report the
+ number of [site origins](https://searchfox.org/
+ mozilla-central/rev/
+ 3300072e993ae05d50d5c63d815260367eaf9179/
+ caps/nsIPrincipal.idl#264) of the entire browser
+ if it has been at least 5 minutes since last
+ time we collect this data.
+ range_min: 0
+ range_max: 100
+ bucket_count: 50
+ histogram_type: exponential
+ unit: number of site_origin
+ gecko_datapoint: FX_NUMBER_OF_UNIQUE_SITE_ORIGINS_ALL_TABS
+ bugs:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1589700
+ data_reviews:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1589700#c5
+ notification_emails:
+ - sefeng@mozilla.com
+ - perfteam@mozilla.com
+ expires: never
+
gfx:
composite_time:
type: timing_distribution
diff --git a/toolkit/modules/BrowserUtils.jsm b/toolkit/modules/BrowserUtils.jsm
index 432f32712e18..bfac5cf81443 100644
--- a/toolkit/modules/BrowserUtils.jsm
+++ b/toolkit/modules/BrowserUtils.jsm
@@ -826,4 +826,72 @@ var BrowserUtils = {
}
return url;
},
+
+ recordSiteOriginTelemetry(aWindows, aIsGeckoView) {
+ Services.tm.idleDispatchToMainThread(() => {
+ this._recordSiteOriginTelemetry(aWindows, aIsGeckoView);
+ });
+ },
+
+ _recordSiteOriginTelemetry(aWindows, aIsGeckoView) {
+ let currentTime = Date.now();
+
+ // default is 5 minutes
+ if (!this.min_interval) {
+ this.min_interval = Services.prefs.getIntPref(
+ "telemetry.number_of_site_origin.min_interval",
+ 300000
+ );
+ }
+
+ // Discard the first load because most of the time the first load only has 1
+ // tab and 1 window open, so it is useless to report it.
+ if (
+ !this._lastRecordSiteOrigin ||
+ currentTime < this._lastRecordSiteOrigin + this.min_interval
+ ) {
+ if (!this._lastRecordSiteOrigin) {
+ this._lastRecordSiteOrigin = currentTime;
+ }
+ return;
+ }
+
+ this._lastRecordSiteOrigin = currentTime;
+
+ // Geckoview and Desktop work differently. On desktop, aBrowser objects
+ // holds an array of tabs which we can use to get the objects.
+ // In Geckoview, it is apps' responsibility to keep track of the tabs, so
+ // there isn't an easy way for us to get the tabs.
+ let tabs = [];
+ if (aIsGeckoView) {
+ // To get all active windows; Each tab has its own window
+ tabs = aWindows;
+ } else {
+ for (const win of aWindows) {
+ tabs = tabs.concat(win.gBrowser.tabs);
+ }
+ }
+
+ let topLevelBC = [];
+
+ for (const tab of tabs) {
+ let browser;
+ if (aIsGeckoView) {
+ browser = tab.browser;
+ } else {
+ browser = tab.linkedBrowser;
+ }
+
+ if (browser.browsingContext) {
+ // This is the top level browsingContext
+ topLevelBC.push(browser.browsingContext);
+ }
+ }
+
+ const count = CanonicalBrowsingContext.countSiteOrigins(topLevelBC);
+
+ Services.telemetry
+ .getHistogramById("FX_NUMBER_OF_UNIQUE_SITE_ORIGINS_ALL_TABS")
+ .add(count);
+ },
};
diff --git a/tools/lint/eslint/eslint-plugin-mozilla/lib/environments/privileged.js b/tools/lint/eslint/eslint-plugin-mozilla/lib/environments/privileged.js
index fa674f177ca1..54a5c28ad3f7 100644
--- a/tools/lint/eslint/eslint-plugin-mozilla/lib/environments/privileged.js
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/environments/privileged.js
@@ -57,6 +57,7 @@ module.exports = {
BoxObject: false,
BroadcastChannel: false,
BrowsingContext: false,
+ CanonicalBrowsingContext: false,
CDATASection: false,
CSS: false,
CSS2Properties: false,