diff options
Diffstat (limited to '')
38 files changed, 3856 insertions, 0 deletions
diff --git a/tools/profiler/tests/browser/browser.ini b/tools/profiler/tests/browser/browser.ini new file mode 100644 index 0000000000..9a02b673d2 --- /dev/null +++ b/tools/profiler/tests/browser/browser.ini @@ -0,0 +1,102 @@ +[DEFAULT] +skip-if = tsan # Bug 1804081 - TSan times out on pretty much all of these tests +support-files = + ../shared-head.js + head.js + +[browser_test_feature_ipcmessages.js] +support-files = simple.html + +[browser_test_feature_jsallocations.js] +support-files = do_work_500ms.html + +[browser_test_feature_nostacksampling.js] +support-files = do_work_500ms.html + +[browser_test_feature_preferencereads.js] +support-files = single_frame.html + +[browser_test_markers_parent_process.js] +skip-if = + os == "win" && os_version == "6.1" # Skip on Azure - frequent failure + +[browser_test_markers_gc_cc.js] + +[browser_test_profile_capture_by_pid.js] +skip-if = os == "win" && os_version == "6.1" # No thread names on win7, needed for these tests +https_first_disabled = true +support-files = single_frame.html + +[browser_test_profile_fission.js] +support-files = single_frame.html + +[browser_test_profile_single_frame_page_info.js] +https_first_disabled = true +support-files = single_frame.html + +[browser_test_profile_slow_capture.js] +https_first_disabled = true +support-files = single_frame.html +skip-if = !debug + +[browser_test_profile_multi_frame_page_info.js] +https_first_disabled = true +support-files = + multi_frame.html + single_frame.html + +[browser_test_marker_network_simple.js] +https_first_disabled = true +support-files = simple.html + +[browser_test_marker_network_private_browsing.js] +support-files = simple.html + +[browser_test_marker_network_cancel.js] +https_first_disabled = true +support-files = simple.html + +[browser_test_marker_network_sts.js] +support-files = simple.html + +[browser_test_marker_network_redirect.js] +https_first_disabled = true +support-files = + redirect.sjs + simple.html + page_with_resources.html + firefox-logo-nightly.svg +skip-if = + os == "win" && os_version == "6.1" # Skip on Azure - frequent failure + +[browser_test_marker_network_serviceworker_cache_first.js] +support-files = + serviceworkers/serviceworker-utils.js + serviceworkers/serviceworker_register.html + serviceworkers/serviceworker_page.html + serviceworkers/firefox-logo-nightly.svg + serviceworkers/serviceworker_cache_first.js + +[browser_test_marker_network_serviceworker_no_fetch_handler.js] +support-files = + serviceworkers/serviceworker-utils.js + serviceworkers/serviceworker_register.html + serviceworkers/serviceworker_page.html + serviceworkers/firefox-logo-nightly.svg + serviceworkers/serviceworker_no_fetch_handler.js + +[browser_test_marker_network_serviceworker_no_respondWith_in_fetch_handler.js] +support-files = + serviceworkers/serviceworker-utils.js + serviceworkers/serviceworker_register.html + serviceworkers/serviceworker_page.html + serviceworkers/firefox-logo-nightly.svg + serviceworkers/serviceworker_no_respondWith_in_fetch_handler.js + +[browser_test_marker_network_serviceworker_synthetized_response.js] +support-files = + serviceworkers/serviceworker-utils.js + serviceworkers/serviceworker_register.html + serviceworkers/serviceworker_simple.html + serviceworkers/firefox-logo-nightly.svg + serviceworkers/serviceworker_synthetized_response.js diff --git a/tools/profiler/tests/browser/browser_test_feature_ipcmessages.js b/tools/profiler/tests/browser/browser_test_feature_ipcmessages.js new file mode 100644 index 0000000000..4691be6434 --- /dev/null +++ b/tools/profiler/tests/browser/browser_test_feature_ipcmessages.js @@ -0,0 +1,109 @@ +/* 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/. */ + +requestLongerTimeout(10); + +async function waitForLoad() { + return SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => { + return new Promise(function(resolve) { + if (content.document.readyState !== "complete") { + content.document.addEventListener("readystatechange", () => { + if (content.document.readyState === "complete") { + resolve(); + } + }); + } else { + resolve(); + } + }); + }); +} + +/** + * Test the IPCMessages feature. + */ +add_task(async function test_profile_feature_ipcmessges() { + Assert.ok( + !Services.profiler.IsActive(), + "The profiler is not currently active" + ); + + const url = BASE_URL + "simple.html"; + + info("Open a tab while profiling IPC messages."); + await startProfiler({ features: ["js", "ipcmessages"] }); + info("Started the profiler sucessfully! Now, let's open a tab."); + + await BrowserTestUtils.withNewTab(url, async contentBrowser => { + info("We opened a tab!"); + const contentPid = await SpecialPowers.spawn( + contentBrowser, + [], + () => Services.appinfo.processID + ); + info("Now let's wait until it's fully loaded."); + await waitForLoad(); + + info( + "Check that some IPC profile markers were generated when " + + "the feature is enabled." + ); + { + const { + parentThread, + contentThread, + } = await waitSamplingAndStopProfilerAndGetThreads(contentPid); + + Assert.greater( + getPayloadsOfType(parentThread, "IPC").length, + 0, + "IPC profile markers were recorded for the parent process' main " + + "thread when the IPCMessages feature was turned on." + ); + + Assert.greater( + getPayloadsOfType(contentThread, "IPC").length, + 0, + "IPC profile markers were recorded for the content process' main " + + "thread when the IPCMessages feature was turned on." + ); + } + }); + + info("Now open a tab without profiling IPC messages."); + await startProfiler({ features: ["js"] }); + + await BrowserTestUtils.withNewTab(url, async contentBrowser => { + const contentPid = await SpecialPowers.spawn( + contentBrowser, + [], + () => Services.appinfo.processID + ); + await waitForLoad(); + + info( + "Check that no IPC profile markers were recorded when the " + + "feature is turned off." + ); + { + const { + parentThread, + contentThread, + } = await waitSamplingAndStopProfilerAndGetThreads(contentPid); + Assert.equal( + getPayloadsOfType(parentThread, "IPC").length, + 0, + "No IPC profile markers were recorded for the parent process' main " + + "thread when the IPCMessages feature was turned off." + ); + + Assert.equal( + getPayloadsOfType(contentThread, "IPC").length, + 0, + "No IPC profile markers were recorded for the content process' main " + + "thread when the IPCMessages feature was turned off." + ); + } + }); +}); diff --git a/tools/profiler/tests/browser/browser_test_feature_jsallocations.js b/tools/profiler/tests/browser/browser_test_feature_jsallocations.js new file mode 100644 index 0000000000..0ae3b53a14 --- /dev/null +++ b/tools/profiler/tests/browser/browser_test_feature_jsallocations.js @@ -0,0 +1,78 @@ +/* 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/. */ + +requestLongerTimeout(10); + +/** + * Test the JS Allocations feature. This is done as a browser test to ensure that + * we realistically try out how the JS allocations are running. This ensures that + * we are collecting allocations for the content process and the parent process. + */ +add_task(async function test_profile_feature_jsallocations() { + Assert.ok( + !Services.profiler.IsActive(), + "The profiler is not currently active" + ); + + await startProfiler({ features: ["js", "jsallocations"] }); + + const url = BASE_URL + "do_work_500ms.html"; + await BrowserTestUtils.withNewTab(url, async contentBrowser => { + const contentPid = await SpecialPowers.spawn( + contentBrowser, + [], + () => Services.appinfo.processID + ); + + // Wait 500ms so that the tab finishes executing. + await wait(500); + + // Check that we can get some allocations when the feature is turned on. + { + const { + parentThread, + contentThread, + } = await waitSamplingAndStopProfilerAndGetThreads(contentPid); + Assert.greater( + getPayloadsOfType(parentThread, "JS allocation").length, + 0, + "Allocations were recorded for the parent process' main thread when the " + + "JS Allocation feature was turned on." + ); + Assert.greater( + getPayloadsOfType(contentThread, "JS allocation").length, + 0, + "Allocations were recorded for the content process' main thread when the " + + "JS Allocation feature was turned on." + ); + } + + await startProfiler({ features: ["js"] }); + // Now reload the tab with a clean run. + gBrowser.reload(); + await wait(500); + + // Check that no allocations were recorded, and allocation tracking was correctly + // turned off. + { + const { + parentThread, + contentThread, + } = await waitSamplingAndStopProfilerAndGetThreads(contentPid); + Assert.equal( + getPayloadsOfType(parentThread, "JS allocation").length, + 0, + "No allocations were recorded for the parent processes' main thread when " + + "JS allocation was not turned on." + ); + + Assert.equal( + getPayloadsOfType(contentThread, "JS allocation").length, + 0, + "No allocations were recorded for the content processes' main thread when " + + "JS allocation was not turned on." + ); + } + }); +}); diff --git a/tools/profiler/tests/browser/browser_test_feature_nostacksampling.js b/tools/profiler/tests/browser/browser_test_feature_nostacksampling.js new file mode 100644 index 0000000000..695e6031aa --- /dev/null +++ b/tools/profiler/tests/browser/browser_test_feature_nostacksampling.js @@ -0,0 +1,76 @@ +/* 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 the No Stack Sampling feature. + */ +add_task(async function test_profile_feature_nostacksampling() { + Assert.ok( + !Services.profiler.IsActive(), + "The profiler is not currently active" + ); + + await startProfiler({ features: ["js", "nostacksampling"] }); + + const url = BASE_URL + "do_work_500ms.html"; + await BrowserTestUtils.withNewTab(url, async contentBrowser => { + const contentPid = await SpecialPowers.spawn( + contentBrowser, + [], + () => Services.appinfo.processID + ); + + // Wait 500ms so that the tab finishes executing. + await wait(500); + + // Check that we can get no stacks when the feature is turned on. + { + const { + parentThread, + contentThread, + } = await stopProfilerNowAndGetThreads(contentPid); + Assert.equal( + parentThread.samples.data.length, + 0, + "Stack samples were recorded from the parent process' main thread" + + "when the No Stack Sampling feature was turned on." + ); + Assert.equal( + contentThread.samples.data.length, + 0, + "Stack samples were recorded from the content process' main thread" + + "when the No Stack Sampling feature was turned on." + ); + } + + // Flush out any straggling allocation markers that may have not been collected + // yet by starting and stopping the profiler once. + await startProfiler({ features: ["js"] }); + + // Now reload the tab with a clean run. + gBrowser.reload(); + await wait(500); + + // Check that stack samples were recorded. + { + const { + parentThread, + contentThread, + } = await waitSamplingAndStopProfilerAndGetThreads(contentPid); + Assert.greater( + parentThread.samples.data.length, + 0, + "No Stack samples were recorded from the parent process' main thread" + + "when the No Stack Sampling feature was not turned on." + ); + + Assert.greater( + contentThread.samples.data.length, + 0, + "No Stack samples were recorded from the content process' main thread" + + "when the No Stack Sampling feature was not turned on." + ); + } + }); +}); diff --git a/tools/profiler/tests/browser/browser_test_feature_preferencereads.js b/tools/profiler/tests/browser/browser_test_feature_preferencereads.js new file mode 100644 index 0000000000..d0f16054d8 --- /dev/null +++ b/tools/profiler/tests/browser/browser_test_feature_preferencereads.js @@ -0,0 +1,116 @@ +/* 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/. */ + +requestLongerTimeout(10); + +const kContentPref = "font.size.variable.x-western"; + +function countPrefReadsInThread(pref, thread) { + let count = 0; + for (let payload of getPayloadsOfType(thread, "PreferenceRead")) { + if (payload.prefName === pref) { + count++; + } + } + return count; +} + +async function waitForPaintAfterLoad() { + return SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => { + return new Promise(function(resolve) { + function listener() { + if (content.document.readyState == "complete") { + content.requestAnimationFrame(() => content.setTimeout(resolve, 0)); + } + } + if (content.document.readyState != "complete") { + content.document.addEventListener("readystatechange", listener); + } else { + listener(); + } + }); + }); +} + +/** + * Test the PreferenceRead feature. + */ +add_task(async function test_profile_feature_preferencereads() { + Assert.ok( + !Services.profiler.IsActive(), + "The profiler is not currently active" + ); + + await startProfiler({ features: ["js", "preferencereads"] }); + + const url = BASE_URL + "single_frame.html"; + await BrowserTestUtils.withNewTab(url, async contentBrowser => { + const contentPid = await SpecialPowers.spawn( + contentBrowser, + [], + () => Services.appinfo.processID + ); + + await waitForPaintAfterLoad(); + + // Ensure we read a pref in the content process. + await SpecialPowers.spawn(contentBrowser, [kContentPref], pref => { + Services.prefs.getIntPref(pref); + }); + + // Check that some PreferenceRead profile markers were generated when the + // feature is enabled. + { + const { contentThread } = await stopProfilerNowAndGetThreads(contentPid); + + Assert.greater( + countPrefReadsInThread(kContentPref, contentThread), + 0, + `PreferenceRead profile markers for ${kContentPref} were recorded ` + + "when the PreferenceRead feature was turned on." + ); + } + + await startProfiler({ features: ["js"] }); + // Now reload the tab with a clean run. + await ContentTask.spawn(contentBrowser, null, () => { + return new Promise(resolve => { + addEventListener("pageshow", () => resolve(), { + capturing: true, + once: true, + }); + content.location.reload(); + }); + }); + + await waitForPaintAfterLoad(); + + // Ensure we read a pref in the content process. + await SpecialPowers.spawn(contentBrowser, [kContentPref], pref => { + Services.prefs.getIntPref(pref); + }); + + // Check that no PreferenceRead markers were recorded when the feature + // is turned off. + { + const { + parentThread, + contentThread, + } = await stopProfilerNowAndGetThreads(contentPid); + Assert.equal( + getPayloadsOfType(parentThread, "PreferenceRead").length, + 0, + "No PreferenceRead profile were recorded " + + "when the PreferenceRead feature was turned off." + ); + + Assert.equal( + getPayloadsOfType(contentThread, "PreferenceRead").length, + 0, + "No PreferenceRead profile were recorded " + + "when the PreferenceRead feature was turned off." + ); + } + }); +}); diff --git a/tools/profiler/tests/browser/browser_test_marker_network_cancel.js b/tools/profiler/tests/browser/browser_test_marker_network_cancel.js new file mode 100644 index 0000000000..ee7f1ef3ca --- /dev/null +++ b/tools/profiler/tests/browser/browser_test_marker_network_cancel.js @@ -0,0 +1,71 @@ +/* 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 we emit network markers with the cancel status. + */ +add_task(async function test_network_markers_early_cancel() { + Assert.ok( + !Services.profiler.IsActive(), + "The profiler is not currently active" + ); + + startProfilerForMarkerTests(); + + const url = BASE_URL + "simple.html?cacheBust=" + Math.random(); + const options = { + gBrowser, + url: "about:blank", + waitForLoad: false, + }; + + const tab = await BrowserTestUtils.openNewForegroundTab(options); + const loadPromise = BrowserTestUtils.waitForDocLoadAndStopIt(url, tab); + BrowserTestUtils.loadURI(tab.linkedBrowser, url); + const contentPid = await SpecialPowers.spawn( + tab.linkedBrowser, + [], + () => Services.appinfo.processID + ); + await loadPromise; + const { parentThread, contentThread } = await stopProfilerNowAndGetThreads( + contentPid + ); + BrowserTestUtils.removeTab(tab); + + const parentNetworkMarkers = getInflatedNetworkMarkers(parentThread); + const contentNetworkMarkers = getInflatedNetworkMarkers(contentThread); + + info("parent process: " + JSON.stringify(parentNetworkMarkers, null, 2)); + info("content process: " + JSON.stringify(contentNetworkMarkers, null, 2)); + + Assert.equal( + parentNetworkMarkers.length, + 2, + `We should get a pair of network markers in the parent thread.` + ); + + // We don't test the markers in the content process, because depending on some + // timing we can have 0 or 1 (and maybe even 2 (?)). + + const parentStopMarker = parentNetworkMarkers[1]; + + const expectedProperties = { + name: Expect.stringMatches(`Load \\d+:.*${escapeStringRegexp(url)}`), + data: Expect.objectContainsOnly({ + type: "Network", + status: "STATUS_CANCEL", + URI: url, + requestMethod: "GET", + contentType: null, + startTime: Expect.number(), + endTime: Expect.number(), + id: Expect.number(), + pri: Expect.number(), + cache: "Unresolved", + }), + }; + + Assert.objectContains(parentStopMarker, expectedProperties); +}); diff --git a/tools/profiler/tests/browser/browser_test_marker_network_private_browsing.js b/tools/profiler/tests/browser/browser_test_marker_network_private_browsing.js new file mode 100644 index 0000000000..0a10ea7157 --- /dev/null +++ b/tools/profiler/tests/browser/browser_test_marker_network_private_browsing.js @@ -0,0 +1,91 @@ +/* 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 we emit network markers accordingly + */ +add_task(async function test_network_markers() { + Assert.ok( + !Services.profiler.IsActive(), + "The profiler is not currently active" + ); + + startProfilerForMarkerTests(); + + const win = await BrowserTestUtils.openNewBrowserWindow({ + private: true, + fission: true, + }); + try { + const url = BASE_URL_HTTPS + "simple.html?cacheBust=" + Math.random(); + const contentBrowser = win.gBrowser.selectedBrowser; + BrowserTestUtils.loadURI(contentBrowser, url); + await BrowserTestUtils.browserLoaded(contentBrowser, false, url); + const contentPid = await SpecialPowers.spawn( + contentBrowser, + [], + () => Services.appinfo.processID + ); + + const { parentThread, contentThread } = await stopProfilerNowAndGetThreads( + contentPid + ); + + const parentNetworkMarkers = getInflatedNetworkMarkers(parentThread); + const contentNetworkMarkers = getInflatedNetworkMarkers(contentThread); + info(JSON.stringify(parentNetworkMarkers, null, 2)); + info(JSON.stringify(contentNetworkMarkers, null, 2)); + + Assert.equal( + parentNetworkMarkers.length, + 2, + `We should get a pair of network markers in the parent thread.` + ); + Assert.equal( + contentNetworkMarkers.length, + 2, + `We should get a pair of network markers in the content thread.` + ); + + const parentStopMarker = parentNetworkMarkers[1]; + const contentStopMarker = contentNetworkMarkers[1]; + + const expectedProperties = { + name: Expect.stringMatches(`Load \\d+:.*${escapeStringRegexp(url)}`), + data: Expect.objectContains({ + status: "STATUS_STOP", + URI: url, + requestMethod: "GET", + contentType: "text/html", + startTime: Expect.number(), + endTime: Expect.number(), + domainLookupStart: Expect.number(), + domainLookupEnd: Expect.number(), + connectStart: Expect.number(), + tcpConnectEnd: Expect.number(), + connectEnd: Expect.number(), + requestStart: Expect.number(), + responseStart: Expect.number(), + responseEnd: Expect.number(), + id: Expect.number(), + count: Expect.number(), + pri: Expect.number(), + isPrivateBrowsing: true, + }), + }; + + Assert.objectContains(parentStopMarker, expectedProperties); + // The cache information is missing from the content marker, it's only part + // of the parent marker. See Bug 1544821. + Assert.objectContains(parentStopMarker.data, { + // Because the request races with the cache, these 2 values are valid: + // "Missed" when the cache answered before we get a result from the network. + // "Unresolved" when we got a response from the network before the cache subsystem. + cache: Expect.stringMatches(/^(Missed|Unresolved)$/), + }); + Assert.objectContains(contentStopMarker, expectedProperties); + } finally { + await BrowserTestUtils.closeWindow(win); + } +}); diff --git a/tools/profiler/tests/browser/browser_test_marker_network_redirect.js b/tools/profiler/tests/browser/browser_test_marker_network_redirect.js new file mode 100644 index 0000000000..28478c2b3b --- /dev/null +++ b/tools/profiler/tests/browser/browser_test_marker_network_redirect.js @@ -0,0 +1,341 @@ +/* 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 we emit network markers accordingly. + * In this file we'll test the redirect cases. + */ +add_task(async function test_network_markers_service_worker_setup() { + // Disabling cache makes the result more predictable especially in verify mode. + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.cache.disk.enable", false], + ["browser.cache.memory.enable", false], + ], + }); +}); + +add_task(async function test_network_markers_redirect_simple() { + // In this test, we request an HTML page that gets redirected. This is a + // top-level navigation. + Assert.ok( + !Services.profiler.IsActive(), + "The profiler is not currently active" + ); + + startProfilerForMarkerTests(); + + const targetFileNameWithCacheBust = "simple.html"; + const url = + BASE_URL + + "redirect.sjs?" + + encodeURIComponent(targetFileNameWithCacheBust); + const targetUrl = BASE_URL + targetFileNameWithCacheBust; + + await BrowserTestUtils.withNewTab(url, async contentBrowser => { + const contentPid = await SpecialPowers.spawn( + contentBrowser, + [], + () => Services.appinfo.processID + ); + + const { parentThread, contentThread } = await stopProfilerNowAndGetThreads( + contentPid + ); + + const parentNetworkMarkers = getInflatedNetworkMarkers(parentThread); + const contentNetworkMarkers = getInflatedNetworkMarkers(contentThread); + info(JSON.stringify(parentNetworkMarkers, null, 2)); + info(JSON.stringify(contentNetworkMarkers, null, 2)); + + Assert.equal( + parentNetworkMarkers.length, + 4, + `We should get 2 pairs of network markers in the parent thread.` + ); + + /* It looks like that for a redirection for the top level navigation, the + * content thread sees the markers for the second request only. + * See Bug 1692879. */ + Assert.equal( + contentNetworkMarkers.length, + 2, + `We should get one pair of network markers in the content thread.` + ); + + const parentRedirectMarker = parentNetworkMarkers[1]; + const parentStopMarker = parentNetworkMarkers[3]; + // There's no content redirect marker for the reason outlined above. + const contentStopMarker = contentNetworkMarkers[1]; + + Assert.objectContains(parentRedirectMarker, { + name: Expect.stringMatches(`Load \\d+:.*${escapeStringRegexp(url)}`), + data: Expect.objectContainsOnly({ + type: "Network", + status: "STATUS_REDIRECT", + URI: url, + RedirectURI: targetUrl, + requestMethod: "GET", + contentType: null, + startTime: Expect.number(), + endTime: Expect.number(), + domainLookupStart: Expect.number(), + domainLookupEnd: Expect.number(), + connectStart: Expect.number(), + tcpConnectEnd: Expect.number(), + connectEnd: Expect.number(), + requestStart: Expect.number(), + responseStart: Expect.number(), + responseEnd: Expect.number(), + id: Expect.number(), + redirectId: parentStopMarker.data.id, + pri: Expect.number(), + cache: Expect.stringMatches(/Missed|Unresolved/), + redirectType: "Permanent", + isHttpToHttpsRedirect: false, + }), + }); + + const expectedProperties = { + name: Expect.stringMatches( + `Load \\d+:.*${escapeStringRegexp(targetUrl)}` + ), + }; + const expectedDataProperties = { + type: "Network", + status: "STATUS_STOP", + URI: targetUrl, + requestMethod: "GET", + contentType: "text/html", + startTime: Expect.number(), + endTime: Expect.number(), + domainLookupStart: Expect.number(), + domainLookupEnd: Expect.number(), + connectStart: Expect.number(), + tcpConnectEnd: Expect.number(), + connectEnd: Expect.number(), + requestStart: Expect.number(), + responseStart: Expect.number(), + responseEnd: Expect.number(), + id: Expect.number(), + count: Expect.number(), + pri: Expect.number(), + }; + + Assert.objectContains(parentStopMarker, expectedProperties); + Assert.objectContains(contentStopMarker, expectedProperties); + + // The cache information is missing from the content marker, it's only part + // of the parent marker. See Bug 1544821. + Assert.objectContainsOnly(parentStopMarker.data, { + ...expectedDataProperties, + // Because the request races with the cache, these 2 values are valid: + // "Missed" when the cache answered before we get a result from the network. + // "Unresolved" when we got a response from the network before the cache subsystem. + cache: Expect.stringMatches(/^(Missed|Unresolved)$/), + }); + Assert.objectContainsOnly(contentStopMarker.data, expectedDataProperties); + }); +}); + +add_task(async function test_network_markers_redirect_resources() { + // In this test we request an HTML file that itself contains resources that + // are redirected. + Assert.ok( + !Services.profiler.IsActive(), + "The profiler is not currently active" + ); + + startProfilerForMarkerTests(); + + const url = BASE_URL + "page_with_resources.html?cacheBust=" + Math.random(); + await BrowserTestUtils.withNewTab(url, async contentBrowser => { + const contentPid = await SpecialPowers.spawn( + contentBrowser, + [], + () => Services.appinfo.processID + ); + + const { parentThread, contentThread } = await stopProfilerNowAndGetThreads( + contentPid + ); + + const parentNetworkMarkers = getInflatedNetworkMarkers(parentThread); + const contentNetworkMarkers = getInflatedNetworkMarkers(contentThread); + info(JSON.stringify(parentNetworkMarkers, null, 2)); + info(JSON.stringify(contentNetworkMarkers, null, 2)); + + Assert.equal( + parentNetworkMarkers.length, + 8, + `We should get 4 pairs of network markers in the parent thread.` + // 1 - The main page + // 2 - The SVG + // 3 - The redirected request for the second SVG request. + // 4 - The SVG, again + ); + + /* In this second test, the top level navigation request isn't redirected. + * Contrary to Bug 1692879 we get all network markers for redirected + * resources. */ + Assert.equal( + contentNetworkMarkers.length, + 8, + `We should get 4 pairs of network markers in the content thread.` + ); + + // The same resource firefox-logo-nightly.svg is requested twice, but the + // second time it is redirected. + // We're not interested in the main page, as we test that in other files. + // In this page we're only interested in the marker for requested resources. + + const parentPairs = getPairsOfNetworkMarkers(parentNetworkMarkers); + const contentPairs = getPairsOfNetworkMarkers(contentNetworkMarkers); + + // First, make sure we properly matched all start with stop markers. This + // means that both arrays should contain only arrays of 2 elements. + parentPairs.forEach(pair => + Assert.equal( + pair.length, + 2, + `For the URL ${pair[0].data.URI} we should get 2 markers in the parent process.` + ) + ); + contentPairs.forEach(pair => + Assert.equal( + pair.length, + 2, + `For the URL ${pair[0].data.URI} we should get 2 markers in the content process.` + ) + ); + + const parentFirstStopMarker = parentPairs[1][1]; + const parentRedirectMarker = parentPairs[2][1]; + const parentSecondStopMarker = parentPairs[3][1]; + const contentFirstStopMarker = contentPairs[1][1]; + const contentRedirectMarker = contentPairs[2][1]; + const contentSecondStopMarker = contentPairs[3][1]; + + const expectedCommonDataProperties = { + type: "Network", + requestMethod: "GET", + startTime: Expect.number(), + endTime: Expect.number(), + id: Expect.number(), + pri: Expect.number(), + innerWindowID: Expect.number(), + }; + + // These properties are present when a connection is fully opened. This is + // most often the case, unless we're in verify mode, because in that case + // we run the same tests several times in the same Firefox and they might be + // cached, or in chaos mode Firefox may make all requests sequentially on + // the same connection. + // In these cases, these properties won't always be present. + const expectedConnectionProperties = { + domainLookupStart: Expect.number(), + domainLookupEnd: Expect.number(), + connectStart: Expect.number(), + tcpConnectEnd: Expect.number(), + connectEnd: Expect.number(), + requestStart: Expect.number(), + responseStart: Expect.number(), + responseEnd: Expect.number(), + }; + + const expectedPropertiesForStopMarker = { + name: Expect.stringMatches(/Load \d+:.*\/firefox-logo-nightly\.svg/), + }; + + const expectedDataPropertiesForStopMarker = { + ...expectedCommonDataProperties, + ...expectedConnectionProperties, + status: "STATUS_STOP", + URI: Expect.stringContains("/firefox-logo-nightly.svg"), + contentType: "image/svg+xml", + count: Expect.number(), + }; + + const expectedPropertiesForRedirectMarker = { + name: Expect.stringMatches( + /Load \d+:.*\/redirect.sjs\?firefox-logo-nightly\.svg/ + ), + }; + + const expectedDataPropertiesForRedirectMarker = { + ...expectedCommonDataProperties, + ...expectedConnectionProperties, + status: "STATUS_REDIRECT", + URI: Expect.stringContains("/redirect.sjs?firefox-logo-nightly.svg"), + RedirectURI: Expect.stringContains("/firefox-logo-nightly.svg"), + contentType: null, + redirectType: "Permanent", + isHttpToHttpsRedirect: false, + }; + + Assert.objectContains( + parentFirstStopMarker, + expectedPropertiesForStopMarker + ); + Assert.objectContainsOnly(parentFirstStopMarker.data, { + ...expectedDataPropertiesForStopMarker, + // The cache information is missing from the content marker, it's only part + // of the parent marker. See Bug 1544821. + // Also, because the request races with the cache, these 2 values are valid: + // "Missed" when the cache answered before we get a result from the network. + // "Unresolved" when we got a response from the network before the cache subsystem. + cache: Expect.stringMatches(/^(Missed|Unresolved)$/), + }); + + Assert.objectContains( + contentFirstStopMarker, + expectedPropertiesForStopMarker + ); + Assert.objectContainsOnly( + contentFirstStopMarker.data, + expectedDataPropertiesForStopMarker + ); + + Assert.objectContains( + parentRedirectMarker, + expectedPropertiesForRedirectMarker + ); + Assert.objectContainsOnly(parentRedirectMarker.data, { + ...expectedDataPropertiesForRedirectMarker, + redirectId: parentSecondStopMarker.data.id, + // See above for the full explanation about the cache property. + cache: Expect.stringMatches(/^(Missed|Unresolved)$/), + }); + + Assert.objectContains( + contentRedirectMarker, + expectedPropertiesForRedirectMarker + ); + Assert.objectContainsOnly(contentRedirectMarker.data, { + ...expectedDataPropertiesForRedirectMarker, + redirectId: contentSecondStopMarker.data.id, + }); + + Assert.objectContains( + parentSecondStopMarker, + expectedPropertiesForStopMarker + ); + Assert.objectContainsOnly(parentSecondStopMarker.data, { + ...expectedDataPropertiesForStopMarker, + // The "count" property is absent from the content marker. + count: Expect.number(), + // See above for the full explanation about the cache property. + cache: Expect.stringMatches(/^(Missed|Unresolved)$/), + }); + + Assert.objectContains( + contentSecondStopMarker, + expectedPropertiesForStopMarker + ); + Assert.objectContainsOnly( + contentSecondStopMarker.data, + expectedDataPropertiesForStopMarker + ); + }); +}); diff --git a/tools/profiler/tests/browser/browser_test_marker_network_serviceworker_cache_first.js b/tools/profiler/tests/browser/browser_test_marker_network_serviceworker_cache_first.js new file mode 100644 index 0000000000..4b715efa1c --- /dev/null +++ b/tools/profiler/tests/browser/browser_test_marker_network_serviceworker_cache_first.js @@ -0,0 +1,388 @@ +/* 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 we emit network markers accordingly. + * In this file we'll test a caching service worker. This service worker will + * fetch and store requests at install time, and serve them when the page + * requests them. + */ + +const serviceWorkerFileName = "serviceworker_cache_first.js"; +registerCleanupFunction(() => SpecialPowers.removeAllServiceWorkerData()); + +add_task(async function test_network_markers_service_worker_setup() { + // Disabling cache makes the result more predictable. Also this makes things + // simpler when dealing with service workers. + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.cache.disk.enable", false], + ["browser.cache.memory.enable", false], + ], + }); +}); + +add_task(async function test_network_markers_service_worker_register() { + // In this first step, we request an HTML page that will register a service + // worker. We'll wait until the service worker is fully installed before + // checking various things. + Assert.ok( + !Services.profiler.IsActive(), + "The profiler is not currently active" + ); + + startProfilerForMarkerTests(); + + const url = `${BASE_URL_HTTPS}serviceworkers/serviceworker_register.html`; + await BrowserTestUtils.withNewTab(url, async contentBrowser => { + const contentPid = await SpecialPowers.spawn( + contentBrowser, + [], + () => Services.appinfo.processID + ); + + await SpecialPowers.spawn( + contentBrowser, + [serviceWorkerFileName], + async function(serviceWorkerFileName) { + await content.wrappedJSObject.registerServiceWorkerAndWait( + serviceWorkerFileName + ); + } + ); + + const { + parentThread, + contentThread, + profile, + } = await stopProfilerNowAndGetThreads(contentPid); + + // The service worker work happens in a third "thread" or process, let's try + // to find it. + // Currently the fetches happen on the main thread for the content process, + // this may change in the future and we may have to adapt this function. + // Also please note this isn't necessarily the same content process as the + // ones for the tab. + const { serviceWorkerParentThread } = findServiceWorkerThreads(profile); + + // Here are a few sanity checks. + ok( + serviceWorkerParentThread, + "We should find a thread for the service worker." + ); + + Assert.notEqual( + serviceWorkerParentThread.pid, + parentThread.pid, + "We should have a different pid than the parent thread." + ); + Assert.notEqual( + serviceWorkerParentThread.tid, + parentThread.tid, + "We should have a different tid than the parent thread." + ); + + // Let's make sure we actually have a registered service workers. + const workers = await SpecialPowers.registeredServiceWorkers(); + Assert.equal( + workers.length, + 1, + "One service worker should be properly registered." + ); + + // By logging a few information about the threads we make debugging easier. + logInformationForThread("parentThread information", parentThread); + logInformationForThread("contentThread information", contentThread); + logInformationForThread( + "serviceWorkerParentThread information", + serviceWorkerParentThread + ); + + // Now let's check the marker payloads. + const parentNetworkMarkers = getInflatedNetworkMarkers(parentThread) + // When we load a page, Firefox will check the service worker freshness + // after a few seconds. So when the test lasts a long time (with some test + // environments) we might see spurious markers about that that we're not + // interesting in in this part of the test. They're only present in the + // parent process. + .filter(marker => !marker.data.URI.includes(serviceWorkerFileName)); + const contentNetworkMarkers = getInflatedNetworkMarkers(contentThread); + const serviceWorkerNetworkMarkers = getInflatedNetworkMarkers( + serviceWorkerParentThread + ); + + // Some more logs for debugging purposes. + info( + "Parent network markers: " + JSON.stringify(parentNetworkMarkers, null, 2) + ); + info( + "Content network markers: " + + JSON.stringify(contentNetworkMarkers, null, 2) + ); + info( + "Serviceworker network markers: " + + JSON.stringify(serviceWorkerNetworkMarkers, null, 2) + ); + + const parentPairs = getPairsOfNetworkMarkers(parentNetworkMarkers); + const contentPairs = getPairsOfNetworkMarkers(contentNetworkMarkers); + const serviceWorkerPairs = getPairsOfNetworkMarkers( + serviceWorkerNetworkMarkers + ); + + // First, make sure we properly matched all start with stop markers. This + // means that both arrays should contain only arrays of 2 elements. + parentPairs.forEach(pair => + Assert.equal( + pair.length, + 2, + `For the URL ${pair[0].data.URI} we should get 2 markers in the parent process.` + ) + ); + contentPairs.forEach(pair => + Assert.equal( + pair.length, + 2, + `For the URL ${pair[0].data.URI} we should get 2 markers in the content process.` + ) + ); + serviceWorkerPairs.forEach(pair => + Assert.equal( + pair.length, + 2, + `For the URL ${pair[0].data.URI} we should get 2 markers in the service worker process.` + ) + ); + + // Let's look at all pairs and make sure we requested all expected files. + const parentStopMarkers = parentPairs.map(([_, stopMarker]) => stopMarker); + const serviceWorkerStopMarkers = serviceWorkerPairs.map( + ([_, stopMarker]) => stopMarker + ); + + // These are the files cached by the service worker. We should see markers + // for both the parent thread and the service worker thread. + const expectedFiles = [ + "serviceworker_page.html", + "firefox-logo-nightly.svg", + ].map(filename => `${BASE_URL_HTTPS}serviceworkers/${filename}`); + + for (const expectedFile of expectedFiles) { + info( + `Checking if "${expectedFile}" is present in the network markers in both processes.` + ); + const parentMarker = parentStopMarkers.find( + marker => marker.data.URI === expectedFile + ); + const serviceWorkerMarker = serviceWorkerStopMarkers.find( + marker => marker.data.URI === expectedFile + ); + + const expectedProperties = { + name: Expect.stringMatches( + `Load \\d+:.*${escapeStringRegexp(expectedFile)}` + ), + data: Expect.objectContains({ + status: "STATUS_STOP", + URI: expectedFile, + requestMethod: "GET", + contentType: Expect.stringMatches(/^(text\/html|image\/svg\+xml)$/), + startTime: Expect.number(), + endTime: Expect.number(), + domainLookupStart: Expect.number(), + domainLookupEnd: Expect.number(), + connectStart: Expect.number(), + tcpConnectEnd: Expect.number(), + connectEnd: Expect.number(), + requestStart: Expect.number(), + responseStart: Expect.number(), + responseEnd: Expect.number(), + id: Expect.number(), + count: Expect.number(), + pri: Expect.number(), + }), + }; + + Assert.objectContains(parentMarker, expectedProperties); + Assert.objectContains(serviceWorkerMarker, expectedProperties); + } + }); +}); + +add_task(async function test_network_markers_service_worker_use() { + // In this test we request an HTML file that itself contains resources that + // are redirected. + Assert.ok( + !Services.profiler.IsActive(), + "The profiler is not currently active" + ); + + startProfilerForMarkerTests(); + + const url = `${BASE_URL_HTTPS}serviceworkers/serviceworker_page.html`; + await BrowserTestUtils.withNewTab(url, async contentBrowser => { + const contentPid = await SpecialPowers.spawn( + contentBrowser, + [], + () => Services.appinfo.processID + ); + + const { parentThread, contentThread } = await stopProfilerNowAndGetThreads( + contentPid + ); + + // By logging a few information about the threads we make debugging easier. + logInformationForThread("parentThread information", parentThread); + logInformationForThread("contentThread information", contentThread); + + const parentNetworkMarkers = getInflatedNetworkMarkers(parentThread) + // When we load a page, Firefox will check the service worker freshness + // after a few seconds. So when the test lasts a long time (with some test + // environments) we might see spurious markers about that that we're not + // interesting in in this part of the test. They're only present in the + // parent process. + .filter(marker => !marker.data.URI.includes(serviceWorkerFileName)); + const contentNetworkMarkers = getInflatedNetworkMarkers(contentThread); + + // Here are some logs to ease debugging. + info( + "Parent network markers: " + JSON.stringify(parentNetworkMarkers, null, 2) + ); + info( + "Content network markers: " + + JSON.stringify(contentNetworkMarkers, null, 2) + ); + + const parentPairs = getPairsOfNetworkMarkers(parentNetworkMarkers); + const contentPairs = getPairsOfNetworkMarkers(contentNetworkMarkers); + + // These are the files cached by the service worker. We should see markers + // for the parent thread and the content thread. + const expectedFiles = [ + "serviceworker_page.html", + "firefox-logo-nightly.svg", + ].map(filename => `${BASE_URL_HTTPS}serviceworkers/${filename}`); + + // First, make sure we properly matched all start with stop markers. This + // means that both arrays should contain only arrays of 2 elements. + parentPairs.forEach(pair => + Assert.equal( + pair.length, + 2, + `For the URL ${pair[0].data.URI} we should get 2 markers in the parent process.` + ) + ); + + contentPairs.forEach(pair => + Assert.equal( + pair.length, + 2, + `For the URL ${pair[0].data.URI} we should get 2 markers in the content process.` + ) + ); + + // Let's look at all pairs and make sure we requested all expected files. + const parentEndMarkers = parentPairs.map(([_, endMarker]) => endMarker); + const contentStopMarkers = contentPairs.map( + ([_, stopMarker]) => stopMarker + ); + + Assert.equal( + parentEndMarkers.length, + expectedFiles.length * 2, // one redirect + one stop + "There should be twice as many end markers in the parent process as requested files." + ); + Assert.equal( + contentStopMarkers.length, + expectedFiles.length, + "There should be as many stop markers in the content process as requested files." + ); + + for (const [i, expectedFile] of expectedFiles.entries()) { + info( + `Checking if "${expectedFile}" if present in the network markers in both processes.` + ); + const [parentRedirectMarker, parentStopMarker] = parentEndMarkers.filter( + marker => marker.data.URI === expectedFile + ); + const contentMarker = contentStopMarkers.find( + marker => marker.data.URI === expectedFile + ); + + const commonDataProperties = { + type: "Network", + URI: expectedFile, + requestMethod: "GET", + contentType: Expect.stringMatches(/^(text\/html|image\/svg\+xml)$/), + startTime: Expect.number(), + endTime: Expect.number(), + id: Expect.number(), + pri: Expect.number(), + }; + + const expectedProperties = { + name: Expect.stringMatches( + `Load \\d+:.*${escapeStringRegexp(expectedFile)}` + ), + }; + + Assert.objectContains(parentRedirectMarker, expectedProperties); + Assert.objectContains(parentStopMarker, expectedProperties); + Assert.objectContains(contentMarker, expectedProperties); + if (i === 0) { + // This is the top level navigation, the HTML file. + Assert.objectContainsOnly(parentRedirectMarker.data, { + ...commonDataProperties, + status: "STATUS_REDIRECT", + contentType: null, + cache: "Unresolved", + RedirectURI: expectedFile, + redirectType: "Internal", + redirectId: parentStopMarker.data.id, + isHttpToHttpsRedirect: false, + }); + + Assert.objectContainsOnly(parentStopMarker.data, { + ...commonDataProperties, + status: "STATUS_STOP", + }); + + Assert.objectContainsOnly(contentMarker.data, { + ...commonDataProperties, + status: "STATUS_STOP", + }); + } else { + Assert.objectContainsOnly(parentRedirectMarker.data, { + ...commonDataProperties, + status: "STATUS_REDIRECT", + contentType: null, + cache: "Unresolved", + innerWindowID: Expect.number(), + RedirectURI: expectedFile, + redirectType: "Internal", + redirectId: parentStopMarker.data.id, + isHttpToHttpsRedirect: false, + }); + + Assert.objectContainsOnly( + parentStopMarker.data, + // Note: in the future we may have more properties. We're using the + // "Only" flavor of the matcher so that we don't forget to update this + // test when this changes. + { + ...commonDataProperties, + innerWindowID: Expect.number(), + status: "STATUS_STOP", + } + ); + + Assert.objectContainsOnly(contentMarker.data, { + ...commonDataProperties, + innerWindowID: Expect.number(), + status: "STATUS_STOP", + }); + } + } + }); +}); diff --git a/tools/profiler/tests/browser/browser_test_marker_network_serviceworker_no_fetch_handler.js b/tools/profiler/tests/browser/browser_test_marker_network_serviceworker_no_fetch_handler.js new file mode 100644 index 0000000000..775e81f565 --- /dev/null +++ b/tools/profiler/tests/browser/browser_test_marker_network_serviceworker_no_fetch_handler.js @@ -0,0 +1,218 @@ +/* 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 we emit network markers accordingly. + * In this file we'll test the case of a service worker that has no fetch + * handlers. In this case, a fetch is done to the network. There may be + * shortcuts in our code in this case, that's why it's important to test it + * separately. + */ + +const serviceWorkerFileName = "serviceworker_no_fetch_handler.js"; +registerCleanupFunction(() => SpecialPowers.removeAllServiceWorkerData()); + +add_task(async function test_network_markers_service_worker_setup() { + // Disabling cache makes the result more predictable. Also this makes things + // simpler when dealing with service workers. + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.cache.disk.enable", false], + ["browser.cache.memory.enable", false], + ], + }); +}); + +add_task(async function test_network_markers_service_worker_register() { + // In this first step, we request an HTML page that will register a service + // worker. We'll wait until the service worker is fully installed before + // checking various things. + const url = `${BASE_URL_HTTPS}serviceworkers/serviceworker_register.html`; + await BrowserTestUtils.withNewTab(url, async contentBrowser => { + await SpecialPowers.spawn( + contentBrowser, + [serviceWorkerFileName], + async function(serviceWorkerFileName) { + await content.wrappedJSObject.registerServiceWorkerAndWait( + serviceWorkerFileName + ); + } + ); + + // Let's make sure we actually have a registered service workers. + const workers = await SpecialPowers.registeredServiceWorkers(); + Assert.equal( + workers.length, + 1, + "One service worker should be properly registered." + ); + }); +}); + +add_task(async function test_network_markers_service_worker_use() { + Assert.ok( + !Services.profiler.IsActive(), + "The profiler is not currently active" + ); + + startProfilerForMarkerTests(); + + const url = `${BASE_URL_HTTPS}serviceworkers/serviceworker_page.html`; + await BrowserTestUtils.withNewTab(url, async contentBrowser => { + const contentPid = await SpecialPowers.spawn( + contentBrowser, + [], + () => Services.appinfo.processID + ); + + const { parentThread, contentThread } = await stopProfilerNowAndGetThreads( + contentPid + ); + + // By logging a few information about the threads we make debugging easier. + logInformationForThread("parentThread information", parentThread); + logInformationForThread("contentThread information", contentThread); + + const parentNetworkMarkers = getInflatedNetworkMarkers(parentThread) + // When we load a page, Firefox will check the service worker freshness + // after a few seconds. So when the test lasts a long time (with some test + // environments) we might see spurious markers about that that we're not + // interesting in in this part of the test. They're only present in the + // parent process. + .filter(marker => !marker.data.URI.includes(serviceWorkerFileName)); + const contentNetworkMarkers = getInflatedNetworkMarkers(contentThread); + + // Here are some logs to ease debugging. + info( + "Parent network markers:" + JSON.stringify(parentNetworkMarkers, null, 2) + ); + info( + "Content network markers:" + + JSON.stringify(contentNetworkMarkers, null, 2) + ); + + const parentPairs = getPairsOfNetworkMarkers(parentNetworkMarkers); + const contentPairs = getPairsOfNetworkMarkers(contentNetworkMarkers); + + // First, make sure we properly matched all start with stop markers. This + // means that both arrays should contain only arrays of 2 elements. + parentPairs.forEach(pair => + Assert.equal( + pair.length, + 2, + `For the URL ${pair[0].data.URI} we should get 2 markers in the parent process.` + ) + ); + + contentPairs.forEach(pair => + Assert.equal( + pair.length, + 2, + `For the URL ${pair[0].data.URI} we should get 2 markers in the content process.` + ) + ); + + // Let's look at all pairs and make sure we requested all expected files. + const parentStopMarkers = parentPairs.map(([_, stopMarker]) => stopMarker); + const contentStopMarkers = contentPairs.map( + ([_, stopMarker]) => stopMarker + ); + + // These are the files requested by the page. + // We should see markers for the parent thread and the content thread. + const expectedFiles = [ + // Please take care that the first element is the top level navigation, as + // this is special-cased below. + "serviceworker_page.html", + "firefox-logo-nightly.svg", + ].map(filename => `${BASE_URL_HTTPS}serviceworkers/${filename}`); + + Assert.equal( + parentStopMarkers.length, + expectedFiles.length, + "There should be as many stop markers in the parent process as requested files." + ); + Assert.equal( + contentStopMarkers.length, + expectedFiles.length, + "There should be as many stop markers in the content process as requested files." + ); + + for (const [i, expectedFile] of expectedFiles.entries()) { + info( + `Checking if "${expectedFile}" if present in the network markers in both processes.` + ); + const parentMarker = parentStopMarkers.find( + marker => marker.data.URI === expectedFile + ); + const contentMarker = contentStopMarkers.find( + marker => marker.data.URI === expectedFile + ); + + const commonProperties = { + name: Expect.stringMatches( + `Load \\d+:.*${escapeStringRegexp(expectedFile)}` + ), + }; + Assert.objectContains(parentMarker, commonProperties); + Assert.objectContains(contentMarker, commonProperties); + + // We get the full set of properties in this case, because we do an actual + // fetch to the network. + const commonDataProperties = { + type: "Network", + status: "STATUS_STOP", + URI: expectedFile, + requestMethod: "GET", + contentType: Expect.stringMatches(/^(text\/html|image\/svg\+xml)$/), + startTime: Expect.number(), + endTime: Expect.number(), + id: Expect.number(), + pri: Expect.number(), + count: Expect.number(), + domainLookupStart: Expect.number(), + domainLookupEnd: Expect.number(), + connectStart: Expect.number(), + tcpConnectEnd: Expect.number(), + connectEnd: Expect.number(), + requestStart: Expect.number(), + responseStart: Expect.number(), + responseEnd: Expect.number(), + }; + + if (i === 0) { + // The first marker is special cased: this is the top level navigation + // serviceworker_page.html, + // and in this case we don't have all the same properties. Especially + // the innerWindowID information is missing. + Assert.objectContainsOnly(parentMarker.data, { + ...commonDataProperties, + // Note that the parent process has the "cache" information, but not the content + // process. See Bug 1544821. + // Also because the request races with the cache, these 2 values are valid: + // "Missed" when the cache answered before we get a result from the network. + // "Unresolved" when we got a response from the network before the cache subsystem. + cache: Expect.stringMatches(/^(Missed|Unresolved)$/), + }); + + Assert.objectContainsOnly(contentMarker.data, commonDataProperties); + } else { + // This is the other file firefox-logo-nightly.svg. + Assert.objectContainsOnly(parentMarker.data, { + ...commonDataProperties, + // Because the request races with the cache, these 2 values are valid: + // "Missed" when the cache answered before we get a result from the network. + // "Unresolved" when we got a response from the network before the cache subsystem. + cache: Expect.stringMatches(/^(Missed|Unresolved)$/), + innerWindowID: Expect.number(), + }); + + Assert.objectContainsOnly(contentMarker.data, { + ...commonDataProperties, + innerWindowID: Expect.number(), + }); + } + } + }); +}); diff --git a/tools/profiler/tests/browser/browser_test_marker_network_serviceworker_no_respondWith_in_fetch_handler.js b/tools/profiler/tests/browser/browser_test_marker_network_serviceworker_no_respondWith_in_fetch_handler.js new file mode 100644 index 0000000000..65686257de --- /dev/null +++ b/tools/profiler/tests/browser/browser_test_marker_network_serviceworker_no_respondWith_in_fetch_handler.js @@ -0,0 +1,296 @@ +/* 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 we emit network markers accordingly. + * In this file we'll test the case of a service worker that has a fetch + * handler, but no respondWith. In this case, some process called "reset + * interception" happens, and the fetch is still carried on by our code. Because + * this is a bit of an edge case, it's important to have a test for this case. + */ + +const serviceWorkerFileName = + "serviceworker_no_respondWith_in_fetch_handler.js"; +registerCleanupFunction(() => SpecialPowers.removeAllServiceWorkerData()); + +add_task(async function test_network_markers_service_worker_setup() { + // Disabling cache makes the result more predictable. Also this makes things + // simpler when dealing with service workers. + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.cache.disk.enable", false], + ["browser.cache.memory.enable", false], + ], + }); +}); + +add_task(async function test_network_markers_service_worker_register() { + // In this first step, we request an HTML page that will register a service + // worker. We'll wait until the service worker is fully installed before + // checking various things. + const url = `${BASE_URL_HTTPS}serviceworkers/serviceworker_register.html`; + await BrowserTestUtils.withNewTab(url, async contentBrowser => { + await SpecialPowers.spawn( + contentBrowser, + [serviceWorkerFileName], + async function(serviceWorkerFileName) { + await content.wrappedJSObject.registerServiceWorkerAndWait( + serviceWorkerFileName + ); + } + ); + + // Let's make sure we actually have a registered service workers. + const workers = await SpecialPowers.registeredServiceWorkers(); + Assert.equal( + workers.length, + 1, + "One service worker should be properly registered." + ); + }); +}); + +add_task(async function test_network_markers_service_worker_use() { + Assert.ok( + !Services.profiler.IsActive(), + "The profiler is not currently active" + ); + + startProfilerForMarkerTests(); + + const url = `${BASE_URL_HTTPS}serviceworkers/serviceworker_page.html`; + await BrowserTestUtils.withNewTab(url, async contentBrowser => { + const contentPid = await SpecialPowers.spawn( + contentBrowser, + [], + () => Services.appinfo.processID + ); + + const { parentThread, contentThread } = await stopProfilerNowAndGetThreads( + contentPid + ); + + // By logging a few information about the threads we make debugging easier. + logInformationForThread("parentThread information", parentThread); + logInformationForThread("contentThread information", contentThread); + + const parentNetworkMarkers = getInflatedNetworkMarkers(parentThread) + // When we load a page, Firefox will check the service worker freshness + // after a few seconds. So when the test lasts a long time (with some test + // environments) we might see spurious markers about that that we're not + // interesting in in this part of the test. They're only present in the + // parent process. + .filter(marker => !marker.data.URI.includes(serviceWorkerFileName)); + const contentNetworkMarkers = getInflatedNetworkMarkers(contentThread); + + // Here are some logs to ease debugging. + info( + "Parent network markers:" + JSON.stringify(parentNetworkMarkers, null, 2) + ); + info( + "Content network markers:" + + JSON.stringify(contentNetworkMarkers, null, 2) + ); + + const parentPairs = getPairsOfNetworkMarkers(parentNetworkMarkers); + const contentPairs = getPairsOfNetworkMarkers(contentNetworkMarkers); + + // First, make sure we properly matched all start with stop markers. This + // means that both arrays should contain only arrays of 2 elements. + parentPairs.forEach(pair => + Assert.equal( + pair.length, + 2, + `For the URL ${pair[0].data.URI} we should get 2 markers in the parent process.` + ) + ); + + contentPairs.forEach(pair => + Assert.equal( + pair.length, + 2, + `For the URL ${pair[0].data.URI} we should get 2 markers in the content process.` + ) + ); + + // Let's look at all pairs and make sure we requested all expected files. + // In this test, we should have redirect markers as well as stop markers, + // because this case generates internal redirects. We may want to change + // that in the future, or handle this specially in the frontend. + // Let's create various arrays to help assert. + + const parentEndMarkers = parentPairs.map(([_, stopMarker]) => stopMarker); + const parentStopMarkers = parentEndMarkers.filter( + marker => marker.data.status === "STATUS_STOP" + ); + const parentRedirectMarkers = parentEndMarkers.filter( + marker => marker.data.status === "STATUS_REDIRECT" + ); + const contentEndMarkers = contentPairs.map(([_, stopMarker]) => stopMarker); + const contentStopMarkers = contentEndMarkers.filter( + marker => marker.data.status === "STATUS_STOP" + ); + const contentRedirectMarkers = contentEndMarkers.filter( + marker => marker.data.status === "STATUS_REDIRECT" + ); + + // These are the files requested by the page. + // We should see markers for the parent thread and the content thread. + const expectedFiles = [ + // Please take care that the first element is the top level navigation, as + // this is special-cased below. + "serviceworker_page.html", + "firefox-logo-nightly.svg", + ].map(filename => `${BASE_URL_HTTPS}serviceworkers/${filename}`); + + Assert.equal( + parentStopMarkers.length, + expectedFiles.length, + "There should be as many stop markers in the parent process as requested files." + ); + Assert.equal( + parentRedirectMarkers.length, + expectedFiles.length * 2, // http -> intercepted, intercepted -> http + "There should be twice as many redirect markers in the parent process as requested files." + ); + Assert.equal( + contentStopMarkers.length, + expectedFiles.length, + "There should be as many stop markers in the content process as requested files." + ); + // Note: there will no redirect markers in the content process for + // ServiceWorker fallbacks request to network. + // See Bug 1793940. + Assert.equal( + contentRedirectMarkers.length, + 0, + "There should be no redirect markers in the content process than requested files." + ); + + for (const [i, expectedFile] of expectedFiles.entries()) { + info( + `Checking if "${expectedFile}" if present in the network markers in both processes.` + ); + const [ + parentRedirectMarkerIntercept, + parentRedirectMarkerReset, + ] = parentRedirectMarkers.filter( + marker => marker.data.URI === expectedFile + ); + const parentStopMarker = parentStopMarkers.find( + marker => marker.data.URI === expectedFile + ); + const contentStopMarker = contentStopMarkers.find( + marker => marker.data.URI === expectedFile + ); + + const commonProperties = { + name: Expect.stringMatches( + `Load \\d+:.*${escapeStringRegexp(expectedFile)}` + ), + }; + Assert.objectContains(parentRedirectMarkerIntercept, commonProperties); + Assert.objectContains(parentRedirectMarkerReset, commonProperties); + Assert.objectContains(parentStopMarker, commonProperties); + Assert.objectContains(contentStopMarker, commonProperties); + // Note: there's no check for the contentRedirectMarker, because there's + // no marker for a top level navigation redirect in the content process. + + // We get the full set of properties in this case, because we do an actual + // fetch to the network. + const commonDataProperties = { + type: "Network", + status: "STATUS_STOP", + URI: expectedFile, + requestMethod: "GET", + contentType: Expect.stringMatches(/^(text\/html|image\/svg\+xml)$/), + startTime: Expect.number(), + endTime: Expect.number(), + id: Expect.number(), + pri: Expect.number(), + count: Expect.number(), + domainLookupStart: Expect.number(), + domainLookupEnd: Expect.number(), + connectStart: Expect.number(), + tcpConnectEnd: Expect.number(), + connectEnd: Expect.number(), + requestStart: Expect.number(), + responseStart: Expect.number(), + responseEnd: Expect.number(), + }; + + const commonRedirectProperties = { + type: "Network", + status: "STATUS_REDIRECT", + URI: expectedFile, + RedirectURI: expectedFile, + requestMethod: "GET", + contentType: null, + startTime: Expect.number(), + endTime: Expect.number(), + id: Expect.number(), + pri: Expect.number(), + redirectType: "Internal", + isHttpToHttpsRedirect: false, + }; + + if (i === 0) { + // The first marker is special cased: this is the top level navigation + // serviceworker_page.html, + // and in this case we don't have all the same properties. Especially + // the innerWindowID information is missing. + Assert.objectContainsOnly(parentStopMarker.data, { + ...commonDataProperties, + // Note that the parent process has the "cache" information, but not the content + // process. See Bug 1544821. + // Also, because the request races with the cache, these 2 values are valid: + // "Missed" when the cache answered before we get a result from the network. + // "Unresolved" when we got a response from the network before the cache subsystem. + cache: Expect.stringMatches(/^(Missed|Unresolved)$/), + }); + Assert.objectContainsOnly(contentStopMarker.data, commonDataProperties); + + Assert.objectContainsOnly(parentRedirectMarkerIntercept.data, { + ...commonRedirectProperties, + redirectId: parentRedirectMarkerReset.data.id, + cache: "Unresolved", + }); + Assert.objectContainsOnly(parentRedirectMarkerReset.data, { + ...commonRedirectProperties, + redirectId: parentStopMarker.data.id, + }); + + // Note: there's no check for the contentRedirectMarker, because there's + // no marker for a top level navigation redirect in the content process. + } else { + // This is the other file firefox-logo-nightly.svg. + Assert.objectContainsOnly(parentStopMarker.data, { + ...commonDataProperties, + // Because the request races with the cache, these 2 values are valid: + // "Missed" when the cache answered before we get a result from the network. + // "Unresolved" when we got a response from the network before the cache subsystem. + cache: Expect.stringMatches(/^(Missed|Unresolved)$/), + innerWindowID: Expect.number(), + }); + Assert.objectContains(contentStopMarker, commonProperties); + Assert.objectContainsOnly(contentStopMarker.data, { + ...commonDataProperties, + innerWindowID: Expect.number(), + }); + + Assert.objectContainsOnly(parentRedirectMarkerIntercept.data, { + ...commonRedirectProperties, + innerWindowID: Expect.number(), + redirectId: parentRedirectMarkerReset.data.id, + cache: "Unresolved", + }); + Assert.objectContainsOnly(parentRedirectMarkerReset.data, { + ...commonRedirectProperties, + innerWindowID: Expect.number(), + redirectId: parentStopMarker.data.id, + }); + } + } + }); +}); diff --git a/tools/profiler/tests/browser/browser_test_marker_network_serviceworker_synthetized_response.js b/tools/profiler/tests/browser/browser_test_marker_network_serviceworker_synthetized_response.js new file mode 100644 index 0000000000..41690e11a6 --- /dev/null +++ b/tools/profiler/tests/browser/browser_test_marker_network_serviceworker_synthetized_response.js @@ -0,0 +1,558 @@ +/* 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 we emit network markers accordingly. + * In this file we'll test a service worker that returns a synthetized response. + * This means the service worker will make up a response by itself. + */ + +const serviceWorkerFileName = "serviceworker_synthetized_response.js"; +registerCleanupFunction(() => SpecialPowers.removeAllServiceWorkerData()); + +add_task(async function test_network_markers_service_worker_setup() { + // Disabling cache makes the result more predictable. Also this makes things + // simpler when dealing with service workers. + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.cache.disk.enable", false], + ["browser.cache.memory.enable", false], + ], + }); +}); + +add_task(async function test_network_markers_service_worker_register() { + // In this first step, we request an HTML page that will register a service + // worker. We'll wait until the service worker is fully installed before + // checking various things. + Assert.ok( + !Services.profiler.IsActive(), + "The profiler is not currently active" + ); + + const url = `${BASE_URL_HTTPS}serviceworkers/serviceworker_register.html`; + await BrowserTestUtils.withNewTab(url, async contentBrowser => { + await SpecialPowers.spawn( + contentBrowser, + [serviceWorkerFileName], + async function(serviceWorkerFileName) { + await content.wrappedJSObject.registerServiceWorkerAndWait( + serviceWorkerFileName + ); + } + ); + + // Let's make sure we actually have a registered service workers. + const workers = await SpecialPowers.registeredServiceWorkers(); + Assert.equal( + workers.length, + 1, + "One service worker should be properly registered." + ); + }); +}); + +add_task(async function test_network_markers_service_worker_use() { + // In this test, we'll first load a plain html file, then do some fetch + // requests in the context of the page. One request is served with a + // synthetized response, the other request is served with a real "fetch" done + // by the service worker. + Assert.ok( + !Services.profiler.IsActive(), + "The profiler is not currently active" + ); + + startProfilerForMarkerTests(); + + const url = `${BASE_URL_HTTPS}serviceworkers/serviceworker_simple.html`; + await BrowserTestUtils.withNewTab(url, async contentBrowser => { + const contentPid = await SpecialPowers.spawn( + contentBrowser, + [], + () => Services.appinfo.processID + ); + + await SpecialPowers.spawn(contentBrowser, [], async () => { + // This request is served directly by the service worker as a synthetized response. + await content + .fetch("firefox-generated.svg") + .then(res => res.arrayBuffer()); + + // This request is served by a fetch done inside the service worker. + await content + .fetch("firefox-logo-nightly.svg") + .then(res => res.arrayBuffer()); + }); + + const { + parentThread, + contentThread, + profile, + } = await stopProfilerNowAndGetThreads(contentPid); + + // The service worker work happens in a third "thread" or process, let's try + // to find it. + // Currently the fetches happen on the main thread for the content process, + // this may change in the future and we may have to adapt this function. + // Also please note this isn't necessarily the same content process as the + // ones for the tab. + const { serviceWorkerParentThread } = findServiceWorkerThreads(profile); + + ok( + serviceWorkerParentThread, + "We should find a thread for the service worker." + ); + + // By logging a few information about the threads we make debugging easier. + logInformationForThread("parentThread information", parentThread); + logInformationForThread("contentThread information", contentThread); + logInformationForThread( + "serviceWorkerParentThread information", + serviceWorkerParentThread + ); + + const parentNetworkMarkers = getInflatedNetworkMarkers(parentThread) + // When we load a page, Firefox will check the service worker freshness + // after a few seconds. So when the test lasts a long time (with some test + // environments) we might see spurious markers about that that we're not + // interesting in in this part of the test. They're only present in the + // parent process. + .filter(marker => !marker.data.URI.includes(serviceWorkerFileName)); + + const contentNetworkMarkers = getInflatedNetworkMarkers(contentThread); + const serviceWorkerNetworkMarkers = getInflatedNetworkMarkers( + serviceWorkerParentThread + ); + + // Some more logs for debugging purposes. + info( + "Parent network markers: " + JSON.stringify(parentNetworkMarkers, null, 2) + ); + info( + "Content network markers: " + + JSON.stringify(contentNetworkMarkers, null, 2) + ); + info( + "Serviceworker network markers: " + + JSON.stringify(serviceWorkerNetworkMarkers, null, 2) + ); + + const parentPairs = getPairsOfNetworkMarkers(parentNetworkMarkers); + const contentPairs = getPairsOfNetworkMarkers(contentNetworkMarkers); + const serviceWorkerPairs = getPairsOfNetworkMarkers( + serviceWorkerNetworkMarkers + ); + + // First, make sure we properly matched all start with stop markers. This + // means that both arrays should contain only arrays of 2 elements. + parentPairs.forEach(pair => + Assert.equal( + pair.length, + 2, + `For the URL ${pair[0].data.URI} we should get 2 markers in the parent process.` + ) + ); + + contentPairs.forEach(pair => + Assert.equal( + pair.length, + 2, + `For the URL ${pair[0].data.URI} we should get 2 markers in the content process.` + ) + ); + serviceWorkerPairs.forEach(pair => + Assert.equal( + pair.length, + 2, + `For the URL ${pair[0].data.URI} we should get 2 markers in the service worker process.` + ) + ); + + // Let's look at all pairs and make sure we requested all expected files. + // In this test, we should have redirect markers as well as stop markers, + // because this case generates internal redirects. + // Let's create various arrays to help assert. + + let parentStopMarkers = parentPairs.map(([_, stopMarker]) => stopMarker); + const contentStopMarkers = contentPairs.map( + ([_, stopMarker]) => stopMarker + ); + const serviceWorkerStopMarkers = serviceWorkerPairs.map( + ([_, stopMarker]) => stopMarker + ); + + // In this test we have very different results in the various threads, so + // we'll assert every case separately. + // A simple function to help constructing better assertions: + const fullUrl = filename => `${BASE_URL_HTTPS}serviceworkers/${filename}`; + + { + // In the parent process, we have 8 network markers: + // - twice the html file -- because it's not cached by the SW, we get the + // marker both for the initial request and for the request initied from the + // SW. + // - twice the firefox svg file -- similar situation + // - once the generated svg file -- this one isn't fetched by the SW but + // rather forged directly, so there's no "second fetch", and thus we have + // only one marker. + // - for each of these files, we have first an internal redirect from the + // main channel to the service worker. => 3 redirect markers more. + Assert.equal( + parentStopMarkers.length, + 8, // 3 html files, 3 firefox svg files, 2 generated svg file + "There should be 8 stop markers in the parent process." + ); + + // The "1" requests are the initial requests that are intercepted, coming + // from the web page, while the "2" requests are requests to the network, + // coming from the service worker. The 1 were requested before 2, 2 ends + // before 1. + // "Intercept" requests are the internal redirects from the main channel + // to the service worker. They happen before others. + const [ + htmlFetchIntercept, + htmlFetch1, + htmlFetch2, + generatedSvgIntercept, + generatedSvgFetch, + firefoxSvgIntercept, + firefoxSvgFetch1, + firefoxSvgFetch2, + ] = parentStopMarkers; + + /* ----- /HTML FILE ---- */ + Assert.objectContains(htmlFetchIntercept, { + name: Expect.stringMatches(/Load \d+:.*serviceworker_simple.html/), + data: Expect.objectContainsOnly({ + type: "Network", + status: "STATUS_REDIRECT", + URI: fullUrl("serviceworker_simple.html"), + requestMethod: "GET", + contentType: null, + startTime: Expect.number(), + endTime: Expect.number(), + id: Expect.number(), + pri: Expect.number(), + redirectId: htmlFetch1.data.id, + redirectType: "Internal", + isHttpToHttpsRedirect: false, + RedirectURI: fullUrl("serviceworker_simple.html"), + cache: "Unresolved", + }), + }); + + Assert.objectContains(htmlFetch1, { + name: Expect.stringMatches(/Load \d+:.*serviceworker_simple.html/), + data: Expect.objectContainsOnly({ + type: "Network", + status: "STATUS_STOP", + URI: fullUrl("serviceworker_simple.html"), + requestMethod: "GET", + contentType: "text/html", + startTime: Expect.number(), + endTime: Expect.number(), + id: Expect.number(), + pri: Expect.number(), + }), + }); + Assert.objectContains(htmlFetch2, { + name: Expect.stringMatches(/Load \d+:.*serviceworker_simple.html/), + data: Expect.objectContainsOnly({ + type: "Network", + status: "STATUS_STOP", + URI: fullUrl("serviceworker_simple.html"), + requestMethod: "GET", + contentType: "text/html", + // Because the request races with the cache, these 2 values are valid: + // "Missed" when the cache answered before we get a result from the network. + // "Unresolved" when we got a response from the network before the cache subsystem. + cache: Expect.stringMatches(/^(Missed|Unresolved)$/), + startTime: Expect.number(), + endTime: Expect.number(), + domainLookupStart: Expect.number(), + domainLookupEnd: Expect.number(), + connectStart: Expect.number(), + tcpConnectEnd: Expect.number(), + connectEnd: Expect.number(), + requestStart: Expect.number(), + responseStart: Expect.number(), + responseEnd: Expect.number(), + id: Expect.number(), + count: Expect.number(), + pri: Expect.number(), + }), + }); + /* ----- /HTML FILE ---- */ + + /* ----- GENERATED SVG FILE ---- */ + Assert.objectContains(generatedSvgIntercept, { + name: Expect.stringMatches(/Load \d+:.*firefox-generated.svg/), + data: Expect.objectContainsOnly({ + type: "Network", + status: "STATUS_REDIRECT", + URI: fullUrl("firefox-generated.svg"), + requestMethod: "GET", + contentType: null, + startTime: Expect.number(), + endTime: Expect.number(), + id: Expect.number(), + pri: Expect.number(), + redirectId: generatedSvgFetch.data.id, + redirectType: "Internal", + isHttpToHttpsRedirect: false, + RedirectURI: fullUrl("firefox-generated.svg"), + cache: "Unresolved", + innerWindowID: Expect.number(), + }), + }); + Assert.objectContains(generatedSvgFetch, { + name: Expect.stringMatches(/Load \d+:.*firefox-generated.svg/), + data: Expect.objectContainsOnly({ + type: "Network", + status: "STATUS_STOP", + URI: fullUrl("firefox-generated.svg"), + requestMethod: "GET", + contentType: "image/svg+xml", + startTime: Expect.number(), + endTime: Expect.number(), + id: Expect.number(), + pri: Expect.number(), + innerWindowID: Expect.number(), + }), + }); + /* ----- ∕GENERATED SVG FILE ---- */ + /* ----- REQUESTED SVG FILE ---- */ + Assert.objectContains(firefoxSvgIntercept, { + name: Expect.stringMatches(/Load \d+:.*firefox-logo-nightly.svg/), + data: Expect.objectContainsOnly({ + type: "Network", + status: "STATUS_REDIRECT", + URI: fullUrl("firefox-logo-nightly.svg"), + requestMethod: "GET", + contentType: null, + startTime: Expect.number(), + endTime: Expect.number(), + id: Expect.number(), + pri: Expect.number(), + redirectId: firefoxSvgFetch1.data.id, + redirectType: "Internal", + isHttpToHttpsRedirect: false, + RedirectURI: fullUrl("firefox-logo-nightly.svg"), + cache: "Unresolved", + innerWindowID: Expect.number(), + }), + }); + Assert.objectContains(firefoxSvgFetch1, { + name: Expect.stringMatches(/Load \d+:.*firefox-logo-nightly.svg/), + data: Expect.objectContainsOnly({ + type: "Network", + status: "STATUS_STOP", + URI: fullUrl("firefox-logo-nightly.svg"), + requestMethod: "GET", + contentType: "image/svg+xml", + startTime: Expect.number(), + endTime: Expect.number(), + id: Expect.number(), + pri: Expect.number(), + innerWindowID: Expect.number(), + }), + }); + Assert.objectContains(firefoxSvgFetch2, { + name: Expect.stringMatches(/Load \d+:.*firefox-logo-nightly.svg/), + data: Expect.objectContainsOnly({ + type: "Network", + status: "STATUS_STOP", + URI: fullUrl("firefox-logo-nightly.svg"), + requestMethod: "GET", + contentType: "image/svg+xml", + // Because the request races with the cache, these 2 values are valid: + // "Missed" when the cache answered before we get a result from the network. + // "Unresolved" when we got a response from the network before the cache subsystem. + cache: Expect.stringMatches(/^(Missed|Unresolved)$/), + startTime: Expect.number(), + endTime: Expect.number(), + domainLookupStart: Expect.number(), + domainLookupEnd: Expect.number(), + connectStart: Expect.number(), + tcpConnectEnd: Expect.number(), + connectEnd: Expect.number(), + requestStart: Expect.number(), + responseStart: Expect.number(), + responseEnd: Expect.number(), + id: Expect.number(), + count: Expect.number(), + pri: Expect.number(), + // Note: no innerWindowID here, is that a bug? + }), + }); + /* ----- ∕REQUESTED SVG FILE ---- */ + } + + // It's possible that the service worker thread IS the content thread, in + // that case we'll get all markers in the same thread. + // The "1" requests are the initial requests that are intercepted, coming + // from the web page, while the "2" requests are the requests coming from + // the service worker. + let htmlFetch1, + htmlFetch2, + generatedSvgFetch1, + firefoxSvgFetch1, + firefoxSvgFetch2; + + // First, let's handle the case where the threads are different: + if (serviceWorkerParentThread !== contentThread) { + // In the content process (that is the process for the web page), we have + // 3 network markers: + // - 1 for the HTML page + // - 1 for the generated svg file + // - 1 for the firefox svg file + // Indeed, the service worker interception is invisible from the context + // of the web page, so we just get 3 "normal" requests. However these + // requests will miss all timing information, because they're hidden by + // the service worker interception. We may want to fix this... + Assert.equal( + contentStopMarkers.length, + 3, // 1 for each file + "There should be 3 stop markers in the content process." + ); + + [htmlFetch1, generatedSvgFetch1, firefoxSvgFetch1] = contentStopMarkers; + + // In the service worker parent thread, we have 2 network markers: + // - the HTML file + // - the firefox SVG file. + // Remember that the generated SVG file is returned directly by the SW. + Assert.equal( + serviceWorkerStopMarkers.length, + 2, + "There should be 2 stop markers in the service worker thread." + ); + + [htmlFetch2, firefoxSvgFetch2] = serviceWorkerStopMarkers; + } else { + // Else case: the service worker parent thread IS the content thread + // (note: this is always the case with fission). In that case all network + // markers tested in the above block are together in the same object. + Assert.equal( + contentStopMarkers.length, + 5, + "There should be 5 stop markers in the combined process (containing both the content page and the service worker)" + ); + + // Because of how the test is done, these markers are ordered by the + // position of the START markers. + [ + // For the htmlFetch request, note that 2 is before 1, because that's + // the top level navigation. Indeed for the top level navigation + // everything happens first in the main process, possibly before a + // content process even exists, and the content process is merely + // notified at the end. + htmlFetch2, + htmlFetch1, + generatedSvgFetch1, + firefoxSvgFetch1, + firefoxSvgFetch2, + ] = contentStopMarkers; + } + + // Let's test first the markers coming from the content page. + Assert.objectContains(htmlFetch1, { + name: Expect.stringMatches(/Load \d+:.*serviceworker_simple.html/), + data: Expect.objectContainsOnly({ + type: "Network", + status: "STATUS_STOP", + URI: fullUrl("serviceworker_simple.html"), + requestMethod: "GET", + contentType: "text/html", + startTime: Expect.number(), + endTime: Expect.number(), + id: Expect.number(), + pri: Expect.number(), + }), + }); + Assert.objectContains(generatedSvgFetch1, { + name: Expect.stringMatches(/Load \d+:.*firefox-generated.svg/), + data: Expect.objectContainsOnly({ + type: "Network", + status: "STATUS_STOP", + URI: fullUrl("firefox-generated.svg"), + requestMethod: "GET", + contentType: "image/svg+xml", + startTime: Expect.number(), + endTime: Expect.number(), + id: Expect.number(), + pri: Expect.number(), + innerWindowID: Expect.number(), + }), + }); + Assert.objectContains(firefoxSvgFetch1, { + name: Expect.stringMatches(/Load \d+:.*firefox-logo-nightly.svg/), + data: Expect.objectContainsOnly({ + type: "Network", + status: "STATUS_STOP", + URI: fullUrl("firefox-logo-nightly.svg"), + requestMethod: "GET", + contentType: "image/svg+xml", + startTime: Expect.number(), + endTime: Expect.number(), + id: Expect.number(), + pri: Expect.number(), + innerWindowID: Expect.number(), + }), + }); + + // Now let's test the markers coming from the service worker. + Assert.objectContains(htmlFetch2, { + name: Expect.stringMatches(/Load \d+:.*serviceworker_simple.html/), + data: Expect.objectContainsOnly({ + type: "Network", + status: "STATUS_STOP", + URI: fullUrl("serviceworker_simple.html"), + requestMethod: "GET", + contentType: "text/html", + startTime: Expect.number(), + endTime: Expect.number(), + domainLookupStart: Expect.number(), + domainLookupEnd: Expect.number(), + connectStart: Expect.number(), + tcpConnectEnd: Expect.number(), + connectEnd: Expect.number(), + requestStart: Expect.number(), + responseStart: Expect.number(), + responseEnd: Expect.number(), + id: Expect.number(), + count: Expect.number(), + pri: Expect.number(), + // Note: no innerWindowID here, is that a bug? + // Note: no cache either, this is bug 1544821. + }), + }); + + Assert.objectContains(firefoxSvgFetch2, { + name: Expect.stringMatches(/Load \d+:.*firefox-logo-nightly.svg/), + data: Expect.objectContainsOnly({ + type: "Network", + status: "STATUS_STOP", + URI: fullUrl("firefox-logo-nightly.svg"), + requestMethod: "GET", + contentType: "image/svg+xml", + startTime: Expect.number(), + endTime: Expect.number(), + domainLookupStart: Expect.number(), + domainLookupEnd: Expect.number(), + connectStart: Expect.number(), + tcpConnectEnd: Expect.number(), + connectEnd: Expect.number(), + requestStart: Expect.number(), + responseStart: Expect.number(), + responseEnd: Expect.number(), + id: Expect.number(), + count: Expect.number(), + pri: Expect.number(), + // Note: no innerWindowID here, is that a bug? + // Note: no cache either, this is bug 1544821. + }), + }); + }); +}); diff --git a/tools/profiler/tests/browser/browser_test_marker_network_simple.js b/tools/profiler/tests/browser/browser_test_marker_network_simple.js new file mode 100644 index 0000000000..15894305a7 --- /dev/null +++ b/tools/profiler/tests/browser/browser_test_marker_network_simple.js @@ -0,0 +1,81 @@ +/* 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 we emit network markers accordingly + */ +add_task(async function test_network_markers() { + Assert.ok( + !Services.profiler.IsActive(), + "The profiler is not currently active" + ); + + startProfilerForMarkerTests(); + + const url = BASE_URL + "simple.html?cacheBust=" + Math.random(); + await BrowserTestUtils.withNewTab(url, async contentBrowser => { + const contentPid = await SpecialPowers.spawn( + contentBrowser, + [], + () => Services.appinfo.processID + ); + + const { parentThread, contentThread } = await stopProfilerNowAndGetThreads( + contentPid + ); + + const parentNetworkMarkers = getInflatedNetworkMarkers(parentThread); + const contentNetworkMarkers = getInflatedNetworkMarkers(contentThread); + info(JSON.stringify(parentNetworkMarkers, null, 2)); + info(JSON.stringify(contentNetworkMarkers, null, 2)); + + Assert.equal( + parentNetworkMarkers.length, + 2, + `We should get a pair of network markers in the parent thread.` + ); + Assert.equal( + contentNetworkMarkers.length, + 2, + `We should get a pair of network markers in the content thread.` + ); + + const parentStopMarker = parentNetworkMarkers[1]; + const contentStopMarker = contentNetworkMarkers[1]; + + const expectedProperties = { + name: Expect.stringMatches(`Load \\d+:.*${escapeStringRegexp(url)}`), + data: Expect.objectContains({ + status: "STATUS_STOP", + URI: url, + requestMethod: "GET", + contentType: "text/html", + startTime: Expect.number(), + endTime: Expect.number(), + domainLookupStart: Expect.number(), + domainLookupEnd: Expect.number(), + connectStart: Expect.number(), + tcpConnectEnd: Expect.number(), + connectEnd: Expect.number(), + requestStart: Expect.number(), + responseStart: Expect.number(), + responseEnd: Expect.number(), + id: Expect.number(), + count: Expect.number(), + pri: Expect.number(), + }), + }; + + Assert.objectContains(parentStopMarker, expectedProperties); + // The cache information is missing from the content marker, it's only part + // of the parent marker. See Bug 1544821. + Assert.objectContains(parentStopMarker.data, { + // Because the request races with the cache, these 2 values are valid: + // "Missed" when the cache answered before we get a result from the network. + // "Unresolved" when we got a response from the network before the cache subsystem. + cache: Expect.stringMatches(/^(Missed|Unresolved)$/), + }); + Assert.objectContains(contentStopMarker, expectedProperties); + }); +}); diff --git a/tools/profiler/tests/browser/browser_test_marker_network_sts.js b/tools/profiler/tests/browser/browser_test_marker_network_sts.js new file mode 100644 index 0000000000..26f2a1c756 --- /dev/null +++ b/tools/profiler/tests/browser/browser_test_marker_network_sts.js @@ -0,0 +1,130 @@ +/* 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 we emit network markers accordingly. + * In this file we'll test that we behave properly with STS redirections. + */ + +add_task(async function test_network_markers_service_worker_setup() { + await SpecialPowers.pushPrefEnv({ + set: [ + // Disabling cache makes the result more predictable especially in verify mode. + ["browser.cache.disk.enable", false], + ["browser.cache.memory.enable", false], + // We want to test upgrading requests + ["dom.security.https_only_mode", true], + ], + }); +}); + +add_task(async function test_network_markers_redirect_to_https() { + // In this test, we request an HTML page with http that gets redirected to https. + Assert.ok( + !Services.profiler.IsActive(), + "The profiler is not currently active" + ); + + startProfilerForMarkerTests(); + + const url = BASE_URL + "simple.html"; + const targetUrl = BASE_URL_HTTPS + "simple.html"; + + await BrowserTestUtils.withNewTab(url, async contentBrowser => { + const contentPid = await SpecialPowers.spawn( + contentBrowser, + [], + () => Services.appinfo.processID + ); + + const { parentThread, contentThread } = await stopProfilerNowAndGetThreads( + contentPid + ); + + const parentNetworkMarkers = getInflatedNetworkMarkers(parentThread); + const contentNetworkMarkers = getInflatedNetworkMarkers(contentThread); + info(JSON.stringify(parentNetworkMarkers, null, 2)); + info(JSON.stringify(contentNetworkMarkers, null, 2)); + + Assert.equal( + parentNetworkMarkers.length, + 4, + `We should get 2 pairs of network markers in the parent thread.` + ); + + /* It looks like that for a redirection for the top level navigation, the + * content thread sees the markers for the second request only. + * See Bug 1692879. */ + Assert.equal( + contentNetworkMarkers.length, + 2, + `We should get one pair of network markers in the content thread.` + ); + + const parentRedirectMarker = parentNetworkMarkers[1]; + const parentStopMarker = parentNetworkMarkers[3]; + // There's no content redirect marker for the reason outlined above. + const contentStopMarker = contentNetworkMarkers[1]; + + Assert.objectContains(parentRedirectMarker, { + name: Expect.stringMatches(`Load \\d+:.*${escapeStringRegexp(url)}`), + data: Expect.objectContainsOnly({ + type: "Network", + status: "STATUS_REDIRECT", + URI: url, + RedirectURI: targetUrl, + requestMethod: "GET", + contentType: null, + startTime: Expect.number(), + endTime: Expect.number(), + id: Expect.number(), + redirectId: parentStopMarker.data.id, + pri: Expect.number(), + cache: "Unresolved", + redirectType: "Permanent", + isHttpToHttpsRedirect: true, + }), + }); + + const expectedProperties = { + name: Expect.stringMatches( + `Load \\d+:.*${escapeStringRegexp(targetUrl)}` + ), + }; + const expectedDataProperties = { + type: "Network", + status: "STATUS_STOP", + URI: targetUrl, + requestMethod: "GET", + contentType: "text/html", + startTime: Expect.number(), + endTime: Expect.number(), + domainLookupStart: Expect.number(), + domainLookupEnd: Expect.number(), + connectStart: Expect.number(), + tcpConnectEnd: Expect.number(), + connectEnd: Expect.number(), + requestStart: Expect.number(), + responseStart: Expect.number(), + responseEnd: Expect.number(), + id: Expect.number(), + count: Expect.number(), + pri: Expect.number(), + }; + + Assert.objectContains(parentStopMarker, expectedProperties); + Assert.objectContains(contentStopMarker, expectedProperties); + + // The cache information is missing from the content marker, it's only part + // of the parent marker. See Bug 1544821. + Assert.objectContainsOnly(parentStopMarker.data, { + ...expectedDataProperties, + // Because the request races with the cache, these 2 values are valid: + // "Missed" when the cache answered before we get a result from the network. + // "Unresolved" when we got a response from the network before the cache subsystem. + cache: Expect.stringMatches(/^(Missed|Unresolved)$/), + }); + Assert.objectContainsOnly(contentStopMarker.data, expectedDataProperties); + }); +}); diff --git a/tools/profiler/tests/browser/browser_test_markers_gc_cc.js b/tools/profiler/tests/browser/browser_test_markers_gc_cc.js new file mode 100644 index 0000000000..a4a94d60cc --- /dev/null +++ b/tools/profiler/tests/browser/browser_test_markers_gc_cc.js @@ -0,0 +1,49 @@ +/* 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/. */ + +add_task(async function test_markers_gc_cc() { + info("Test GC&CC markers."); + + info("Create a throwaway profile."); + await startProfiler({}); + let tempProfileContainer = { profile: null }; + tempProfileContainer.profile = await waitSamplingAndStopAndGetProfile(); + + info("Restart the profiler."); + await startProfiler({}); + + info("Throw away the previous profile, which should be garbage-collected."); + Assert.equal( + typeof tempProfileContainer.profile, + "object", + "Previously-captured profile should be an object" + ); + delete tempProfileContainer.profile; + Assert.equal( + typeof tempProfileContainer.profile, + "undefined", + "Deleted profile should now be undefined" + ); + + info("Force GC&CC"); + SpecialPowers.gc(); + SpecialPowers.forceShrinkingGC(); + SpecialPowers.forceCC(); + SpecialPowers.gc(); + SpecialPowers.forceShrinkingGC(); + SpecialPowers.forceCC(); + + info("Stop the profiler and get the profile."); + const profile = await waitSamplingAndStopAndGetProfile(); + + const markers = getInflatedMarkerData(profile.threads[0]); + Assert.ok( + markers.some(({ data }) => data?.type === "GCSlice"), + "A GCSlice marker was recorded" + ); + Assert.ok( + markers.some(({ data }) => data?.type === "CCSlice"), + "A CCSlice marker was recorded" + ); +}); diff --git a/tools/profiler/tests/browser/browser_test_markers_parent_process.js b/tools/profiler/tests/browser/browser_test_markers_parent_process.js new file mode 100644 index 0000000000..28b82f8054 --- /dev/null +++ b/tools/profiler/tests/browser/browser_test_markers_parent_process.js @@ -0,0 +1,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/. */ + +add_task(async function test_markers_parent_process() { + info("Test markers that are generated by the browser's parent process."); + + info("Start the profiler in nostacksampling mode."); + await startProfiler({ features: ["nostacksampling"] }); + + info("Dispatch a DOMEvent"); + window.dispatchEvent(new Event("synthetic")); + + info("Stop the profiler and get the profile."); + const profile = await stopNowAndGetProfile(); + + const markers = getInflatedMarkerData(profile.threads[0]); + { + const domEventStart = markers.find( + ({ phase, data }) => + phase === INTERVAL_START && data?.eventType === "synthetic" + ); + const domEventEnd = markers.find( + ({ phase, data }) => + phase === INTERVAL_END && data?.eventType === "synthetic" + ); + ok(domEventStart, "A start DOMEvent was generated"); + ok(domEventEnd, "An end DOMEvent was generated"); + ok( + domEventEnd.data.latency > 0, + "DOMEvent had a a latency value generated." + ); + ok(domEventEnd.data.type === "DOMEvent"); + ok(domEventEnd.name === "DOMEvent"); + } + // Add more marker tests. +}); diff --git a/tools/profiler/tests/browser/browser_test_profile_capture_by_pid.js b/tools/profiler/tests/browser/browser_test_profile_capture_by_pid.js new file mode 100644 index 0000000000..3694ffd93e --- /dev/null +++ b/tools/profiler/tests/browser/browser_test_profile_capture_by_pid.js @@ -0,0 +1,206 @@ +/* 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/. */ + +function ProcessHasSamplerThread(process) { + return process.threads.some(t => t.name == "SamplerThread"); +} + +async function GetPidsWithSamplerThread() { + let parentProc = await ChromeUtils.requestProcInfo(); + + let pids = parentProc.children + .filter(ProcessHasSamplerThread) + .map(proc => proc.pid); + if (ProcessHasSamplerThread(parentProc)) { + pids.unshift(parentProc.pid); + } + return pids; +} + +// fnFilterWithContentId: Called with content child pid, returns filters to use. +// E.g.: 123 => ["GeckoMain", "pid:123"], or 123 => ["pid:456"]. +async function test_with_filter(fnFilterWithContentId) { + Assert.ok(!Services.profiler.IsActive()); + info("Clear the previous pages just in case we still some open tabs."); + await Services.profiler.ClearAllPages(); + + info("Open a tab with single_frame.html in it."); + const url = BASE_URL + "single_frame.html"; + return BrowserTestUtils.withNewTab(url, async function(contentBrowser) { + const contentPid = await SpecialPowers.spawn(contentBrowser, [], () => { + return Services.appinfo.processID; + }); + + Assert.deepEqual( + await GetPidsWithSamplerThread(), + [], + "There should be no SamplerThreads before starting the profiler" + ); + + info("Start the profiler to test filters including 'pid:<content>'."); + await startProfiler({ threads: fnFilterWithContentId(contentPid) }); + + let pidsWithSamplerThread = null; + await TestUtils.waitForCondition( + async function() { + let pidsStringBefore = JSON.stringify(pidsWithSamplerThread); + pidsWithSamplerThread = await GetPidsWithSamplerThread(); + return JSON.stringify(pidsWithSamplerThread) == pidsStringBefore; + }, + "Wait for sampler threads to stabilize after profiler start", + /* interval (ms) */ 250, + /* maxTries */ 10 + ); + + info("Capture the profile data."); + const profile = await waitSamplingAndStopAndGetProfile(); + + await TestUtils.waitForCondition(async function() { + return !(await GetPidsWithSamplerThread()).length; + }, "Wait for all sampler threads to stop after profiler stop"); + + return { contentPid, pidsWithSamplerThread, profile }; + }); +} + +add_task(async function browser_test_profile_capture_along_with_content_pid() { + const { + contentPid, + pidsWithSamplerThread, + profile, + } = await test_with_filter(contentPid => ["GeckoMain", "pid:" + contentPid]); + + Assert.greater( + pidsWithSamplerThread.length, + 2, + "There should be lots of SamplerThreads after starting the profiler" + ); + + let contentProcessIndex = profile.processes.findIndex( + p => p.threads[0].pid == contentPid + ); + Assert.notEqual( + contentProcessIndex, + -1, + "The content process should be present" + ); + + // Note: Some threads may not be registered, so we can't expect that many. But + // 10 is much more than the default 4. + Assert.greater( + profile.processes[contentProcessIndex].threads.length, + 10, + "The content process should have many threads" + ); + + Assert.equal( + profile.threads.length, + 1, + "The parent process should have only one thread" + ); + Assert.equal( + profile.threads[0].name, + "GeckoMain", + "The parent process should have the main thread" + ); +}); + +add_task(async function browser_test_profile_capture_along_with_other_pid() { + const parentPid = Services.appinfo.processID; + const { + contentPid, + pidsWithSamplerThread, + profile, + } = await test_with_filter(contentPid => ["GeckoMain", "pid:" + parentPid]); + + Assert.greater( + pidsWithSamplerThread.length, + 2, + "There should be lots of SamplerThreads after starting the profiler" + ); + + let contentProcessIndex = profile.processes.findIndex( + p => p.threads[0].pid == contentPid + ); + Assert.notEqual( + contentProcessIndex, + -1, + "The content process should be present" + ); + + Assert.equal( + profile.processes[contentProcessIndex].threads.length, + 1, + "The content process should have only one thread" + ); + + // Note: Some threads may not be registered, so we can't expect that many. But + // 10 is much more than the default 4. + Assert.greater( + profile.threads.length, + 10, + "The parent process should have many threads" + ); +}); + +add_task(async function browser_test_profile_capture_by_only_content_pid() { + const parentPid = Services.appinfo.processID; + const { + contentPid, + pidsWithSamplerThread, + profile, + } = await test_with_filter(contentPid => ["pid:" + contentPid]); + + // The sampler thread always runs in the parent process, see bug 1754100. + Assert.deepEqual( + pidsWithSamplerThread, + [parentPid, contentPid], + "There should only be SamplerThreads in the parent and the target child" + ); + + Assert.equal( + profile.processes.length, + 1, + "There should only be one child process" + ); + // Note: Some threads may not be registered, so we can't expect that many. But + // 10 is much more than the default 4. + Assert.greater( + profile.processes[0].threads.length, + 10, + "The child process should have many threads" + ); + Assert.equal( + profile.processes[0].threads[0].pid, + contentPid, + "The only child process should be our content" + ); +}); + +add_task(async function browser_test_profile_capture_by_only_parent_pid() { + const parentPid = Services.appinfo.processID; + const { + pidsWithSamplerThread, + profile, + } = await test_with_filter(contentPid => ["pid:" + parentPid]); + + Assert.deepEqual( + pidsWithSamplerThread, + [parentPid], + "There should only be a SamplerThread in the parent" + ); + + // Note: Some threads may not be registered, so we can't expect that many. But + // 10 is much more than the default 4. + Assert.greater( + profile.threads.length, + 10, + "The parent process should have many threads" + ); + Assert.equal( + profile.processes.length, + 0, + "There should be no child processes" + ); +}); diff --git a/tools/profiler/tests/browser/browser_test_profile_fission.js b/tools/profiler/tests/browser/browser_test_profile_fission.js new file mode 100644 index 0000000000..f3d8016569 --- /dev/null +++ b/tools/profiler/tests/browser/browser_test_profile_fission.js @@ -0,0 +1,196 @@ +/* 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 (SpecialPowers.useRemoteSubframes) { + // Bug 1586105: these tests could time out in some extremely slow conditions, + // when fission is enabled. + // Requesting a longer timeout should make it pass. + requestLongerTimeout(2); +} + +add_task(async function test_profile_fission_no_private_browsing() { + // Requesting the complete log to be able to debug Bug 1586105. + SimpleTest.requestCompleteLog(); + Assert.ok(!Services.profiler.IsActive()); + info("Clear the previous pages just in case we still have some open tabs."); + await Services.profiler.ClearAllPages(); + + info( + "Start the profiler to test the page information with single frame page." + ); + await startProfiler(); + + info("Open a private window with single_frame.html in it."); + const win = await BrowserTestUtils.openNewBrowserWindow({ + fission: true, + }); + + try { + const url = BASE_URL_HTTPS + "single_frame.html"; + const contentBrowser = win.gBrowser.selectedBrowser; + BrowserTestUtils.loadURI(contentBrowser, url); + await BrowserTestUtils.browserLoaded(contentBrowser, false, url); + + const parentPid = Services.appinfo.processID; + const contentPid = await SpecialPowers.spawn(contentBrowser, [], () => { + return Services.appinfo.processID; + }); + + // Getting the active Browser ID to assert the page info tabID later. + const activeTabID = contentBrowser.browsingContext.browserId; + + info("Capture the profile data."); + const { + profile, + contentProcess, + contentThread, + } = await stopProfilerNowAndGetThreads(contentPid); + + Assert.equal( + contentThread.isPrivateBrowsing, + false, + "The content process has the private browsing flag set to false." + ); + + Assert.equal( + contentThread.userContextId, + 0, + "The content process has the information about the container used for this process" + ); + + info( + "Check if the captured page is the one with correct values we created." + ); + + let pageFound = false; + for (const page of contentProcess.pages) { + if (page.url == url) { + Assert.equal(page.url, url); + Assert.equal(typeof page.tabID, "number"); + Assert.equal(page.tabID, activeTabID); + Assert.equal(typeof page.innerWindowID, "number"); + // Top level document will have no embedder. + Assert.equal(page.embedderInnerWindowID, 0); + Assert.equal(typeof page.isPrivateBrowsing, "boolean"); + Assert.equal(page.isPrivateBrowsing, false); + pageFound = true; + break; + } + } + Assert.equal(pageFound, true); + + info("Check that the profiling logs exist with the expected properties."); + Assert.equal(typeof profile.profilingLog, "object"); + Assert.equal(typeof profile.profilingLog[parentPid], "object"); + const parentLog = profile.profilingLog[parentPid]; + Assert.equal(typeof parentLog.profilingLogBegin_TSms, "number"); + Assert.equal(typeof parentLog.profilingLogEnd_TSms, "number"); + Assert.equal(typeof parentLog.bufferGlobalController, "object"); + Assert.equal( + typeof parentLog.bufferGlobalController.controllerCreationTime_TSms, + "number" + ); + + Assert.equal(typeof profile.profileGatheringLog, "object"); + Assert.equal(typeof profile.profileGatheringLog[parentPid], "object"); + Assert.equal( + typeof profile.profileGatheringLog[parentPid] + .profileGatheringLogBegin_TSms, + "number" + ); + Assert.equal( + typeof profile.profileGatheringLog[parentPid].profileGatheringLogEnd_TSms, + "number" + ); + + Assert.equal(typeof contentProcess.profilingLog, "object"); + Assert.equal(typeof contentProcess.profilingLog[contentPid], "object"); + Assert.equal( + typeof contentProcess.profilingLog[contentPid].profilingLogBegin_TSms, + "number" + ); + Assert.equal( + typeof contentProcess.profilingLog[contentPid].profilingLogEnd_TSms, + "number" + ); + + Assert.equal(typeof contentProcess.profileGatheringLog, "undefined"); + } finally { + await BrowserTestUtils.closeWindow(win); + } +}); + +add_task(async function test_profile_fission_private_browsing() { + // Requesting the complete log to be able to debug Bug 1586105. + SimpleTest.requestCompleteLog(); + Assert.ok(!Services.profiler.IsActive()); + info("Clear the previous pages just in case we still have some open tabs."); + await Services.profiler.ClearAllPages(); + + info( + "Start the profiler to test the page information with single frame page." + ); + await startProfiler(); + + info("Open a private window with single_frame.html in it."); + const win = await BrowserTestUtils.openNewBrowserWindow({ + private: true, + fission: true, + }); + + try { + const url = BASE_URL_HTTPS + "single_frame.html"; + const contentBrowser = win.gBrowser.selectedBrowser; + BrowserTestUtils.loadURI(contentBrowser, url); + await BrowserTestUtils.browserLoaded(contentBrowser, false, url); + + const contentPid = await SpecialPowers.spawn(contentBrowser, [], () => { + return Services.appinfo.processID; + }); + + // Getting the active Browser ID to assert the page info tabID later. + const activeTabID = contentBrowser.browsingContext.browserId; + + info("Capture the profile data."); + const { + contentProcess, + contentThread, + } = await stopProfilerNowAndGetThreads(contentPid); + + Assert.equal( + contentThread.isPrivateBrowsing, + true, + "The content process has the private browsing flag set to true." + ); + + Assert.equal( + contentThread.userContextId, + 0, + "The content process has the information about the container used for this process" + ); + + info( + "Check if the captured page is the one with correct values we created." + ); + + let pageFound = false; + for (const page of contentProcess.pages) { + if (page.url == url) { + Assert.equal(page.url, url); + Assert.equal(typeof page.tabID, "number"); + Assert.equal(page.tabID, activeTabID); + Assert.equal(typeof page.innerWindowID, "number"); + // Top level document will have no embedder. + Assert.equal(page.embedderInnerWindowID, 0); + Assert.equal(typeof page.isPrivateBrowsing, "boolean"); + Assert.equal(page.isPrivateBrowsing, true); + pageFound = true; + break; + } + } + Assert.equal(pageFound, true); + } finally { + await BrowserTestUtils.closeWindow(win); + } +}); diff --git a/tools/profiler/tests/browser/browser_test_profile_multi_frame_page_info.js b/tools/profiler/tests/browser/browser_test_profile_multi_frame_page_info.js new file mode 100644 index 0000000000..6d48306432 --- /dev/null +++ b/tools/profiler/tests/browser/browser_test_profile_multi_frame_page_info.js @@ -0,0 +1,83 @@ +/* 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 (SpecialPowers.useRemoteSubframes) { + // Bug 1586105: these tests could time out in some extremely slow conditions, + // when fission is enabled. + // Requesting a longer timeout should make it pass. + requestLongerTimeout(2); +} + +add_task(async function test_profile_multi_frame_page_info() { + // Requesting the complete log to be able to debug Bug 1586105. + SimpleTest.requestCompleteLog(); + Assert.ok(!Services.profiler.IsActive()); + info("Clear the previous pages just in case we still have some open tabs."); + await Services.profiler.ClearAllPages(); + + info( + "Start the profiler to test the page information with multi frame page." + ); + await startProfiler(); + + info("Open a tab with multi_frame.html in it."); + // multi_frame.html embeds single_frame.html inside an iframe. + const url = BASE_URL + "multi_frame.html"; + await BrowserTestUtils.withNewTab(url, async function(contentBrowser) { + const contentPid = await SpecialPowers.spawn(contentBrowser, [], () => { + return Services.appinfo.processID; + }); + + // Getting the active Browser ID to assert the page info tabID later. + const win = Services.wm.getMostRecentWindow("navigator:browser"); + const activeTabID = win.gBrowser.selectedBrowser.browsingContext.browserId; + + info("Capture the profile data."); + const { contentProcess } = await stopProfilerNowAndGetThreads(contentPid); + + info( + "Check if the captured pages are the ones with correct values we created." + ); + + let parentPage; + let foundPage = 0; + for (const page of contentProcess.pages) { + // Parent page + if (page.url == url) { + Assert.equal(page.url, url); + Assert.equal(typeof page.tabID, "number"); + Assert.equal(page.tabID, activeTabID); + Assert.equal(typeof page.innerWindowID, "number"); + // Top level document will have no embedder. + Assert.equal(page.embedderInnerWindowID, 0); + Assert.equal(typeof page.isPrivateBrowsing, "boolean"); + Assert.equal(page.isPrivateBrowsing, false); + parentPage = page; + foundPage++; + break; + } + } + + Assert.notEqual(typeof parentPage, "undefined"); + + for (const page of contentProcess.pages) { + // Child page (iframe) + if (page.url == BASE_URL + "single_frame.html") { + Assert.equal(page.url, BASE_URL + "single_frame.html"); + Assert.equal(typeof page.tabID, "number"); + Assert.equal(page.tabID, activeTabID); + Assert.equal(typeof page.innerWindowID, "number"); + Assert.equal(typeof page.embedderInnerWindowID, "number"); + Assert.notEqual(typeof parentPage, "undefined"); + Assert.equal(page.embedderInnerWindowID, parentPage.innerWindowID); + Assert.equal(typeof page.isPrivateBrowsing, "boolean"); + Assert.equal(page.isPrivateBrowsing, false); + foundPage++; + break; + } + } + + Assert.equal(foundPage, 2); + }); +}); diff --git a/tools/profiler/tests/browser/browser_test_profile_single_frame_page_info.js b/tools/profiler/tests/browser/browser_test_profile_single_frame_page_info.js new file mode 100644 index 0000000000..6f59ae6a7b --- /dev/null +++ b/tools/profiler/tests/browser/browser_test_profile_single_frame_page_info.js @@ -0,0 +1,134 @@ +/* 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 (SpecialPowers.useRemoteSubframes) { + // Bug 1586105: these tests could time out in some extremely slow conditions, + // when fission is enabled. + // Requesting a longer timeout should make it pass. + requestLongerTimeout(2); +} + +add_task(async function test_profile_single_frame_page_info() { + // Requesting the complete log to be able to debug Bug 1586105. + SimpleTest.requestCompleteLog(); + Assert.ok(!Services.profiler.IsActive()); + info("Clear the previous pages just in case we still have some open tabs."); + await Services.profiler.ClearAllPages(); + + info( + "Start the profiler to test the page information with single frame page." + ); + await startProfiler(); + + info("Open a tab with single_frame.html in it."); + const url = BASE_URL + "single_frame.html"; + await BrowserTestUtils.withNewTab(url, async function(contentBrowser) { + const contentPid = await SpecialPowers.spawn(contentBrowser, [], () => { + return Services.appinfo.processID; + }); + + // Getting the active Browser ID to assert the page info tabID later. + const win = Services.wm.getMostRecentWindow("navigator:browser"); + const activeTabID = win.gBrowser.selectedBrowser.browsingContext.browserId; + + info("Capture the profile data."); + const { contentProcess } = await stopProfilerNowAndGetThreads(contentPid); + + info( + "Check if the captured page is the one with correct values we created." + ); + + let pageFound = false; + for (const page of contentProcess.pages) { + if (page.url == url) { + Assert.equal(page.url, url); + Assert.equal(typeof page.tabID, "number"); + Assert.equal(page.tabID, activeTabID); + Assert.equal(typeof page.innerWindowID, "number"); + // Top level document will have no embedder. + Assert.equal(page.embedderInnerWindowID, 0); + Assert.equal(typeof page.isPrivateBrowsing, "boolean"); + Assert.equal(page.isPrivateBrowsing, false); + pageFound = true; + break; + } + } + Assert.equal(pageFound, true); + }); +}); + +add_task(async function test_profile_private_browsing() { + // Requesting the complete log to be able to debug Bug 1586105. + SimpleTest.requestCompleteLog(); + Assert.ok(!Services.profiler.IsActive()); + info("Clear the previous pages just in case we still have some open tabs."); + await Services.profiler.ClearAllPages(); + + info( + "Start the profiler to test the page information with single frame page." + ); + await startProfiler(); + + info("Open a private window with single_frame.html in it."); + const win = await BrowserTestUtils.openNewBrowserWindow({ + fission: false, + private: true, + }); + + try { + const url = BASE_URL_HTTPS + "single_frame.html"; + const contentBrowser = win.gBrowser.selectedBrowser; + BrowserTestUtils.loadURI(contentBrowser, url); + await BrowserTestUtils.browserLoaded(contentBrowser, false, url); + + const contentPid = await SpecialPowers.spawn(contentBrowser, [], () => { + return Services.appinfo.processID; + }); + + // Getting the active Browser ID to assert the page info tabID later. + const activeTabID = contentBrowser.browsingContext.browserId; + + info("Capture the profile data."); + const { + contentProcess, + contentThread, + } = await stopProfilerNowAndGetThreads(contentPid); + + // This information is available with fission only. + Assert.equal( + contentThread.isPrivateBrowsing, + undefined, + "The content process has no private browsing flag." + ); + + Assert.equal( + contentThread.userContextId, + undefined, + "The content process has no information about the container used for this process." + ); + + info( + "Check if the captured page is the one with correct values we created." + ); + + let pageFound = false; + for (const page of contentProcess.pages) { + if (page.url == url) { + Assert.equal(page.url, url); + Assert.equal(typeof page.tabID, "number"); + Assert.equal(page.tabID, activeTabID); + Assert.equal(typeof page.innerWindowID, "number"); + // Top level document will have no embedder. + Assert.equal(page.embedderInnerWindowID, 0); + Assert.equal(typeof page.isPrivateBrowsing, "boolean"); + Assert.equal(page.isPrivateBrowsing, true); + pageFound = true; + break; + } + } + Assert.equal(pageFound, true); + } finally { + await BrowserTestUtils.closeWindow(win); + } +}); diff --git a/tools/profiler/tests/browser/browser_test_profile_slow_capture.js b/tools/profiler/tests/browser/browser_test_profile_slow_capture.js new file mode 100644 index 0000000000..83795aaf38 --- /dev/null +++ b/tools/profiler/tests/browser/browser_test_profile_slow_capture.js @@ -0,0 +1,104 @@ +/* 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/. */ + +add_task(async function browser_test_profile_slow_capture() { + Assert.ok(!Services.profiler.IsActive()); + info("Clear the previous pages just in case we still some open tabs."); + await Services.profiler.ClearAllPages(); + + info( + "Start the profiler to test the page information with single frame page." + ); + await startProfiler({ threads: ["GeckoMain", "test-debug-child-slow-json"] }); + + info("Open a tab with single_frame.html in it."); + const url = BASE_URL + "single_frame.html"; + await BrowserTestUtils.withNewTab(url, async function(contentBrowser) { + const contentPid = await SpecialPowers.spawn(contentBrowser, [], () => { + return Services.appinfo.processID; + }); + + // Getting the active Browser ID to assert the page info tabID later. + const win = Services.wm.getMostRecentWindow("navigator:browser"); + const activeTabID = win.gBrowser.selectedBrowser.browsingContext.browserId; + + info("Capture the profile data."); + const profile = await waitSamplingAndStopAndGetProfile(); + + let pageFound = false; + // We need to find the correct content process for that tab. + let contentProcess = profile.processes.find( + p => p.threads[0].pid == contentPid + ); + + if (!contentProcess) { + throw new Error( + `Could not find the content process with given pid: ${contentPid}` + ); + } + + info( + "Check if the captured page is the one with correct values we created." + ); + + for (const page of contentProcess.pages) { + if (page.url == url) { + Assert.equal(page.url, url); + Assert.equal(typeof page.tabID, "number"); + Assert.equal(page.tabID, activeTabID); + Assert.equal(typeof page.innerWindowID, "number"); + // Top level document will have no embedder. + Assert.equal(page.embedderInnerWindowID, 0); + pageFound = true; + break; + } + } + Assert.equal(pageFound, true); + + info("Flush slow processes with a quick profile."); + await startProfiler(); + for (let i = 0; i < 10; ++i) { + await Services.profiler.waitOnePeriodicSampling(); + } + await stopNowAndGetProfile(); + }); +}); + +add_task(async function browser_test_profile_very_slow_capture() { + Assert.ok(!Services.profiler.IsActive()); + info("Clear the previous pages just in case we still some open tabs."); + await Services.profiler.ClearAllPages(); + + info( + "Start the profiler to test the page information with single frame page." + ); + await startProfiler({ + threads: ["GeckoMain", "test-debug-child-very-slow-json"], + }); + + info("Open a tab with single_frame.html in it."); + const url = BASE_URL + "single_frame.html"; + await BrowserTestUtils.withNewTab(url, async function(contentBrowser) { + const contentPid = await SpecialPowers.spawn(contentBrowser, [], () => { + return Services.appinfo.processID; + }); + + info("Capture the profile data."); + const profile = await waitSamplingAndStopAndGetProfile(); + + info("Check that the content process is missing."); + + let contentProcessIndex = profile.processes.findIndex( + p => p.threads[0].pid == contentPid + ); + Assert.equal(contentProcessIndex, -1); + + info("Flush slow processes with a quick profile."); + await startProfiler(); + for (let i = 0; i < 10; ++i) { + await Services.profiler.waitOnePeriodicSampling(); + } + await stopNowAndGetProfile(); + }); +}); diff --git a/tools/profiler/tests/browser/do_work_500ms.html b/tools/profiler/tests/browser/do_work_500ms.html new file mode 100644 index 0000000000..9713a80671 --- /dev/null +++ b/tools/profiler/tests/browser/do_work_500ms.html @@ -0,0 +1,41 @@ +<!DOCTYPE html> +<html> +<head> + <meta charset="utf-8"> + <title>Do some work for 500ms</title> + <script> + const milliseconds = 500; + const millisecondsPerBatch = 10; + const end = Date.now() + milliseconds; + window.total = 0; + let i = 0; + + /** + * Do work for a set number of milliseconds, but only do the work in batches + * so the browser does not get unresponsive. + */ + function doWork() { + const batchEnd = Date.now() + millisecondsPerBatch; + // Do some work for a set amount of time. + while (Date.now() < end) { + // Do some kind of work that is non-deterministic to guard against optimizations. + window.total += Math.random(); + i++; + + // Check if a batch is done yet. + if (Date.now() > batchEnd) { + // Defer the rest of the work into a micro task. Keep on doing this until + // the total milliseconds have elapsed. + setTimeout(doWork, 0); + return; + } + } + } + + doWork(); + </script> +</head> +<body> + Do some work for 500ms. +</body> +</html> diff --git a/tools/profiler/tests/browser/firefox-logo-nightly.svg b/tools/profiler/tests/browser/firefox-logo-nightly.svg new file mode 100644 index 0000000000..f1af370d87 --- /dev/null +++ b/tools/profiler/tests/browser/firefox-logo-nightly.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 953.37 984"><defs><linearGradient id="linear-gradient" x1="-14706.28" y1="9250.14" x2="-14443.04" y2="9250.14" gradientTransform="matrix(0.76, 0.03, 0.05, -1.12, 11485.47, 11148)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#0083ff"/><stop offset="0.1" stop-color="#0092f8"/><stop offset="0.31" stop-color="#00abeb"/><stop offset="0.52" stop-color="#00bee1"/><stop offset="0.75" stop-color="#00c8dc"/><stop offset="1" stop-color="#00ccda"/></linearGradient><radialGradient id="radial-gradient" cx="-7588.66" cy="8866.53" r="791.23" gradientTransform="matrix(1.23, 0, 0, -1.22, 9958.21, 11048.11)" gradientUnits="userSpaceOnUse"><stop offset="0.02" stop-color="#005fe7"/><stop offset="0.18" stop-color="#0042b4"/><stop offset="0.32" stop-color="#002989"/><stop offset="0.4" stop-color="#002079"/><stop offset="0.47" stop-color="#131d78"/><stop offset="0.66" stop-color="#3b1676"/><stop offset="0.75" stop-color="#4a1475"/></radialGradient><linearGradient id="linear-gradient-2" x1="539.64" y1="254.8" x2="348.2" y2="881.03" gradientTransform="matrix(1, 0, 0, -1, 1, 984)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#000f43" stop-opacity="0.4"/><stop offset="0.48" stop-color="#001962" stop-opacity="0.17"/><stop offset="1" stop-color="#002079" stop-opacity="0"/></linearGradient><linearGradient id="linear-gradient-3" x1="540.64" y1="254.8" x2="349.2" y2="881.03" gradientTransform="matrix(1, 0, 0, -1, 0, 984)" xlink:href="#linear-gradient-2"/><linearGradient id="linear-gradient-4" x1="-8367.12" y1="7348.87" x2="-8482.36" y2="7357.76" gradientTransform="matrix(1.22, 0.12, 0.12, -1.22, 10241.06, 10765.32)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#812cc9"/><stop offset="1" stop-color="#005fe7"/></linearGradient><linearGradient id="linear-gradient-5" x1="-8449.89" y1="7496.97" x2="-8341.94" y2="7609.09" gradientTransform="matrix(1.22, 0.12, 0.12, -1.22, 10241.06, 10765.32)" gradientUnits="userSpaceOnUse"><stop offset="0.05" stop-color="#005fe7"/><stop offset="0.18" stop-color="#065de6"/><stop offset="0.35" stop-color="#1856e1"/><stop offset="0.56" stop-color="#354adb"/><stop offset="0.78" stop-color="#5d3ad1"/><stop offset="0.95" stop-color="#812cc9"/></linearGradient><linearGradient id="linear-gradient-6" x1="-8653.41" y1="7245.3" x2="-8422.52" y2="7244.76" gradientTransform="matrix(1.22, 0.12, 0.12, -1.22, 10241.06, 10765.32)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#002079"/><stop offset="0.99" stop-color="#a238ff"/></linearGradient><radialGradient id="radial-gradient-2" cx="644.11" cy="599.83" fx="785.0454815336918" fy="470.6889181532662" r="793.95" gradientTransform="matrix(1, 0, 0, -1, 0, 984)" gradientUnits="userSpaceOnUse"><stop offset="0.2" stop-color="#00fdff"/><stop offset="0.26" stop-color="#0af1ff"/><stop offset="0.37" stop-color="#23d2ff"/><stop offset="0.52" stop-color="#4da0ff"/><stop offset="0.69" stop-color="#855bff"/><stop offset="0.77" stop-color="#a238ff"/><stop offset="0.81" stop-color="#a738fd"/><stop offset="0.86" stop-color="#b539f9"/><stop offset="0.9" stop-color="#cd39f1"/><stop offset="0.96" stop-color="#ee3ae6"/><stop offset="0.98" stop-color="#ff3be0"/></radialGradient><linearGradient id="linear-gradient-7" x1="-7458.97" y1="9093.17" x2="-7531.06" y2="8282.84" gradientTransform="matrix(1.23, 0, 0, -1.22, 9958.21, 11048.11)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#00ec00"/><stop offset="0.1" stop-color="#00e244"/><stop offset="0.22" stop-color="#00d694"/><stop offset="0.31" stop-color="#00cfc7"/><stop offset="0.35" stop-color="#00ccda"/><stop offset="0.42" stop-color="#0bc2dd" stop-opacity="0.92"/><stop offset="0.57" stop-color="#29a7e4" stop-opacity="0.72"/><stop offset="0.77" stop-color="#597df0" stop-opacity="0.4"/><stop offset="1" stop-color="#9448ff" stop-opacity="0"/></linearGradient><linearGradient id="linear-gradient-8" x1="-8926.61" y1="7680.53" x2="-8790.14" y2="7680.53" gradientTransform="matrix(1.22, 0.12, 0.12, -1.22, 10241.06, 10765.32)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#005fe7"/><stop offset="0.46" stop-color="#0071f3" stop-opacity="0.51"/><stop offset="0.83" stop-color="#007efc" stop-opacity="0.14"/><stop offset="1" stop-color="#0083ff" stop-opacity="0"/></linearGradient><radialGradient id="radial-gradient-3" cx="-8914.62" cy="7721.05" r="165.97" gradientTransform="matrix(1.22, 0.12, 0.12, -1.22, 10241.06, 10765.32)" gradientUnits="userSpaceOnUse"><stop offset="0.63" stop-color="#ffe302" stop-opacity="0"/><stop offset="0.67" stop-color="#ffe302" stop-opacity="0.05"/><stop offset="0.75" stop-color="#ffe302" stop-opacity="0.19"/><stop offset="0.86" stop-color="#ffe302" stop-opacity="0.4"/><stop offset="0.99" stop-color="#ffe302" stop-opacity="0.7"/></radialGradient><linearGradient id="linear-gradient-9" x1="214.02" y1="2032.47" x2="96.19" y2="2284.31" gradientTransform="matrix(0.99, 0.1, 0.1, -0.99, -250.1, 2306.29)" gradientUnits="userSpaceOnUse"><stop offset="0.19" stop-color="#4a1475" stop-opacity="0.5"/><stop offset="0.62" stop-color="#2277ac" stop-opacity="0.23"/><stop offset="0.94" stop-color="#00ccda" stop-opacity="0"/></linearGradient><linearGradient id="linear-gradient-10" x1="-38.44" y1="278.18" x2="55.67" y2="171.29" gradientTransform="matrix(0.99, 0.1, 0.1, -0.99, 229.04, 745.87)" gradientUnits="userSpaceOnUse"><stop offset="0.01" stop-color="#002079" stop-opacity="0.5"/><stop offset="1" stop-color="#0083ff" stop-opacity="0"/></linearGradient><linearGradient id="linear-gradient-11" x1="142.45" y1="96.25" x2="142.5" y2="149.68" gradientTransform="matrix(0.99, 0.1, 0.1, -0.99, 229.04, 745.87)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#4a1475" stop-opacity="0.9"/><stop offset="0.18" stop-color="#6720a2" stop-opacity="0.6"/><stop offset="0.38" stop-color="#812acb" stop-opacity="0.34"/><stop offset="0.57" stop-color="#9332e8" stop-opacity="0.15"/><stop offset="0.76" stop-color="#9e36f9" stop-opacity="0.04"/><stop offset="0.93" stop-color="#a238ff" stop-opacity="0"/></linearGradient><linearGradient id="linear-gradient-12" x1="620.52" y1="947.88" x2="926.18" y2="264.39" gradientTransform="matrix(1, 0, 0, -1, 0, 984)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#00ec00" stop-opacity="0"/><stop offset="0.28" stop-color="#00dc6d" stop-opacity="0.5"/><stop offset="0.5" stop-color="#00d1bb" stop-opacity="0.86"/><stop offset="0.6" stop-color="#00ccda"/><stop offset="0.68" stop-color="#04c9db"/><stop offset="0.75" stop-color="#0fc1df"/><stop offset="0.83" stop-color="#23b2e6"/><stop offset="0.9" stop-color="#3e9ef0"/><stop offset="0.98" stop-color="#6184fc"/><stop offset="0.99" stop-color="#6680fe"/></linearGradient><linearGradient id="linear-gradient-13" x1="680.88" y1="554.79" x2="536.1" y2="166.04" gradientTransform="matrix(1, 0, 0, -1, 0, 984)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#0083ff"/><stop offset="0.04" stop-color="#0083ff" stop-opacity="0.92"/><stop offset="0.14" stop-color="#0083ff" stop-opacity="0.71"/><stop offset="0.26" stop-color="#0083ff" stop-opacity="0.52"/><stop offset="0.37" stop-color="#0083ff" stop-opacity="0.36"/><stop offset="0.49" stop-color="#0083ff" stop-opacity="0.23"/><stop offset="0.61" stop-color="#0083ff" stop-opacity="0.13"/><stop offset="0.73" stop-color="#0083ff" stop-opacity="0.06"/><stop offset="0.86" stop-color="#0083ff" stop-opacity="0.01"/><stop offset="1" stop-color="#0083ff" stop-opacity="0"/></linearGradient></defs><title>firefox-logo-nightly</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><g id="Layer_2-2" data-name="Layer 2"><g id="Firefox"><path d="M770.28,91.56c-23.95,27.88-35.1,90.64-10.82,154.26s61.5,49.8,84.7,114.67c30.62,85.6,16.37,200.59,16.37,200.59s36.81,106.61,62.47-6.63C979.79,341.74,770.28,143.94,770.28,91.56Z" style="fill:url(#linear-gradient)"/><path id="_Path_" data-name=" Path " d="M476.92,972.83c245.24,0,443.9-199.74,443.9-446s-198.66-446-443.66-446S33.5,280.51,33.5,526.8C33,773.33,231.92,972.83,476.92,972.83Z" style="fill:url(#radial-gradient)"/><path d="M810.67,803.64a246.8,246.8,0,0,1-30.12,18.18,705.31,705.31,0,0,0,38.3-63c9.46-10.47,18.13-20.65,25.19-31.65,3.44-5.41,7.31-12.08,11.42-19.82,24.92-44.9,52.4-117.56,53.18-192.2v-5.66a257.25,257.25,0,0,0-5.71-55.75c.2,1.43.38,2.86.56,4.29-.22-1.1-.41-2.21-.64-3.31.37,2,.66,4,1,6,5.09,43.22,1.47,85.37-16.68,116.45-.29.45-.58.88-.87,1.32,9.41-47.23,12.56-99.39,2.09-151.6,0,0-4.19-25.38-35.38-102.44-18-44.35-49.83-80.72-78-107.21-24.69-30.55-47.11-51-59.47-64.06C689.72,126,678.9,105.61,674.45,92.31c-3.85-1.93-53.14-49.81-57.05-51.63-21.51,33.35-89.16,137.67-57,235.15,14.58,44.17,51.47,90,90.07,115.74,1.69,1.94,23,25,33.09,77.16,10.45,53.85,5,95.86-16.54,158C641.73,681.24,577,735.12,516.3,740.63c-129.67,11.78-177.15-65.11-177.15-65.11C385.49,694,436.72,690.17,467.87,671c31.4-19.43,50.39-33.83,65.81-28.15C548.86,648.43,561,632,550.1,615a78.5,78.5,0,0,0-79.4-34.57c-31.43,5.11-60.23,30-101.41,5.89a86.29,86.29,0,0,1-7.73-5.06c-2.71-1.79,8.83,2.72,6.13.69-8-4.35-22.2-13.84-25.88-17.22-.61-.56,6.22,2.18,5.61,1.62-38.51-31.71-33.7-53.13-32.49-66.57,1-10.75,8-24.52,19.75-30.11,5.69,3.11,9.24,5.48,9.24,5.48s-2.43-5-3.74-7.58c.46-.2.9-.15,1.36-.34,4.66,2.25,15,8.1,20.41,11.67,7.07,5,9.33,9.44,9.33,9.44s1.86-1,.48-5.37c-.5-1.78-2.65-7.45-9.65-13.17h.44A81.61,81.61,0,0,1,374.42,478c2-7.18,5.53-14.68,4.75-28.09-.48-9.43-.26-11.87-1.92-15.51-1.49-3.13.83-4.35,3.42-1.1a32.5,32.5,0,0,0-2.21-7.4v-.24c3.23-11.24,68.25-40.46,73-43.88A67.2,67.2,0,0,0,470.59,361c3.62-5.76,6.34-13.85,7-26.11.36-8.84-3.76-14.73-69.51-21.62-18-1.77-28.53-14.8-34.53-26.82-1.09-2.59-2.21-4.94-3.33-7.28a57.68,57.68,0,0,1-2.56-8.43c10.75-30.87,28.81-57,55.37-76.7,1.45-1.32-5.78.34-4.34-1,1.69-1.54,12.71-6,14.79-7,2.54-1.2-10.88-6.9-22.73-5.51-12.07,1.36-14.63,2.8-21.07,5.53,2.67-2.66,11.17-6.15,9.18-6.13-13,2-29.18,9.56-43,18.12a10.66,10.66,0,0,1,.83-4.35c-6.44,2.73-22.26,13.79-26.87,23.14a44.29,44.29,0,0,0,.27-5.4,84.17,84.17,0,0,0-13.19,13.82l-.24.22c-37.36-15-70.23-16-98.05-9.28-6.09-6.11-9.06-1.64-22.91-32.07-.94-1.83.72,1.81,0,0-2.28-5.9,1.39,7.87,0,0-23.28,18.37-53.92,39.19-68.63,53.89-.18.59,17.16-4.9,0,0-6,1.72-5.6,5.28-6.51,37.5-.22,2.44,0,5.18-.22,7.38-11.75,15-19.75,27.64-22.78,34.21-15.19,26.18-31.93,67-48.15,131.55A334.82,334.82,0,0,1,75.2,398.36C61.71,432.63,48.67,486.44,46.07,569.3A482.08,482.08,0,0,1,58.6,518.64,473,473,0,0,0,93.33,719.71c9.33,22.82,24.76,57.46,51,95.4C226.9,902,343.31,956,472.21,956,606.79,956,727.64,897.13,810.67,803.64Z" style="fill:url(#linear-gradient-2)"/><path d="M810.67,803.64a246.8,246.8,0,0,1-30.12,18.18,705.31,705.31,0,0,0,38.3-63c9.46-10.47,18.13-20.65,25.19-31.65,3.44-5.41,7.31-12.08,11.42-19.82,24.92-44.9,52.4-117.56,53.18-192.2v-5.66a257.25,257.25,0,0,0-5.71-55.75c.2,1.43.38,2.86.56,4.29-.22-1.1-.41-2.21-.64-3.31.37,2,.66,4,1,6,5.09,43.22,1.47,85.37-16.68,116.45-.29.45-.58.88-.87,1.32,9.41-47.23,12.56-99.39,2.09-151.6,0,0-4.19-25.38-35.38-102.44-18-44.35-49.83-80.72-78-107.21-24.69-30.55-47.11-51-59.47-64.06C689.72,126,678.9,105.61,674.45,92.31c-3.85-1.93-53.14-49.81-57.05-51.63-21.51,33.35-89.16,137.67-57,235.15,14.58,44.17,51.47,90,90.07,115.74,1.69,1.94,23,25,33.09,77.16,10.45,53.85,5,95.86-16.54,158C641.73,681.24,577,735.12,516.3,740.63c-129.67,11.78-177.15-65.11-177.15-65.11C385.49,694,436.72,690.17,467.87,671c31.4-19.43,50.39-33.83,65.81-28.15C548.86,648.43,561,632,550.1,615a78.5,78.5,0,0,0-79.4-34.57c-31.43,5.11-60.23,30-101.41,5.89a86.29,86.29,0,0,1-7.73-5.06c-2.71-1.79,8.83,2.72,6.13.69-8-4.35-22.2-13.84-25.88-17.22-.61-.56,6.22,2.18,5.61,1.62-38.51-31.71-33.7-53.13-32.49-66.57,1-10.75,8-24.52,19.75-30.11,5.69,3.11,9.24,5.48,9.24,5.48s-2.43-5-3.74-7.58c.46-.2.9-.15,1.36-.34,4.66,2.25,15,8.1,20.41,11.67,7.07,5,9.33,9.44,9.33,9.44s1.86-1,.48-5.37c-.5-1.78-2.65-7.45-9.65-13.17h.44A81.61,81.61,0,0,1,374.42,478c2-7.18,5.53-14.68,4.75-28.09-.48-9.43-.26-11.87-1.92-15.51-1.49-3.13.83-4.35,3.42-1.1a32.5,32.5,0,0,0-2.21-7.4v-.24c3.23-11.24,68.25-40.46,73-43.88A67.2,67.2,0,0,0,470.59,361c3.62-5.76,6.34-13.85,7-26.11.36-8.84-3.76-14.73-69.51-21.62-18-1.77-28.53-14.8-34.53-26.82-1.09-2.59-2.21-4.94-3.33-7.28a57.68,57.68,0,0,1-2.56-8.43c10.75-30.87,28.81-57,55.37-76.7,1.45-1.32-5.78.34-4.34-1,1.69-1.54,12.71-6,14.79-7,2.54-1.2-10.88-6.9-22.73-5.51-12.07,1.36-14.63,2.8-21.07,5.53,2.67-2.66,11.17-6.15,9.18-6.13-13,2-29.18,9.56-43,18.12a10.66,10.66,0,0,1,.83-4.35c-6.44,2.73-22.26,13.79-26.87,23.14a44.29,44.29,0,0,0,.27-5.4,84.17,84.17,0,0,0-13.19,13.82l-.24.22c-37.36-15-70.23-16-98.05-9.28-6.09-6.11-9.06-1.64-22.91-32.07-.94-1.83.72,1.81,0,0-2.28-5.9,1.39,7.87,0,0-23.28,18.37-53.92,39.19-68.63,53.89-.18.59,17.16-4.9,0,0-6,1.72-5.6,5.28-6.51,37.5-.22,2.44,0,5.18-.22,7.38-11.75,15-19.75,27.64-22.78,34.21-15.19,26.18-31.93,67-48.15,131.55A334.82,334.82,0,0,1,75.2,398.36C61.71,432.63,48.67,486.44,46.07,569.3A482.08,482.08,0,0,1,58.6,518.64,473,473,0,0,0,93.33,719.71c9.33,22.82,24.76,57.46,51,95.4C226.9,902,343.31,956,472.21,956,606.79,956,727.64,897.13,810.67,803.64Z" style="fill:url(#linear-gradient-3)"/><path d="M711.1,866.71c162.87-18.86,235-186.7,142.38-190C769.85,674,634,875.61,711.1,866.71Z" style="fill:url(#linear-gradient-4)"/><path d="M865.21,642.42C977.26,577.21,948,436.34,948,436.34s-43.25,50.24-72.62,130.32C846.4,646,797.84,681.81,865.21,642.42Z" style="fill:url(#linear-gradient-5)"/><path d="M509.47,950.06C665.7,999.91,800,876.84,717.21,835.74,642,798.68,435.32,926.49,509.47,950.06Z" style="fill:url(#linear-gradient-6)"/><path d="M638.58,21.42l.53-.57A1.7,1.7,0,0,0,638.58,21.42ZM876.85,702.23c3.8-5.36,8.94-22.53,13.48-30.21,27.58-44.52,27.78-80,27.78-80.84,16.66-83.22,15.15-117.2,4.9-180-8.25-50.6-44.32-123.09-75.57-158-32.2-36-9.51-24.25-40.69-50.52-27.33-30.29-53.82-60.29-68.25-72.36C634.22,43.09,636.57,24.58,638.58,21.42c-.34.37-.84.92-1.47,1.64C635.87,18.14,635,14,635,14s-57,57-69,152c-7.83,62,15.38,126.68,49,168a381.62,381.62,0,0,0,59,58h0c25.4,36.48,39.38,81.49,39.38,129.91,0,121.24-98.34,219.53-219.65,219.53a220.14,220.14,0,0,1-49.13-5.52c-57.24-10.92-90.3-39.8-106.78-59.41-9.45-11.23-13.46-19.42-13.46-19.42,51.28,18.37,108,14.53,142.47-4.52,34.75-19.26,55.77-33.55,72.84-27.92,16.82,5.61,30.21-10.67,18.2-27.54-11.77-16.85-42.4-41-87.88-34.29-34.79,5.07-66.66,29.76-112.24,5.84a97.34,97.34,0,0,1-8.55-5c-3-1.77,9.77,2.69,6.79.68-8.87-4.32-24.57-13.73-28.64-17.07-.68-.56,6.88,2.16,6.2,1.6-42.62-31.45-37.3-52.69-36-66,1.07-10.66,8.81-24.32,21.86-29.86,6.3,3.08,10.23,5.43,10.23,5.43s-2.69-4.92-4.14-7.51c.51-.19,1-.15,1.5-.34,5.16,2.23,16.58,8,22.59,11.57,7.83,4.95,10.32,9.36,10.32,9.36s2.06-1,.54-5.33c-.56-1.77-2.93-7.39-10.68-13.07h.48a91.65,91.65,0,0,1,13.13,8.17c2.19-7.12,6.12-14.56,5.25-27.86-.53-9.35-.28-11.78-2.12-15.39-1.65-3.1.92-4.31,3.78-1.09a29.73,29.73,0,0,0-2.44-7.34v-.24c3.57-11.14,75.53-40.12,80.77-43.51a70.24,70.24,0,0,0,21.17-20.63c4-5.72,7-13.73,7.75-25.89.25-5.48-1.44-9.82-20.5-14-11.44-2.49-29.14-4.91-56.43-7.47-19.9-1.76-31.58-14.68-38.21-26.6-1.21-2.57-2.45-4.9-3.68-7.22a53.41,53.41,0,0,1-2.83-8.36,158.47,158.47,0,0,1,61.28-76.06c1.6-1.31-6.4.33-4.8-1,1.87-1.52,14.06-5.93,16.37-6.92,2.81-1.19-12-6.84-25.16-5.47-13.36,1.35-16.19,2.78-23.32,5.49,3-2.64,12.37-6.1,10.16-6.08-14.4,2-32.3,9.48-47.6,18a9.72,9.72,0,0,1,.92-4.31c-7.13,2.71-24.64,13.67-29.73,23a39.79,39.79,0,0,0,.29-5.35,88.55,88.55,0,0,0-14.6,13.7l-.27.22C258.14,196,221.75,195,191,201.72c-6.74-6.06-17.57-15.23-32.89-45.4-1-1.82-1.6,3.75-2.4,2-6-13.81-9.55-36.44-9-52,0,0-12.32,5.61-22.51,29.06-1.89,4.21-3.11,6.54-4.32,8.87-.56.68,1.27-7.7,1-7.24-1.77,3-6.36,7.19-8.37,12.62-1.38,4-3.32,6.27-4.56,11.29l-.29.46c-.1-1.48.37-6.08,0-5.14A235.4,235.4,0,0,0,95.34,186c-5.49,18-11.88,42.61-12.89,74.57-.24,2.42,0,5.14-.25,7.32-13,14.83-21.86,27.39-25.2,33.91-16.81,26-35.33,66.44-53.29,130.46a319.35,319.35,0,0,1,28.54-50C17.32,416.25,2.89,469.62,0,551.8a436.92,436.92,0,0,1,13.87-50.24C11.29,556.36,17.68,624.3,52.32,701c20.57,45,67.92,136.6,183.62,208h0s39.36,29.3,107,51.26c5,1.81,10.06,3.6,15.23,5.33q-2.43-1-4.71-2A484.9,484.9,0,0,0,492.27,984c175.18.15,226.85-70.2,226.85-70.2l-.51.38q3.71-3.49,7.14-7.26c-27.64,26.08-90.75,27.84-114.3,26,40.22-11.81,66.69-21.81,118.17-41.52q9-3.36,18.48-7.64l2-.94c1.25-.58,2.49-1.13,3.75-1.74a349.3,349.3,0,0,0,70.26-44c51.7-41.3,63-81.56,68.83-108.1-.82,2.54-3.37,8.47-5.17,12.32-13.31,28.48-42.84,46-74.91,61a689.05,689.05,0,0,0,42.38-62.44C865.77,729.39,869,713.15,876.85,702.23Z" style="fill:url(#radial-gradient-2)"/><path d="M813.92,801c21.08-23.24,40-49.82,54.35-80,36.9-77.58,94-206.58,49-341.31C881.77,273.22,833,215,771.11,158.12,670.56,65.76,642.48,24.52,642.48,0c0,0-116.09,129.41-65.74,264.38s153.46,130,221.68,270.87c80.27,165.74-64.95,346.61-185,397.24,7.35-1.63,267-60.38,280.61-208.88C893.68,726.34,887.83,767.41,813.92,801Z" style="fill:url(#linear-gradient-7)"/><path d="M477.59,319.37c.39-8.77-4.16-14.66-76.68-21.46-29.84-2.76-41.26-30.33-44.75-41.94-10.61,27.56-15,56.49-12.64,91.48,1.61,22.92,17,47.52,24.37,62,0,0,1.64-2.13,2.39-2.91,13.86-14.43,71.94-36.42,77.39-39.54C453.69,363.16,476.58,346.44,477.59,319.37Z" style="fill:url(#linear-gradient-8)"/><path d="M477.59,319.37c.39-8.77-4.16-14.66-76.68-21.46-29.84-2.76-41.26-30.33-44.75-41.94-10.61,27.56-15,56.49-12.64,91.48,1.61,22.92,17,47.52,24.37,62,0,0,1.64-2.13,2.39-2.91,13.86-14.43,71.94-36.42,77.39-39.54C453.69,363.16,476.58,346.44,477.59,319.37Z" style="opacity:0.5;isolation:isolate;fill:url(#radial-gradient-3)"/><path d="M158.31,156.47c-1-1.82-1.6,3.75-2.4,2-6-13.81-9.58-36.2-8.72-52,0,0-12.32,5.61-22.51,29.06-1.89,4.21-3.11,6.54-4.32,8.86-.56.68,1.27-7.7,1-7.24-1.77,3-6.36,7.19-8.35,12.38-1.65,4.24-3.35,6.52-4.61,11.77-.39,1.43.39-6.32,0-5.38C84.72,201.68,80.19,271,82.69,268,133.17,214.14,191,201.36,191,201.36c-6.15-4.53-19.53-17.63-32.7-44.89Z" style="fill:url(#linear-gradient-9)"/><path d="M349.84,720.1c-69.72-29.77-149-71.75-146-167.14C207.92,427.35,321,452.18,321,452.18c-4.27,1-15.68,9.16-19.72,17.82-4.27,10.83-12.07,35.28,11.55,60.9,37.09,40.19-76.2,95.36,98.66,199.57,4.41,2.4-41-1.43-61.64-10.36Z" style="fill:url(#linear-gradient-10)"/><path d="M325.07,657.5c49.44,17.21,107,14.19,141.52-4.86,23.09-12.85,52.7-33.43,70.92-28.35-15.78-6.24-27.73-9.15-42.1-9.86-2.45,0-5.38,0-8-.32a136,136,0,0,0-15.76.86c-8.9.82-18.77,6.43-27.74,5.53-.48,0,8.7-3.77,8-3.61-4.75,1-9.92,1.21-15.37,1.88-3.47.39-6.45.82-9.89,1-103,8.73-190-55.81-190-55.81-7.41,25,33.17,74.3,88.52,93.57Z" style="opacity:0.5;isolation:isolate;fill:url(#linear-gradient-11)"/><path d="M813.74,801.65c104.16-102.27,156.86-226.58,134.58-366,0,0,8.9,71.5-24.85,144.63,16.21-71.39,18.1-160.11-25-252C841,205.64,746.45,141.11,710.35,114.19,655.66,73.4,633,31.87,632.57,23.3c-16.34,33.48-65.77,148.2-5.31,247,56.64,92.56,145.86,120,208.33,205C950.67,631.67,813.74,801.65,813.74,801.65Z" style="fill:url(#linear-gradient-12)"/><path d="M798.81,535.55C762.41,460.35,717,427.55,674,392c5,7,6.23,9.47,9,14,37.83,40.32,93.61,138.66,53.11,262.11C659.88,900.48,355,791.06,323,760.32,335.93,894.81,561,959.16,707.6,872,791,793,858.47,658.79,798.81,535.55Z" style="fill:url(#linear-gradient-13)"/></g></g></g></g></svg>
\ No newline at end of file diff --git a/tools/profiler/tests/browser/head.js b/tools/profiler/tests/browser/head.js new file mode 100644 index 0000000000..ef0e3128c0 --- /dev/null +++ b/tools/profiler/tests/browser/head.js @@ -0,0 +1,159 @@ +/* import-globals-from ../shared-head.js */ + +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/tools/profiler/tests/browser/shared-head.js", + this +); + +const BASE_URL = "http://example.com/browser/tools/profiler/tests/browser/"; +const BASE_URL_HTTPS = + "https://example.com/browser/tools/profiler/tests/browser/"; + +registerCleanupFunction(async () => { + if (Services.profiler.IsActive()) { + info( + "The profiler was found to still be running at the end of the test, which means that some error likely occured. Let's stop it to prevent issues with following tests!" + ); + await Services.profiler.StopProfiler(); + } +}); + +/** + * This is a helper function that will stop the profiler and returns the main + * threads for the parent process and the content process with PID contentPid. + * This happens immediately, without waiting for any sampling to happen or + * finish. Use waitSamplingAndStopProfilerAndGetThreads below instead to wait + * for samples before stopping. + * This returns also the full profile in case the caller wants more information. + * + * @param {number} contentPid + * @returns {Promise<{profile, parentThread, contentProcess, contentThread}>} + */ +async function stopProfilerNowAndGetThreads(contentPid) { + const profile = await stopNowAndGetProfile(); + + const parentThread = profile.threads[0]; + const contentProcess = profile.processes.find( + p => p.threads[0].pid == contentPid + ); + if (!contentProcess) { + throw new Error( + `Could not find the content process with given pid: ${contentPid}` + ); + } + + if (!parentThread) { + throw new Error("The parent thread was not found in the profile."); + } + + const contentThread = contentProcess.threads[0]; + if (!contentThread) { + throw new Error("The content thread was not found in the profile."); + } + + return { profile, parentThread, contentProcess, contentThread }; +} + +/** + * This is a helper function that will stop the profiler and returns the main + * threads for the parent process and the content process with PID contentPid. + * As opposed to stopProfilerNowAndGetThreads (with "Now") above, the profiler + * in that PID will not stop until there is at least one periodic sample taken. + * + * @param {number} contentPid + * @returns {Promise<{profile, parentThread, contentProcess, contentThread}>} + */ +async function waitSamplingAndStopProfilerAndGetThreads(contentPid) { + await Services.profiler.waitOnePeriodicSampling(); + + return stopProfilerNowAndGetThreads(contentPid); +} + +/** This tries to find the service worker thread by targeting a very specific + * UserTiming marker. Indeed we use performance.mark to add this marker from the + * service worker's events. + * Then from this thread we get its parent thread. Indeed the parent thread is + * where all network stuff happens, so this is useful for network marker tests. + * + * @param {Object} profile + * @returns {{ serviceWorkerThread: Object, serviceWorkerParentThread: Object }} the found threads + */ +function findServiceWorkerThreads(profile) { + const allThreads = [ + profile.threads, + ...profile.processes.map(process => process.threads), + ].flat(); + + const serviceWorkerThread = allThreads.find( + ({ processType, markers }) => + processType === "tab" && + markers.data.some(markerTuple => { + const data = markerTuple[markers.schema.data]; + return ( + data && + data.type === "UserTiming" && + data.name === "__serviceworker_event" + ); + }) + ); + + if (!serviceWorkerThread) { + info( + "We couldn't find a service worker thread. Here are all the threads in this profile:" + ); + allThreads.forEach(logInformationForThread.bind(null, "")); + return null; + } + + const serviceWorkerParentThread = allThreads.find( + ({ name, pid }) => pid === serviceWorkerThread.pid && name === "GeckoMain" + ); + + if (!serviceWorkerParentThread) { + info( + `We couldn't find a parent thread for the service worker thread (pid: ${serviceWorkerThread.pid}, tid: ${serviceWorkerThread.tid}).` + ); + info("Here are all the threads in this profile:"); + allThreads.forEach(logInformationForThread.bind(null, "")); + + // Let's write the profile on disk if MOZ_UPLOAD_DIR is present + const path = Services.env.get("MOZ_UPLOAD_DIR"); + if (path) { + const profileName = `profile_${Date.now()}.json`; + const profilePath = PathUtils.join(path, profileName); + info( + `We wrote down the profile on disk as an artifact, with name ${profileName}.` + ); + // This function returns a Promise, but we're not waiting on it because + // we're in a synchronous function. Hopefully writing will be finished + // when the process ends. + IOUtils.writeJSON(profilePath, profile).catch(err => + console.error("An error happened when writing the profile on disk", err) + ); + } + throw new Error( + "We couldn't find a parent thread for the service worker thread. Please read logs to find more information." + ); + } + + return { serviceWorkerThread, serviceWorkerParentThread }; +} + +/** + * This logs some basic information about the passed thread. + * + * @param {string} prefix + * @param {Object} thread + */ +function logInformationForThread(prefix, thread) { + if (!thread) { + info(prefix + ": thread is null or undefined."); + return; + } + + const { name, pid, tid, processName, processType } = thread; + info( + `${prefix}: ` + + `name(${name}) pid(${pid}) tid(${tid}) processName(${processName}) processType(${processType})` + ); +} diff --git a/tools/profiler/tests/browser/multi_frame.html b/tools/profiler/tests/browser/multi_frame.html new file mode 100644 index 0000000000..b2efcedd50 --- /dev/null +++ b/tools/profiler/tests/browser/multi_frame.html @@ -0,0 +1,11 @@ +<!DOCTYPE html> +<html> +<head> + <meta charset="utf-8"> + <title>Multi Frame</title> +</head> +<body> + Multi Frame + <iframe src="single_frame.html"></iframe> +</body> +</html> diff --git a/tools/profiler/tests/browser/page_with_resources.html b/tools/profiler/tests/browser/page_with_resources.html new file mode 100644 index 0000000000..9d2bb8f218 --- /dev/null +++ b/tools/profiler/tests/browser/page_with_resources.html @@ -0,0 +1,11 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"/> + </head> + <body> + Testing + <img src='firefox-logo-nightly.svg' width="24"/> + <img src='redirect.sjs?firefox-logo-nightly.svg' width="24"/> + </body> +</html> diff --git a/tools/profiler/tests/browser/redirect.sjs b/tools/profiler/tests/browser/redirect.sjs new file mode 100644 index 0000000000..2a325c3d0b --- /dev/null +++ b/tools/profiler/tests/browser/redirect.sjs @@ -0,0 +1,8 @@ +function handleRequest(request, response) { + response.setStatusLine(request.httpVersion, 301, "Moved Permanently"); + response.setHeader( + "Location", + decodeURIComponent(request.queryString), + false + ); +} diff --git a/tools/profiler/tests/browser/serviceworkers/firefox-logo-nightly.svg b/tools/profiler/tests/browser/serviceworkers/firefox-logo-nightly.svg new file mode 100644 index 0000000000..f1af370d87 --- /dev/null +++ b/tools/profiler/tests/browser/serviceworkers/firefox-logo-nightly.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 953.37 984"><defs><linearGradient id="linear-gradient" x1="-14706.28" y1="9250.14" x2="-14443.04" y2="9250.14" gradientTransform="matrix(0.76, 0.03, 0.05, -1.12, 11485.47, 11148)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#0083ff"/><stop offset="0.1" stop-color="#0092f8"/><stop offset="0.31" stop-color="#00abeb"/><stop offset="0.52" stop-color="#00bee1"/><stop offset="0.75" stop-color="#00c8dc"/><stop offset="1" stop-color="#00ccda"/></linearGradient><radialGradient id="radial-gradient" cx="-7588.66" cy="8866.53" r="791.23" gradientTransform="matrix(1.23, 0, 0, -1.22, 9958.21, 11048.11)" gradientUnits="userSpaceOnUse"><stop offset="0.02" stop-color="#005fe7"/><stop offset="0.18" stop-color="#0042b4"/><stop offset="0.32" stop-color="#002989"/><stop offset="0.4" stop-color="#002079"/><stop offset="0.47" stop-color="#131d78"/><stop offset="0.66" stop-color="#3b1676"/><stop offset="0.75" stop-color="#4a1475"/></radialGradient><linearGradient id="linear-gradient-2" x1="539.64" y1="254.8" x2="348.2" y2="881.03" gradientTransform="matrix(1, 0, 0, -1, 1, 984)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#000f43" stop-opacity="0.4"/><stop offset="0.48" stop-color="#001962" stop-opacity="0.17"/><stop offset="1" stop-color="#002079" stop-opacity="0"/></linearGradient><linearGradient id="linear-gradient-3" x1="540.64" y1="254.8" x2="349.2" y2="881.03" gradientTransform="matrix(1, 0, 0, -1, 0, 984)" xlink:href="#linear-gradient-2"/><linearGradient id="linear-gradient-4" x1="-8367.12" y1="7348.87" x2="-8482.36" y2="7357.76" gradientTransform="matrix(1.22, 0.12, 0.12, -1.22, 10241.06, 10765.32)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#812cc9"/><stop offset="1" stop-color="#005fe7"/></linearGradient><linearGradient id="linear-gradient-5" x1="-8449.89" y1="7496.97" x2="-8341.94" y2="7609.09" gradientTransform="matrix(1.22, 0.12, 0.12, -1.22, 10241.06, 10765.32)" gradientUnits="userSpaceOnUse"><stop offset="0.05" stop-color="#005fe7"/><stop offset="0.18" stop-color="#065de6"/><stop offset="0.35" stop-color="#1856e1"/><stop offset="0.56" stop-color="#354adb"/><stop offset="0.78" stop-color="#5d3ad1"/><stop offset="0.95" stop-color="#812cc9"/></linearGradient><linearGradient id="linear-gradient-6" x1="-8653.41" y1="7245.3" x2="-8422.52" y2="7244.76" gradientTransform="matrix(1.22, 0.12, 0.12, -1.22, 10241.06, 10765.32)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#002079"/><stop offset="0.99" stop-color="#a238ff"/></linearGradient><radialGradient id="radial-gradient-2" cx="644.11" cy="599.83" fx="785.0454815336918" fy="470.6889181532662" r="793.95" gradientTransform="matrix(1, 0, 0, -1, 0, 984)" gradientUnits="userSpaceOnUse"><stop offset="0.2" stop-color="#00fdff"/><stop offset="0.26" stop-color="#0af1ff"/><stop offset="0.37" stop-color="#23d2ff"/><stop offset="0.52" stop-color="#4da0ff"/><stop offset="0.69" stop-color="#855bff"/><stop offset="0.77" stop-color="#a238ff"/><stop offset="0.81" stop-color="#a738fd"/><stop offset="0.86" stop-color="#b539f9"/><stop offset="0.9" stop-color="#cd39f1"/><stop offset="0.96" stop-color="#ee3ae6"/><stop offset="0.98" stop-color="#ff3be0"/></radialGradient><linearGradient id="linear-gradient-7" x1="-7458.97" y1="9093.17" x2="-7531.06" y2="8282.84" gradientTransform="matrix(1.23, 0, 0, -1.22, 9958.21, 11048.11)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#00ec00"/><stop offset="0.1" stop-color="#00e244"/><stop offset="0.22" stop-color="#00d694"/><stop offset="0.31" stop-color="#00cfc7"/><stop offset="0.35" stop-color="#00ccda"/><stop offset="0.42" stop-color="#0bc2dd" stop-opacity="0.92"/><stop offset="0.57" stop-color="#29a7e4" stop-opacity="0.72"/><stop offset="0.77" stop-color="#597df0" stop-opacity="0.4"/><stop offset="1" stop-color="#9448ff" stop-opacity="0"/></linearGradient><linearGradient id="linear-gradient-8" x1="-8926.61" y1="7680.53" x2="-8790.14" y2="7680.53" gradientTransform="matrix(1.22, 0.12, 0.12, -1.22, 10241.06, 10765.32)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#005fe7"/><stop offset="0.46" stop-color="#0071f3" stop-opacity="0.51"/><stop offset="0.83" stop-color="#007efc" stop-opacity="0.14"/><stop offset="1" stop-color="#0083ff" stop-opacity="0"/></linearGradient><radialGradient id="radial-gradient-3" cx="-8914.62" cy="7721.05" r="165.97" gradientTransform="matrix(1.22, 0.12, 0.12, -1.22, 10241.06, 10765.32)" gradientUnits="userSpaceOnUse"><stop offset="0.63" stop-color="#ffe302" stop-opacity="0"/><stop offset="0.67" stop-color="#ffe302" stop-opacity="0.05"/><stop offset="0.75" stop-color="#ffe302" stop-opacity="0.19"/><stop offset="0.86" stop-color="#ffe302" stop-opacity="0.4"/><stop offset="0.99" stop-color="#ffe302" stop-opacity="0.7"/></radialGradient><linearGradient id="linear-gradient-9" x1="214.02" y1="2032.47" x2="96.19" y2="2284.31" gradientTransform="matrix(0.99, 0.1, 0.1, -0.99, -250.1, 2306.29)" gradientUnits="userSpaceOnUse"><stop offset="0.19" stop-color="#4a1475" stop-opacity="0.5"/><stop offset="0.62" stop-color="#2277ac" stop-opacity="0.23"/><stop offset="0.94" stop-color="#00ccda" stop-opacity="0"/></linearGradient><linearGradient id="linear-gradient-10" x1="-38.44" y1="278.18" x2="55.67" y2="171.29" gradientTransform="matrix(0.99, 0.1, 0.1, -0.99, 229.04, 745.87)" gradientUnits="userSpaceOnUse"><stop offset="0.01" stop-color="#002079" stop-opacity="0.5"/><stop offset="1" stop-color="#0083ff" stop-opacity="0"/></linearGradient><linearGradient id="linear-gradient-11" x1="142.45" y1="96.25" x2="142.5" y2="149.68" gradientTransform="matrix(0.99, 0.1, 0.1, -0.99, 229.04, 745.87)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#4a1475" stop-opacity="0.9"/><stop offset="0.18" stop-color="#6720a2" stop-opacity="0.6"/><stop offset="0.38" stop-color="#812acb" stop-opacity="0.34"/><stop offset="0.57" stop-color="#9332e8" stop-opacity="0.15"/><stop offset="0.76" stop-color="#9e36f9" stop-opacity="0.04"/><stop offset="0.93" stop-color="#a238ff" stop-opacity="0"/></linearGradient><linearGradient id="linear-gradient-12" x1="620.52" y1="947.88" x2="926.18" y2="264.39" gradientTransform="matrix(1, 0, 0, -1, 0, 984)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#00ec00" stop-opacity="0"/><stop offset="0.28" stop-color="#00dc6d" stop-opacity="0.5"/><stop offset="0.5" stop-color="#00d1bb" stop-opacity="0.86"/><stop offset="0.6" stop-color="#00ccda"/><stop offset="0.68" stop-color="#04c9db"/><stop offset="0.75" stop-color="#0fc1df"/><stop offset="0.83" stop-color="#23b2e6"/><stop offset="0.9" stop-color="#3e9ef0"/><stop offset="0.98" stop-color="#6184fc"/><stop offset="0.99" stop-color="#6680fe"/></linearGradient><linearGradient id="linear-gradient-13" x1="680.88" y1="554.79" x2="536.1" y2="166.04" gradientTransform="matrix(1, 0, 0, -1, 0, 984)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#0083ff"/><stop offset="0.04" stop-color="#0083ff" stop-opacity="0.92"/><stop offset="0.14" stop-color="#0083ff" stop-opacity="0.71"/><stop offset="0.26" stop-color="#0083ff" stop-opacity="0.52"/><stop offset="0.37" stop-color="#0083ff" stop-opacity="0.36"/><stop offset="0.49" stop-color="#0083ff" stop-opacity="0.23"/><stop offset="0.61" stop-color="#0083ff" stop-opacity="0.13"/><stop offset="0.73" stop-color="#0083ff" stop-opacity="0.06"/><stop offset="0.86" stop-color="#0083ff" stop-opacity="0.01"/><stop offset="1" stop-color="#0083ff" stop-opacity="0"/></linearGradient></defs><title>firefox-logo-nightly</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><g id="Layer_2-2" data-name="Layer 2"><g id="Firefox"><path d="M770.28,91.56c-23.95,27.88-35.1,90.64-10.82,154.26s61.5,49.8,84.7,114.67c30.62,85.6,16.37,200.59,16.37,200.59s36.81,106.61,62.47-6.63C979.79,341.74,770.28,143.94,770.28,91.56Z" style="fill:url(#linear-gradient)"/><path id="_Path_" data-name=" Path " d="M476.92,972.83c245.24,0,443.9-199.74,443.9-446s-198.66-446-443.66-446S33.5,280.51,33.5,526.8C33,773.33,231.92,972.83,476.92,972.83Z" style="fill:url(#radial-gradient)"/><path d="M810.67,803.64a246.8,246.8,0,0,1-30.12,18.18,705.31,705.31,0,0,0,38.3-63c9.46-10.47,18.13-20.65,25.19-31.65,3.44-5.41,7.31-12.08,11.42-19.82,24.92-44.9,52.4-117.56,53.18-192.2v-5.66a257.25,257.25,0,0,0-5.71-55.75c.2,1.43.38,2.86.56,4.29-.22-1.1-.41-2.21-.64-3.31.37,2,.66,4,1,6,5.09,43.22,1.47,85.37-16.68,116.45-.29.45-.58.88-.87,1.32,9.41-47.23,12.56-99.39,2.09-151.6,0,0-4.19-25.38-35.38-102.44-18-44.35-49.83-80.72-78-107.21-24.69-30.55-47.11-51-59.47-64.06C689.72,126,678.9,105.61,674.45,92.31c-3.85-1.93-53.14-49.81-57.05-51.63-21.51,33.35-89.16,137.67-57,235.15,14.58,44.17,51.47,90,90.07,115.74,1.69,1.94,23,25,33.09,77.16,10.45,53.85,5,95.86-16.54,158C641.73,681.24,577,735.12,516.3,740.63c-129.67,11.78-177.15-65.11-177.15-65.11C385.49,694,436.72,690.17,467.87,671c31.4-19.43,50.39-33.83,65.81-28.15C548.86,648.43,561,632,550.1,615a78.5,78.5,0,0,0-79.4-34.57c-31.43,5.11-60.23,30-101.41,5.89a86.29,86.29,0,0,1-7.73-5.06c-2.71-1.79,8.83,2.72,6.13.69-8-4.35-22.2-13.84-25.88-17.22-.61-.56,6.22,2.18,5.61,1.62-38.51-31.71-33.7-53.13-32.49-66.57,1-10.75,8-24.52,19.75-30.11,5.69,3.11,9.24,5.48,9.24,5.48s-2.43-5-3.74-7.58c.46-.2.9-.15,1.36-.34,4.66,2.25,15,8.1,20.41,11.67,7.07,5,9.33,9.44,9.33,9.44s1.86-1,.48-5.37c-.5-1.78-2.65-7.45-9.65-13.17h.44A81.61,81.61,0,0,1,374.42,478c2-7.18,5.53-14.68,4.75-28.09-.48-9.43-.26-11.87-1.92-15.51-1.49-3.13.83-4.35,3.42-1.1a32.5,32.5,0,0,0-2.21-7.4v-.24c3.23-11.24,68.25-40.46,73-43.88A67.2,67.2,0,0,0,470.59,361c3.62-5.76,6.34-13.85,7-26.11.36-8.84-3.76-14.73-69.51-21.62-18-1.77-28.53-14.8-34.53-26.82-1.09-2.59-2.21-4.94-3.33-7.28a57.68,57.68,0,0,1-2.56-8.43c10.75-30.87,28.81-57,55.37-76.7,1.45-1.32-5.78.34-4.34-1,1.69-1.54,12.71-6,14.79-7,2.54-1.2-10.88-6.9-22.73-5.51-12.07,1.36-14.63,2.8-21.07,5.53,2.67-2.66,11.17-6.15,9.18-6.13-13,2-29.18,9.56-43,18.12a10.66,10.66,0,0,1,.83-4.35c-6.44,2.73-22.26,13.79-26.87,23.14a44.29,44.29,0,0,0,.27-5.4,84.17,84.17,0,0,0-13.19,13.82l-.24.22c-37.36-15-70.23-16-98.05-9.28-6.09-6.11-9.06-1.64-22.91-32.07-.94-1.83.72,1.81,0,0-2.28-5.9,1.39,7.87,0,0-23.28,18.37-53.92,39.19-68.63,53.89-.18.59,17.16-4.9,0,0-6,1.72-5.6,5.28-6.51,37.5-.22,2.44,0,5.18-.22,7.38-11.75,15-19.75,27.64-22.78,34.21-15.19,26.18-31.93,67-48.15,131.55A334.82,334.82,0,0,1,75.2,398.36C61.71,432.63,48.67,486.44,46.07,569.3A482.08,482.08,0,0,1,58.6,518.64,473,473,0,0,0,93.33,719.71c9.33,22.82,24.76,57.46,51,95.4C226.9,902,343.31,956,472.21,956,606.79,956,727.64,897.13,810.67,803.64Z" style="fill:url(#linear-gradient-2)"/><path d="M810.67,803.64a246.8,246.8,0,0,1-30.12,18.18,705.31,705.31,0,0,0,38.3-63c9.46-10.47,18.13-20.65,25.19-31.65,3.44-5.41,7.31-12.08,11.42-19.82,24.92-44.9,52.4-117.56,53.18-192.2v-5.66a257.25,257.25,0,0,0-5.71-55.75c.2,1.43.38,2.86.56,4.29-.22-1.1-.41-2.21-.64-3.31.37,2,.66,4,1,6,5.09,43.22,1.47,85.37-16.68,116.45-.29.45-.58.88-.87,1.32,9.41-47.23,12.56-99.39,2.09-151.6,0,0-4.19-25.38-35.38-102.44-18-44.35-49.83-80.72-78-107.21-24.69-30.55-47.11-51-59.47-64.06C689.72,126,678.9,105.61,674.45,92.31c-3.85-1.93-53.14-49.81-57.05-51.63-21.51,33.35-89.16,137.67-57,235.15,14.58,44.17,51.47,90,90.07,115.74,1.69,1.94,23,25,33.09,77.16,10.45,53.85,5,95.86-16.54,158C641.73,681.24,577,735.12,516.3,740.63c-129.67,11.78-177.15-65.11-177.15-65.11C385.49,694,436.72,690.17,467.87,671c31.4-19.43,50.39-33.83,65.81-28.15C548.86,648.43,561,632,550.1,615a78.5,78.5,0,0,0-79.4-34.57c-31.43,5.11-60.23,30-101.41,5.89a86.29,86.29,0,0,1-7.73-5.06c-2.71-1.79,8.83,2.72,6.13.69-8-4.35-22.2-13.84-25.88-17.22-.61-.56,6.22,2.18,5.61,1.62-38.51-31.71-33.7-53.13-32.49-66.57,1-10.75,8-24.52,19.75-30.11,5.69,3.11,9.24,5.48,9.24,5.48s-2.43-5-3.74-7.58c.46-.2.9-.15,1.36-.34,4.66,2.25,15,8.1,20.41,11.67,7.07,5,9.33,9.44,9.33,9.44s1.86-1,.48-5.37c-.5-1.78-2.65-7.45-9.65-13.17h.44A81.61,81.61,0,0,1,374.42,478c2-7.18,5.53-14.68,4.75-28.09-.48-9.43-.26-11.87-1.92-15.51-1.49-3.13.83-4.35,3.42-1.1a32.5,32.5,0,0,0-2.21-7.4v-.24c3.23-11.24,68.25-40.46,73-43.88A67.2,67.2,0,0,0,470.59,361c3.62-5.76,6.34-13.85,7-26.11.36-8.84-3.76-14.73-69.51-21.62-18-1.77-28.53-14.8-34.53-26.82-1.09-2.59-2.21-4.94-3.33-7.28a57.68,57.68,0,0,1-2.56-8.43c10.75-30.87,28.81-57,55.37-76.7,1.45-1.32-5.78.34-4.34-1,1.69-1.54,12.71-6,14.79-7,2.54-1.2-10.88-6.9-22.73-5.51-12.07,1.36-14.63,2.8-21.07,5.53,2.67-2.66,11.17-6.15,9.18-6.13-13,2-29.18,9.56-43,18.12a10.66,10.66,0,0,1,.83-4.35c-6.44,2.73-22.26,13.79-26.87,23.14a44.29,44.29,0,0,0,.27-5.4,84.17,84.17,0,0,0-13.19,13.82l-.24.22c-37.36-15-70.23-16-98.05-9.28-6.09-6.11-9.06-1.64-22.91-32.07-.94-1.83.72,1.81,0,0-2.28-5.9,1.39,7.87,0,0-23.28,18.37-53.92,39.19-68.63,53.89-.18.59,17.16-4.9,0,0-6,1.72-5.6,5.28-6.51,37.5-.22,2.44,0,5.18-.22,7.38-11.75,15-19.75,27.64-22.78,34.21-15.19,26.18-31.93,67-48.15,131.55A334.82,334.82,0,0,1,75.2,398.36C61.71,432.63,48.67,486.44,46.07,569.3A482.08,482.08,0,0,1,58.6,518.64,473,473,0,0,0,93.33,719.71c9.33,22.82,24.76,57.46,51,95.4C226.9,902,343.31,956,472.21,956,606.79,956,727.64,897.13,810.67,803.64Z" style="fill:url(#linear-gradient-3)"/><path d="M711.1,866.71c162.87-18.86,235-186.7,142.38-190C769.85,674,634,875.61,711.1,866.71Z" style="fill:url(#linear-gradient-4)"/><path d="M865.21,642.42C977.26,577.21,948,436.34,948,436.34s-43.25,50.24-72.62,130.32C846.4,646,797.84,681.81,865.21,642.42Z" style="fill:url(#linear-gradient-5)"/><path d="M509.47,950.06C665.7,999.91,800,876.84,717.21,835.74,642,798.68,435.32,926.49,509.47,950.06Z" style="fill:url(#linear-gradient-6)"/><path d="M638.58,21.42l.53-.57A1.7,1.7,0,0,0,638.58,21.42ZM876.85,702.23c3.8-5.36,8.94-22.53,13.48-30.21,27.58-44.52,27.78-80,27.78-80.84,16.66-83.22,15.15-117.2,4.9-180-8.25-50.6-44.32-123.09-75.57-158-32.2-36-9.51-24.25-40.69-50.52-27.33-30.29-53.82-60.29-68.25-72.36C634.22,43.09,636.57,24.58,638.58,21.42c-.34.37-.84.92-1.47,1.64C635.87,18.14,635,14,635,14s-57,57-69,152c-7.83,62,15.38,126.68,49,168a381.62,381.62,0,0,0,59,58h0c25.4,36.48,39.38,81.49,39.38,129.91,0,121.24-98.34,219.53-219.65,219.53a220.14,220.14,0,0,1-49.13-5.52c-57.24-10.92-90.3-39.8-106.78-59.41-9.45-11.23-13.46-19.42-13.46-19.42,51.28,18.37,108,14.53,142.47-4.52,34.75-19.26,55.77-33.55,72.84-27.92,16.82,5.61,30.21-10.67,18.2-27.54-11.77-16.85-42.4-41-87.88-34.29-34.79,5.07-66.66,29.76-112.24,5.84a97.34,97.34,0,0,1-8.55-5c-3-1.77,9.77,2.69,6.79.68-8.87-4.32-24.57-13.73-28.64-17.07-.68-.56,6.88,2.16,6.2,1.6-42.62-31.45-37.3-52.69-36-66,1.07-10.66,8.81-24.32,21.86-29.86,6.3,3.08,10.23,5.43,10.23,5.43s-2.69-4.92-4.14-7.51c.51-.19,1-.15,1.5-.34,5.16,2.23,16.58,8,22.59,11.57,7.83,4.95,10.32,9.36,10.32,9.36s2.06-1,.54-5.33c-.56-1.77-2.93-7.39-10.68-13.07h.48a91.65,91.65,0,0,1,13.13,8.17c2.19-7.12,6.12-14.56,5.25-27.86-.53-9.35-.28-11.78-2.12-15.39-1.65-3.1.92-4.31,3.78-1.09a29.73,29.73,0,0,0-2.44-7.34v-.24c3.57-11.14,75.53-40.12,80.77-43.51a70.24,70.24,0,0,0,21.17-20.63c4-5.72,7-13.73,7.75-25.89.25-5.48-1.44-9.82-20.5-14-11.44-2.49-29.14-4.91-56.43-7.47-19.9-1.76-31.58-14.68-38.21-26.6-1.21-2.57-2.45-4.9-3.68-7.22a53.41,53.41,0,0,1-2.83-8.36,158.47,158.47,0,0,1,61.28-76.06c1.6-1.31-6.4.33-4.8-1,1.87-1.52,14.06-5.93,16.37-6.92,2.81-1.19-12-6.84-25.16-5.47-13.36,1.35-16.19,2.78-23.32,5.49,3-2.64,12.37-6.1,10.16-6.08-14.4,2-32.3,9.48-47.6,18a9.72,9.72,0,0,1,.92-4.31c-7.13,2.71-24.64,13.67-29.73,23a39.79,39.79,0,0,0,.29-5.35,88.55,88.55,0,0,0-14.6,13.7l-.27.22C258.14,196,221.75,195,191,201.72c-6.74-6.06-17.57-15.23-32.89-45.4-1-1.82-1.6,3.75-2.4,2-6-13.81-9.55-36.44-9-52,0,0-12.32,5.61-22.51,29.06-1.89,4.21-3.11,6.54-4.32,8.87-.56.68,1.27-7.7,1-7.24-1.77,3-6.36,7.19-8.37,12.62-1.38,4-3.32,6.27-4.56,11.29l-.29.46c-.1-1.48.37-6.08,0-5.14A235.4,235.4,0,0,0,95.34,186c-5.49,18-11.88,42.61-12.89,74.57-.24,2.42,0,5.14-.25,7.32-13,14.83-21.86,27.39-25.2,33.91-16.81,26-35.33,66.44-53.29,130.46a319.35,319.35,0,0,1,28.54-50C17.32,416.25,2.89,469.62,0,551.8a436.92,436.92,0,0,1,13.87-50.24C11.29,556.36,17.68,624.3,52.32,701c20.57,45,67.92,136.6,183.62,208h0s39.36,29.3,107,51.26c5,1.81,10.06,3.6,15.23,5.33q-2.43-1-4.71-2A484.9,484.9,0,0,0,492.27,984c175.18.15,226.85-70.2,226.85-70.2l-.51.38q3.71-3.49,7.14-7.26c-27.64,26.08-90.75,27.84-114.3,26,40.22-11.81,66.69-21.81,118.17-41.52q9-3.36,18.48-7.64l2-.94c1.25-.58,2.49-1.13,3.75-1.74a349.3,349.3,0,0,0,70.26-44c51.7-41.3,63-81.56,68.83-108.1-.82,2.54-3.37,8.47-5.17,12.32-13.31,28.48-42.84,46-74.91,61a689.05,689.05,0,0,0,42.38-62.44C865.77,729.39,869,713.15,876.85,702.23Z" style="fill:url(#radial-gradient-2)"/><path d="M813.92,801c21.08-23.24,40-49.82,54.35-80,36.9-77.58,94-206.58,49-341.31C881.77,273.22,833,215,771.11,158.12,670.56,65.76,642.48,24.52,642.48,0c0,0-116.09,129.41-65.74,264.38s153.46,130,221.68,270.87c80.27,165.74-64.95,346.61-185,397.24,7.35-1.63,267-60.38,280.61-208.88C893.68,726.34,887.83,767.41,813.92,801Z" style="fill:url(#linear-gradient-7)"/><path d="M477.59,319.37c.39-8.77-4.16-14.66-76.68-21.46-29.84-2.76-41.26-30.33-44.75-41.94-10.61,27.56-15,56.49-12.64,91.48,1.61,22.92,17,47.52,24.37,62,0,0,1.64-2.13,2.39-2.91,13.86-14.43,71.94-36.42,77.39-39.54C453.69,363.16,476.58,346.44,477.59,319.37Z" style="fill:url(#linear-gradient-8)"/><path d="M477.59,319.37c.39-8.77-4.16-14.66-76.68-21.46-29.84-2.76-41.26-30.33-44.75-41.94-10.61,27.56-15,56.49-12.64,91.48,1.61,22.92,17,47.52,24.37,62,0,0,1.64-2.13,2.39-2.91,13.86-14.43,71.94-36.42,77.39-39.54C453.69,363.16,476.58,346.44,477.59,319.37Z" style="opacity:0.5;isolation:isolate;fill:url(#radial-gradient-3)"/><path d="M158.31,156.47c-1-1.82-1.6,3.75-2.4,2-6-13.81-9.58-36.2-8.72-52,0,0-12.32,5.61-22.51,29.06-1.89,4.21-3.11,6.54-4.32,8.86-.56.68,1.27-7.7,1-7.24-1.77,3-6.36,7.19-8.35,12.38-1.65,4.24-3.35,6.52-4.61,11.77-.39,1.43.39-6.32,0-5.38C84.72,201.68,80.19,271,82.69,268,133.17,214.14,191,201.36,191,201.36c-6.15-4.53-19.53-17.63-32.7-44.89Z" style="fill:url(#linear-gradient-9)"/><path d="M349.84,720.1c-69.72-29.77-149-71.75-146-167.14C207.92,427.35,321,452.18,321,452.18c-4.27,1-15.68,9.16-19.72,17.82-4.27,10.83-12.07,35.28,11.55,60.9,37.09,40.19-76.2,95.36,98.66,199.57,4.41,2.4-41-1.43-61.64-10.36Z" style="fill:url(#linear-gradient-10)"/><path d="M325.07,657.5c49.44,17.21,107,14.19,141.52-4.86,23.09-12.85,52.7-33.43,70.92-28.35-15.78-6.24-27.73-9.15-42.1-9.86-2.45,0-5.38,0-8-.32a136,136,0,0,0-15.76.86c-8.9.82-18.77,6.43-27.74,5.53-.48,0,8.7-3.77,8-3.61-4.75,1-9.92,1.21-15.37,1.88-3.47.39-6.45.82-9.89,1-103,8.73-190-55.81-190-55.81-7.41,25,33.17,74.3,88.52,93.57Z" style="opacity:0.5;isolation:isolate;fill:url(#linear-gradient-11)"/><path d="M813.74,801.65c104.16-102.27,156.86-226.58,134.58-366,0,0,8.9,71.5-24.85,144.63,16.21-71.39,18.1-160.11-25-252C841,205.64,746.45,141.11,710.35,114.19,655.66,73.4,633,31.87,632.57,23.3c-16.34,33.48-65.77,148.2-5.31,247,56.64,92.56,145.86,120,208.33,205C950.67,631.67,813.74,801.65,813.74,801.65Z" style="fill:url(#linear-gradient-12)"/><path d="M798.81,535.55C762.41,460.35,717,427.55,674,392c5,7,6.23,9.47,9,14,37.83,40.32,93.61,138.66,53.11,262.11C659.88,900.48,355,791.06,323,760.32,335.93,894.81,561,959.16,707.6,872,791,793,858.47,658.79,798.81,535.55Z" style="fill:url(#linear-gradient-13)"/></g></g></g></g></svg>
\ No newline at end of file diff --git a/tools/profiler/tests/browser/serviceworkers/serviceworker-utils.js b/tools/profiler/tests/browser/serviceworkers/serviceworker-utils.js new file mode 100644 index 0000000000..16a9f0c91f --- /dev/null +++ b/tools/profiler/tests/browser/serviceworkers/serviceworker-utils.js @@ -0,0 +1,39 @@ +// Most of this file has been stolen from dom/serviceworkers/test/utils.js. + +function waitForState(worker, state) { + return new Promise((resolve, reject) => { + function onStateChange() { + if (worker.state === state) { + worker.removeEventListener("statechange", onStateChange); + resolve(); + } + if (worker.state === "redundant") { + worker.removeEventListener("statechange", onStateChange); + reject(new Error("The service worker failed to install.")); + } + } + + // First add an event listener, so we won't miss any change that happens + // before we check the current state. + worker.addEventListener("statechange", onStateChange); + + // Now check if the worker is already in the desired state. + onStateChange(); + }); +} + +async function registerServiceWorkerAndWait(serviceWorkerFile) { + if (!serviceWorkerFile) { + throw new Error( + "No service worker filename has been specified. Please specify a valid filename." + ); + } + + console.log(`...registering the serviceworker "${serviceWorkerFile}"`); + const reg = await navigator.serviceWorker.register(`./${serviceWorkerFile}`, { + scope: "./", + }); + console.log("...waiting for activation"); + await waitForState(reg.installing, "activated"); + console.log("...activated!"); +} diff --git a/tools/profiler/tests/browser/serviceworkers/serviceworker_cache_first.js b/tools/profiler/tests/browser/serviceworkers/serviceworker_cache_first.js new file mode 100644 index 0000000000..baa07fd6d8 --- /dev/null +++ b/tools/profiler/tests/browser/serviceworkers/serviceworker_cache_first.js @@ -0,0 +1,34 @@ +const files = ["serviceworker_page.html", "firefox-logo-nightly.svg"]; +const cacheName = "v1"; + +self.addEventListener("install", event => { + performance.mark("__serviceworker_event"); + console.log("[SW]:", "Install event"); + + event.waitUntil(cacheAssets()); +}); + +async function cacheAssets() { + const cache = await caches.open(cacheName); + await cache.addAll(files); +} + +self.addEventListener("fetch", event => { + performance.mark("__serviceworker_event"); + console.log("Handling fetch event for", event.request.url); + event.respondWith(handleFetch(event.request)); +}); + +async function handleFetch(request) { + const cachedResponse = await caches.match(request); + if (cachedResponse) { + console.log("Found response in cache:", cachedResponse); + + return cachedResponse; + } + console.log("No response found in cache. About to fetch from network..."); + + const networkResponse = await fetch(request); + console.log("Response from network is:", networkResponse); + return networkResponse; +} diff --git a/tools/profiler/tests/browser/serviceworkers/serviceworker_no_fetch_handler.js b/tools/profiler/tests/browser/serviceworkers/serviceworker_no_fetch_handler.js new file mode 100644 index 0000000000..f656665ca0 --- /dev/null +++ b/tools/profiler/tests/browser/serviceworkers/serviceworker_no_fetch_handler.js @@ -0,0 +1,4 @@ +self.addEventListener("install", event => { + performance.mark("__serviceworker_event"); + console.log("[SW]:", "Install event"); +}); diff --git a/tools/profiler/tests/browser/serviceworkers/serviceworker_no_respondWith_in_fetch_handler.js b/tools/profiler/tests/browser/serviceworkers/serviceworker_no_respondWith_in_fetch_handler.js new file mode 100644 index 0000000000..255c8269a1 --- /dev/null +++ b/tools/profiler/tests/browser/serviceworkers/serviceworker_no_respondWith_in_fetch_handler.js @@ -0,0 +1,9 @@ +self.addEventListener("install", event => { + performance.mark("__serviceworker_event"); + console.log("[SW]:", "Install event"); +}); + +self.addEventListener("fetch", event => { + performance.mark("__serviceworker_event"); + console.log("Handling fetch event for", event.request.url); +}); diff --git a/tools/profiler/tests/browser/serviceworkers/serviceworker_page.html b/tools/profiler/tests/browser/serviceworkers/serviceworker_page.html new file mode 100644 index 0000000000..1c2100a9d6 --- /dev/null +++ b/tools/profiler/tests/browser/serviceworkers/serviceworker_page.html @@ -0,0 +1,10 @@ +<!doctype html> +<html> + <head> + <meta charset='utf-8'> + <meta name='viewport' content='initial-scale=1'> + </head> + <body> + <img src='firefox-logo-nightly.svg' width="24"> + </body> +</html> diff --git a/tools/profiler/tests/browser/serviceworkers/serviceworker_register.html b/tools/profiler/tests/browser/serviceworkers/serviceworker_register.html new file mode 100644 index 0000000000..86719787f4 --- /dev/null +++ b/tools/profiler/tests/browser/serviceworkers/serviceworker_register.html @@ -0,0 +1,9 @@ +<!doctype html> +<html> + <head> + <meta charset='utf-8'> + <script src='serviceworker-utils.js'></script> + </head> + <body> + </body> +</html> diff --git a/tools/profiler/tests/browser/serviceworkers/serviceworker_simple.html b/tools/profiler/tests/browser/serviceworkers/serviceworker_simple.html new file mode 100644 index 0000000000..f7c32d02c3 --- /dev/null +++ b/tools/profiler/tests/browser/serviceworkers/serviceworker_simple.html @@ -0,0 +1,9 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"/> + </head> + <body> + Testing + </body> +</html> diff --git a/tools/profiler/tests/browser/serviceworkers/serviceworker_synthetized_response.js b/tools/profiler/tests/browser/serviceworkers/serviceworker_synthetized_response.js new file mode 100644 index 0000000000..891b679a5f --- /dev/null +++ b/tools/profiler/tests/browser/serviceworkers/serviceworker_synthetized_response.js @@ -0,0 +1,27 @@ +self.addEventListener("install", event => { + performance.mark("__serviceworker_event"); + dump("[SW]:", "Install event\n"); +}); + +self.addEventListener("fetch", event => { + performance.mark("__serviceworker_event"); + dump(`Handling fetch event for ${event.request.url}\n`); + event.respondWith(handleFetch(event.request)); +}); + +async function handleFetch(request) { + if (request.url.endsWith("-generated.svg")) { + dump( + "An icon file that should be generated was requested, let's answer directly.\n" + ); + return new Response( + `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 953.37 984"><defs><linearGradient id="linear-gradient" x1="-14706.28" y1="9250.14" x2="-14443.04" y2="9250.14" gradientTransform="matrix(0.76, 0.03, 0.05, -1.12, 11485.47, 11148)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#0083ff"/><stop offset="0.1" stop-color="#0092f8"/><stop offset="0.31" stop-color="#00abeb"/><stop offset="0.52" stop-color="#00bee1"/><stop offset="0.75" stop-color="#00c8dc"/><stop offset="1" stop-color="#00ccda"/></linearGradient><radialGradient id="radial-gradient" cx="-7588.66" cy="8866.53" r="791.23" gradientTransform="matrix(1.23, 0, 0, -1.22, 9958.21, 11048.11)" gradientUnits="userSpaceOnUse"><stop offset="0.02" stop-color="#005fe7"/><stop offset="0.18" stop-color="#0042b4"/><stop offset="0.32" stop-color="#002989"/><stop offset="0.4" stop-color="#002079"/><stop offset="0.47" stop-color="#131d78"/><stop offset="0.66" stop-color="#3b1676"/><stop offset="0.75" stop-color="#4a1475"/></radialGradient><linearGradient id="linear-gradient-2" x1="539.64" y1="254.8" x2="348.2" y2="881.03" gradientTransform="matrix(1, 0, 0, -1, 1, 984)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#000f43" stop-opacity="0.4"/><stop offset="0.48" stop-color="#001962" stop-opacity="0.17"/><stop offset="1" stop-color="#002079" stop-opacity="0"/></linearGradient><linearGradient id="linear-gradient-3" x1="540.64" y1="254.8" x2="349.2" y2="881.03" gradientTransform="matrix(1, 0, 0, -1, 0, 984)" xlink:href="#linear-gradient-2"/><linearGradient id="linear-gradient-4" x1="-8367.12" y1="7348.87" x2="-8482.36" y2="7357.76" gradientTransform="matrix(1.22, 0.12, 0.12, -1.22, 10241.06, 10765.32)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#812cc9"/><stop offset="1" stop-color="#005fe7"/></linearGradient><linearGradient id="linear-gradient-5" x1="-8449.89" y1="7496.97" x2="-8341.94" y2="7609.09" gradientTransform="matrix(1.22, 0.12, 0.12, -1.22, 10241.06, 10765.32)" gradientUnits="userSpaceOnUse"><stop offset="0.05" stop-color="#005fe7"/><stop offset="0.18" stop-color="#065de6"/><stop offset="0.35" stop-color="#1856e1"/><stop offset="0.56" stop-color="#354adb"/><stop offset="0.78" stop-color="#5d3ad1"/><stop offset="0.95" stop-color="#812cc9"/></linearGradient><linearGradient id="linear-gradient-6" x1="-8653.41" y1="7245.3" x2="-8422.52" y2="7244.76" gradientTransform="matrix(1.22, 0.12, 0.12, -1.22, 10241.06, 10765.32)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#002079"/><stop offset="0.99" stop-color="#a238ff"/></linearGradient><radialGradient id="radial-gradient-2" cx="644.11" cy="599.83" fx="785.0454815336918" fy="470.6889181532662" r="793.95" gradientTransform="matrix(1, 0, 0, -1, 0, 984)" gradientUnits="userSpaceOnUse"><stop offset="0.2" stop-color="#00fdff"/><stop offset="0.26" stop-color="#0af1ff"/><stop offset="0.37" stop-color="#23d2ff"/><stop offset="0.52" stop-color="#4da0ff"/><stop offset="0.69" stop-color="#855bff"/><stop offset="0.77" stop-color="#a238ff"/><stop offset="0.81" stop-color="#a738fd"/><stop offset="0.86" stop-color="#b539f9"/><stop offset="0.9" stop-color="#cd39f1"/><stop offset="0.96" stop-color="#ee3ae6"/><stop offset="0.98" stop-color="#ff3be0"/></radialGradient><linearGradient id="linear-gradient-7" x1="-7458.97" y1="9093.17" x2="-7531.06" y2="8282.84" gradientTransform="matrix(1.23, 0, 0, -1.22, 9958.21, 11048.11)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#00ec00"/><stop offset="0.1" stop-color="#00e244"/><stop offset="0.22" stop-color="#00d694"/><stop offset="0.31" stop-color="#00cfc7"/><stop offset="0.35" stop-color="#00ccda"/><stop offset="0.42" stop-color="#0bc2dd" stop-opacity="0.92"/><stop offset="0.57" stop-color="#29a7e4" stop-opacity="0.72"/><stop offset="0.77" stop-color="#597df0" stop-opacity="0.4"/><stop offset="1" stop-color="#9448ff" stop-opacity="0"/></linearGradient><linearGradient id="linear-gradient-8" x1="-8926.61" y1="7680.53" x2="-8790.14" y2="7680.53" gradientTransform="matrix(1.22, 0.12, 0.12, -1.22, 10241.06, 10765.32)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#005fe7"/><stop offset="0.46" stop-color="#0071f3" stop-opacity="0.51"/><stop offset="0.83" stop-color="#007efc" stop-opacity="0.14"/><stop offset="1" stop-color="#0083ff" stop-opacity="0"/></linearGradient><radialGradient id="radial-gradient-3" cx="-8914.62" cy="7721.05" r="165.97" gradientTransform="matrix(1.22, 0.12, 0.12, -1.22, 10241.06, 10765.32)" gradientUnits="userSpaceOnUse"><stop offset="0.63" stop-color="#ffe302" stop-opacity="0"/><stop offset="0.67" stop-color="#ffe302" stop-opacity="0.05"/><stop offset="0.75" stop-color="#ffe302" stop-opacity="0.19"/><stop offset="0.86" stop-color="#ffe302" stop-opacity="0.4"/><stop offset="0.99" stop-color="#ffe302" stop-opacity="0.7"/></radialGradient><linearGradient id="linear-gradient-9" x1="214.02" y1="2032.47" x2="96.19" y2="2284.31" gradientTransform="matrix(0.99, 0.1, 0.1, -0.99, -250.1, 2306.29)" gradientUnits="userSpaceOnUse"><stop offset="0.19" stop-color="#4a1475" stop-opacity="0.5"/><stop offset="0.62" stop-color="#2277ac" stop-opacity="0.23"/><stop offset="0.94" stop-color="#00ccda" stop-opacity="0"/></linearGradient><linearGradient id="linear-gradient-10" x1="-38.44" y1="278.18" x2="55.67" y2="171.29" gradientTransform="matrix(0.99, 0.1, 0.1, -0.99, 229.04, 745.87)" gradientUnits="userSpaceOnUse"><stop offset="0.01" stop-color="#002079" stop-opacity="0.5"/><stop offset="1" stop-color="#0083ff" stop-opacity="0"/></linearGradient><linearGradient id="linear-gradient-11" x1="142.45" y1="96.25" x2="142.5" y2="149.68" gradientTransform="matrix(0.99, 0.1, 0.1, -0.99, 229.04, 745.87)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#4a1475" stop-opacity="0.9"/><stop offset="0.18" stop-color="#6720a2" stop-opacity="0.6"/><stop offset="0.38" stop-color="#812acb" stop-opacity="0.34"/><stop offset="0.57" stop-color="#9332e8" stop-opacity="0.15"/><stop offset="0.76" stop-color="#9e36f9" stop-opacity="0.04"/><stop offset="0.93" stop-color="#a238ff" stop-opacity="0"/></linearGradient><linearGradient id="linear-gradient-12" x1="620.52" y1="947.88" x2="926.18" y2="264.39" gradientTransform="matrix(1, 0, 0, -1, 0, 984)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#00ec00" stop-opacity="0"/><stop offset="0.28" stop-color="#00dc6d" stop-opacity="0.5"/><stop offset="0.5" stop-color="#00d1bb" stop-opacity="0.86"/><stop offset="0.6" stop-color="#00ccda"/><stop offset="0.68" stop-color="#04c9db"/><stop offset="0.75" stop-color="#0fc1df"/><stop offset="0.83" stop-color="#23b2e6"/><stop offset="0.9" stop-color="#3e9ef0"/><stop offset="0.98" stop-color="#6184fc"/><stop offset="0.99" stop-color="#6680fe"/></linearGradient><linearGradient id="linear-gradient-13" x1="680.88" y1="554.79" x2="536.1" y2="166.04" gradientTransform="matrix(1, 0, 0, -1, 0, 984)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#0083ff"/><stop offset="0.04" stop-color="#0083ff" stop-opacity="0.92"/><stop offset="0.14" stop-color="#0083ff" stop-opacity="0.71"/><stop offset="0.26" stop-color="#0083ff" stop-opacity="0.52"/><stop offset="0.37" stop-color="#0083ff" stop-opacity="0.36"/><stop offset="0.49" stop-color="#0083ff" stop-opacity="0.23"/><stop offset="0.61" stop-color="#0083ff" stop-opacity="0.13"/><stop offset="0.73" stop-color="#0083ff" stop-opacity="0.06"/><stop offset="0.86" stop-color="#0083ff" stop-opacity="0.01"/><stop offset="1" stop-color="#0083ff" stop-opacity="0"/></linearGradient></defs><title>firefox-logo-nightly</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><g id="Layer_2-2" data-name="Layer 2"><g id="Firefox"><path d="M770.28,91.56c-23.95,27.88-35.1,90.64-10.82,154.26s61.5,49.8,84.7,114.67c30.62,85.6,16.37,200.59,16.37,200.59s36.81,106.61,62.47-6.63C979.79,341.74,770.28,143.94,770.28,91.56Z" style="fill:url(#linear-gradient)"/><path id="_Path_" data-name=" Path " d="M476.92,972.83c245.24,0,443.9-199.74,443.9-446s-198.66-446-443.66-446S33.5,280.51,33.5,526.8C33,773.33,231.92,972.83,476.92,972.83Z" style="fill:url(#radial-gradient)"/><path d="M810.67,803.64a246.8,246.8,0,0,1-30.12,18.18,705.31,705.31,0,0,0,38.3-63c9.46-10.47,18.13-20.65,25.19-31.65,3.44-5.41,7.31-12.08,11.42-19.82,24.92-44.9,52.4-117.56,53.18-192.2v-5.66a257.25,257.25,0,0,0-5.71-55.75c.2,1.43.38,2.86.56,4.29-.22-1.1-.41-2.21-.64-3.31.37,2,.66,4,1,6,5.09,43.22,1.47,85.37-16.68,116.45-.29.45-.58.88-.87,1.32,9.41-47.23,12.56-99.39,2.09-151.6,0,0-4.19-25.38-35.38-102.44-18-44.35-49.83-80.72-78-107.21-24.69-30.55-47.11-51-59.47-64.06C689.72,126,678.9,105.61,674.45,92.31c-3.85-1.93-53.14-49.81-57.05-51.63-21.51,33.35-89.16,137.67-57,235.15,14.58,44.17,51.47,90,90.07,115.74,1.69,1.94,23,25,33.09,77.16,10.45,53.85,5,95.86-16.54,158C641.73,681.24,577,735.12,516.3,740.63c-129.67,11.78-177.15-65.11-177.15-65.11C385.49,694,436.72,690.17,467.87,671c31.4-19.43,50.39-33.83,65.81-28.15C548.86,648.43,561,632,550.1,615a78.5,78.5,0,0,0-79.4-34.57c-31.43,5.11-60.23,30-101.41,5.89a86.29,86.29,0,0,1-7.73-5.06c-2.71-1.79,8.83,2.72,6.13.69-8-4.35-22.2-13.84-25.88-17.22-.61-.56,6.22,2.18,5.61,1.62-38.51-31.71-33.7-53.13-32.49-66.57,1-10.75,8-24.52,19.75-30.11,5.69,3.11,9.24,5.48,9.24,5.48s-2.43-5-3.74-7.58c.46-.2.9-.15,1.36-.34,4.66,2.25,15,8.1,20.41,11.67,7.07,5,9.33,9.44,9.33,9.44s1.86-1,.48-5.37c-.5-1.78-2.65-7.45-9.65-13.17h.44A81.61,81.61,0,0,1,374.42,478c2-7.18,5.53-14.68,4.75-28.09-.48-9.43-.26-11.87-1.92-15.51-1.49-3.13.83-4.35,3.42-1.1a32.5,32.5,0,0,0-2.21-7.4v-.24c3.23-11.24,68.25-40.46,73-43.88A67.2,67.2,0,0,0,470.59,361c3.62-5.76,6.34-13.85,7-26.11.36-8.84-3.76-14.73-69.51-21.62-18-1.77-28.53-14.8-34.53-26.82-1.09-2.59-2.21-4.94-3.33-7.28a57.68,57.68,0,0,1-2.56-8.43c10.75-30.87,28.81-57,55.37-76.7,1.45-1.32-5.78.34-4.34-1,1.69-1.54,12.71-6,14.79-7,2.54-1.2-10.88-6.9-22.73-5.51-12.07,1.36-14.63,2.8-21.07,5.53,2.67-2.66,11.17-6.15,9.18-6.13-13,2-29.18,9.56-43,18.12a10.66,10.66,0,0,1,.83-4.35c-6.44,2.73-22.26,13.79-26.87,23.14a44.29,44.29,0,0,0,.27-5.4,84.17,84.17,0,0,0-13.19,13.82l-.24.22c-37.36-15-70.23-16-98.05-9.28-6.09-6.11-9.06-1.64-22.91-32.07-.94-1.83.72,1.81,0,0-2.28-5.9,1.39,7.87,0,0-23.28,18.37-53.92,39.19-68.63,53.89-.18.59,17.16-4.9,0,0-6,1.72-5.6,5.28-6.51,37.5-.22,2.44,0,5.18-.22,7.38-11.75,15-19.75,27.64-22.78,34.21-15.19,26.18-31.93,67-48.15,131.55A334.82,334.82,0,0,1,75.2,398.36C61.71,432.63,48.67,486.44,46.07,569.3A482.08,482.08,0,0,1,58.6,518.64,473,473,0,0,0,93.33,719.71c9.33,22.82,24.76,57.46,51,95.4C226.9,902,343.31,956,472.21,956,606.79,956,727.64,897.13,810.67,803.64Z" style="fill:url(#linear-gradient-2)"/><path d="M810.67,803.64a246.8,246.8,0,0,1-30.12,18.18,705.31,705.31,0,0,0,38.3-63c9.46-10.47,18.13-20.65,25.19-31.65,3.44-5.41,7.31-12.08,11.42-19.82,24.92-44.9,52.4-117.56,53.18-192.2v-5.66a257.25,257.25,0,0,0-5.71-55.75c.2,1.43.38,2.86.56,4.29-.22-1.1-.41-2.21-.64-3.31.37,2,.66,4,1,6,5.09,43.22,1.47,85.37-16.68,116.45-.29.45-.58.88-.87,1.32,9.41-47.23,12.56-99.39,2.09-151.6,0,0-4.19-25.38-35.38-102.44-18-44.35-49.83-80.72-78-107.21-24.69-30.55-47.11-51-59.47-64.06C689.72,126,678.9,105.61,674.45,92.31c-3.85-1.93-53.14-49.81-57.05-51.63-21.51,33.35-89.16,137.67-57,235.15,14.58,44.17,51.47,90,90.07,115.74,1.69,1.94,23,25,33.09,77.16,10.45,53.85,5,95.86-16.54,158C641.73,681.24,577,735.12,516.3,740.63c-129.67,11.78-177.15-65.11-177.15-65.11C385.49,694,436.72,690.17,467.87,671c31.4-19.43,50.39-33.83,65.81-28.15C548.86,648.43,561,632,550.1,615a78.5,78.5,0,0,0-79.4-34.57c-31.43,5.11-60.23,30-101.41,5.89a86.29,86.29,0,0,1-7.73-5.06c-2.71-1.79,8.83,2.72,6.13.69-8-4.35-22.2-13.84-25.88-17.22-.61-.56,6.22,2.18,5.61,1.62-38.51-31.71-33.7-53.13-32.49-66.57,1-10.75,8-24.52,19.75-30.11,5.69,3.11,9.24,5.48,9.24,5.48s-2.43-5-3.74-7.58c.46-.2.9-.15,1.36-.34,4.66,2.25,15,8.1,20.41,11.67,7.07,5,9.33,9.44,9.33,9.44s1.86-1,.48-5.37c-.5-1.78-2.65-7.45-9.65-13.17h.44A81.61,81.61,0,0,1,374.42,478c2-7.18,5.53-14.68,4.75-28.09-.48-9.43-.26-11.87-1.92-15.51-1.49-3.13.83-4.35,3.42-1.1a32.5,32.5,0,0,0-2.21-7.4v-.24c3.23-11.24,68.25-40.46,73-43.88A67.2,67.2,0,0,0,470.59,361c3.62-5.76,6.34-13.85,7-26.11.36-8.84-3.76-14.73-69.51-21.62-18-1.77-28.53-14.8-34.53-26.82-1.09-2.59-2.21-4.94-3.33-7.28a57.68,57.68,0,0,1-2.56-8.43c10.75-30.87,28.81-57,55.37-76.7,1.45-1.32-5.78.34-4.34-1,1.69-1.54,12.71-6,14.79-7,2.54-1.2-10.88-6.9-22.73-5.51-12.07,1.36-14.63,2.8-21.07,5.53,2.67-2.66,11.17-6.15,9.18-6.13-13,2-29.18,9.56-43,18.12a10.66,10.66,0,0,1,.83-4.35c-6.44,2.73-22.26,13.79-26.87,23.14a44.29,44.29,0,0,0,.27-5.4,84.17,84.17,0,0,0-13.19,13.82l-.24.22c-37.36-15-70.23-16-98.05-9.28-6.09-6.11-9.06-1.64-22.91-32.07-.94-1.83.72,1.81,0,0-2.28-5.9,1.39,7.87,0,0-23.28,18.37-53.92,39.19-68.63,53.89-.18.59,17.16-4.9,0,0-6,1.72-5.6,5.28-6.51,37.5-.22,2.44,0,5.18-.22,7.38-11.75,15-19.75,27.64-22.78,34.21-15.19,26.18-31.93,67-48.15,131.55A334.82,334.82,0,0,1,75.2,398.36C61.71,432.63,48.67,486.44,46.07,569.3A482.08,482.08,0,0,1,58.6,518.64,473,473,0,0,0,93.33,719.71c9.33,22.82,24.76,57.46,51,95.4C226.9,902,343.31,956,472.21,956,606.79,956,727.64,897.13,810.67,803.64Z" style="fill:url(#linear-gradient-3)"/><path d="M711.1,866.71c162.87-18.86,235-186.7,142.38-190C769.85,674,634,875.61,711.1,866.71Z" style="fill:url(#linear-gradient-4)"/><path d="M865.21,642.42C977.26,577.21,948,436.34,948,436.34s-43.25,50.24-72.62,130.32C846.4,646,797.84,681.81,865.21,642.42Z" style="fill:url(#linear-gradient-5)"/><path d="M509.47,950.06C665.7,999.91,800,876.84,717.21,835.74,642,798.68,435.32,926.49,509.47,950.06Z" style="fill:url(#linear-gradient-6)"/><path d="M638.58,21.42l.53-.57A1.7,1.7,0,0,0,638.58,21.42ZM876.85,702.23c3.8-5.36,8.94-22.53,13.48-30.21,27.58-44.52,27.78-80,27.78-80.84,16.66-83.22,15.15-117.2,4.9-180-8.25-50.6-44.32-123.09-75.57-158-32.2-36-9.51-24.25-40.69-50.52-27.33-30.29-53.82-60.29-68.25-72.36C634.22,43.09,636.57,24.58,638.58,21.42c-.34.37-.84.92-1.47,1.64C635.87,18.14,635,14,635,14s-57,57-69,152c-7.83,62,15.38,126.68,49,168a381.62,381.62,0,0,0,59,58h0c25.4,36.48,39.38,81.49,39.38,129.91,0,121.24-98.34,219.53-219.65,219.53a220.14,220.14,0,0,1-49.13-5.52c-57.24-10.92-90.3-39.8-106.78-59.41-9.45-11.23-13.46-19.42-13.46-19.42,51.28,18.37,108,14.53,142.47-4.52,34.75-19.26,55.77-33.55,72.84-27.92,16.82,5.61,30.21-10.67,18.2-27.54-11.77-16.85-42.4-41-87.88-34.29-34.79,5.07-66.66,29.76-112.24,5.84a97.34,97.34,0,0,1-8.55-5c-3-1.77,9.77,2.69,6.79.68-8.87-4.32-24.57-13.73-28.64-17.07-.68-.56,6.88,2.16,6.2,1.6-42.62-31.45-37.3-52.69-36-66,1.07-10.66,8.81-24.32,21.86-29.86,6.3,3.08,10.23,5.43,10.23,5.43s-2.69-4.92-4.14-7.51c.51-.19,1-.15,1.5-.34,5.16,2.23,16.58,8,22.59,11.57,7.83,4.95,10.32,9.36,10.32,9.36s2.06-1,.54-5.33c-.56-1.77-2.93-7.39-10.68-13.07h.48a91.65,91.65,0,0,1,13.13,8.17c2.19-7.12,6.12-14.56,5.25-27.86-.53-9.35-.28-11.78-2.12-15.39-1.65-3.1.92-4.31,3.78-1.09a29.73,29.73,0,0,0-2.44-7.34v-.24c3.57-11.14,75.53-40.12,80.77-43.51a70.24,70.24,0,0,0,21.17-20.63c4-5.72,7-13.73,7.75-25.89.25-5.48-1.44-9.82-20.5-14-11.44-2.49-29.14-4.91-56.43-7.47-19.9-1.76-31.58-14.68-38.21-26.6-1.21-2.57-2.45-4.9-3.68-7.22a53.41,53.41,0,0,1-2.83-8.36,158.47,158.47,0,0,1,61.28-76.06c1.6-1.31-6.4.33-4.8-1,1.87-1.52,14.06-5.93,16.37-6.92,2.81-1.19-12-6.84-25.16-5.47-13.36,1.35-16.19,2.78-23.32,5.49,3-2.64,12.37-6.1,10.16-6.08-14.4,2-32.3,9.48-47.6,18a9.72,9.72,0,0,1,.92-4.31c-7.13,2.71-24.64,13.67-29.73,23a39.79,39.79,0,0,0,.29-5.35,88.55,88.55,0,0,0-14.6,13.7l-.27.22C258.14,196,221.75,195,191,201.72c-6.74-6.06-17.57-15.23-32.89-45.4-1-1.82-1.6,3.75-2.4,2-6-13.81-9.55-36.44-9-52,0,0-12.32,5.61-22.51,29.06-1.89,4.21-3.11,6.54-4.32,8.87-.56.68,1.27-7.7,1-7.24-1.77,3-6.36,7.19-8.37,12.62-1.38,4-3.32,6.27-4.56,11.29l-.29.46c-.1-1.48.37-6.08,0-5.14A235.4,235.4,0,0,0,95.34,186c-5.49,18-11.88,42.61-12.89,74.57-.24,2.42,0,5.14-.25,7.32-13,14.83-21.86,27.39-25.2,33.91-16.81,26-35.33,66.44-53.29,130.46a319.35,319.35,0,0,1,28.54-50C17.32,416.25,2.89,469.62,0,551.8a436.92,436.92,0,0,1,13.87-50.24C11.29,556.36,17.68,624.3,52.32,701c20.57,45,67.92,136.6,183.62,208h0s39.36,29.3,107,51.26c5,1.81,10.06,3.6,15.23,5.33q-2.43-1-4.71-2A484.9,484.9,0,0,0,492.27,984c175.18.15,226.85-70.2,226.85-70.2l-.51.38q3.71-3.49,7.14-7.26c-27.64,26.08-90.75,27.84-114.3,26,40.22-11.81,66.69-21.81,118.17-41.52q9-3.36,18.48-7.64l2-.94c1.25-.58,2.49-1.13,3.75-1.74a349.3,349.3,0,0,0,70.26-44c51.7-41.3,63-81.56,68.83-108.1-.82,2.54-3.37,8.47-5.17,12.32-13.31,28.48-42.84,46-74.91,61a689.05,689.05,0,0,0,42.38-62.44C865.77,729.39,869,713.15,876.85,702.23Z" style="fill:url(#radial-gradient-2)"/><path d="M813.92,801c21.08-23.24,40-49.82,54.35-80,36.9-77.58,94-206.58,49-341.31C881.77,273.22,833,215,771.11,158.12,670.56,65.76,642.48,24.52,642.48,0c0,0-116.09,129.41-65.74,264.38s153.46,130,221.68,270.87c80.27,165.74-64.95,346.61-185,397.24,7.35-1.63,267-60.38,280.61-208.88C893.68,726.34,887.83,767.41,813.92,801Z" style="fill:url(#linear-gradient-7)"/><path d="M477.59,319.37c.39-8.77-4.16-14.66-76.68-21.46-29.84-2.76-41.26-30.33-44.75-41.94-10.61,27.56-15,56.49-12.64,91.48,1.61,22.92,17,47.52,24.37,62,0,0,1.64-2.13,2.39-2.91,13.86-14.43,71.94-36.42,77.39-39.54C453.69,363.16,476.58,346.44,477.59,319.37Z" style="fill:url(#linear-gradient-8)"/><path d="M477.59,319.37c.39-8.77-4.16-14.66-76.68-21.46-29.84-2.76-41.26-30.33-44.75-41.94-10.61,27.56-15,56.49-12.64,91.48,1.61,22.92,17,47.52,24.37,62,0,0,1.64-2.13,2.39-2.91,13.86-14.43,71.94-36.42,77.39-39.54C453.69,363.16,476.58,346.44,477.59,319.37Z" style="opacity:0.5;isolation:isolate;fill:url(#radial-gradient-3)"/><path d="M158.31,156.47c-1-1.82-1.6,3.75-2.4,2-6-13.81-9.58-36.2-8.72-52,0,0-12.32,5.61-22.51,29.06-1.89,4.21-3.11,6.54-4.32,8.86-.56.68,1.27-7.7,1-7.24-1.77,3-6.36,7.19-8.35,12.38-1.65,4.24-3.35,6.52-4.61,11.77-.39,1.43.39-6.32,0-5.38C84.72,201.68,80.19,271,82.69,268,133.17,214.14,191,201.36,191,201.36c-6.15-4.53-19.53-17.63-32.7-44.89Z" style="fill:url(#linear-gradient-9)"/><path d="M349.84,720.1c-69.72-29.77-149-71.75-146-167.14C207.92,427.35,321,452.18,321,452.18c-4.27,1-15.68,9.16-19.72,17.82-4.27,10.83-12.07,35.28,11.55,60.9,37.09,40.19-76.2,95.36,98.66,199.57,4.41,2.4-41-1.43-61.64-10.36Z" style="fill:url(#linear-gradient-10)"/><path d="M325.07,657.5c49.44,17.21,107,14.19,141.52-4.86,23.09-12.85,52.7-33.43,70.92-28.35-15.78-6.24-27.73-9.15-42.1-9.86-2.45,0-5.38,0-8-.32a136,136,0,0,0-15.76.86c-8.9.82-18.77,6.43-27.74,5.53-.48,0,8.7-3.77,8-3.61-4.75,1-9.92,1.21-15.37,1.88-3.47.39-6.45.82-9.89,1-103,8.73-190-55.81-190-55.81-7.41,25,33.17,74.3,88.52,93.57Z" style="opacity:0.5;isolation:isolate;fill:url(#linear-gradient-11)"/><path d="M813.74,801.65c104.16-102.27,156.86-226.58,134.58-366,0,0,8.9,71.5-24.85,144.63,16.21-71.39,18.1-160.11-25-252C841,205.64,746.45,141.11,710.35,114.19,655.66,73.4,633,31.87,632.57,23.3c-16.34,33.48-65.77,148.2-5.31,247,56.64,92.56,145.86,120,208.33,205C950.67,631.67,813.74,801.65,813.74,801.65Z" style="fill:url(#linear-gradient-12)"/><path d="M798.81,535.55C762.41,460.35,717,427.55,674,392c5,7,6.23,9.47,9,14,37.83,40.32,93.61,138.66,53.11,262.11C659.88,900.48,355,791.06,323,760.32,335.93,894.81,561,959.16,707.6,872,791,793,858.47,658.79,798.81,535.55Z" style="fill:url(#linear-gradient-13)"/></g></g></g></g></svg>`, + { headers: { "content-type": "image/svg+xml" } } + ); + } + + dump( + `A normal URL ${request.url} has been requested, let's fetch it from the network.\n` + ); + return fetch(request); +} diff --git a/tools/profiler/tests/browser/simple.html b/tools/profiler/tests/browser/simple.html new file mode 100644 index 0000000000..f7c32d02c3 --- /dev/null +++ b/tools/profiler/tests/browser/simple.html @@ -0,0 +1,9 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"/> + </head> + <body> + Testing + </body> +</html> diff --git a/tools/profiler/tests/browser/single_frame.html b/tools/profiler/tests/browser/single_frame.html new file mode 100644 index 0000000000..ebdfc41da2 --- /dev/null +++ b/tools/profiler/tests/browser/single_frame.html @@ -0,0 +1,10 @@ +<!DOCTYPE html> +<html> +<head> + <meta charset="utf-8"> + <title>Single Frame</title> +</head> +<body> + Single Frame +</body> +</html> |