Skip to content

Commit fc1ce28

Browse files
committed
Bug 1982318 - Add profile storage logic and save the uploaded profiles in about:logging r=padenot,julienw,fluent-reviewers,flod
This patch saves the uploaded profiles in indexeddb, but it doesn't display them yet. The following patch exposes a UI that displays the uploaded profiles and adds an option to delete them. Differential Revision: https://phabricator.services.mozilla.com/D260805
1 parent 07d5d1b commit fc1ce28

File tree

4 files changed

+190
-0
lines changed

4 files changed

+190
-0
lines changed

toolkit/content/aboutLogging/profileSaveUploadLogic.mjs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,43 @@ export class ProfileSaveOrUploadDialog {
279279
url: profileUrl,
280280
});
281281
this.#uploadedUrl.href = profileUrl;
282+
283+
// Save the uploaded profile information to IndexedDB
284+
try {
285+
const { saveUploadedProfile } = await import(
286+
"chrome://global/content/aboutLogging/profileStorage.mjs"
287+
);
288+
289+
const uploadDate = new Date();
290+
const profileName = await document.l10n.formatValue(
291+
"about-logging-uploaded-profile-name",
292+
{ date: uploadDate.getTime() }
293+
);
294+
295+
await saveUploadedProfile({
296+
jwtToken: uploadResult,
297+
profileToken: hash,
298+
profileUrl,
299+
uploadDate,
300+
profileName,
301+
});
302+
303+
// Refresh the uploaded profiles list if it's visible
304+
this.#refreshUploadedProfilesList();
305+
} catch (storageError) {
306+
console.error(
307+
"Error saving uploaded profile to storage:",
308+
storageError
309+
);
310+
this.#setState("error");
311+
document.l10n.setAttributes(
312+
this.#errorElement,
313+
"about-logging-profile-storage-error",
314+
{ errorText: String(storageError) }
315+
);
316+
return;
317+
}
318+
282319
this.#setState("uploaded");
283320
} catch (e) {
284321
console.error("Error while uploading", e);
@@ -300,4 +337,15 @@ export class ProfileSaveOrUploadDialog {
300337
const url = this.#uploadedUrl.href;
301338
await navigator.share({ url });
302339
};
340+
341+
/**
342+
* Refresh the uploaded profiles list if it exists.
343+
*/
344+
#refreshUploadedProfilesList() {
345+
// This method will be called from the main aboutLogging.mjs
346+
// when the UploadedProfilesManager is available
347+
if (window.gUploadedProfilesManager) {
348+
window.gUploadedProfilesManager.refresh();
349+
}
350+
}
303351
}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4+
5+
// This module handles storage of uploaded profile information in IndexedDB
6+
// for the about:logging page.
7+
8+
const DB_NAME = "aboutLoggingProfiles";
9+
const DB_VERSION = 1;
10+
const STORE_NAME = "uploadedProfiles";
11+
12+
/**
13+
* Initialize the IndexedDB database for storing uploaded profile information.
14+
* @returns {Promise<IDBDatabase>}
15+
*/
16+
function initDB() {
17+
return new Promise((resolve, reject) => {
18+
const request = indexedDB.open(DB_NAME, DB_VERSION);
19+
20+
request.onerror = () => reject(request.error);
21+
request.onsuccess = () => resolve(request.result);
22+
23+
request.onupgradeneeded = e => {
24+
const db = request.result;
25+
db.onerror = reject;
26+
27+
if (e.oldVersion < 1) {
28+
// Version 1: this is the first version of the DB.
29+
const store = db.createObjectStore(STORE_NAME, {
30+
keyPath: "id",
31+
autoIncrement: true,
32+
});
33+
store.createIndex("uploadDate", "uploadDate", { unique: false });
34+
store.createIndex("profileToken", "profileToken", { unique: true });
35+
}
36+
};
37+
});
38+
}
39+
40+
/**
41+
* Save uploaded profile information to IndexedDB.
42+
* @param {Object} profileInfo
43+
* @param {string} profileInfo.jwtToken - The JWT token returned by the server
44+
* @param {string} profileInfo.profileToken - The profile token extracted from JWT
45+
* @param {string} profileInfo.profileUrl - The full profile URL
46+
* @param {Date} profileInfo.uploadDate - When the profile was uploaded
47+
* @param {string} profileInfo.profileName - A descriptive name for the profile
48+
* @returns {Promise<number>} The ID of the stored profile
49+
*/
50+
export async function saveUploadedProfile(profileInfo) {
51+
const db = await initDB();
52+
53+
return new Promise((resolve, reject) => {
54+
const transaction = db.transaction([STORE_NAME], "readwrite");
55+
const store = transaction.objectStore(STORE_NAME);
56+
57+
const request = store.add({
58+
jwtToken: profileInfo.jwtToken,
59+
profileToken: profileInfo.profileToken,
60+
profileUrl: profileInfo.profileUrl,
61+
uploadDate: profileInfo.uploadDate,
62+
profileName: profileInfo.profileName,
63+
});
64+
65+
request.onerror = () => reject(request.error);
66+
request.onsuccess = () => resolve(request.result);
67+
});
68+
}
69+
70+
/**
71+
* Get all uploaded profiles from IndexedDB.
72+
* @returns {Promise<Array>} Array of uploaded profile information
73+
*/
74+
export async function getAllUploadedProfiles() {
75+
const db = await initDB();
76+
77+
return new Promise((resolve, reject) => {
78+
const transaction = db.transaction([STORE_NAME], "readonly");
79+
const store = transaction.objectStore(STORE_NAME);
80+
const index = store.index("uploadDate");
81+
82+
const request = index.getAll();
83+
84+
request.onerror = () => reject(request.error);
85+
request.onsuccess = () => {
86+
// Sort the list with the most recent uploaded profile first.
87+
resolve(request.result.reverse());
88+
};
89+
});
90+
}
91+
92+
/**
93+
* Delete an uploaded profile from IndexedDB.
94+
* @param {number} profileId - The ID of the profile to delete
95+
* @returns {Promise<void>}
96+
*/
97+
export async function deleteUploadedProfile(profileId) {
98+
const db = await initDB();
99+
100+
return new Promise((resolve, reject) => {
101+
const transaction = db.transaction([STORE_NAME], "readwrite");
102+
const store = transaction.objectStore(STORE_NAME);
103+
104+
const request = store.delete(profileId);
105+
106+
request.onerror = () => reject(request.error);
107+
request.onsuccess = () => resolve();
108+
});
109+
}
110+
111+
/**
112+
* Get a specific uploaded profile by ID.
113+
* @param {number} profileId - The ID of the profile to retrieve
114+
* @returns {Promise<Object|null>} The profile information or null if not found
115+
*/
116+
export async function getUploadedProfile(profileId) {
117+
const db = await initDB();
118+
119+
return new Promise((resolve, reject) => {
120+
const transaction = db.transaction([STORE_NAME], "readonly");
121+
const store = transaction.objectStore(STORE_NAME);
122+
123+
const request = store.get(profileId);
124+
125+
request.onerror = () => reject(request.error);
126+
request.onsuccess = () => resolve(request.result || null);
127+
});
128+
}

