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,