tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit e088da1ad9e21512a456549e9de205c666e092df
parent 0f8c64c34a94618e416cd8589fc791604fef89bb
Author: Nazım Can Altınova <canaltinova@gmail.com>
Date:   Tue, 21 Oct 2025 21:15:37 +0000

Bug 1979114 - Add some tests for jsSource retrieval r=mstange,profiler-reviewers

Differential Revision: https://phabricator.services.mozilla.com/D259270

Diffstat:
Mtools/profiler/tests/browser/browser.toml | 7+++++++
Atools/profiler/tests/browser/browser_test_feature_js_sources_retrieval.js | 239+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atools/profiler/tests/browser/external_script.js | 16++++++++++++++++
Atools/profiler/tests/browser/page_with_external_js.html | 30++++++++++++++++++++++++++++++
4 files changed, 292 insertions(+), 0 deletions(-)

diff --git a/tools/profiler/tests/browser/browser.toml b/tools/profiler/tests/browser/browser.toml @@ -11,6 +11,13 @@ support-files = ["simple.html"] ["browser_test_feature_js_sourceindex.js"] support-files = ["simple.html", "tracing.html"] +["browser_test_feature_js_sources_retrieval.js"] +support-files = [ + "simple.html", + "page_with_external_js.html", + "external_script.js" +] + ["browser_test_feature_jsallocations.js"] support-files = ["do_work_500ms.html"] diff --git a/tools/profiler/tests/browser/browser_test_feature_js_sources_retrieval.js b/tools/profiler/tests/browser/browser_test_feature_js_sources_retrieval.js @@ -0,0 +1,239 @@ +/* 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/. */ + +/** + * Test that JS sources are collected and included in profile additional information. + */ +add_task(async function test_js_sources_in_profile_additional_info() { + Assert.ok( + !Services.profiler.IsActive(), + "The profiler is not currently active" + ); + + const url = BASE_URL + "simple.html"; + await BrowserTestUtils.withNewTab(url, async contentBrowser => { + await ProfilerTestUtils.startProfiler({ features: ["js", "jssources"] }); + // Execute some JavaScript to create sources + await SpecialPowers.spawn(contentBrowser, [], () => { + content.window.eval(` + function testSourceFunction() { + console.log("This is a test function"); + return 42; + } + testSourceFunction(); + `); + }); + + // Get the profile with additional information + // Note: We don't have to await here for the pause because the await for + // getProfileDataAsGzippedArrayBuffer will actively wait for the pause to + // be executed in the child process first. + Services.profiler.Pause(); + const profileData = + await Services.profiler.getProfileDataAsGzippedArrayBuffer(); + await Services.profiler.StopProfiler(); + + // Verify we got profile data + Assert.ok(!!profileData, "Should receive profile data"); + Assert.ok(!!profileData.profile, "Should have profile data"); + Assert.ok( + !!profileData.additionalInformation, + "Should have additional information" + ); + + // Check that the additional information has JS sources + const sources = profileData.additionalInformation.jsSources; + Assert.ok(!!sources, "Additional info should contain jsSources"); + Assert.equal(typeof sources, "object", "jsSources should be an object"); + + // Check that we have some JS sources + Assert.greater( + Object.keys(sources).length, + 0, + "Should have at least one JS source" + ); + + info(`Total JS sources collected: ${sources.length}`); + + // Check the sources to verify they contain actual source text + for (const sourceUuid in sources) { + const sourceText = sources[sourceUuid]; + Assert.ok( + typeof sourceUuid === "string" && !!sourceUuid.length, + "sourceUuid should be a non-empty string" + ); + Assert.ok( + typeof sourceText === "string" && !!sourceText.length, + `Source ${sourceUuid} should be a non-empty string` + ); + } + }); +}); + +/** + * Test that different types of JS sources are handled correctly. + */ +add_task(async function test_js_sources_different_types() { + Assert.ok( + !Services.profiler.IsActive(), + "The profiler is not currently active" + ); + + const url = BASE_URL + "simple.html"; + await BrowserTestUtils.withNewTab(url, async contentBrowser => { + await ProfilerTestUtils.startProfiler({ features: ["js", "jssources"] }); + // Execute different types of JavaScript to test source collection + await SpecialPowers.spawn(contentBrowser, [], () => { + // Inline script + content.window.eval(` + function inlineFunction() { + return "inline"; + } + inlineFunction(); + `); + + // Anonymous function + content.window.eval(` + (function() { + console.log("anonymous function"); + })(); + `); + }); + + // Get profile data + // Note: We don't have to await here for the pause because the await for + // getProfileDataAsGzippedArrayBuffer will actively wait for the pause to + // be executed in the child process first. + Services.profiler.Pause(); + const profileData = + await Services.profiler.getProfileDataAsGzippedArrayBuffer(); + await Services.profiler.StopProfiler(); + + const sources = profileData.additionalInformation.jsSources; + Assert.ok(!!sources, "Additional info should contain jsSources"); + Assert.equal(typeof sources, "object", "jsSources should be an object"); + + // Check that we captured sources for different types of JavaScript + let inlineSourceCount = 0; + let inlineSourceLength = 0; + + for (const sourceUuid in sources) { + const sourceText = sources[sourceUuid]; + if (typeof sourceText === "string" && sourceText.length) { + // Check if we found our test functions + if ( + sourceText.includes("inlineFunction") || + sourceText.includes("anonymous function") + ) { + inlineSourceCount += 1; + inlineSourceLength += sourceText.length; + } + } + } + + Assert.greater( + inlineSourceLength, + 0, + "Should have collected source text with content" + ); + + info(`Total source text length: ${inlineSourceLength} characters`); + + Assert.greaterOrEqual( + inlineSourceCount, + 2, + "Should have collected all the test functions" + ); + }); +}); + +/** + * Test that external JS files are properly collected in JS sources. + */ +add_task(async function test_js_sources_external_scripts() { + Assert.ok( + !Services.profiler.IsActive(), + "The profiler is not currently active" + ); + + const url = BASE_URL + "page_with_external_js.html"; + await BrowserTestUtils.withNewTab(url, async contentBrowser => { + await ProfilerTestUtils.startProfiler({ features: ["js", "jssources"] }); + // Wait for page to load and execute scripts + await SpecialPowers.spawn(contentBrowser, [], () => { + return new Promise(resolve => { + if (content.document.readyState === "complete") { + resolve(); + } else { + content.window.addEventListener("load", resolve); + } + }); + }); + + // Get profile data + // Note: We don't have to await here for the pause because the await for + // getProfileDataAsGzippedArrayBuffer will actively wait for the pause to + // be executed in the child process first. + Services.profiler.Pause(); + const profileData = + await Services.profiler.getProfileDataAsGzippedArrayBuffer(); + await Services.profiler.StopProfiler(); + + const sources = profileData.additionalInformation.jsSources; + Assert.ok(!!sources, "Additional info should contain jsSources"); + Assert.equal(typeof sources, "object", "jsSources should be an object"); + + // Look for external script sources + let foundExternalScript = false; + let foundInlineScript = false; + let externalScriptSource = null; + + for (const sourceUuid in sources) { + const sourceText = sources[sourceUuid]; + if (typeof sourceText === "string" && sourceText.length) { + // Check for external script content + if ( + sourceText.includes("externalFunction") && + sourceText.includes("calculateSum") + ) { + foundExternalScript = true; + externalScriptSource = sourceText; + info(`Found external script source (${sourceText.length} chars)`); + } + + // Check for inline script content + if ( + sourceText.includes("inlineFunction") && + sourceText.includes("window.onload") + ) { + foundInlineScript = true; + info(`Found inline script source (${sourceText.length} chars)`); + } + } + } + + Assert.ok( + foundExternalScript, + "Should find external JavaScript file content in sources" + ); + + Assert.ok( + foundInlineScript, + "Should find inline JavaScript content in sources" + ); + + // Verify external script has expected functions + Assert.ok( + externalScriptSource.includes("This is an external function"), + "External script should contain expected comment" + ); + + Assert.ok( + externalScriptSource.includes("calculateSum"), + "External script should contain calculateSum function" + ); + + info("Successfully verified external JS file collection"); + }); +}); diff --git a/tools/profiler/tests/browser/external_script.js b/tools/profiler/tests/browser/external_script.js @@ -0,0 +1,16 @@ +// This is an external function +function externalFunction() { + // Do some work for ~2ms to generate samples + const start = performance.now(); + let result = 0; + while (performance.now() - start < 10) { + for (let i = 0; i < 1000; i++) { + result += Math.random() * i; + } + } + return result; +} + +function calculateSum(a, b) { + return a + b; +} diff --git a/tools/profiler/tests/browser/page_with_external_js.html b/tools/profiler/tests/browser/page_with_external_js.html @@ -0,0 +1,30 @@ +<!DOCTYPE html> +<html> +<head> + <title>Test Page with External JS</title> + <script src="external_script.js"></script> +</head> +<body> + <h1>Test Page for External JS Sources</h1> + <p>This page loads an external JavaScript file to test JS source collection.</p> + + <script> + function inlineFunction() { + // Do some work for ~2ms to generate samples + const start = performance.now(); + let result = 0; + while (performance.now() - start < 10) { + for (let i = 0; i < 1000; i++) { + result += Math.sin(i) * Math.cos(i); + } + } + return result; + } + + window.onload = function() { + inlineFunction(); + externalFunction(); + }; + </script> +</body> +</html>