toolkit/content/jar.mn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ toolkit.jar:
1010
content/global/aboutLogging/aboutLogging.mjs (aboutLogging/aboutLogging.mjs)
1111
content/global/aboutLogging/jwt.mjs (aboutLogging/jwt.mjs)
1212
content/global/aboutLogging/profileSaveUploadLogic.mjs (aboutLogging/profileSaveUploadLogic.mjs)
13+
content/global/aboutLogging/profileStorage.mjs (aboutLogging/profileStorage.mjs)
1314
content/global/aboutNetError.mjs
1415
content/global/aboutNetError.html
1516
content/global/aboutNetErrorHelpers.mjs

toolkit/locales/en-US/toolkit/about/aboutLogging.ftl

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,4 +114,17 @@ about-logging-share-uploaded-url = <img data-l10n-name="share-image"/> Share URL
114114
about-logging-upload-error = An error happened while uploading the profile: { $errorText }
115115
# Variables:
116116
# $errorText (string) - The received error message, inserted as is.
117+
about-logging-profile-storage-error = An error happened while storing the uploaded profile: { $errorText }
118+
# Variables:
119+
# $errorText (string) - The received error message, inserted as is.
117120
about-logging-save-error = An error happened while saving the file: { $errorText }
121+
122+
## Uploaded Profiles section
123+
124+
# This string is used as the default name for performance profiles when they are
125+
# uploaded from about:logging and saved to the local database. The generated
126+
# name will appear in the "Uploaded Profiles" section list, allowing users to
127+
# identify when each profile was captured.
128+
# Variables:
129+
# $date (date) - The date and time when the profile was uploaded
130+
about-logging-uploaded-profile-name = Profile { DATETIME($date, dateStyle: "short", timeStyle: "medium") }

0 commit comments

Comments
 (0)