From 20dd73f2fe91c726b4c5243431351957fd3dd690 Mon Sep 17 00:00:00 2001 From: Pontoon Date: Wed, 28 Jan 2026 08:20:37 +0000 Subject: [PATCH 01/59] Pontoon/Firefox Profiler: Update 20 localizations --- locales/be/app.ftl | 3 +++ locales/de/app.ftl | 3 +++ locales/el/app.ftl | 3 +++ locales/en-CA/app.ftl | 3 +++ locales/en-GB/app.ftl | 3 +++ locales/es-CL/app.ftl | 3 +++ locales/fr/app.ftl | 3 +++ locales/fur/app.ftl | 3 +++ locales/fy-NL/app.ftl | 3 +++ locales/ia/app.ftl | 3 +++ locales/it/app.ftl | 3 +++ locales/kab/app.ftl | 3 +++ locales/nl/app.ftl | 3 +++ locales/pt-BR/app.ftl | 3 +++ locales/ru/app.ftl | 3 +++ locales/sv-SE/app.ftl | 3 +++ locales/tr/app.ftl | 3 +++ locales/uk/app.ftl | 3 +++ locales/zh-CN/app.ftl | 3 +++ locales/zh-TW/app.ftl | 3 +++ 20 files changed, 60 insertions(+) diff --git a/locales/be/app.ftl b/locales/be/app.ftl index 2d70f8efab..558610e1d8 100644 --- a/locales/be/app.ftl +++ b/locales/be/app.ftl @@ -79,6 +79,9 @@ CallNodeContextMenu--transform-focus-function = Фокус на функцыі .title = { CallNodeContextMenu--transform-focus-function-title } CallNodeContextMenu--transform-focus-function-inverted = Фокус на функцыі (інвертавана) .title = { CallNodeContextMenu--transform-focus-function-title } + +## + CallNodeContextMenu--transform-focus-subtree = Фокус толькі на паддрэве .title = Фокус на паддрэве прывядзе да выдалення любога ўзору, які не ўключае гэтую канкрэтную частку дрэва выклікаў. Гэта выдаляе галіну дрэва выклікаў, але робіць гэта толькі для аднаго вузла выкліку. Усе іншыя выклікі функцый ігнаруюцца. # This is used as the context menu item to apply the "Focus on category" transform. diff --git a/locales/de/app.ftl b/locales/de/app.ftl index 9ebd91f04e..e93b51871f 100644 --- a/locales/de/app.ftl +++ b/locales/de/app.ftl @@ -76,6 +76,9 @@ CallNodeContextMenu--transform-focus-function = Auf Funktion fokussieren .title = { CallNodeContextMenu--transform-focus-function-title } CallNodeContextMenu--transform-focus-function-inverted = Auf Funktion fokussieren (invertiert) .title = { CallNodeContextMenu--transform-focus-function-title } + +## + CallNodeContextMenu--transform-focus-subtree = Nur auf Unterbaum konzentrieren .title = Der Fokus auf einen Unterbaum entfernt jede Teilmenge, die diesen diff --git a/locales/el/app.ftl b/locales/el/app.ftl index 0b2651b9eb..931583a1e6 100644 --- a/locales/el/app.ftl +++ b/locales/el/app.ftl @@ -79,6 +79,9 @@ CallNodeContextMenu--transform-focus-function = Εστίαση στη συνάρ .title = { CallNodeContextMenu--transform-focus-function-title } CallNodeContextMenu--transform-focus-function-inverted = Εστίαση στη συνάρτηση (ανεστραμμένη) .title = { CallNodeContextMenu--transform-focus-function-title } + +## + CallNodeContextMenu--transform-focus-subtree = Εστίαση στο υπόδεντρο μόνο .title = Η εστίαση σε υπόδεντρο θα αφαιρέσει κάθε δείγμα που δεν περιλαμβάνει αυτό το diff --git a/locales/en-CA/app.ftl b/locales/en-CA/app.ftl index 1452fdf931..59cde82745 100644 --- a/locales/en-CA/app.ftl +++ b/locales/en-CA/app.ftl @@ -79,6 +79,9 @@ CallNodeContextMenu--transform-focus-function = Focus on function .title = { CallNodeContextMenu--transform-focus-function-title } CallNodeContextMenu--transform-focus-function-inverted = Focus on function (inverted) .title = { CallNodeContextMenu--transform-focus-function-title } + +## + CallNodeContextMenu--transform-focus-subtree = Focus on subtree only .title = Focusing on a subtree will remove any sample that does not include that diff --git a/locales/en-GB/app.ftl b/locales/en-GB/app.ftl index e4c74fd58c..3b23280829 100644 --- a/locales/en-GB/app.ftl +++ b/locales/en-GB/app.ftl @@ -79,6 +79,9 @@ CallNodeContextMenu--transform-focus-function = Focus on function .title = { CallNodeContextMenu--transform-focus-function-title } CallNodeContextMenu--transform-focus-function-inverted = Focus on function (inverted) .title = { CallNodeContextMenu--transform-focus-function-title } + +## + CallNodeContextMenu--transform-focus-subtree = Focus on subtree only .title = Focusing on a subtree will remove any sample that does not include that diff --git a/locales/es-CL/app.ftl b/locales/es-CL/app.ftl index 1d5cdae746..0355e3edbf 100644 --- a/locales/es-CL/app.ftl +++ b/locales/es-CL/app.ftl @@ -69,6 +69,9 @@ CallNodeContextMenu--transform-focus-function = Enfocarse en la función .title = { CallNodeContextMenu--transform-focus-function-title } CallNodeContextMenu--transform-focus-function-inverted = Enfocarse en la función (invertido) .title = { CallNodeContextMenu--transform-focus-function-title } + +## + CallNodeContextMenu--transform-focus-subtree = Enfocarse solo en el subárbol .title = Enfocarse en el subárbol removerá cualquier muestra que no incluya esa parte específica del árbol de llamados. Retira una rama del árbol de llamados, no obstante solo lo hace para un único nodo de llamada. Todo el resto de las llamadas de la función son ignoradas. # This is used as the context menu item to apply the "Focus on category" transform. diff --git a/locales/fr/app.ftl b/locales/fr/app.ftl index 636a7e3c17..d4593554c8 100644 --- a/locales/fr/app.ftl +++ b/locales/fr/app.ftl @@ -65,6 +65,9 @@ CallNodeContextMenu--transform-focus-function = Focus sur la fonction .title = { CallNodeContextMenu--transform-focus-function-title } CallNodeContextMenu--transform-focus-function-inverted = Focus sur la fonction (inversé) .title = { CallNodeContextMenu--transform-focus-function-title } + +## + CallNodeContextMenu--transform-focus-subtree = Focus sur le sous-arbre uniquement .title = Mettre le focus sur un sous-arbre supprime tout échantillon qui n’inclut pas cette partie spécifique de l’arbre d’appels. Il extrait une branche de l’arborescence des appels, mais il ne le fait que pour ce seul nœud d’appel. Tous les autres appels de la fonction sont ignorés. # This is used as the context menu item to apply the "Focus on category" transform. diff --git a/locales/fur/app.ftl b/locales/fur/app.ftl index a20e32ab9e..8d68abf639 100644 --- a/locales/fur/app.ftl +++ b/locales/fur/app.ftl @@ -77,6 +77,9 @@ CallNodeContextMenu--transform-focus-function = Concentrazion su funzion .title = { CallNodeContextMenu--transform-focus-function-title } CallNodeContextMenu--transform-focus-function-inverted = Concentrazion su funzion (invertide) .title = { CallNodeContextMenu--transform-focus-function-title } + +## + CallNodeContextMenu--transform-focus-subtree = Concentrazion dome su sot-arbul .title = La concentrazion suntun sot-arbul e gjavara ducj i campions che no includin chê diff --git a/locales/fy-NL/app.ftl b/locales/fy-NL/app.ftl index acab8586bf..832c15396e 100644 --- a/locales/fy-NL/app.ftl +++ b/locales/fy-NL/app.ftl @@ -79,6 +79,9 @@ CallNodeContextMenu--transform-focus-function = Fokus op funksje .title = { CallNodeContextMenu--transform-focus-function-title } CallNodeContextMenu--transform-focus-function-inverted = Fokus op funksje (omkeard) .title = { CallNodeContextMenu--transform-focus-function-title } + +## + CallNodeContextMenu--transform-focus-subtree = Allinnich fokus op substruktuer .title = As jo op in substruktuer fokust, wurdt elk foarbyld dat dat spesifike part diff --git a/locales/ia/app.ftl b/locales/ia/app.ftl index f07de8bba9..b9c188e6dd 100644 --- a/locales/ia/app.ftl +++ b/locales/ia/app.ftl @@ -79,6 +79,9 @@ CallNodeContextMenu--transform-focus-function = Foco sur function. .title = { CallNodeContextMenu--transform-focus-function-title } CallNodeContextMenu--transform-focus-function-inverted = Foco sur function (invertite). .title = { CallNodeContextMenu--transform-focus-function-title } + +## + CallNodeContextMenu--transform-focus-subtree = Foco solo sur sub-arbore. .title = Concentrar se sur un sub-arbore removera ulle specimen que non include ille diff --git a/locales/it/app.ftl b/locales/it/app.ftl index eab848f17e..6a9b6d07a2 100644 --- a/locales/it/app.ftl +++ b/locales/it/app.ftl @@ -65,6 +65,9 @@ CallNodeContextMenu--transform-focus-function = Focus sulla funzione .title = { CallNodeContextMenu--transform-focus-function-title } CallNodeContextMenu--transform-focus-function-inverted = Focus sulla funzione (invertito) .title = { CallNodeContextMenu--transform-focus-function-title } + +## + CallNodeContextMenu--transform-focus-subtree = Focus solo sul sottoalbero .title = Il focus sul sottoalbero rimuoverà tutti i campioni che non includono quella specifica parte dell’albero delle chiamate. Estrae un ramo dell’albero, ma solo per quel singolo nodo di chiamata. Tutte le altre chiamate a quella funzione vengono ignorate. # This is used as the context menu item to apply the "Focus on category" transform. diff --git a/locales/kab/app.ftl b/locales/kab/app.ftl index bd514b2b0a..c002367445 100644 --- a/locales/kab/app.ftl +++ b/locales/kab/app.ftl @@ -50,6 +50,9 @@ CallNodeContextMenu--transform-focus-function = Siḍes ef twuri .title = { CallNodeContextMenu--transform-focus-function-title } CallNodeContextMenu--transform-focus-function-inverted = Siḍeṣ ɣef twuri (imitti) .title = { CallNodeContextMenu--transform-focus-function-title } + +## + CallNodeContextMenu--transform-collapse-function-subtree = Ṭebbeq tawuri .title = Aṭebbeq n twuri ad yekkes meṛṛa ayen iwumi tessawaldaɣen ad tefk akk akud n uselkem i twuri. Aya ad yefk tallelt i usifses n umaɣnu ara yessiwlen i tengalt ur yattwaslaḍen ara. CallNodeContextMenu--expand-all = Snefli akk diff --git a/locales/nl/app.ftl b/locales/nl/app.ftl index bc8ffc1db6..c21d9a0c26 100644 --- a/locales/nl/app.ftl +++ b/locales/nl/app.ftl @@ -79,6 +79,9 @@ CallNodeContextMenu--transform-focus-function = Focussen op functie .title = { CallNodeContextMenu--transform-focus-function-title } CallNodeContextMenu--transform-focus-function-inverted = Focussen op functie (omgekeerd) .title = { CallNodeContextMenu--transform-focus-function-title } + +## + CallNodeContextMenu--transform-focus-subtree = Alleen focussen op substructuur .title = Als u op een substructuur focust, wordt elk voorbeeld dat dat specifieke deel diff --git a/locales/pt-BR/app.ftl b/locales/pt-BR/app.ftl index dfeb3d053f..08f40fe3f1 100644 --- a/locales/pt-BR/app.ftl +++ b/locales/pt-BR/app.ftl @@ -65,6 +65,9 @@ CallNodeContextMenu--transform-focus-function = Foco na função .title = { CallNodeContextMenu--transform-focus-function-title } CallNodeContextMenu--transform-focus-function-inverted = Foco na função (invertido) .title = { CallNodeContextMenu--transform-focus-function-title } + +## + CallNodeContextMenu--transform-focus-subtree = Foco em subárvore apenas .title = Focar em uma subárvore remove amostras que não incluem aquela parte específica da árvore de chamadas. É retirado um ramo da árvore de chamadas, mas o faz somente naquele único node de chamadas. Todas as outras chamadas da função são ignoradas. # This is used as the context menu item to apply the "Focus on category" transform. diff --git a/locales/ru/app.ftl b/locales/ru/app.ftl index b0d2d5b19a..a0e6a018f2 100644 --- a/locales/ru/app.ftl +++ b/locales/ru/app.ftl @@ -79,6 +79,9 @@ CallNodeContextMenu--transform-focus-function = Сфокусироваться .title = { CallNodeContextMenu--transform-focus-function-title } CallNodeContextMenu--transform-focus-function-inverted = Сфокусироваться на функции (инвертировано) .title = { CallNodeContextMenu--transform-focus-function-title } + +## + CallNodeContextMenu--transform-focus-subtree = Сфокусироваться только на поддереве .title = Фокусировка на поддереве приведёт к удалению любого сэмпла, который не включает эту diff --git a/locales/sv-SE/app.ftl b/locales/sv-SE/app.ftl index f9dbf24446..a4a4ace43f 100644 --- a/locales/sv-SE/app.ftl +++ b/locales/sv-SE/app.ftl @@ -79,6 +79,9 @@ CallNodeContextMenu--transform-focus-function = Fokusera på funktion .title = { CallNodeContextMenu--transform-focus-function-title } CallNodeContextMenu--transform-focus-function-inverted = Fokus på funktion (inverterad) .title = { CallNodeContextMenu--transform-focus-function-title } + +## + CallNodeContextMenu--transform-focus-subtree = Fokusera endast på underträd .title = Genom att fokusera på ett underträd kommer alla prov tas bort som inte diff --git a/locales/tr/app.ftl b/locales/tr/app.ftl index 8c2f18f593..c2266b3a6c 100644 --- a/locales/tr/app.ftl +++ b/locales/tr/app.ftl @@ -67,6 +67,9 @@ CallNodeContextMenu--transform-focus-function = Fonksiyona odaklan .title = { CallNodeContextMenu--transform-focus-function-title } CallNodeContextMenu--transform-focus-function-inverted = Fonksiyona odaklan (tersine) .title = { CallNodeContextMenu--transform-focus-function-title } + +## + CallNodeContextMenu--transform-focus-subtree = Yalnızca alt ağaca odaklan .title = Alt ağaca odaklanmak, çağrı ağacının belirtilen bölgesini içermeyen her örneği kaldıracaktır. Bu işlem, çağrı ağacının bir dalını koparır ama bunu yalnızca belirtilen çağrı düğümü için yapar. Fonksiyonun yaptığı diğer çağrılar görmezden gelinir. # This is used as the context menu item to apply the "Focus on category" transform. diff --git a/locales/uk/app.ftl b/locales/uk/app.ftl index 2ebeac3a22..d97f18dbe3 100644 --- a/locales/uk/app.ftl +++ b/locales/uk/app.ftl @@ -71,6 +71,9 @@ CallNodeContextMenu--transform-focus-function = Фокус на функції .title = { CallNodeContextMenu--transform-focus-function-title } CallNodeContextMenu--transform-focus-function-inverted = Фокус на функції (інвертовано) .title = { CallNodeContextMenu--transform-focus-function-title } + +## + CallNodeContextMenu--transform-focus-subtree = Фокус лише на піддереві .title = Фокус на піддереві вилучить будь-який зразок, який не включає цю diff --git a/locales/zh-CN/app.ftl b/locales/zh-CN/app.ftl index feae44c7d0..01730342e7 100644 --- a/locales/zh-CN/app.ftl +++ b/locales/zh-CN/app.ftl @@ -65,6 +65,9 @@ CallNodeContextMenu--transform-focus-function = 聚焦于函数 .title = { CallNodeContextMenu--transform-focus-function-title } CallNodeContextMenu--transform-focus-function-inverted = 聚焦于函数(反向) .title = { CallNodeContextMenu--transform-focus-function-title } + +## + CallNodeContextMenu--transform-focus-subtree = 只聚焦于子树 .title = 聚焦于子树,将从调用树中拉出分支,并移除不属于该分支的内容。然而此功能只对单一调用节点有效,将忽略其他调用该函数的部分。 # This is used as the context menu item to apply the "Focus on category" transform. diff --git a/locales/zh-TW/app.ftl b/locales/zh-TW/app.ftl index 518a5f9127..f6462dcb9c 100644 --- a/locales/zh-TW/app.ftl +++ b/locales/zh-TW/app.ftl @@ -65,6 +65,9 @@ CallNodeContextMenu--transform-focus-function = 聚焦於函數 .title = { CallNodeContextMenu--transform-focus-function-title } CallNodeContextMenu--transform-focus-function-inverted = 聚焦於函數(反向) .title = { CallNodeContextMenu--transform-focus-function-title } + +## + CallNodeContextMenu--transform-focus-subtree = 只聚焦於子樹 .title = 聚焦於子樹,將從呼叫樹中拉出分支,並移除不屬於該分支的內容。然而此功能只對單一呼叫節點有效,將忽略其他呼叫該函數的部分。 # This is used as the context menu item to apply the "Focus on category" transform. From 794b1297553fa62b78b407de47d4a319134685e3 Mon Sep 17 00:00:00 2001 From: Pontoon Date: Wed, 28 Jan 2026 08:30:30 +0000 Subject: [PATCH 02/59] Pontoon/Firefox Profiler: Update Chinese (Taiwan) (zh-TW) Co-authored-by: Pin-guang Chen (zh-TW) --- locales/zh-TW/app.ftl | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/locales/zh-TW/app.ftl b/locales/zh-TW/app.ftl index f6462dcb9c..409b011626 100644 --- a/locales/zh-TW/app.ftl +++ b/locales/zh-TW/app.ftl @@ -66,6 +66,15 @@ CallNodeContextMenu--transform-focus-function = 聚焦於函數 CallNodeContextMenu--transform-focus-function-inverted = 聚焦於函數(反向) .title = { CallNodeContextMenu--transform-focus-function-title } +## The translation for "self" in these strings should match the translation used +## in CallTree--samples-self and CallTree--bytes-self. Alternatively it can be +## translated as "self values" or "self time" (though "self time" is less desirable +## because this menu item is also shown in "bytes" mode). + +CallNodeContextMenu--transform-focus-self-title = 聚焦於 self 與聚焦於函數類似,但只保留與函數的 self 時間有關的取樣。將捨棄被呼叫者的取樣,並將呼叫樹重新放置於聚焦的函數根上。 +CallNodeContextMenu--transform-focus-self = 只聚焦於 self + .title = { CallNodeContextMenu--transform-focus-self-title } + ## CallNodeContextMenu--transform-focus-subtree = 只聚焦於子樹 @@ -939,6 +948,12 @@ TransformNavigator--focus-subtree = 聚焦節點:{ $item } # Variables: # $item (String) - Name of the function that transform applied to. TransformNavigator--focus-function = 聚焦:{ $item } +# "Focus self" transform. +# See: https://profiler.firefox.com/docs/#/./guide-filtering-call-trees?id=focus-on-function-self +# Also see the translation note above CallNodeContextMenu--transform-focus-self. +# Variables: +# $item (String) - Name of the function that transform applied to. +TransformNavigator--focus-self = 聚焦 self:{ $item } # "Focus category" transform. The word "Focus" has the meaning of an adjective here. # See: https://profiler.firefox.com/docs/#/./guide-filtering-call-trees?id=focus-category # Variables: From ff96532e3a1d353c06e53d123829d0e78e52ed13 Mon Sep 17 00:00:00 2001 From: Pontoon Date: Wed, 28 Jan 2026 08:40:31 +0000 Subject: [PATCH 03/59] Pontoon/Firefox Profiler: Update Italian (it) Co-authored-by: Francesco Lodolo [:flod] (it) --- locales/it/app.ftl | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/locales/it/app.ftl b/locales/it/app.ftl index 6a9b6d07a2..cb7a2a1faf 100644 --- a/locales/it/app.ftl +++ b/locales/it/app.ftl @@ -66,6 +66,15 @@ CallNodeContextMenu--transform-focus-function = Focus sulla funzione CallNodeContextMenu--transform-focus-function-inverted = Focus sulla funzione (invertito) .title = { CallNodeContextMenu--transform-focus-function-title } +## The translation for "self" in these strings should match the translation used +## in CallTree--samples-self and CallTree--bytes-self. Alternatively it can be +## translated as "self values" or "self time" (though "self time" is less desirable +## because this menu item is also shown in "bytes" mode). + +CallNodeContextMenu--transform-focus-self-title = Il focus su “self” è analogo al focus su una funzione, ma vengono mantenuti solo i campioni che incidono sul tempo stesso della funzione. I campioni presenti nelle funzioni chiamate vengono ignorati e l’albero delle chiamate viene riorganizzato avendo come radice la funzione che si sta analizzando. +CallNodeContextMenu--transform-focus-self = Focus solo su self + .title = { CallNodeContextMenu--transform-focus-self-title } + ## CallNodeContextMenu--transform-focus-subtree = Focus solo sul sottoalbero @@ -949,6 +958,12 @@ TransformNavigator--focus-subtree = Focus sul nodo: { $item } # Variables: # $item (String) - Name of the function that transform applied to. TransformNavigator--focus-function = Focus: { $item } +# "Focus self" transform. +# See: https://profiler.firefox.com/docs/#/./guide-filtering-call-trees?id=focus-on-function-self +# Also see the translation note above CallNodeContextMenu--transform-focus-self. +# Variables: +# $item (String) - Name of the function that transform applied to. +TransformNavigator--focus-self = Focus su self: { $item } # "Focus category" transform. The word "Focus" has the meaning of an adjective here. # See: https://profiler.firefox.com/docs/#/./guide-filtering-call-trees?id=focus-category # Variables: From dbfbd81a9981350f889c3792ceeb923a01950fd4 Mon Sep 17 00:00:00 2001 From: Pontoon Date: Wed, 28 Jan 2026 10:20:46 +0000 Subject: [PATCH 04/59] Pontoon/Firefox Profiler: Update Frisian (fy-NL), Dutch (nl) Co-authored-by: Mark Heijl (nl) Co-authored-by: Fjoerfoks (fy-NL) --- locales/fy-NL/app.ftl | 18 ++++++++++++++++++ locales/nl/app.ftl | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/locales/fy-NL/app.ftl b/locales/fy-NL/app.ftl index 832c15396e..a97101fec5 100644 --- a/locales/fy-NL/app.ftl +++ b/locales/fy-NL/app.ftl @@ -80,6 +80,18 @@ CallNodeContextMenu--transform-focus-function = Fokus op funksje CallNodeContextMenu--transform-focus-function-inverted = Fokus op funksje (omkeard) .title = { CallNodeContextMenu--transform-focus-function-title } +## The translation for "self" in these strings should match the translation used +## in CallTree--samples-self and CallTree--bytes-self. Alternatively it can be +## translated as "self values" or "self time" (though "self time" is less desirable +## because this menu item is also shown in "bytes" mode). + +CallNodeContextMenu--transform-focus-self-title = + Fokussen op himsels liket op fokussen op in funksje, mar behâldt allinnich meunsters + dy’t bydrage oan de selstiid fan de funksje. Meunsters yn oanroppen + wurde fuortsmiten, en de oanropbeam wurdt opnij root nei de funksje mei fokus. +CallNodeContextMenu--transform-focus-self = Allinnich fokussen op sels + .title = { CallNodeContextMenu--transform-focus-self-title } + ## CallNodeContextMenu--transform-focus-subtree = Allinnich fokus op substruktuer @@ -1037,6 +1049,12 @@ TransformNavigator--focus-subtree = Node fokusje: { $item } # Variables: # $item (String) - Name of the function that transform applied to. TransformNavigator--focus-function = Fokusje: { $item } +# "Focus self" transform. +# See: https://profiler.firefox.com/docs/#/./guide-filtering-call-trees?id=focus-on-function-self +# Also see the translation note above CallNodeContextMenu--transform-focus-self. +# Variables: +# $item (String) - Name of the function that transform applied to. +TransformNavigator--focus-self = Fokussen op sels: { $item } # "Focus category" transform. The word "Focus" has the meaning of an adjective here. # See: https://profiler.firefox.com/docs/#/./guide-filtering-call-trees?id=focus-category # Variables: diff --git a/locales/nl/app.ftl b/locales/nl/app.ftl index c21d9a0c26..3f0ec6a807 100644 --- a/locales/nl/app.ftl +++ b/locales/nl/app.ftl @@ -80,6 +80,18 @@ CallNodeContextMenu--transform-focus-function = Focussen op functie CallNodeContextMenu--transform-focus-function-inverted = Focussen op functie (omgekeerd) .title = { CallNodeContextMenu--transform-focus-function-title } +## The translation for "self" in these strings should match the translation used +## in CallTree--samples-self and CallTree--bytes-self. Alternatively it can be +## translated as "self values" or "self time" (though "self time" is less desirable +## because this menu item is also shown in "bytes" mode). + +CallNodeContextMenu--transform-focus-self-title = + Focussen op zichzelf lijkt op focussen op een functie, maar behoudt alleen monsters + die bijdragen aan de zelftijd van de functie. Monsters in aanroepen + worden verwijderd, en de aanroepboom wordt opnieuw geroot naar de functie met focus. +CallNodeContextMenu--transform-focus-self = Alleen focussen op zelf + .title = { CallNodeContextMenu--transform-focus-self-title } + ## CallNodeContextMenu--transform-focus-subtree = Alleen focussen op substructuur @@ -1037,6 +1049,12 @@ TransformNavigator--focus-subtree = Node focussen: { $item } # Variables: # $item (String) - Name of the function that transform applied to. TransformNavigator--focus-function = Focussen: { $item } +# "Focus self" transform. +# See: https://profiler.firefox.com/docs/#/./guide-filtering-call-trees?id=focus-on-function-self +# Also see the translation note above CallNodeContextMenu--transform-focus-self. +# Variables: +# $item (String) - Name of the function that transform applied to. +TransformNavigator--focus-self = Focussen op zelf: { $item } # "Focus category" transform. The word "Focus" has the meaning of an adjective here. # See: https://profiler.firefox.com/docs/#/./guide-filtering-call-trees?id=focus-category # Variables: From ec756daeabc4985e209ff29265a71add6d1f9da0 Mon Sep 17 00:00:00 2001 From: "depfu[bot]" <23717796+depfu[bot]@users.noreply.github.com> Date: Wed, 28 Jan 2026 12:17:40 +0100 Subject: [PATCH 05/59] Update all Yarn dependencies (2026-01-28) (#5789) --- package.json | 12 +- yarn.lock | 322 ++++++++++++++++----------------------------------- 2 files changed, 104 insertions(+), 230 deletions(-) diff --git a/package.json b/package.json index e8ee81c575..e805b20810 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ "@codemirror/lang-javascript": "^6.2.4", "@codemirror/lang-rust": "^6.0.2", "@codemirror/language": "^6.12.1", - "@codemirror/state": "^6.5.3", + "@codemirror/state": "^6.5.4", "@codemirror/view": "^6.39.11", "@firefox-devtools/react-contextmenu": "^5.2.3", "@fluent/bundle": "^0.19.1", @@ -98,7 +98,7 @@ "query-string": "^9.3.1", "react": "^18.3.1", "react-dom": "^18.3.1", - "react-intersection-observer": "^10.0.0", + "react-intersection-observer": "^10.0.2", "react-redux": "^9.2.0", "react-splitter-layout": "^4.0.0", "react-transition-group": "^4.4.5", @@ -123,7 +123,7 @@ "@eslint/js": "^9.39.2", "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.9.1", - "@testing-library/react": "^16.3.1", + "@testing-library/react": "^16.3.2", "@types/clamp": "^1.0.3", "@types/common-tags": "^1.8.4", "@types/jest": "^30.0.0", @@ -164,7 +164,7 @@ "fetch-mock": "^12.6.0", "file-loader": "^6.2.0", "globals": "^17.0.0", - "html-webpack-plugin": "^5.6.5", + "html-webpack-plugin": "^5.6.6", "husky": "^4.3.8", "jest": "^30.2.0", "jest-environment-jsdom": "^30.2.0", @@ -179,14 +179,14 @@ "postcss": "^8.5.6", "postcss-loader": "^8.2.0", "postinstall-postinstall": "^2.1.0", - "prettier": "^3.7.4", + "prettier": "^3.8.1", "rimraf": "^6.1.2", "style-loader": "^4.0.0", "stylelint": "^17.0.0", "stylelint-config-idiomatic-order": "^10.0.0", "stylelint-config-standard": "^40.0.0", "typescript": "^5.9.3", - "typescript-eslint": "^8.52.0", + "typescript-eslint": "^8.54.0", "webpack": "^5.104.1", "webpack-cli": "^6.0.1", "webpack-dev-server": "^5.2.3", diff --git a/yarn.lock b/yarn.lock index bb95e2c2d4..4711473676 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1131,10 +1131,10 @@ "@codemirror/view" "^6.0.0" crelt "^1.0.5" -"@codemirror/state@^6.0.0", "@codemirror/state@^6.5.0", "@codemirror/state@^6.5.3": - version "6.5.3" - resolved "https://registry.yarnpkg.com/@codemirror/state/-/state-6.5.3.tgz#256e256d466f49ed0879d462031de8bd541e1403" - integrity sha512-MerMzJzlXogk2fxWFU1nKp36bY5orBG59HnPiz0G9nLRebWa0zXuv2siH6PLIHBvv5TH8CkQRqjBs0MlxCZu+A== +"@codemirror/state@^6.0.0", "@codemirror/state@^6.5.0", "@codemirror/state@^6.5.4": + version "6.5.4" + resolved "https://registry.yarnpkg.com/@codemirror/state/-/state-6.5.4.tgz#f5be4b8c0d2310180d5f15a9f641c21ca69faf19" + integrity sha512-8y7xqG/hpB53l25CIoit9/ngxdfoG+fx+V3SHBrinnhOtLvKHRyAJJuHzkWrR4YXXLX8eXBsejgAAxHUOdW1yw== dependencies: "@marijn/find-cluster-break" "^1.0.0" @@ -1238,19 +1238,14 @@ resolved "https://registry.yarnpkg.com/@epic-web/invariant/-/invariant-1.0.0.tgz#1073e5dee6dd540410784990eb73e4acd25c9813" integrity sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA== -"@eslint-community/eslint-utils@^4.7.0", "@eslint-community/eslint-utils@^4.8.0", "@eslint-community/eslint-utils@^4.9.1": +"@eslint-community/eslint-utils@^4.8.0", "@eslint-community/eslint-utils@^4.9.1": version "4.9.1" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz#4e90af67bc51ddee6cdef5284edf572ec376b595" integrity sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ== dependencies: eslint-visitor-keys "^3.4.3" -"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.12.1": - version "4.12.1" - resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" - integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== - -"@eslint-community/regexpp@^4.12.2": +"@eslint-community/regexpp@^4.12.1", "@eslint-community/regexpp@^4.12.2": version "4.12.2" resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.2.tgz#bccdf615bcf7b6e8db830ec0b8d21c9a25de597b" integrity sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew== @@ -2172,10 +2167,10 @@ picocolors "^1.1.1" redent "^3.0.0" -"@testing-library/react@^16.3.1": - version "16.3.1" - resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-16.3.1.tgz#60a9f1f6a930399d9e41b506a8bf68dbf4831fe8" - integrity sha512-gr4KtAWqIOQoucWYD/f6ki+j5chXfcPc74Col/6poTyqTmn7zRmodWahWRCp8tYd+GMqBonw6hstNzqjbs6gjw== +"@testing-library/react@^16.3.2": + version "16.3.2" + resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-16.3.2.tgz#672883b7acb8e775fc0492d9e9d25e06e89786d0" + integrity sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g== dependencies: "@babel/runtime" "^7.12.5" @@ -2650,196 +2645,100 @@ dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@8.53.1": - version "8.53.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.53.1.tgz#f6640f6f8749b71d9ab457263939e8932a3c6b46" - integrity sha512-cFYYFZ+oQFi6hUnBTbLRXfTJiaQtYE3t4O692agbBl+2Zy+eqSKWtPjhPXJu1G7j4RLjKgeJPDdq3EqOwmX5Ag== +"@typescript-eslint/eslint-plugin@8.54.0", "@typescript-eslint/eslint-plugin@^8.51.0": + version "8.54.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.54.0.tgz#d8899e5c2eccf5c4a20d01c036a193753748454d" + integrity sha512-hAAP5io/7csFStuOmR782YmTthKBJ9ND3WVL60hcOjvtGFb+HJxH4O5huAcmcZ9v9G8P+JETiZ/G1B8MALnWZQ== dependencies: "@eslint-community/regexpp" "^4.12.2" - "@typescript-eslint/scope-manager" "8.53.1" - "@typescript-eslint/type-utils" "8.53.1" - "@typescript-eslint/utils" "8.53.1" - "@typescript-eslint/visitor-keys" "8.53.1" + "@typescript-eslint/scope-manager" "8.54.0" + "@typescript-eslint/type-utils" "8.54.0" + "@typescript-eslint/utils" "8.54.0" + "@typescript-eslint/visitor-keys" "8.54.0" ignore "^7.0.5" natural-compare "^1.4.0" ts-api-utils "^2.4.0" -"@typescript-eslint/eslint-plugin@^8.51.0": - version "8.51.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.51.0.tgz#8985230730c0d955bf6aa0aed98c5c2c95102e1a" - integrity sha512-XtssGWJvypyM2ytBnSnKtHYOGT+4ZwTnBVl36TA4nRO2f4PRNGz5/1OszHzcZCvcBMh+qb7I06uoCmLTRdR9og== - dependencies: - "@eslint-community/regexpp" "^4.10.0" - "@typescript-eslint/scope-manager" "8.51.0" - "@typescript-eslint/type-utils" "8.51.0" - "@typescript-eslint/utils" "8.51.0" - "@typescript-eslint/visitor-keys" "8.51.0" - ignore "^7.0.0" - natural-compare "^1.4.0" - ts-api-utils "^2.2.0" - -"@typescript-eslint/parser@8.53.1": - version "8.53.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.53.1.tgz#58d4a70cc2daee2becf7d4521d65ea1782d6ec68" - integrity sha512-nm3cvFN9SqZGXjmw5bZ6cGmvJSyJPn0wU9gHAZZHDnZl2wF9PhHv78Xf06E0MaNk4zLVHL8hb2/c32XvyJOLQg== +"@typescript-eslint/parser@8.54.0", "@typescript-eslint/parser@^8.51.0": + version "8.54.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.54.0.tgz#3d01a6f54ed247deb9982621f70e7abf1810bd97" + integrity sha512-BtE0k6cjwjLZoZixN0t5AKP0kSzlGu7FctRXYuPAm//aaiZhmfq1JwdYpYr1brzEspYyFeF+8XF5j2VK6oalrA== dependencies: - "@typescript-eslint/scope-manager" "8.53.1" - "@typescript-eslint/types" "8.53.1" - "@typescript-eslint/typescript-estree" "8.53.1" - "@typescript-eslint/visitor-keys" "8.53.1" + "@typescript-eslint/scope-manager" "8.54.0" + "@typescript-eslint/types" "8.54.0" + "@typescript-eslint/typescript-estree" "8.54.0" + "@typescript-eslint/visitor-keys" "8.54.0" debug "^4.4.3" -"@typescript-eslint/parser@^8.51.0": - version "8.51.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.51.0.tgz#584fb8be3a867cbf980917aabed5f7528f615d6b" - integrity sha512-3xP4XzzDNQOIqBMWogftkwxhg5oMKApqY0BAflmLZiFYHqyhSOxv/cd/zPQLTcCXr4AkaKb25joocY0BD1WC6A== - dependencies: - "@typescript-eslint/scope-manager" "8.51.0" - "@typescript-eslint/types" "8.51.0" - "@typescript-eslint/typescript-estree" "8.51.0" - "@typescript-eslint/visitor-keys" "8.51.0" - debug "^4.3.4" - -"@typescript-eslint/project-service@8.51.0": - version "8.51.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.51.0.tgz#3cfef313d8bebbf4b2442675a4dd463cef4c8369" - integrity sha512-Luv/GafO07Z7HpiI7qeEW5NW8HUtZI/fo/kE0YbtQEFpJRUuR0ajcWfCE5bnMvL7QQFrmT/odMe8QZww8X2nfQ== +"@typescript-eslint/project-service@8.54.0": + version "8.54.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.54.0.tgz#f582aceb3d752544c8e1b11fea8d95d00cf9adc6" + integrity sha512-YPf+rvJ1s7MyiWM4uTRhE4DvBXrEV+d8oC3P9Y2eT7S+HBS0clybdMIPnhiATi9vZOYDc7OQ1L/i6ga6NFYK/g== dependencies: - "@typescript-eslint/tsconfig-utils" "^8.51.0" - "@typescript-eslint/types" "^8.51.0" - debug "^4.3.4" - -"@typescript-eslint/project-service@8.53.1": - version "8.53.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.53.1.tgz#4e47856a0b14a1ceb28b0294b4badef3be1e9734" - integrity sha512-WYC4FB5Ra0xidsmlPb+1SsnaSKPmS3gsjIARwbEkHkoWloQmuzcfypljaJcR78uyLA1h8sHdWWPHSLDI+MtNog== - dependencies: - "@typescript-eslint/tsconfig-utils" "^8.53.1" - "@typescript-eslint/types" "^8.53.1" + "@typescript-eslint/tsconfig-utils" "^8.54.0" + "@typescript-eslint/types" "^8.54.0" debug "^4.4.3" -"@typescript-eslint/scope-manager@8.51.0": - version "8.51.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.51.0.tgz#19b42f65680c21f7b6f40fe9024327f6bb1893c1" - integrity sha512-JhhJDVwsSx4hiOEQPeajGhCWgBMBwVkxC/Pet53EpBVs7zHHtayKefw1jtPaNRXpI9RA2uocdmpdfE7T+NrizA== +"@typescript-eslint/scope-manager@8.54.0", "@typescript-eslint/scope-manager@^8.51.0": + version "8.54.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.54.0.tgz#307dc8cbd80157e2772c2d36216857415a71ab33" + integrity sha512-27rYVQku26j/PbHYcVfRPonmOlVI6gihHtXFbTdB5sb6qA0wdAQAbyXFVarQ5t4HRojIz64IV90YtsjQSSGlQg== dependencies: - "@typescript-eslint/types" "8.51.0" - "@typescript-eslint/visitor-keys" "8.51.0" - -"@typescript-eslint/scope-manager@8.53.1", "@typescript-eslint/scope-manager@^8.51.0": - version "8.53.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.53.1.tgz#6c4b8c82cd45ae3b365afc2373636e166743a8fa" - integrity sha512-Lu23yw1uJMFY8cUeq7JlrizAgeQvWugNQzJp8C3x8Eo5Jw5Q2ykMdiiTB9vBVOOUBysMzmRRmUfwFrZuI2C4SQ== - dependencies: - "@typescript-eslint/types" "8.53.1" - "@typescript-eslint/visitor-keys" "8.53.1" - -"@typescript-eslint/tsconfig-utils@8.51.0": - version "8.51.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.51.0.tgz#a575e9885e62dbd260fb64474eff1dae6e317515" - integrity sha512-Qi5bSy/vuHeWyir2C8u/uqGMIlIDu8fuiYWv48ZGlZ/k+PRPHtaAu7erpc7p5bzw2WNNSniuxoMSO4Ar6V9OXw== + "@typescript-eslint/types" "8.54.0" + "@typescript-eslint/visitor-keys" "8.54.0" -"@typescript-eslint/tsconfig-utils@8.53.1", "@typescript-eslint/tsconfig-utils@^8.51.0", "@typescript-eslint/tsconfig-utils@^8.53.1": - version "8.53.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.53.1.tgz#efe80b8d019cd49e5a1cf46c2eb0cd2733076424" - integrity sha512-qfvLXS6F6b1y43pnf0pPbXJ+YoXIC7HKg0UGZ27uMIemKMKA6XH2DTxsEDdpdN29D+vHV07x/pnlPNVLhdhWiA== +"@typescript-eslint/tsconfig-utils@8.54.0", "@typescript-eslint/tsconfig-utils@^8.54.0": + version "8.54.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.54.0.tgz#71dd7ba1674bd48b172fc4c85b2f734b0eae3dbc" + integrity sha512-dRgOyT2hPk/JwxNMZDsIXDgyl9axdJI3ogZ2XWhBPsnZUv+hPesa5iuhdYt2gzwA9t8RE5ytOJ6xB0moV0Ujvw== -"@typescript-eslint/type-utils@8.51.0": - version "8.51.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.51.0.tgz#ec165b0312a6025c2a2a3f39641e46ab4f049564" - integrity sha512-0XVtYzxnobc9K0VU7wRWg1yiUrw4oQzexCG2V2IDxxCxhqBMSMbjB+6o91A+Uc0GWtgjCa3Y8bi7hwI0Tu4n5Q== +"@typescript-eslint/type-utils@8.54.0": + version "8.54.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.54.0.tgz#64965317dd4118346c2fa5ee94492892200e9fb9" + integrity sha512-hiLguxJWHjjwL6xMBwD903ciAwd7DmK30Y9Axs/etOkftC3ZNN9K44IuRD/EB08amu+Zw6W37x9RecLkOo3pMA== dependencies: - "@typescript-eslint/types" "8.51.0" - "@typescript-eslint/typescript-estree" "8.51.0" - "@typescript-eslint/utils" "8.51.0" - debug "^4.3.4" - ts-api-utils "^2.2.0" - -"@typescript-eslint/type-utils@8.53.1": - version "8.53.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.53.1.tgz#95de2651a96d580bf5c6c6089ddd694284d558ad" - integrity sha512-MOrdtNvyhy0rHyv0ENzub1d4wQYKb2NmIqG7qEqPWFW7Mpy2jzFC3pQ2yKDvirZB7jypm5uGjF2Qqs6OIqu47w== - dependencies: - "@typescript-eslint/types" "8.53.1" - "@typescript-eslint/typescript-estree" "8.53.1" - "@typescript-eslint/utils" "8.53.1" + "@typescript-eslint/types" "8.54.0" + "@typescript-eslint/typescript-estree" "8.54.0" + "@typescript-eslint/utils" "8.54.0" debug "^4.4.3" ts-api-utils "^2.4.0" -"@typescript-eslint/types@8.51.0": - version "8.51.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.51.0.tgz#6996e59d49e92fb893531bdc249f0d92a7bebdbb" - integrity sha512-TizAvWYFM6sSscmEakjY3sPqGwxZRSywSsPEiuZF6d5GmGD9Gvlsv0f6N8FvAAA0CD06l3rIcWNbsN1e5F/9Ag== - -"@typescript-eslint/types@8.53.1", "@typescript-eslint/types@^8.51.0", "@typescript-eslint/types@^8.53.1": - version "8.53.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.53.1.tgz#101f203f0807a63216cceceedb815fabe21d5793" - integrity sha512-jr/swrr2aRmUAUjW5/zQHbMaui//vQlsZcJKijZf3M26bnmLj8LyZUpj8/Rd6uzaek06OWsqdofN/Thenm5O8A== - -"@typescript-eslint/typescript-estree@8.51.0": - version "8.51.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.51.0.tgz#b57f5157d1ac2127bd7c2c9ad8060fa017df4a1a" - integrity sha512-1qNjGqFRmlq0VW5iVlcyHBbCjPB7y6SxpBkrbhNWMy/65ZoncXCEPJxkRZL8McrseNH6lFhaxCIaX+vBuFnRng== - dependencies: - "@typescript-eslint/project-service" "8.51.0" - "@typescript-eslint/tsconfig-utils" "8.51.0" - "@typescript-eslint/types" "8.51.0" - "@typescript-eslint/visitor-keys" "8.51.0" - debug "^4.3.4" - minimatch "^9.0.4" - semver "^7.6.0" - tinyglobby "^0.2.15" - ts-api-utils "^2.2.0" +"@typescript-eslint/types@8.54.0", "@typescript-eslint/types@^8.54.0": + version "8.54.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.54.0.tgz#c12d41f67a2e15a8a96fbc5f2d07b17331130889" + integrity sha512-PDUI9R1BVjqu7AUDsRBbKMtwmjWcn4J3le+5LpcFgWULN3LvHC5rkc9gCVxbrsrGmO1jfPybN5s6h4Jy+OnkAA== -"@typescript-eslint/typescript-estree@8.53.1": - version "8.53.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.53.1.tgz#b6dce2303c9e27e95b8dcd8c325868fff53e488f" - integrity sha512-RGlVipGhQAG4GxV1s34O91cxQ/vWiHJTDHbXRr0li2q/BGg3RR/7NM8QDWgkEgrwQYCvmJV9ichIwyoKCQ+DTg== +"@typescript-eslint/typescript-estree@8.54.0": + version "8.54.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.54.0.tgz#3c7716905b2b811fadbd2114804047d1bfc86527" + integrity sha512-BUwcskRaPvTk6fzVWgDPdUndLjB87KYDrN5EYGetnktoeAvPtO4ONHlAZDnj5VFnUANg0Sjm7j4usBlnoVMHwA== dependencies: - "@typescript-eslint/project-service" "8.53.1" - "@typescript-eslint/tsconfig-utils" "8.53.1" - "@typescript-eslint/types" "8.53.1" - "@typescript-eslint/visitor-keys" "8.53.1" + "@typescript-eslint/project-service" "8.54.0" + "@typescript-eslint/tsconfig-utils" "8.54.0" + "@typescript-eslint/types" "8.54.0" + "@typescript-eslint/visitor-keys" "8.54.0" debug "^4.4.3" minimatch "^9.0.5" semver "^7.7.3" tinyglobby "^0.2.15" ts-api-utils "^2.4.0" -"@typescript-eslint/utils@8.51.0": - version "8.51.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.51.0.tgz#b9a071cd210647f860a38873acf9bc5157bea56a" - integrity sha512-11rZYxSe0zabiKaCP2QAwRf/dnmgFgvTmeDTtZvUvXG3UuAdg/GU02NExmmIXzz3vLGgMdtrIosI84jITQOxUA== - dependencies: - "@eslint-community/eslint-utils" "^4.7.0" - "@typescript-eslint/scope-manager" "8.51.0" - "@typescript-eslint/types" "8.51.0" - "@typescript-eslint/typescript-estree" "8.51.0" - -"@typescript-eslint/utils@8.53.1", "@typescript-eslint/utils@^8.0.0", "@typescript-eslint/utils@^8.51.0": - version "8.53.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.53.1.tgz#81fe6c343de288701b774f4d078382f567e6edaa" - integrity sha512-c4bMvGVWW4hv6JmDUEG7fSYlWOl3II2I4ylt0NM+seinYQlZMQIaKaXIIVJWt9Ofh6whrpM+EdDQXKXjNovvrg== +"@typescript-eslint/utils@8.54.0", "@typescript-eslint/utils@^8.0.0", "@typescript-eslint/utils@^8.51.0": + version "8.54.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.54.0.tgz#c79a4bcbeebb4f571278c0183ed1cb601d84c6c8" + integrity sha512-9Cnda8GS57AQakvRyG0PTejJNlA2xhvyNtEVIMlDWOOeEyBkYWhGPnfrIAnqxLMTSTo6q8g12XVjjev5l1NvMA== dependencies: "@eslint-community/eslint-utils" "^4.9.1" - "@typescript-eslint/scope-manager" "8.53.1" - "@typescript-eslint/types" "8.53.1" - "@typescript-eslint/typescript-estree" "8.53.1" - -"@typescript-eslint/visitor-keys@8.51.0": - version "8.51.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.51.0.tgz#d37f5c82b9bece2c8aeb3ba7bb836bbba0f92bb8" - integrity sha512-mM/JRQOzhVN1ykejrvwnBRV3+7yTKK8tVANVN3o1O0t0v7o+jqdVu9crPy5Y9dov15TJk/FTIgoUGHrTOVL3Zg== - dependencies: - "@typescript-eslint/types" "8.51.0" - eslint-visitor-keys "^4.2.1" + "@typescript-eslint/scope-manager" "8.54.0" + "@typescript-eslint/types" "8.54.0" + "@typescript-eslint/typescript-estree" "8.54.0" -"@typescript-eslint/visitor-keys@8.53.1": - version "8.53.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.53.1.tgz#405f04959be22b9be364939af8ac19c3649b6eb7" - integrity sha512-oy+wV7xDKFPRyNggmXuZQSBzvoLnpmJs+GhzRhPjrxl2b/jIlyjVokzm47CZCDUdXKr2zd7ZLodPfOBpOPyPlg== +"@typescript-eslint/visitor-keys@8.54.0": + version "8.54.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.54.0.tgz#0e4b50124b210b8600b245dd66cbad52deb15590" + integrity sha512-VFlhGSl4opC0bprJiItPQ1RfUhGDIBokcPwaFH4yiBCaNPeld/9VeXbiPO1cLyorQi1G1vL+ecBk1x8o1axORA== dependencies: - "@typescript-eslint/types" "8.53.1" + "@typescript-eslint/types" "8.54.0" eslint-visitor-keys "^4.2.1" "@ungap/structured-clone@^1.3.0": @@ -6459,10 +6358,10 @@ html-tags@^5.1.0: resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-5.1.0.tgz#ec7214b57b3e50e2a4cec39414454338a94291f8" integrity sha512-n6l5uca7/y5joxZ3LUePhzmBFUJ+U2YWzhMa8XUTecSeSlQiZdF5XAd/Q3/WUl0VsXgUwWi8I7CNIwdI5WN1SQ== -html-webpack-plugin@^5.6.5: - version "5.6.5" - resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.6.5.tgz#d57defb83cabbf29bf56b2d4bf10b67b650066be" - integrity sha512-4xynFbKNNk+WlzXeQQ+6YYsH2g7mpfPszQZUi3ovKlj+pDmngQ7vRXjrrmGROabmKwyQkcgcX5hqfOwHbFmK5g== +html-webpack-plugin@^5.6.6: + version "5.6.6" + resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.6.6.tgz#5321b9579f4a1949318550ced99c2a4a4e60cbaf" + integrity sha512-bLjW01UTrvoWTJQL5LsMRo1SypHW80FTm12OJRSnr3v6YHNhfe+1r0MYUZJMACxnCHURVnBWRwAsWs2yPU9Ezw== dependencies: "@types/html-minifier-terser" "^6.0.0" html-minifier-terser "^6.0.2" @@ -6667,7 +6566,7 @@ ignore@^5.0.0, ignore@^5.2.0: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== -ignore@^7.0.0, ignore@^7.0.5: +ignore@^7.0.5: version "7.0.5" resolved "https://registry.yarnpkg.com/ignore/-/ignore-7.0.5.tgz#4cb5f6cd7d4c7ab0365738c7aea888baa6d7efd9" integrity sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg== @@ -10431,10 +10330,10 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== -prettier@^3.7.4: - version "3.7.4" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.7.4.tgz#d2f8335d4b1cec47e1c8098645411b0c9dff9c0f" - integrity sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA== +prettier@^3.8.1: + version "3.8.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.8.1.tgz#edf48977cf991558f4fcbd8a3ba6015ba2a3a173" + integrity sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg== pretty-bytes@^5.3.0: version "5.6.0" @@ -10671,10 +10570,10 @@ react-dom@^18.3.1: loose-envify "^1.1.0" scheduler "^0.23.2" -react-intersection-observer@^10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/react-intersection-observer/-/react-intersection-observer-10.0.0.tgz#77613d4e819dd170fbac4821c126c046aab69978" - integrity sha512-JJRgcnFQoVXmbE5+GXr1OS1NDD1gHk0HyfpLcRf0575IbJz+io8yzs4mWVlfaqOQq1FiVjLvuYAdEEcrrCfveg== +react-intersection-observer@^10.0.2: + version "10.0.2" + resolved "https://registry.yarnpkg.com/react-intersection-observer/-/react-intersection-observer-10.0.2.tgz#de63a87859f9b59f8e46ec29092d77ff69e2d20b" + integrity sha512-lAMzxVWrBko6SLd1jx6l84fVrzJu91hpxHlvD2as2Wec9mDCjdYXwc5xNOFBchpeBir0Y7AGBW+C/AYMa7CSFg== react-is@^16.13.1: version "16.13.1" @@ -11839,16 +11738,7 @@ string-length@^4.0.2: char-regex "^1.0.2" strip-ansi "^6.0.0" -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -11978,7 +11868,7 @@ stringify-object@^3.3.0: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -11992,13 +11882,6 @@ strip-ansi@^0.3.0: dependencies: ansi-regex "^0.2.1" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.1, strip-ansi@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -12444,7 +12327,7 @@ trough@^2.0.0: resolved "https://registry.yarnpkg.com/trough/-/trough-2.0.2.tgz#94a3aa9d5ce379fc561f6244905b3f36b7458d96" integrity sha512-FnHq5sTMxC0sk957wHDzRnemFnNBvt/gSY99HzK8F7UP5WAbvP70yX5bd7CjEQkN+TjdxwI7g7lJ6podqrG2/w== -ts-api-utils@^2.2.0, ts-api-utils@^2.4.0: +ts-api-utils@^2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.4.0.tgz#2690579f96d2790253bdcf1ca35d569ad78f9ad8" integrity sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA== @@ -12608,15 +12491,15 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== -typescript-eslint@^8.52.0: - version "8.53.1" - resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.53.1.tgz#e8d2888083af4638d2952b938d69458f54865921" - integrity sha512-gB+EVQfP5RDElh9ittfXlhZJdjSU4jUSTyE2+ia8CYyNvet4ElfaLlAIqDvQV9JPknKx0jQH1racTYe/4LaLSg== +typescript-eslint@^8.54.0: + version "8.54.0" + resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.54.0.tgz#f4ef3b8882a5ddc2a41968e014194c178ab23f6a" + integrity sha512-CKsJ+g53QpsNPqbzUsfKVgd3Lny4yKZ1pP4qN3jdMOg/sisIDLGyDMezycquXLE5JsEU0wp3dGNdzig0/fmSVQ== dependencies: - "@typescript-eslint/eslint-plugin" "8.53.1" - "@typescript-eslint/parser" "8.53.1" - "@typescript-eslint/typescript-estree" "8.53.1" - "@typescript-eslint/utils" "8.53.1" + "@typescript-eslint/eslint-plugin" "8.54.0" + "@typescript-eslint/parser" "8.54.0" + "@typescript-eslint/typescript-estree" "8.54.0" + "@typescript-eslint/utils" "8.54.0" typescript@^5.9.3: version "5.9.3" @@ -13587,16 +13470,7 @@ workbox-window@7.4.0, workbox-window@^7.4.0: "@types/trusted-types" "^2.0.2" workbox-core "7.4.0" -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== From 5fe102bd068206fb68d2c545bcf3fe20ce5896c2 Mon Sep 17 00:00:00 2001 From: Pontoon Date: Wed, 28 Jan 2026 13:40:31 +0000 Subject: [PATCH 06/59] Pontoon/Firefox Profiler: Update Portuguese (Brazil) (pt-BR) Co-authored-by: Marcelo Ghelman (pt-BR) --- locales/pt-BR/app.ftl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/locales/pt-BR/app.ftl b/locales/pt-BR/app.ftl index 08f40fe3f1..cb49de2886 100644 --- a/locales/pt-BR/app.ftl +++ b/locales/pt-BR/app.ftl @@ -66,6 +66,13 @@ CallNodeContextMenu--transform-focus-function = Foco na função CallNodeContextMenu--transform-focus-function-inverted = Foco na função (invertido) .title = { CallNodeContextMenu--transform-focus-function-title } +## The translation for "self" in these strings should match the translation used +## in CallTree--samples-self and CallTree--bytes-self. Alternatively it can be +## translated as "self values" or "self time" (though "self time" is less desirable +## because this menu item is also shown in "bytes" mode). + +CallNodeContextMenu--transform-focus-self-title = Colocar o foco em próprio é semelhante a colocar o foco em uma função, mas só mantém amostras que contribuem com o tempo da própria função. Amostras em funções chamadas são ignoradas e a árvore de chamadas é reorganizada para ter como raiz a função em foco. + ## CallNodeContextMenu--transform-focus-subtree = Foco em subárvore apenas From 612edaf124fa6ea9d63bb4a9535f8987d5cc4644 Mon Sep 17 00:00:00 2001 From: Pontoon Date: Wed, 28 Jan 2026 13:50:30 +0000 Subject: [PATCH 07/59] Pontoon/Firefox Profiler: Update Portuguese (Brazil) (pt-BR) Co-authored-by: Marcelo Ghelman (pt-BR) --- locales/pt-BR/app.ftl | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/locales/pt-BR/app.ftl b/locales/pt-BR/app.ftl index cb49de2886..a8460788f8 100644 --- a/locales/pt-BR/app.ftl +++ b/locales/pt-BR/app.ftl @@ -72,6 +72,8 @@ CallNodeContextMenu--transform-focus-function-inverted = Foco na função (inver ## because this menu item is also shown in "bytes" mode). CallNodeContextMenu--transform-focus-self-title = Colocar o foco em próprio é semelhante a colocar o foco em uma função, mas só mantém amostras que contribuem com o tempo da própria função. Amostras em funções chamadas são ignoradas e a árvore de chamadas é reorganizada para ter como raiz a função em foco. +CallNodeContextMenu--transform-focus-self = Foco apenas em próprio + .title = { CallNodeContextMenu--transform-focus-self-title } ## @@ -967,6 +969,12 @@ TransformNavigator--focus-subtree = Focar node: { $item } # Variables: # $item (String) - Name of the function that transform applied to. TransformNavigator--focus-function = Focar: { $item } +# "Focus self" transform. +# See: https://profiler.firefox.com/docs/#/./guide-filtering-call-trees?id=focus-on-function-self +# Also see the translation note above CallNodeContextMenu--transform-focus-self. +# Variables: +# $item (String) - Name of the function that transform applied to. +TransformNavigator--focus-self = Foco em próprio: { $item } # "Focus category" transform. The word "Focus" has the meaning of an adjective here. # See: https://profiler.firefox.com/docs/#/./guide-filtering-call-trees?id=focus-category # Variables: From e057b521c66f763c241c548eb89699ef8b4714c1 Mon Sep 17 00:00:00 2001 From: Pontoon Date: Wed, 28 Jan 2026 14:40:31 +0000 Subject: [PATCH 08/59] Pontoon/Firefox Profiler: Update German (de) Co-authored-by: Ger (de) --- locales/de/app.ftl | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/locales/de/app.ftl b/locales/de/app.ftl index e93b51871f..5fab5a6339 100644 --- a/locales/de/app.ftl +++ b/locales/de/app.ftl @@ -77,6 +77,18 @@ CallNodeContextMenu--transform-focus-function = Auf Funktion fokussieren CallNodeContextMenu--transform-focus-function-inverted = Auf Funktion fokussieren (invertiert) .title = { CallNodeContextMenu--transform-focus-function-title } +## The translation for "self" in these strings should match the translation used +## in CallTree--samples-self and CallTree--bytes-self. Alternatively it can be +## translated as "self values" or "self time" (though "self time" is less desirable +## because this menu item is also shown in "bytes" mode). + +CallNodeContextMenu--transform-focus-self-title = + Die Fokussierung auf sich selbst ähnelt der Fokussierung auf eine Funktion, behält jedoch nur Samples bei, + die zur Zeit der Funktion selbst beitragen. Samples in Angerufenen + werden verworfen und der Aufrufbaum wird auf die Funktion im Fokus zurückgesetzt. +CallNodeContextMenu--transform-focus-self = Nur auf sich selbst konzentrieren + .title = { "\u0009" }{ CallNodeContextMenu--transform-focus-self-title } + ## CallNodeContextMenu--transform-focus-subtree = Nur auf Unterbaum konzentrieren @@ -1013,6 +1025,12 @@ TransformNavigator--focus-subtree = Knoten fokussieren: { $item } # Variables: # $item (String) - Name of the function that transform applied to. TransformNavigator--focus-function = Fokussieren: { $item } +# "Focus self" transform. +# See: https://profiler.firefox.com/docs/#/./guide-filtering-call-trees?id=focus-on-function-self +# Also see the translation note above CallNodeContextMenu--transform-focus-self. +# Variables: +# $item (String) - Name of the function that transform applied to. +TransformNavigator--focus-self = Fokus auf sich selbst: { $item } # "Focus category" transform. The word "Focus" has the meaning of an adjective here. # See: https://profiler.firefox.com/docs/#/./guide-filtering-call-trees?id=focus-category # Variables: From 34edaa46dcbdacfee1c81f4149adef6bcd283af1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Naz=C4=B1m=20Can=20Alt=C4=B1nova?= Date: Wed, 28 Jan 2026 18:08:42 +0100 Subject: [PATCH 09/59] Make the range duration text white again --- src/components/timeline/Selection.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/timeline/Selection.css b/src/components/timeline/Selection.css index 1f07df3970..f10fe4a76b 100644 --- a/src/components/timeline/Selection.css +++ b/src/components/timeline/Selection.css @@ -161,7 +161,7 @@ border-radius: 0 0 4px 4px; background-color: var(--internal-range-background-color); box-shadow: 0 2px 2px var(--base-shadow-color); - color: var(--base-foreground-color); + color: var(--internal-overlay-time-foreground-color); opacity: 1; pointer-events: none; transition: opacity 200ms; From 8ee520c40592db91b13dab2ec0aae0be499b2035 Mon Sep 17 00:00:00 2001 From: Pontoon Date: Wed, 28 Jan 2026 19:20:36 +0000 Subject: [PATCH 10/59] Pontoon/Firefox Profiler: Update English (Great Britain) (en-GB) Co-authored-by: Ian Neal (en-GB) --- locales/en-GB/app.ftl | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/locales/en-GB/app.ftl b/locales/en-GB/app.ftl index 3b23280829..60abfbf59c 100644 --- a/locales/en-GB/app.ftl +++ b/locales/en-GB/app.ftl @@ -80,6 +80,18 @@ CallNodeContextMenu--transform-focus-function = Focus on function CallNodeContextMenu--transform-focus-function-inverted = Focus on function (inverted) .title = { CallNodeContextMenu--transform-focus-function-title } +## The translation for "self" in these strings should match the translation used +## in CallTree--samples-self and CallTree--bytes-self. Alternatively it can be +## translated as "self values" or "self time" (though "self time" is less desirable +## because this menu item is also shown in "bytes" mode). + +CallNodeContextMenu--transform-focus-self-title = + Focusing on self is similar to focusing on a function, but only keeps samples + that contribute to the function’s self time. Samples in callees + are dropped and the call tree is re-rooted to the focused function. +CallNodeContextMenu--transform-focus-self = Focus on self only + .title = { CallNodeContextMenu--transform-focus-self-title } + ## CallNodeContextMenu--transform-focus-subtree = Focus on subtree only @@ -1037,6 +1049,12 @@ TransformNavigator--focus-subtree = Focus Node: { $item } # Variables: # $item (String) - Name of the function that transform applied to. TransformNavigator--focus-function = Focus: { $item } +# "Focus self" transform. +# See: https://profiler.firefox.com/docs/#/./guide-filtering-call-trees?id=focus-on-function-self +# Also see the translation note above CallNodeContextMenu--transform-focus-self. +# Variables: +# $item (String) - Name of the function that transform applied to. +TransformNavigator--focus-self = Focus Self: { $item } # "Focus category" transform. The word "Focus" has the meaning of an adjective here. # See: https://profiler.firefox.com/docs/#/./guide-filtering-call-trees?id=focus-category # Variables: From 938c54736392b1a16297c958d62608474ef1a5d2 Mon Sep 17 00:00:00 2001 From: Pontoon Date: Wed, 28 Jan 2026 20:10:34 +0000 Subject: [PATCH 11/59] Pontoon/Firefox Profiler: Update Russian (ru) Co-authored-by: Valery Ledovskoy (ru) --- locales/ru/app.ftl | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/locales/ru/app.ftl b/locales/ru/app.ftl index a0e6a018f2..9133b619d1 100644 --- a/locales/ru/app.ftl +++ b/locales/ru/app.ftl @@ -80,6 +80,18 @@ CallNodeContextMenu--transform-focus-function = Сфокусироваться CallNodeContextMenu--transform-focus-function-inverted = Сфокусироваться на функции (инвертировано) .title = { CallNodeContextMenu--transform-focus-function-title } +## The translation for "self" in these strings should match the translation used +## in CallTree--samples-self and CallTree--bytes-self. Alternatively it can be +## translated as "self values" or "self time" (though "self time" is less desirable +## because this menu item is also shown in "bytes" mode). + +CallNodeContextMenu--transform-focus-self-title = + Фокусировка на себе похожа на фокусировку на функции, но сохраняет только замеры + которые вносят вклад в собственное время функции. Замеры в вызовах + отбрасываются, а дерево вызовов перенаправляется на функцию, находящуюся в фокусе. +CallNodeContextMenu--transform-focus-self = Сфокусироваться только на себе + .title = { CallNodeContextMenu--transform-focus-self-title } + ## CallNodeContextMenu--transform-focus-subtree = Сфокусироваться только на поддереве @@ -1046,6 +1058,12 @@ TransformNavigator--focus-subtree = Узел фокусировки: { $item } # Variables: # $item (String) - Name of the function that transform applied to. TransformNavigator--focus-function = Фокус: { $item } +# "Focus self" transform. +# See: https://profiler.firefox.com/docs/#/./guide-filtering-call-trees?id=focus-on-function-self +# Also see the translation note above CallNodeContextMenu--transform-focus-self. +# Variables: +# $item (String) - Name of the function that transform applied to. +TransformNavigator--focus-self = Сфокусироваться на себе: { $item } # "Focus category" transform. The word "Focus" has the meaning of an adjective here. # See: https://profiler.firefox.com/docs/#/./guide-filtering-call-trees?id=focus-category # Variables: From 3bd4966a165304c3215fc0ea131b4330ae16f65b Mon Sep 17 00:00:00 2001 From: Alex Thayer Date: Wed, 15 Jan 2025 14:31:06 -0800 Subject: [PATCH 12/59] Handle traced values from JS Execution Tracer profiles --- jest.config.js | 6 +- package.json | 1 + res/css/global.css | 14 ++++ src/components/stack-chart/Canvas.tsx | 13 ++++ src/components/tooltip/CallNode.tsx | 28 ++++++++ src/components/tooltip/Tooltip.css | 14 ++++ src/index.tsx | 1 + src/profile-logic/process-profile.ts | 12 ++++ src/profile-logic/profile-data.ts | 25 +++++++- src/profile-logic/stack-timing.ts | 46 ++++++++++--- src/selectors/per-thread/thread.tsx | 9 +++ .../fixtures/profiles/processed-profile.ts | 3 + src/test/fixtures/utils.ts | 7 +- .../__snapshots__/profile-view.test.ts.snap | 10 +++ src/types/@types/devtools-reps/index.d.ts | 28 ++++++++ src/types/gecko-profile.ts | 11 ++++ src/types/profile-derived.ts | 5 ++ src/types/profile.ts | 5 ++ src/utils/base64.ts | 18 ++++++ webpack.config.js | 1 + yarn.lock | 64 ++++++++++++++++++- 21 files changed, 307 insertions(+), 14 deletions(-) create mode 100644 src/types/@types/devtools-reps/index.d.ts diff --git a/jest.config.js b/jest.config.js index dbb1aa6adc..59ae3cd2e2 100644 --- a/jest.config.js +++ b/jest.config.js @@ -18,10 +18,14 @@ module.exports = { '!src/types/libdef/**', ], + transform: { + "\\.([jt]sx?|mjs)$": "babel-jest" + }, + // Transform ESM modules to CommonJS for Jest // These packages ship as pure ESM and need to be transformed by Babel transformIgnorePatterns: [ - '/node_modules/(?!(query-string|decode-uri-component|iongraph-web|split-on-first|filter-obj|fetch-mock)/)', + '/node_modules/(?!(query-string|decode-uri-component|iongraph-web|split-on-first|filter-obj|fetch-mock|devtools-reps)/)', ], // Mock static assets (images, CSS, etc.) diff --git a/package.json b/package.json index e805b20810..39491702fc 100644 --- a/package.json +++ b/package.json @@ -82,6 +82,7 @@ "common-tags": "^1.8.2", "copy-to-clipboard": "^3.3.3", "core-js": "^3.47.0", + "devtools-reps": "^0.27.3", "escape-string-regexp": "^4.0.0", "gecko-profiler-demangle": "^0.4.0", "idb": "^8.0.3", diff --git a/res/css/global.css b/res/css/global.css index 28de1bcff0..1f7caf8684 100644 --- a/res/css/global.css +++ b/res/css/global.css @@ -153,3 +153,17 @@ a:active { .splitter-layout.splitter-layout.layout-changing > .layout-splitter { background-color: var(--wide-splitter-pressed-color); } + +/* Colors for DevTools Reps */ +:root { + --number-color: #058b00; + --string-color: #dd00a9; + --null-color: #5c5c5f; + --object-color: #0074e8; + --caption-color: #0074e8; + --location-color: #5c5c5f; + --source-link-color: #0060df; + --node-color: #003eaa; + --reference-color: #0074e8; + --comment-node-color: #5c5c5f; +} diff --git a/src/components/stack-chart/Canvas.tsx b/src/components/stack-chart/Canvas.tsx index 81fc837544..2668d6101c 100644 --- a/src/components/stack-chart/Canvas.tsx +++ b/src/components/stack-chart/Canvas.tsx @@ -18,6 +18,7 @@ import { getForegroundColor, getBackgroundColor, } from '../../utils/colors'; +import { ValueSummaryReader } from 'devtools-reps'; import { TooltipCallNode } from '../tooltip/CallNode'; import { TooltipMarker } from '../tooltip/Marker'; @@ -623,6 +624,17 @@ class StackChartCanvasImpl extends React.PureComponent { timelineUnit === 'bytes' ? formatBytes(duration) : formatMilliseconds(duration); + let argumentSummaries = undefined; + if (timing.argumentValues) { + const argumentValues = timing.argumentValues[stackTimingIndex]; + if (argumentValues !== -1) { + argumentSummaries = ValueSummaryReader.getArgumentSummaries( + thread.tracedValuesBuffer as ArrayBuffer, + thread.tracedObjectShapes as Array, + argumentValues + ); + } + } return ( { callTreeSummaryStrategy="timing" durationText={durationText} displayStackType={displayStackType} + argumentValues={argumentSummaries} /> ); }; diff --git a/src/components/tooltip/CallNode.tsx b/src/components/tooltip/CallNode.tsx index e77f765143..fec0bb2a3c 100644 --- a/src/components/tooltip/CallNode.tsx +++ b/src/components/tooltip/CallNode.tsx @@ -31,6 +31,8 @@ import type { OneCategoryBreakdown, } from 'firefox-profiler/profile-logic/profile-data'; import type { CallNodeInfo } from 'firefox-profiler/profile-logic/call-node-info'; +import { REPS, MODE } from 'devtools-reps'; +const { Rep } = REPS; import './CallNode.css'; import classNames from 'classnames'; @@ -129,6 +131,7 @@ type Props = { readonly timings?: TimingsForPath; readonly callTreeSummaryStrategy: CallTreeSummaryStrategy; readonly displayStackType: boolean; + readonly argumentValues?: Array; }; /** @@ -358,6 +361,7 @@ export class TooltipCallNode extends React.PureComponent { thread, durationText, categories, + argumentValues, displayData, timings, callTreeSummaryStrategy, @@ -426,6 +430,29 @@ export class TooltipCallNode extends React.PureComponent { ]; } + let argumentsElement = null; + if (argumentValues) { + if (argumentValues.length === 0) { + argumentsElement =
No arguments.
; + } else { + const argumentValuesEl = []; + for (const previewObject of argumentValues) { + argumentValuesEl.push( + Rep({ + object: previewObject, + mode: MODE.LONG, + }) + ); + } + argumentsElement = ( +
+
Arguments
+ {argumentValuesEl} +
+ ); + } + } + // Finding current frame and parent frame URL(if there is). let pageAndParentPageURL; if (innerWindowIDToPageMap) { @@ -542,6 +569,7 @@ export class TooltipCallNode extends React.PureComponent { {resource} {this._renderCategoryTimings(timings)} + {argumentsElement} ); diff --git a/src/components/tooltip/Tooltip.css b/src/components/tooltip/Tooltip.css index a2a3c150b2..7b75846a94 100644 --- a/src/components/tooltip/Tooltip.css +++ b/src/components/tooltip/Tooltip.css @@ -177,3 +177,17 @@ .sidebar .tooltipDetailSeparator { display: none; } + +.argumentsLabel { + /* match tooltips label, without being aligned to the right */ + color: var(--grey-50); +} + +.arguments > span { + display: block; + padding: 0.25em; +} + +.arguments > span:nth-child(odd) { + background-color: rgb(0 0 0 / 0.05); +} diff --git a/src/index.tsx b/src/index.tsx index 51421b2551..f242c80306 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -13,6 +13,7 @@ import '../res/css/global.css'; import '../res/css/categories.css'; import '../res/css/network.css'; import 'react-splitter-layout/lib/index.css'; +import 'devtools-reps/reps.css'; // React imported for JSX in Root component import { createRoot } from 'react-dom/client'; diff --git a/src/profile-logic/process-profile.ts b/src/profile-logic/process-profile.ts index a1049c0cab..41e0070787 100644 --- a/src/profile-logic/process-profile.ts +++ b/src/profile-logic/process-profile.ts @@ -1015,6 +1015,10 @@ function _processSamples(geckoSamples: GeckoSampleStruct): RawSamplesTable { } } + if ('argumentValues' in geckoSamples) { + samples.argumentValues = geckoSamples.argumentValues; + } + if ('eventDelay' in geckoSamples) { samples.eventDelay = geckoSamples.eventDelay; } else if ('responsiveness' in geckoSamples) { @@ -1255,6 +1259,14 @@ function _processThread( newThread.nativeAllocations = nativeAllocations; } + if (thread.tracedValues) { + newThread.tracedValuesBuffer = thread.tracedValues; + } + + if (thread.tracedObjectShapes) { + newThread.tracedObjectShapes = thread.tracedObjectShapes; + } + function processJsTracer() { // Optionally extract the JS Tracer information, if they exist. const { jsTracerEvents } = thread; diff --git a/src/profile-logic/profile-data.ts b/src/profile-logic/profile-data.ts index 3be6925778..4f710f21b4 100644 --- a/src/profile-logic/profile-data.ts +++ b/src/profile-logic/profile-data.ts @@ -1882,6 +1882,13 @@ export function filterThreadSamplesToRange( ); } + if (samples.argumentValues) { + newSamples.argumentValues = samples.argumentValues.slice( + beginSampleIndex, + endSampleIndex + ); + } + if (samples.threadId) { newSamples.threadId = samples.threadId.slice( beginSampleIndex, @@ -2008,6 +2015,13 @@ export function filterRawThreadSamplesToRange( ); } + if (samples.argumentValues) { + newSamples.argumentValues = samples.argumentValues.slice( + beginSampleIndex, + endSampleIndex + ); + } + if (samples.threadId) { newSamples.threadId = samples.threadId.slice( beginSampleIndex, @@ -2114,6 +2128,9 @@ export function filterCounterSamplesToRange( number: samples.number ? samples.number.slice(beginSampleIndex, endSampleIndex) : undefined, + argumentValues: samples.argumentValues + ? samples.argumentValues.slice(beginSampleIndex, endSampleIndex) + : undefined, }; return newCounter; @@ -2438,6 +2455,7 @@ export function computeSamplesTableFromRawSamplesTable( const { responsiveness, eventDelay, + argumentValues, stack, weight, weightType, @@ -2459,6 +2477,7 @@ export function computeSamplesTableFromRawSamplesTable( // These fields are copied from the raw samples table: responsiveness, eventDelay, + argumentValues, stack, weight, weightType, @@ -2479,7 +2498,8 @@ export function createThreadFromDerivedTables( samples: SamplesTable, stackTable: StackTable, stringTable: StringTable, - sources: SourceTable + sources: SourceTable, + tracedValuesBuffer: ArrayBuffer | undefined ): Thread { const { processType, @@ -2506,6 +2526,7 @@ export function createThreadFromDerivedTables( jsTracer, isPrivateBrowsing, userContextId, + tracedObjectShapes, } = rawThread; const thread: Thread = { @@ -2534,12 +2555,14 @@ export function createThreadFromDerivedTables( jsTracer, isPrivateBrowsing, userContextId, + tracedObjectShapes, // These fields are derived: samples, stackTable, stringTable, sources, + tracedValuesBuffer, }; return thread; } diff --git a/src/profile-logic/stack-timing.ts b/src/profile-logic/stack-timing.ts index 7ea7f20991..e7de80e372 100644 --- a/src/profile-logic/stack-timing.ts +++ b/src/profile-logic/stack-timing.ts @@ -87,6 +87,7 @@ export type StackTiming = { sameWidthsStart: number[]; sameWidthsEnd: number[]; callNode: IndexIntoCallNodeTable[]; + argumentValues?: number[]; length: number; }; @@ -115,14 +116,20 @@ export function getStackTimingByDepth( } = callNodeTable; const stackTimingByDepth: StackTimingByDepth = Array.from( { length: maxDepthPlusOne }, - (): StackTiming => ({ - start: [], - end: [], - sameWidthsStart: [], - sameWidthsEnd: [], - callNode: [], - length: 0, - }) + (): StackTiming => { + const shape: StackTiming = { + start: [], + end: [], + sameWidthsStart: [], + sameWidthsEnd: [], + callNode: [], + length: 0, + }; + if ('argumentValues' in samples) { + shape.argumentValues = []; + } + return shape; + } ); const sameWidthsIndexToTimestampMap: SameWidthsIndexToTimestampMap = []; @@ -154,6 +161,7 @@ export function getStackTimingByDepth( let deepestOpenBoxDepth = -1; const openBoxStartTimeByDepth = new Float64Array(maxDepthPlusOne); const openBoxStartTickByDepth = new Float64Array(maxDepthPlusOne); + const openBoxArgsByDepth = new Int32Array(maxDepthPlusOne); let currentStackTick = 0; for (let sampleIndex = 0; sampleIndex < samples.length; sampleIndex++) { @@ -162,6 +170,14 @@ export function getStackTimingByDepth( continue; } + let sampleArgs: number = -1; + if ('argumentValues' in samples && samples.argumentValues !== undefined) { + const val = samples.argumentValues[sampleIndex]; + if (val !== null) { + sampleArgs = val; + } + } + const sampleTime = samples.time[sampleIndex]; // Phase 1: Commit open boxes which are not shared by the current call node, @@ -193,6 +209,10 @@ export function getStackTimingByDepth( stackTimingForThisDepth.sameWidthsStart[index] = startStackTick; stackTimingForThisDepth.sameWidthsEnd[index] = currentStackTick; stackTimingForThisDepth.callNode[index] = deepestOpenBoxCallNodeIndex; + if (stackTimingForThisDepth.argumentValues) { + stackTimingForThisDepth.argumentValues[index] = + openBoxArgsByDepth[deepestOpenBoxDepth]; + } deepestOpenBoxCallNodeIndex = callNodeTablePrefixColumn[deepestOpenBoxCallNodeIndex]; deepestOpenBoxDepth--; @@ -208,6 +228,12 @@ export function getStackTimingByDepth( deepestOpenBoxDepth++; openBoxStartTimeByDepth[deepestOpenBoxDepth] = sampleTime; openBoxStartTickByDepth[deepestOpenBoxDepth] = currentStackTick; + if ( + 'argumentValues' in samples && + samples.argumentValues !== undefined + ) { + openBoxArgsByDepth[deepestOpenBoxDepth] = sampleArgs; + } } } @@ -229,6 +255,10 @@ export function getStackTimingByDepth( stackTimingForThisDepth.sameWidthsStart[index] = startStackTick; stackTimingForThisDepth.sameWidthsEnd[index] = currentStackTick; stackTimingForThisDepth.callNode[index] = deepestOpenBoxCallNodeIndex; + if (stackTimingForThisDepth.argumentValues) { + stackTimingForThisDepth.argumentValues[index] = + openBoxArgsByDepth[deepestOpenBoxDepth]; + } deepestOpenBoxCallNodeIndex = callNodeTablePrefixColumn[deepestOpenBoxCallNodeIndex]; deepestOpenBoxDepth--; diff --git a/src/selectors/per-thread/thread.tsx b/src/selectors/per-thread/thread.tsx index fc8b619b89..e6fec6c67d 100644 --- a/src/selectors/per-thread/thread.tsx +++ b/src/selectors/per-thread/thread.tsx @@ -18,6 +18,7 @@ import { ensureExists, getFirstItemFromSet, } from '../../utils/types'; +import { base64StringToBytes } from '../../utils/base64'; import type { Thread, @@ -92,6 +93,12 @@ export function getBasicThreadSelectorsPerThread( ? ProfileSelectors.getProfile(state).threads[singleThreadIndex] : getMergedRawThread(state); + const getTracedValuesBuffer: Selector = + createSelector( + (state: State) => getRawThread(state).tracedValuesBuffer, + (tracedValuesBuffer: string | undefined) => + tracedValuesBuffer ? base64StringToBytes(tracedValuesBuffer) : undefined + ); const getRawSamplesTable: Selector = (state) => getRawThread(state).samples; const getSamplesTable: Selector = createSelector( @@ -147,6 +154,7 @@ export function getBasicThreadSelectorsPerThread( getStackTable, ProfileSelectors.getStringTable, ProfileSelectors.getSourceTable, + getTracedValuesBuffer, ProfileData.createThreadFromDerivedTables ); @@ -386,6 +394,7 @@ export function getBasicThreadSelectorsPerThread( getRawThread, getThread, getSamplesTable, + getTracedValuesBuffer, getSamplesWeightType, getNativeAllocations, getJsAllocations, diff --git a/src/test/fixtures/profiles/processed-profile.ts b/src/test/fixtures/profiles/processed-profile.ts index 5813a8e4e8..b5054bc200 100644 --- a/src/test/fixtures/profiles/processed-profile.ts +++ b/src/test/fixtures/profiles/processed-profile.ts @@ -2120,6 +2120,9 @@ export function addInnerWindowIdToStacks( if (samples.responsiveness) { samples.responsiveness.push(samples.responsiveness[sampleIndex]); } + if (samples.argumentValues) { + samples.argumentValues.push(samples.argumentValues[sampleIndex]); + } if (samples.threadCPUDelta) { samples.threadCPUDelta.push(samples.threadCPUDelta[sampleIndex]); } diff --git a/src/test/fixtures/utils.ts b/src/test/fixtures/utils.ts index 11b5f9955a..8e86ecf1a9 100644 --- a/src/test/fixtures/utils.ts +++ b/src/test/fixtures/utils.ts @@ -22,6 +22,7 @@ import { } from 'firefox-profiler/profile-logic/profile-data'; import { getProfileWithDicts } from './profiles/processed-profile'; import { StringTable } from '../../utils/string-table'; +import { base64StringToBytes } from '../../utils/base64'; import type { IndexIntoCallNodeTable, @@ -153,12 +154,16 @@ export function computeThreadFromRawThread( sampleUnits, referenceCPUDeltaPerMs ); + const tracedValuesBuffer = rawThread.tracedValuesBuffer + ? base64StringToBytes(rawThread.tracedValuesBuffer) + : undefined; return createThreadFromDerivedTables( rawThread, samples, stackTable, stringTable, - shared.sources + shared.sources, + tracedValuesBuffer ); } diff --git a/src/test/store/__snapshots__/profile-view.test.ts.snap b/src/test/store/__snapshots__/profile-view.test.ts.snap index 913438be86..974ee20b96 100644 --- a/src/test/store/__snapshots__/profile-view.test.ts.snap +++ b/src/test/store/__snapshots__/profile-view.test.ts.snap @@ -2979,6 +2979,8 @@ CallTree { }, }, "tid": 0, + "tracedObjectShapes": undefined, + "tracedValuesBuffer": undefined, "unregisterTime": null, "userContextId": undefined, }, @@ -3313,6 +3315,8 @@ Object { }, }, "tid": 0, + "tracedObjectShapes": undefined, + "tracedValuesBuffer": undefined, "unregisterTime": null, "userContextId": undefined, } @@ -3719,6 +3723,8 @@ Object { }, }, "tid": 0, + "tracedObjectShapes": undefined, + "tracedValuesBuffer": undefined, "unregisterTime": null, "userContextId": undefined, } @@ -4051,6 +4057,8 @@ Object { }, }, "tid": 0, + "tracedObjectShapes": undefined, + "tracedValuesBuffer": undefined, "unregisterTime": null, "userContextId": undefined, } @@ -4383,6 +4391,8 @@ Object { }, }, "tid": 0, + "tracedObjectShapes": undefined, + "tracedValuesBuffer": undefined, "unregisterTime": null, "userContextId": undefined, } diff --git a/src/types/@types/devtools-reps/index.d.ts b/src/types/@types/devtools-reps/index.d.ts new file mode 100644 index 0000000000..11bbce5292 --- /dev/null +++ b/src/types/@types/devtools-reps/index.d.ts @@ -0,0 +1,28 @@ +/* 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/. */ + +declare module 'devtools-reps' { + export const MODE: { + readonly TINY: unique symbol; + readonly SHORT: unique symbol; + readonly LONG: unique symbol; + readonly HEADER: unique symbol; + }; + + export const REPS: any; + export function getRep(object: any, defaultRep?: any): any; + + export function parseURLEncodedText(text: string): any; + export function parseURLParams(url: string): any; + export function maybeEscapePropertyName(name: string): string; + export function getGripPreviewItems(grip: any): any[]; + + export const ValueSummaryReader: { + getArgumentSummaries: ( + valuesBuffer: ArrayBuffer, + shapes: Array, + valuesBufferIndex: number + ) => any; + }; +} diff --git a/src/types/gecko-profile.ts b/src/types/gecko-profile.ts index fac9c4d1a0..8ba5024612 100644 --- a/src/types/gecko-profile.ts +++ b/src/types/gecko-profile.ts @@ -124,6 +124,7 @@ export type GeckoSamples = { stack: 0; time: 1; eventDelay: 2; + argumentValues?: 3; threadCPUDelta?: 3; }; data: Array< @@ -141,8 +142,14 @@ export type GeckoSamples = { // thread's event loop at the time that the sample was taken Milliseconds, ( + // Index into the values buffer containing a binary representation of the argumentValues + // It's present only when the JS Execution Tracing feature is enabled in Firefox + // OR // CPU usage value of the current thread. // It's present only when the CPU Utilization feature is enabled in Firefox. + // + // NOTE: these two options are mutually exclusive since CPU Utilization is + // mutually exclusive with JS Execution Tracing number | null ), ] @@ -160,6 +167,7 @@ export type GeckoSampleStructWithResponsiveness = { // versions may not have it or that feature could be disabled. No upgrader was // written for this change because it's a completely new data source. threadCPUDelta?: Array; + argumentValues?: Array; length: number; }; @@ -174,6 +182,7 @@ export type GeckoSampleStructWithEventDelay = { // versions may not have it or that feature could be disabled. No upgrader was // written for this change because it's a completely new data source. threadCPUDelta?: Array; + argumentValues?: Array; length: number; }; @@ -294,6 +303,8 @@ export type GeckoThread = { stackTable: GeckoStackTable; stringTable: string[]; jsTracerEvents?: JsTracerTable; + tracedValues?: string; + tracedObjectShapes?: Array>; }; export type GeckoExtensionMeta = { diff --git a/src/types/profile-derived.ts b/src/types/profile-derived.ts index c0ba94655f..97231b487e 100644 --- a/src/types/profile-derived.ts +++ b/src/types/profile-derived.ts @@ -95,6 +95,7 @@ export type Thread = { // It's absent in Firefox 97 and before, or in Firefox 98+ when this thread // had no extra attribute at all. userContextId?: number; + tracedObjectShapes?: Array; // The fields below this comment are derived data, and not present on the RawThread // in the same form. @@ -108,6 +109,7 @@ export type Thread = { // The stack samples collected for this thread. This field is different from // RawThread in that the `time` column is always present. samples: SamplesTable; + tracedValuesBuffer?: ArrayBuffer; }; /** @@ -134,6 +136,7 @@ export type SamplesTable = { // This property isn't present in normal threads. However it's present for // merged threads, so that we know the origin thread for these samples. threadId?: Tid[]; + argumentValues?: Array; length: number; }; @@ -144,6 +147,7 @@ type SamplesLikeTableShape = { // See the WeightType type for more information. weight: null | number[]; weightType: WeightType; + argumentValues?: Array; length: number; }; @@ -160,6 +164,7 @@ export type CounterSamplesTable = { number?: number[]; // The count of the data, for instance for memory this would be bytes. count: number[]; + argumentValues?: Array; length: number; }; diff --git a/src/types/profile.ts b/src/types/profile.ts index 3e5e02466e..d463ca724d 100644 --- a/src/types/profile.ts +++ b/src/types/profile.ts @@ -117,6 +117,7 @@ export type RawSamplesTable = { time?: Milliseconds[]; // If the `time` column is not present, then the `timeDeltas` column must be present. timeDeltas?: Milliseconds[]; + argumentValues?: Array; // An optional weight array. If not present, then the weight is assumed to be 1. // See the WeightType type for more information. weight: null | number[]; @@ -164,6 +165,7 @@ export type UnbalancedNativeAllocationsTable = { weight: Bytes[]; weightType: 'bytes'; stack: Array; + argumentValues?: Array; length: number; }; @@ -503,6 +505,7 @@ export type RawCounterSamplesTable = { number?: number[]; // The count of the data, for instance for memory this would be bytes. count: number[]; + argumentValues?: Array; length: number; }; @@ -670,6 +673,8 @@ export type RawThread = { // It's absent in Firefox 97 and before, or in Firefox 98+ when this thread // had no extra attribute at all. userContextId?: number; + tracedValuesBuffer?: string; + tracedObjectShapes?: Array; }; export type ExtensionTable = { diff --git a/src/utils/base64.ts b/src/utils/base64.ts index 31571311ec..4dbff80a3a 100644 --- a/src/utils/base64.ts +++ b/src/utils/base64.ts @@ -25,3 +25,21 @@ export async function dataUrlToBytes(dataUrl: string): Promise { const res = await fetch(dataUrl); return res.arrayBuffer(); } + +function base64StringToBytesFallback(base64: string): ArrayBuffer { + const binaryString = atob(base64); + const bytes = new Uint8Array(binaryString.length); + for (let i = 0; i < binaryString.length; i++) { + bytes[i] = binaryString.charCodeAt(i); + } + return bytes.buffer; +} + +export function base64StringToBytes(base64: string): ArrayBuffer { + if ('fromBase64' in Uint8Array) { + // @ts-expect-error Uint8Array.fromBase64 is a relatively new API + return Uint8Array.fromBase64(base64).buffer; + } + + return base64StringToBytesFallback(base64); +} diff --git a/webpack.config.js b/webpack.config.js index 274766717a..d42c0cceab 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -58,6 +58,7 @@ const config = { path.join(__dirname, 'node_modules', 'photon-colors'), path.join(__dirname, 'node_modules', 'react-splitter-layout'), path.join(__dirname, 'node_modules', 'iongraph-web'), + path.join(__dirname, 'node_modules', 'devtools-reps'), ], }, { diff --git a/yarn.lock b/yarn.lock index 4711473676..8c15b6a686 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4733,6 +4733,16 @@ devtools-license-check@^0.9.0: dependencies: license-checker "^9.0.3" +devtools-reps@^0.27.3: + version "0.27.3" + resolved "https://registry.yarnpkg.com/devtools-reps/-/devtools-reps-0.27.3.tgz#a41cb6c1c5b4aed5d0bdcac7505b0e3e6ad41526" + integrity sha512-bG3kr0jOvYqx0xAyTvkq/w5cKU6aJm3J53gKetomgFZYrsZiW7Z7U2APHD9uylj2EZ5OrJZaovHkxDRqHqb01g== + dependencies: + prop-types "^15.7.2" + react "^16.8.6" + react-dom "^16.8.6" + react-dom-factories "^1.0.2" + dezalgo@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.3.tgz#7f742de066fc748bc8db820569dddce49bf0d456" @@ -10381,7 +10391,7 @@ process-nextick-args@~2.0.0: resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== -prop-types@^15.6.2, prop-types@^15.8.1: +prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -10562,6 +10572,21 @@ rc@1.2.8: minimist "^1.2.0" strip-json-comments "~2.0.1" +react-dom-factories@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/react-dom-factories/-/react-dom-factories-1.0.2.tgz#eb7705c4db36fb501b3aa38ff759616aa0ff96e0" + integrity sha512-Bmic2N3oKji7vw9qjDr2dmwHvOATbFSnKy7EH0uT/qjvzIUsiXp6Yquk72LJ3WfMtRnq3ujXMMo7GsJeLPfFWw== + +react-dom@^16.8.6: + version "16.14.0" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.14.0.tgz#7ad838ec29a777fb3c75c3a190f661cf92ab8b89" + integrity sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + prop-types "^15.6.2" + scheduler "^0.19.1" + react-dom@^18.3.1: version "18.3.1" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.3.1.tgz#c2265d79511b57d479b3dd3fdfa51536494c5cb4" @@ -10613,6 +10638,15 @@ react-transition-group@^4.4.5: loose-envify "^1.4.0" prop-types "^15.6.2" +react@^16.8.6: + version "16.14.0" + resolved "https://registry.yarnpkg.com/react/-/react-16.14.0.tgz#94d776ddd0aaa37da3eda8fc5b6b18a4c9a3114d" + integrity sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + prop-types "^15.6.2" + react@^18.3.1: version "18.3.1" resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891" @@ -11235,6 +11269,14 @@ saxes@^6.0.0: dependencies: xmlchars "^2.2.0" +scheduler@^0.19.1: + version "0.19.1" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196" + integrity sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + scheduler@^0.23.2: version "0.23.2" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.2.tgz#414ba64a3b282892e944cf2108ecc078d115cdc3" @@ -11738,7 +11780,16 @@ string-length@^4.0.2: char-regex "^1.0.2" strip-ansi "^6.0.0" -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -11868,7 +11919,7 @@ stringify-object@^3.3.0: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -11882,6 +11933,13 @@ strip-ansi@^0.3.0: dependencies: ansi-regex "^0.2.1" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^7.0.1, strip-ansi@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" From 491d058b7b6a5106917dd1c8daa214861cce2f4f Mon Sep 17 00:00:00 2001 From: Alex Thayer Date: Wed, 3 Dec 2025 15:42:39 -0800 Subject: [PATCH 13/59] Add test for basic StackChart display of argument values --- src/test/components/StackChart.test.tsx | 15 + .../__snapshots__/StackChart.test.tsx.snap | 97 + .../fixtures/upgrades/argument-values.json | 2987 +++++++++++++++++ 3 files changed, 3099 insertions(+) create mode 100644 src/test/fixtures/upgrades/argument-values.json diff --git a/src/test/components/StackChart.test.tsx b/src/test/components/StackChart.test.tsx index 19b0a69a56..72481760da 100644 --- a/src/test/components/StackChart.test.tsx +++ b/src/test/components/StackChart.test.tsx @@ -314,6 +314,21 @@ describe('StackChart', function () { }); }); +describe('ArgumentValues', () => { + it('shows argument values when a profile has them', () => { + const profile = require('../fixtures/upgrades/argument-values.json'); + const store = storeWithProfile(profile); + const {getTooltip, moveMouse, findFillTextPosition} = setup(store); + + moveMouse(findFillTextPosition('bar')); + let tooltip = getTooltip(); + expect( + within(ensureExists(tooltip)).getByText('bar') + ).toBeInTheDocument(); + expect(tooltip).toMatchSnapshot(); + }); +}); + describe('MarkerChart', function () { it('can turn on the show user timings', () => { const { getByLabelText, getState } = setupUserTimings({ diff --git a/src/test/components/__snapshots__/StackChart.test.tsx.snap b/src/test/components/__snapshots__/StackChart.test.tsx.snap index ce1ce14e55..df625da24d 100644 --- a/src/test/components/__snapshots__/StackChart.test.tsx.snap +++ b/src/test/components/__snapshots__/StackChart.test.tsx.snap @@ -1,5 +1,102 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing +exports[`ArgumentValues shows argument values when a profile has them 1`] = ` +
+
+
+
+ 36ms +
+
+ bar +
+
+
+
+
+
+ Stack Type: +
+
+ JavaScript +
+
+ Category: +
+
+ + JavaScript: JIT (baseline) +
+
+ Page URL: +
+
+ file://<Page #15> +
+
+ File: +
+
+ file://<URL>:11:13 +
+
+ Resource: +
+ file://<URL> +
+
+
+ Arguments +
+ + 0 + +
+
+
+
+`; + exports[`CombinedChart renders combined stack chart 1`] = `
", + "embedderInnerWindowID": 0, + "isPrivateBrowsing": false, + "favicon": null + }, + { + "tabID": 6, + "innerWindowID": 10737418242, + "url": "moz-extension://", + "embedderInnerWindowID": 0, + "isPrivateBrowsing": false, + "favicon": null + }, + { + "tabID": 7, + "innerWindowID": 10737418243, + "url": "moz-extension://", + "embedderInnerWindowID": 0, + "isPrivateBrowsing": false, + "favicon": null + }, + { + "tabID": 8, + "innerWindowID": 10737418244, + "url": "moz-extension://", + "embedderInnerWindowID": 0, + "isPrivateBrowsing": false, + "favicon": null + }, + { + "tabID": 9, + "innerWindowID": 10737418245, + "url": "moz-extension://", + "embedderInnerWindowID": 0, + "isPrivateBrowsing": false, + "favicon": null + }, + { + "tabID": 10, + "innerWindowID": 10737418246, + "url": "moz-extension://", + "embedderInnerWindowID": 0, + "isPrivateBrowsing": false, + "favicon": null + }, + { + "tabID": 14, + "innerWindowID": 17179869185, + "url": "http://", + "embedderInnerWindowID": 0, + "isPrivateBrowsing": false, + "favicon": null + }, + { + "tabID": 12, + "innerWindowID": 15032385537, + "url": "http://", + "embedderInnerWindowID": 0, + "isPrivateBrowsing": false, + "favicon": null + }, + { + "tabID": 11, + "innerWindowID": 6442450946, + "url": "about:newtab", + "embedderInnerWindowID": 0, + "isPrivateBrowsing": false, + "favicon": null + }, + { + "tabID": 13, + "innerWindowID": 21474836482, + "url": "file://", + "embedderInnerWindowID": 0, + "isPrivateBrowsing": false, + "favicon": null + }, + { + "tabID": 15, + "innerWindowID": 21474836484, + "url": "file://", + "embedderInnerWindowID": 0, + "isPrivateBrowsing": false, + "favicon": null + }, + { + "tabID": 3, + "innerWindowID": 21474836485, + "url": "file://", + "embedderInnerWindowID": 0, + "isPrivateBrowsing": false, + "favicon": null + } + ], + "counters": [], + "profilerOverhead": [], + "shared": { + "stringArray": [ + "default-theme@mozilla.org", + "Extension \"System theme — auto\" (ID: default-theme@mozilla.org)", + "data-leak-blocker@mozilla.com", + "Extension \"Data Leak Blocker\" (ID: data-leak-blocker@mozilla.com)", + "webcompat@mozilla.org", + "Extension \"Web Compatibility Interventions\" (ID: webcompat@mozilla.org)", + "pictureinpicture@mozilla.org", + "Extension \"Picture-In-Picture\" (ID: pictureinpicture@mozilla.org)", + "newtab@mozilla.org", + "Extension \"New Tab\" (ID: newtab@mozilla.org)", + "addons-search-detection@mozilla.com", + "Extension \"Add-ons Search Detection\" (ID: addons-search-detection@mozilla.com)", + "formautofill@mozilla.org", + "Extension \"Form Autofill\" (ID: formautofill@mozilla.org)", + "ipp-activator@mozilla.com", + "Extension \"IPP Activator\" (ID: ipp-activator@mozilla.com)", + "(root)", + "(DOM) Event.type", + "(DOM) MouseEvent.screenX", + "(DOM) MouseEvent.screenY", + "mousedown", + "(DOM) MouseEvent.button", + "(DOM) MouseEvent.altKey", + "(DOM) MouseEvent.ctrlKey", + "(DOM) MouseEvent.metaKey", + "(DOM) MouseEvent.shiftKey", + "EventStateManager::PostHandleEvent", + "js::RunScript", + "XUL", + "Task RefreshDriver::EnsureTimerStarted::catch-up", + "RefreshDriver tick", + "Update the rendering Layout, content-visibility and resize observers", + "EventStateManager::PreHandleEvent", + "ElementStateChanged", + "EventDispatcher::Dispatch", + "RefreshObserver", + "TaskController::AddTask", + "Runnable", + "DOMEvent", + "Awake", + "XRE_InitChildProcess", + "Task NotifyObservers", + "Task PContent::Msg_AddPermission", + "PContent::Msg_AddPermission", + "IPC Accumulator", + "setTimeout", + "(DOM) Document.activeElement", + "file://", + "foo", + "bar", + "(DOM) JSWindowActorChild.constructor", + "(DOM) JSWindowActorChild.document", + "(DOM) JSWindowActorChild.contentWindow", + "(DOM) Window.getSelection", + "(DOM) Selection.__stringifier", + "(DOM) Event.timeStamp", + "(DOM) MouseEvent.isTrusted", + "(DOM) Event.defaultPrevented", + "(DOM) Window.setTimeout", + "(DOM) nsIAsyncShutdownBlocker.name", + "Task PBrowser::Msg_RealMouseEnterExitWidgetEvent", + "PBrowser::Msg_RealMouseEnterExitWidgetEvent", + "Task PBrowser::Msg_RealMouseMoveEvent", + "PBrowser::Msg_RealMouseMoveEvent", + "Task PBrowser::Msg_RealMouseButtonEvent", + "PBrowser::Msg_RealMouseButtonEvent", + "EventDispatcher::Dispatch mousedown", + "PContent::Msg_InsertNewFocusActionId", + "PWindowGlobal::Msg_UpdateDocumentHasUserInteracted", + "EventDispatcher::Dispatch mouseup", + "JSWindowActorProtocol::HandleEvent", + "resource:///actors/GenAIChild.sys.mjs", + "handleEvent", + "Window.setTimeout", + "Task PVsync::Msg_Notify", + "PVsync::Msg_Notify", + "f67ae7353bf8e5000", + "f67ae7353bf8e9600", + "ModuleEvaluation", + "ChromeUtils.importESModule", + "Preference Read", + "mozilla::base_profiler_markers_detail::AddMarkerToBuffer >(mozilla::ProfileChunkedBuffer&, mozilla::ProfilerStringView const&, mozilla::MarkerCategory const&, mozilla::MarkerOptions&&, bool (*)(mozilla::ProfileChunkedBuffer&, mozilla::StackCaptureOptions), nsTString const&)", + "profiler_capture_backtrace()", + "mozilla::base_profiler_markers_detail::AddMarkerToBuffer(mozilla::ProfileChunkedBuffer&, mozilla::ProfilerStringView const&, mozilla::MarkerCategory const&, mozilla::MarkerOptions&&, bool (*)(mozilla::ProfileChunkedBuffer&, mozilla::StackCaptureOptions), Flow const&)::{lambda(mozilla::ProfileChunkedBuffer&)#1}::operator()(mozilla::ProfileChunkedBuffer&) const", + "/Users/alexical/mozilla-unified/obj-opt/dist/include/mozilla/BaseProfilerMarkersDetail.h", + "mozilla::base_profiler_markers_detail::AddMarkerToBuffer >(mozilla::ProfileChunkedBuffer&, mozilla::ProfilerStringView const&, mozilla::MarkerCategory const&, mozilla::MarkerOptions&&, bool (*)(mozilla::ProfileChunkedBuffer&, mozilla::StackCaptureOptions), nsTString const&)::{lambda(mozilla::ProfileChunkedBuffer&)#1}::operator()(mozilla::ProfileChunkedBuffer&) const", + "/Users/alexical/mozilla-unified/tools/profiler/core/platform.cpp" + ], + "sources": { + "length": 4, + "uuid": [ + null, + null, + null, + null + ], + "filename": [ + 47, + 71, + 84, + 86 + ] + } + }, + "threads": [ + { + "name": "GeckoMain", + "isMainThread": true, + "processType": "tab", + "processName": "file:// Content", + "processStartupTime": 5584.521484375, + "processShutdownTime": null, + "registerTime": 5605.748109375, + "unregisterTime": null, + "tid": 2503164, + "pid": "39156", + "pausedRanges": [ + { + "startTime": 319713.765958, + "endTime": null, + "reason": "profiler-paused" + } + ], + "frameTable": { + "address": [ + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + 4256519, + -1, + -1, + -1, + -1, + 75486643, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + 4264179, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + 4256519 + ], + "inlineDepth": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1 + ], + "category": [ + null, + 8, + 4, + 4, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 1, + 1, + null, + 1, + 1, + 8, + 3, + null, + 1, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + null, + 8, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 8, + 1, + 1, + null + ], + "subcategory": [ + null, + 0, + 0, + 6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + null, + 0, + 0, + 0, + 5, + null, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + null, + 0, + 0, + 0, + 0, + 0, + 0, + 4, + 4, + 0, + 0, + 0, + null + ], + "func": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 42, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 54, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 31 + ], + "nativeSymbol": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 0, + null, + null, + null, + null, + 1, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 2, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 0 + ], + "innerWindowID": [ + 0, + 0, + 21474836485, + 21474836485, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "line": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 317, + null, + null, + null, + null, + 7903, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 303, + null, + null, + null, + null, + null, + null, + null, + 81, + null, + null, + null, + 303 + ], + "column": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 50, + null, + null, + null, + null + ], + "length": 55 + }, + "funcTable": { + "isJS": [ + false, + false, + true, + true, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + true, + false, + false, + false, + false + ], + "relevantForJS": [ + false, + true, + false, + false, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + false, + false, + false, + false, + false, + false, + true, + false, + false, + true, + true, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + true, + false, + false, + true, + false, + false, + false + ], + "name": [ + 16, + 20, + 48, + 49, + 50, + 17, + 51, + 46, + 52, + 53, + 54, + 55, + 56, + 57, + 21, + 22, + 23, + 24, + 25, + 18, + 19, + 58, + 59, + 45, + 40, + 41, + 81, + 60, + 61, + 26, + 33, + 85, + 29, + 30, + 31, + 62, + 63, + 64, + 65, + 34, + 66, + 67, + 82, + 32, + 68, + 42, + 43, + 69, + 70, + 27, + 72, + 73, + 74, + 75, + 83 + ], + "resource": [ + -1, + -1, + 8, + 8, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + 9, + -1, + -1, + -1, + -1, + 9, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + 9, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + 10, + -1, + -1, + -1, + 9 + ], + "source": [ + null, + null, + 0, + 0, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 2, + null, + null, + null, + null, + 2, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 3, + null, + null, + null, + null, + null, + null, + null, + 1, + null, + null, + null, + 2 + ], + "lineNumber": [ + null, + null, + 19, + 11, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 47, + null, + null, + null, + null + ], + "columnNumber": [ + null, + null, + 13, + 13, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 14, + null, + null, + null, + null + ], + "length": 55 + }, + "nativeSymbols": { + "libIndex": [ + 0, + 0, + 0 + ], + "address": [ + 4256352, + 75486340, + 4264108 + ], + "name": [ + 81, + 82, + 83 + ], + "functionSize": [ + 840, + 540, + 432 + ], + "length": 3 + }, + "resourceTable": { + "lib": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + 0, + null + ], + "name": [ + 1, + 3, + 5, + 7, + 9, + 11, + 13, + 15, + 47, + 28, + 71 + ], + "host": [ + 0, + 2, + 4, + 6, + 8, + 10, + 12, + 14, + null, + null, + null + ], + "type": [ + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 1, + 5 + ], + "length": 11 + }, + "stackTable": { + "frame": [ + 0, + 0, + 1, + 1, + 2, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 23, + 6, + 7, + 8, + 9, + 10, + 11, + 24, + 25, + 26, + 54, + 27, + 28, + 29, + 30, + 31, + 31, + 32, + 33, + 34, + 26, + 54, + 35, + 36, + 31, + 37, + 38, + 39, + 40, + 26, + 54, + 29, + 41, + 42, + 30, + 31, + 31, + 43, + 26, + 54, + 31, + 43, + 44, + 42, + 45, + 46, + 26, + 54, + 47, + 48, + 49, + 50, + 51, + 31, + 43, + 30, + 31, + 52, + 53, + 33, + 34, + 26, + 54 + ], + "prefix": [ + null, + null, + 0, + 0, + 2, + 2, + 4, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 26, + 26, + 26, + 26, + 26, + 26, + 0, + 34, + 35, + 36, + 34, + 38, + 39, + 40, + 39, + 41, + 34, + 44, + 45, + 46, + 47, + 34, + 49, + 50, + 34, + 52, + 53, + 54, + 55, + 56, + 53, + 58, + 59, + 58, + 53, + 61, + 45, + 64, + 65, + 55, + 53, + 68, + 69, + 34, + 71, + 72, + 73, + 54, + 75, + 76, + 77, + 78, + 79, + 39, + 81, + 82, + 34, + 84, + 85, + 86, + 87, + 88 + ], + "length": 90 + }, + "markers": { + "data": [ + { + "type": "Awake", + "QoS": "User Interactive" + }, + { + "innerWindowID": 21474836485, + "type": "DOMEvent", + "eventType": "mousedown", + "target": "html@10bc0c080", + "latency": 2.965583333333333 + }, + { + "type": "FlowMarker", + "flow": 77 + }, + { + "type": "TextStack", + "name": "" + }, + { + "type": "TextStack", + "name": "resource:///actors/GenAIChild.sys.mjs", + "cause": { + "tid": 2503164, + "time": 324383.877234375, + "stack": 57 + } + }, + { + "type": "Preference", + "prefName": "browser.drag_out_of_frame_style", + "prefKind": "User", + "prefType": "Int", + "prefValue": "1" + }, + { + "type": "TextStack", + "name": "" + }, + { + "type": "TextStack", + "name": "resource://gre/actors/AutoScrollChild.sys.mjs", + "cause": { + "tid": 2503164, + "time": 324384.231984375, + "stack": 57 + } + }, + { + "innerWindowID": 21474836485, + "type": "DOMEvent", + "eventType": "mousedown", + "target": "html@10bc0c080", + "latency": 2.965583333333333 + }, + { + "innerWindowID": 21474836485, + "type": "Text", + "name": "Coalesced input move flusher [Event]", + "cause": { + "tid": 2503164, + "time": 324347.262526375, + "stack": 62 + } + }, + { + "type": "Task", + "name": "PBrowser::Msg_RealMouseButtonEvent", + "priority": 6, + "priorityName": "InputHigh", + "task": 76 + }, + { + "type": "Awake", + "CPU Time": 37.453 + }, + null + ], + "name": [ + 39, + 38, + 36, + 78, + 79, + 80, + 78, + 79, + 38, + 35, + 37, + 39, + 44 + ], + "startTime": [ + 324347.219609375, + 324347.499109375, + 324383.871026375, + 324383.866109375, + 324383.823942375, + 324384.170276375, + 324384.230526375, + 324384.208942375, + 5584.521484375, + 324347.251067375, + 324347.233234375, + 5584.521484375, + 5584.521484375 + ], + "endTime": [ + 5584.521484375, + 5584.521484375, + 5584.521484375, + 324383.873359375, + 324383.874359375, + 5584.521484375, + 324384.231109375, + 324384.231776375, + 324384.271151375, + 324384.347234375, + 324384.353442375, + 324384.692192375, + 324576.394067375 + ], + "phase": [ + 2, + 2, + 0, + 1, + 1, + 0, + 1, + 1, + 3, + 1, + 1, + 3, + 3 + ], + "category": [ + 1, + 8, + 1, + 4, + 4, + 1, + 4, + 4, + 8, + 7, + 1, + 1, + 23 + ], + "length": 13 + }, + "samples": { + "length": 32, + "time": [ + 324347.532151375, + 324347.697817375, + 324347.735734375, + 324383.79677637503, + 324383.806276375, + 324383.811067375, + 324383.89902637503, + 324383.90765137505, + 324383.9553173751, + 324383.95594237506, + 324383.95960937504, + 324383.96073437505, + 324383.97785937507, + 324384.01777637505, + 324384.01919237507, + 324384.0209843751, + 324384.0264843751, + 324384.06756737514, + 324384.0863173751, + 324384.1059013751, + 324384.1337343751, + 324384.1485263751, + 324384.23594237515, + 324384.23819237517, + 324384.25665137515, + 324384.25690137513, + 324384.26069237513, + 324384.26073437516, + 324384.2628173752, + 324384.2628593752, + 324384.2653593752, + 324384.2655263752 + ], + "weight": null, + "weightType": "samples", + "stack": [ + 3, + 5, + 6, + 5, + 3, + 1, + 7, + 1, + 8, + 1, + 9, + 1, + 10, + 1, + 11, + 1, + 12, + 1, + 13, + 1, + 14, + 1, + 7, + 1, + 8, + 1, + 15, + 1, + 16, + 1, + 17, + 1 + ], + "eventDelay": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null + ], + "argumentValues": [ + null, + 6, + 24, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null + ] + }, + "tracedValuesBuffer": "AgAAABIAAQAAAAwHAAAAAAYAAAABAAcAAQAAABE=", + "tracedObjectShapes": [ + [ + "MouseEvent" + ] + ] + } + ], + "profilingLog": { + "39133": { + "bufferGlobalController": { + "controllerCreationTime_TSms": 323947.267208, + "logBegin_TSms": 323947.267042, + "updates": [], + "updatesSchema": "0: pid, 1: chunkRelease_TSms, 3: chunkDiff" + }, + "profilingLogBegin_TSms": 323946.88225, + "profilingLogEnd_TSms": 325307.459125 + }, + "39136": { + "profilingLogBegin_TSms": 323451.186208, + "profilingLogEnd_TSms": 324804.74575 + }, + "39137": { + "profilingLogBegin_TSms": 323414.368792, + "profilingLogEnd_TSms": 324769.997375 + }, + "39138": { + "profilingLogBegin_TSms": 323030.653583, + "profilingLogEnd_TSms": 324388.046958 + }, + "39139": { + "profilingLogBegin_TSms": 323014.021875, + "profilingLogEnd_TSms": 324366.797167 + }, + "39143": { + "profilingLogBegin_TSms": 322645.606, + "profilingLogEnd_TSms": 323999.851042 + }, + "39144": { + "profilingLogBegin_TSms": 322335.497542, + "profilingLogEnd_TSms": 323689.253375 + }, + "39151": { + "profilingLogBegin_TSms": 321587.79575, + "profilingLogEnd_TSms": 322941.864333 + }, + "39152": { + "profilingLogBegin_TSms": 321587.792458, + "profilingLogEnd_TSms": 322941.85975 + }, + "39153": { + "profilingLogBegin_TSms": 321565.734083, + "profilingLogEnd_TSms": 322918.633625 + }, + "39156": { + "profilingLogBegin_TSms": 318363.045375, + "profilingLogEnd_TSms": 319719.776417 + }, + "39162": { + "profilingLogBegin_TSms": 314471.372125, + "profilingLogEnd_TSms": 315826.738375 + }, + "39378": { + "profilingLogBegin_TSms": 142455.614167, + "profilingLogEnd_TSms": 143810.263 + } + }, + "profileGatheringLog": { + "39133": { + "events": [ + [ + 325307.830042, + "Generated parent process profile, size:", + 804072 + ], + [ + 325307.831708, + "No exit profiles." + ], + [ + 325307.837125, + "Waiting for pending profile, pid:", + 39136 + ], + [ + 325307.837667, + "Waiting for pending profile, pid:", + 39137 + ], + [ + 325307.838083, + "Waiting for pending profile, pid:", + 39138 + ], + [ + 325307.838458, + "Waiting for pending profile, pid:", + 39139 + ], + [ + 325307.838833, + "Waiting for pending profile, pid:", + 39143 + ], + [ + 325307.839167, + "Waiting for pending profile, pid:", + 39144 + ], + [ + 325307.8395, + "Waiting for pending profile, pid:", + 39151 + ], + [ + 325307.839792, + "Waiting for pending profile, pid:", + 39152 + ], + [ + 325307.840125, + "Waiting for pending profile, pid:", + 39153 + ], + [ + 325307.840417, + "Waiting for pending profile, pid:", + 39156 + ], + [ + 325307.84075, + "Waiting for pending profile, pid:", + 39162 + ], + [ + 325307.841042, + "Waiting for pending profile, pid:", + 39378 + ], + [ + 325312.892, + "Got profile from pid, with size:", + 39136, + 370202 + ], + [ + 325313.827083, + "Got profile from pid, with size:", + 39144, + 369231 + ], + [ + 325314.569458, + "Got profile from pid, with size:", + 39139, + 370802 + ], + [ + 325315.407292, + "Got profile from pid, with size:", + 39153, + 374670 + ], + [ + 325316.251625, + "Got profile from pid, with size:", + 39143, + 374172 + ], + [ + 325317.081667, + "Got profile from pid, with size:", + 39152, + 376043 + ], + [ + 325317.775167, + "Got profile from pid, with size:", + 39151, + 376231 + ], + [ + 325318.563583, + "Got profile from pid, with size:", + 39378, + 371592 + ], + [ + 325319.515042, + "Got profile from pid, with size:", + 39137, + 481709 + ], + [ + 325320.320875, + "Got profile from pid, with size:", + 39138, + 761951 + ], + [ + 325321.083458, + "Got profile from pid, with size:", + 39162, + 371752 + ], + [ + 325321.857458, + "Got profile from pid, with size:", + 39156, + 541826 + ], + [ + 325322.156667, + "Finished gathering, total size:", + 5944267 + ] + ], + "profileGatheringLogBegin_TSms": 325298.33725, + "profileGatheringLogEnd_TSms": 325322.157125 + } + } +} \ No newline at end of file From b9c3b2df8d9292f6c2b8764c1609f497e0b5a0d2 Mon Sep 17 00:00:00 2001 From: Alex Thayer Date: Wed, 3 Dec 2025 16:05:50 -0800 Subject: [PATCH 14/59] lint fixups --- jest.config.js | 2 +- src/test/components/StackChart.test.tsx | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/jest.config.js b/jest.config.js index 59ae3cd2e2..67cbc9e4ca 100644 --- a/jest.config.js +++ b/jest.config.js @@ -19,7 +19,7 @@ module.exports = { ], transform: { - "\\.([jt]sx?|mjs)$": "babel-jest" + '\\.([jt]sx?|mjs)$': 'babel-jest', }, // Transform ESM modules to CommonJS for Jest diff --git a/src/test/components/StackChart.test.tsx b/src/test/components/StackChart.test.tsx index 72481760da..b4968c2458 100644 --- a/src/test/components/StackChart.test.tsx +++ b/src/test/components/StackChart.test.tsx @@ -318,13 +318,11 @@ describe('ArgumentValues', () => { it('shows argument values when a profile has them', () => { const profile = require('../fixtures/upgrades/argument-values.json'); const store = storeWithProfile(profile); - const {getTooltip, moveMouse, findFillTextPosition} = setup(store); + const { getTooltip, moveMouse, findFillTextPosition } = setup(store); moveMouse(findFillTextPosition('bar')); - let tooltip = getTooltip(); - expect( - within(ensureExists(tooltip)).getByText('bar') - ).toBeInTheDocument(); + const tooltip = getTooltip(); + expect(within(ensureExists(tooltip)).getByText('bar')).toBeInTheDocument(); expect(tooltip).toMatchSnapshot(); }); }); From 6c6e9e64ae94f03413d7d7cd0735b69ea6ab5304 Mon Sep 17 00:00:00 2001 From: Alex Thayer Date: Wed, 14 Jan 2026 22:43:08 -0800 Subject: [PATCH 15/59] Review fixups --- src/components/stack-chart/Canvas.tsx | 22 ++++++++++++++----- src/components/tooltip/CallNode.tsx | 18 +++++++++------ src/components/tooltip/Tooltip.css | 11 ++++++---- src/profile-logic/sanitize.ts | 3 +++ src/profile-logic/stack-timing.ts | 2 ++ .../__snapshots__/StackChart.test.tsx.snap | 20 ++++++++--------- src/test/fixtures/profiles/gecko-profile.ts | 2 ++ src/test/unit/sanitize.test.ts | 16 ++++++++++++++ src/types/@types/devtools-reps/index.d.ts | 2 +- src/types/gecko-profile.ts | 2 +- 10 files changed, 69 insertions(+), 29 deletions(-) diff --git a/src/components/stack-chart/Canvas.tsx b/src/components/stack-chart/Canvas.tsx index 2668d6101c..a30950153b 100644 --- a/src/components/stack-chart/Canvas.tsx +++ b/src/components/stack-chart/Canvas.tsx @@ -626,13 +626,23 @@ class StackChartCanvasImpl extends React.PureComponent { : formatMilliseconds(duration); let argumentSummaries = undefined; if (timing.argumentValues) { - const argumentValues = timing.argumentValues[stackTimingIndex]; - if (argumentValues !== -1) { - argumentSummaries = ValueSummaryReader.getArgumentSummaries( - thread.tracedValuesBuffer as ArrayBuffer, - thread.tracedObjectShapes as Array, - argumentValues + const argumentValuesIndex = timing.argumentValues[stackTimingIndex]; + if ( + argumentValuesIndex !== -1 && + thread.tracedValuesBuffer && + thread.tracedObjectShapes + ) { + const argSummaries = ValueSummaryReader.getArgumentSummaries( + thread.tracedValuesBuffer, + thread.tracedObjectShapes, + argumentValuesIndex ); + // The API maybe needs work - getArgumentSummaries can return a string indicating + // that the argument summaries for a given call were missing (this can happen if they + // were overwritten in the underlying ring buffer) + if (typeof argSummaries !== 'string') { + argumentSummaries = argSummaries; + } } } diff --git a/src/components/tooltip/CallNode.tsx b/src/components/tooltip/CallNode.tsx index fec0bb2a3c..35914662e2 100644 --- a/src/components/tooltip/CallNode.tsx +++ b/src/components/tooltip/CallNode.tsx @@ -433,7 +433,9 @@ export class TooltipCallNode extends React.PureComponent { let argumentsElement = null; if (argumentValues) { if (argumentValues.length === 0) { - argumentsElement =
No arguments.
; + argumentsElement = ( +
No arguments.
+ ); } else { const argumentValuesEl = []; for (const previewObject of argumentValues) { @@ -444,12 +446,14 @@ export class TooltipCallNode extends React.PureComponent { }) ); } - argumentsElement = ( -
-
Arguments
+ argumentsElement = [ +
+ Arguments: +
, +
{argumentValuesEl} -
- ); +
, + ]; } } @@ -567,9 +571,9 @@ export class TooltipCallNode extends React.PureComponent { {pageAndParentPageURL} {fileName} {resource} + {argumentsElement}
{this._renderCategoryTimings(timings)} - {argumentsElement}
); diff --git a/src/components/tooltip/Tooltip.css b/src/components/tooltip/Tooltip.css index 7b75846a94..a2bac05435 100644 --- a/src/components/tooltip/Tooltip.css +++ b/src/components/tooltip/Tooltip.css @@ -178,16 +178,19 @@ display: none; } -.argumentsLabel { +.tooltipArgumentsLabel { /* match tooltips label, without being aligned to the right */ color: var(--grey-50); } -.arguments > span { +.tooltipArguments > span { display: block; - padding: 0.25em; } -.arguments > span:nth-child(odd) { +.tooltipArguments > span:nth-child(odd) { background-color: rgb(0 0 0 / 0.05); } + +.tooltipArguments { + text-indent: 1em hanging; +} diff --git a/src/profile-logic/sanitize.ts b/src/profile-logic/sanitize.ts index e9eefdd8f7..51f2d29728 100644 --- a/src/profile-logic/sanitize.ts +++ b/src/profile-logic/sanitize.ts @@ -411,6 +411,9 @@ function sanitizeThreadPII( delete newThread['eTLD+1']; } + delete newThread.tracedValuesBuffer; + delete newThread.tracedObjectShapes; + if (windowIdFromPrivateBrowsing.size > 0) { // In this block, we'll remove everything related to frame table entries // that have a innerWindowID with a isPrivateBrowsing flag. diff --git a/src/profile-logic/stack-timing.ts b/src/profile-logic/stack-timing.ts index e7de80e372..3f6f97c8f0 100644 --- a/src/profile-logic/stack-timing.ts +++ b/src/profile-logic/stack-timing.ts @@ -87,6 +87,8 @@ export type StackTiming = { sameWidthsStart: number[]; sameWidthsEnd: number[]; callNode: IndexIntoCallNodeTable[]; + // argumentValues is used by the JS Execution Tracing setting and allows + // displaying function calls' argument values. argumentValues?: number[]; length: number; }; diff --git a/src/test/components/__snapshots__/StackChart.test.tsx.snap b/src/test/components/__snapshots__/StackChart.test.tsx.snap index df625da24d..b08ccac01b 100644 --- a/src/test/components/__snapshots__/StackChart.test.tsx.snap +++ b/src/test/components/__snapshots__/StackChart.test.tsx.snap @@ -77,20 +77,20 @@ exports[`ArgumentValues shows argument values when a profile has them 1`] = ` Resource: file://<URL> - -
- Arguments + Arguments:
- - 0 - + + 0 + +
diff --git a/src/test/fixtures/profiles/gecko-profile.ts b/src/test/fixtures/profiles/gecko-profile.ts index 872870eec9..c1d9b57219 100644 --- a/src/test/fixtures/profiles/gecko-profile.ts +++ b/src/test/fixtures/profiles/gecko-profile.ts @@ -934,6 +934,8 @@ function _createGeckoThread( 'RefreshDriverTick', // 21 'Navigation::Start', // 22 ], + tracedObjectShapes: [['Test']], + tracedValues: 'SSdtIGEgbGl0dGxlIHRlYWN1cA==', }; } diff --git a/src/test/unit/sanitize.test.ts b/src/test/unit/sanitize.test.ts index e51be4d509..ac89869166 100644 --- a/src/test/unit/sanitize.test.ts +++ b/src/test/unit/sanitize.test.ts @@ -883,6 +883,22 @@ describe('sanitizePII', function () { expect(marker.flags).toBe('0xf00ba4'); }); + it('should sanitize traced argument values', function () { + const { originalProfile, sanitizedProfile } = setup({}); + expect( + originalProfile.threads.filter((t) => t.tracedValuesBuffer).length + ).toBeGreaterThan(0); + expect( + originalProfile.threads.filter((t) => t.tracedValuesBuffer).length + ).toBeGreaterThan(0); + expect( + sanitizedProfile.threads.filter((t) => t.tracedValuesBuffer).length + ).toBe(0); + expect( + sanitizedProfile.threads.filter((t) => t.tracedObjectShapes).length + ).toBe(0); + }); + it('should sanitize the eTLD+1 field if urls are supposed to be sanitized', function () { // Create a simple profile with eTLD+1 field in its thread. const { profile } = getProfileFromTextSamples('A'); diff --git a/src/types/@types/devtools-reps/index.d.ts b/src/types/@types/devtools-reps/index.d.ts index 11bbce5292..c7c11e7682 100644 --- a/src/types/@types/devtools-reps/index.d.ts +++ b/src/types/@types/devtools-reps/index.d.ts @@ -23,6 +23,6 @@ declare module 'devtools-reps' { valuesBuffer: ArrayBuffer, shapes: Array, valuesBufferIndex: number - ) => any; + ) => Array | string; }; } diff --git a/src/types/gecko-profile.ts b/src/types/gecko-profile.ts index 8ba5024612..30492c4d64 100644 --- a/src/types/gecko-profile.ts +++ b/src/types/gecko-profile.ts @@ -304,7 +304,7 @@ export type GeckoThread = { stringTable: string[]; jsTracerEvents?: JsTracerTable; tracedValues?: string; - tracedObjectShapes?: Array>; + tracedObjectShapes?: Array; }; export type GeckoExtensionMeta = { From a55ba14bc50a1b01a15c5b2edf785cd67abfe667 Mon Sep 17 00:00:00 2001 From: Alex Thayer Date: Wed, 28 Jan 2026 21:31:44 -0800 Subject: [PATCH 16/59] Fix css lint --- res/css/global.css | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/res/css/global.css b/res/css/global.css index 1f7caf8684..80e3ccb15d 100644 --- a/res/css/global.css +++ b/res/css/global.css @@ -47,6 +47,18 @@ --wide-splitter-hover-color: #bbb; --wide-splitter-pressed-color: #aaa; + /* Colors for DevTools Reps */ + --number-color: #058b00; + --string-color: #dd00a9; + --null-color: #5c5c5f; + --object-color: #0074e8; + --caption-color: #0074e8; + --location-color: #5c5c5f; + --source-link-color: #0060df; + --node-color: #003eaa; + --reference-color: #0074e8; + --comment-node-color: #5c5c5f; + color: var(--base-foreground-color); } @@ -153,17 +165,3 @@ a:active { .splitter-layout.splitter-layout.layout-changing > .layout-splitter { background-color: var(--wide-splitter-pressed-color); } - -/* Colors for DevTools Reps */ -:root { - --number-color: #058b00; - --string-color: #dd00a9; - --null-color: #5c5c5f; - --object-color: #0074e8; - --caption-color: #0074e8; - --location-color: #5c5c5f; - --source-link-color: #0060df; - --node-color: #003eaa; - --reference-color: #0074e8; - --comment-node-color: #5c5c5f; -} From 76ffffbbd492bfdbc8c73db62961076611a2fa9e Mon Sep 17 00:00:00 2001 From: Pontoon Date: Thu, 29 Jan 2026 09:50:39 +0000 Subject: [PATCH 17/59] Pontoon/Firefox Profiler: Update Greek (el) Co-authored-by: Jim Spentzos (el) --- locales/el/app.ftl | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/locales/el/app.ftl b/locales/el/app.ftl index 931583a1e6..733363f312 100644 --- a/locales/el/app.ftl +++ b/locales/el/app.ftl @@ -1163,6 +1163,17 @@ AssemblyView--show-button = # Assembly refers to the low-level programming language. AssemblyView--hide-button = .title = Απόκρυψη προβολής assembly +# The "◀" button above the assembly view. +AssemblyView--prev-button = + .title = Προηγούμενο +# The "▶" button above the assembly view. +AssemblyView--next-button = + .title = Επόμενο +# The label showing the current position and total count above the assembly view. +# Variables: +# $current (Number) - The current position (1-indexed). +# $total (Number) - The total count. +AssemblyView--position-label = { $current } από { $total } ## UploadedRecordingsHome ## This is the page that displays all the profiles that user has uploaded. From f21d506deee1621f8c7b25a2ce967eaa4ea95642 Mon Sep 17 00:00:00 2001 From: Pontoon Date: Thu, 29 Jan 2026 13:20:33 +0000 Subject: [PATCH 18/59] Pontoon/Firefox Profiler: Update German (de) Co-authored-by: Ger (de) --- locales/de/app.ftl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/de/app.ftl b/locales/de/app.ftl index 5fab5a6339..56a1bda639 100644 --- a/locales/de/app.ftl +++ b/locales/de/app.ftl @@ -87,7 +87,7 @@ CallNodeContextMenu--transform-focus-self-title = die zur Zeit der Funktion selbst beitragen. Samples in Angerufenen werden verworfen und der Aufrufbaum wird auf die Funktion im Fokus zurückgesetzt. CallNodeContextMenu--transform-focus-self = Nur auf sich selbst konzentrieren - .title = { "\u0009" }{ CallNodeContextMenu--transform-focus-self-title } + .title = { CallNodeContextMenu--transform-focus-self-title } ## From f22c1bc1fca646c991e523b095f0cf85d042046c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Naz=C4=B1m=20Can=20Alt=C4=B1nova?= Date: Thu, 29 Jan 2026 15:53:01 +0100 Subject: [PATCH 19/59] Remove the text indent as we don't need it --- src/components/tooltip/Tooltip.css | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/components/tooltip/Tooltip.css b/src/components/tooltip/Tooltip.css index a2bac05435..230085ffc2 100644 --- a/src/components/tooltip/Tooltip.css +++ b/src/components/tooltip/Tooltip.css @@ -190,7 +190,3 @@ .tooltipArguments > span:nth-child(odd) { background-color: rgb(0 0 0 / 0.05); } - -.tooltipArguments { - text-indent: 1em hanging; -} From b5ffb8a12d8dcca30710306051f78e5c519e8be9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Naz=C4=B1m=20Can=20Alt=C4=B1nova?= Date: Thu, 29 Jan 2026 15:53:01 +0100 Subject: [PATCH 20/59] Refactor how we put the JS tracing traced arguments to the call node tooltip --- src/components/tooltip/CallNode.tsx | 40 +++++++++++++---------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/src/components/tooltip/CallNode.tsx b/src/components/tooltip/CallNode.tsx index 35914662e2..220fa723f2 100644 --- a/src/components/tooltip/CallNode.tsx +++ b/src/components/tooltip/CallNode.tsx @@ -432,29 +432,25 @@ export class TooltipCallNode extends React.PureComponent { let argumentsElement = null; if (argumentValues) { - if (argumentValues.length === 0) { - argumentsElement = ( -
No arguments.
- ); - } else { - const argumentValuesEl = []; - for (const previewObject of argumentValues) { - argumentValuesEl.push( - Rep({ - object: previewObject, - mode: MODE.LONG, - }) - ); - } - argumentsElement = [ -
- Arguments: -
, + argumentsElement = [ +
+ Arguments: +
, + argumentValues.length === 0 ? ( +
+ ) : (
- {argumentValuesEl} -
, - ]; - } + {argumentValues.map((previewObject, index) => ( + + {Rep({ + object: previewObject, + mode: MODE.LONG, + })} + + ))} + + ), + ]; } // Finding current frame and parent frame URL(if there is). From d76de1f98e7b0bb2a2b6ff98e3f78cf1bf374a73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Naz=C4=B1m=20Can=20Alt=C4=B1nova?= Date: Thu, 29 Jan 2026 17:18:40 +0100 Subject: [PATCH 21/59] Add some some tests for tracer argument values in tooltips --- src/test/components/TooltipCallnode.test.tsx | 70 +++++++++- .../TooltipCallnode.test.tsx.snap | 129 ++++++++++++++++++ 2 files changed, 198 insertions(+), 1 deletion(-) diff --git a/src/test/components/TooltipCallnode.test.tsx b/src/test/components/TooltipCallnode.test.tsx index ee2853bbbc..86a4029112 100644 --- a/src/test/components/TooltipCallnode.test.tsx +++ b/src/test/components/TooltipCallnode.test.tsx @@ -4,7 +4,7 @@ import { Provider } from 'react-redux'; -import { render } from 'firefox-profiler/test/fixtures/testing-library'; +import { render, screen } from 'firefox-profiler/test/fixtures/testing-library'; import { TooltipCallNode } from '../../components/tooltip/CallNode'; import { storeWithProfile } from '../fixtures/stores'; import { @@ -165,4 +165,72 @@ describe('TooltipCallNode', function () { expect(displayedUrl).toHaveTextContent(`${pageUrl} (private)`); }); }); + + describe('with argument values', function () { + function setupWithArguments(argumentValues?: Array) { + const { + profile, + funcNamesDictPerThread: [{ A, Bjs, Cjs }], + } = getProfileFromTextSamples(` + A + Bjs + Cjs + `); + const threadIndex = 0; + const callNodePath = [A, Bjs, Cjs]; + + const store = storeWithProfile(profile); + const { getState, dispatch } = store; + dispatch(changeSelectedCallNode(threadIndex, callNodePath)); + + const callTree = selectedThreadSelectors.getCallTree(getState()); + const callNodeIndex = ensureExists( + selectedThreadSelectors.getSelectedCallNodeIndex(getState()), + 'Unable to find a selected call node index.' + ); + const displayData = callTree.getDisplayData(callNodeIndex); + + return render( + + + + ); + } + + it('does not display arguments when argumentValues is undefined', () => { + setupWithArguments(undefined); + expect(screen.queryByText('Arguments:')).not.toBeInTheDocument(); + }); + + it('displays em dash when argumentValues is empty', () => { + const { getByText, container } = setupWithArguments([]); + expect(getByText('Arguments:')).toBeInTheDocument(); + expect(getByText('—')).toBeInTheDocument(); + expect(container.firstChild).toMatchSnapshot(); + }); + + it('displays argument values when provided', () => { + const argumentValues = [42, 'hello']; + const { getByText, container } = setupWithArguments(argumentValues); + expect(getByText('Arguments:')).toBeInTheDocument(); + expect(container.firstChild).toMatchSnapshot(); + }); + }); }); diff --git a/src/test/components/__snapshots__/TooltipCallnode.test.tsx.snap b/src/test/components/__snapshots__/TooltipCallnode.test.tsx.snap index 357818669f..563415c2a6 100644 --- a/src/test/components/__snapshots__/TooltipCallnode.test.tsx.snap +++ b/src/test/components/__snapshots__/TooltipCallnode.test.tsx.snap @@ -71,6 +71,135 @@ exports[`TooltipCallNode handles native allocations 1`] = ` `; +exports[`TooltipCallNode with argument values displays argument values when provided 1`] = ` +
+
+
+ Fake Duration Text +
+
+ Cjs +
+
+
+
+
+
+ Stack Type: +
+
+ JavaScript +
+
+ Category: +
+
+ + JavaScript +
+
+ Arguments: +
+
+ + 42 + + + "hello" + +
+
+
+
+`; + +exports[`TooltipCallNode with argument values displays em dash when argumentValues is empty 1`] = ` +
+
+
+ Fake Duration Text +
+
+ Cjs +
+
+
+
+
+
+ Stack Type: +
+
+ JavaScript +
+
+ Category: +
+
+ + JavaScript +
+
+ Arguments: +
+
+ — +
+
+
+
+`; + exports[`TooltipCallNode with page information displays Page URL for iframe pages 1`] = `
Date: Fri, 30 Jan 2026 01:00:56 +0000 Subject: [PATCH 22/59] Pontoon/Firefox Profiler: Update Spanish (Chile) (es-CL) Co-authored-by: ravmn (es-CL) --- locales/es-CL/app.ftl | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/locales/es-CL/app.ftl b/locales/es-CL/app.ftl index 0355e3edbf..a0c79ab5d6 100644 --- a/locales/es-CL/app.ftl +++ b/locales/es-CL/app.ftl @@ -70,6 +70,15 @@ CallNodeContextMenu--transform-focus-function = Enfocarse en la función CallNodeContextMenu--transform-focus-function-inverted = Enfocarse en la función (invertido) .title = { CallNodeContextMenu--transform-focus-function-title } +## The translation for "self" in these strings should match the translation used +## in CallTree--samples-self and CallTree--bytes-self. Alternatively it can be +## translated as "self values" or "self time" (though "self time" is less desirable +## because this menu item is also shown in "bytes" mode). + +CallNodeContextMenu--transform-focus-self-title = Centrarse en sí misma es similar a centrarse en una función, pero solo conserva las muestras que contribuyen al tiempo propio de la función. Las muestras de las llamadas se descartan y el árbol de llamadas se redirige a la función enfocada. +CallNodeContextMenu--transform-focus-self = Centrarse sólo si misma + .title = { CallNodeContextMenu--transform-focus-self-title } + ## CallNodeContextMenu--transform-focus-subtree = Enfocarse solo en el subárbol @@ -964,6 +973,12 @@ TransformNavigator--focus-subtree = Nodo enfocado: { $item } # Variables: # $item (String) - Name of the function that transform applied to. TransformNavigator--focus-function = Enfocar: { $item } +# "Focus self" transform. +# See: https://profiler.firefox.com/docs/#/./guide-filtering-call-trees?id=focus-on-function-self +# Also see the translation note above CallNodeContextMenu--transform-focus-self. +# Variables: +# $item (String) - Name of the function that transform applied to. +TransformNavigator--focus-self = Centrarse en si misma: { $item } # "Focus category" transform. The word "Focus" has the meaning of an adjective here. # See: https://profiler.firefox.com/docs/#/./guide-filtering-call-trees?id=focus-category # Variables: From 8300a943c8fc96c00c53735028f83056a7795b97 Mon Sep 17 00:00:00 2001 From: Pontoon Date: Fri, 30 Jan 2026 07:50:31 +0000 Subject: [PATCH 23/59] Pontoon/Firefox Profiler: Update Interlingua (ia) Co-authored-by: Melo46 (ia) --- locales/ia/app.ftl | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/locales/ia/app.ftl b/locales/ia/app.ftl index b9c188e6dd..37dedb5de7 100644 --- a/locales/ia/app.ftl +++ b/locales/ia/app.ftl @@ -80,6 +80,17 @@ CallNodeContextMenu--transform-focus-function = Foco sur function. CallNodeContextMenu--transform-focus-function-inverted = Foco sur function (invertite). .title = { CallNodeContextMenu--transform-focus-function-title } +## The translation for "self" in these strings should match the translation used +## in CallTree--samples-self and CallTree--bytes-self. Alternatively it can be +## translated as "self values" or "self time" (though "self time" is less desirable +## because this menu item is also shown in "bytes" mode). + +CallNodeContextMenu--transform-focus-self-title = + Le foco proprie es simile al foco sur un function, ma solo es mantenite + specimens que contribue al tempore del mesme function. Le specimens + presente in le functiones appellate es ignorate e le arbore del appellos + es re-organisate con radice la function analysate. + ## CallNodeContextMenu--transform-focus-subtree = Foco solo sur sub-arbore. From 7b49303e5e6f9d940caee2b57bf0cc1b5c2b7688 Mon Sep 17 00:00:00 2001 From: Pontoon Date: Fri, 30 Jan 2026 11:40:34 +0000 Subject: [PATCH 24/59] Pontoon/Firefox Profiler: Update Interlingua (ia) Co-authored-by: Melo46 (ia) --- locales/ia/app.ftl | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/locales/ia/app.ftl b/locales/ia/app.ftl index 37dedb5de7..efb5610271 100644 --- a/locales/ia/app.ftl +++ b/locales/ia/app.ftl @@ -90,6 +90,8 @@ CallNodeContextMenu--transform-focus-self-title = specimens que contribue al tempore del mesme function. Le specimens presente in le functiones appellate es ignorate e le arbore del appellos es re-organisate con radice la function analysate. +CallNodeContextMenu--transform-focus-self = Foco solo proprie + .title = { CallNodeContextMenu--transform-focus-self-title } ## @@ -1031,6 +1033,12 @@ TransformNavigator--focus-subtree = Foco sur nodo: { $item } # Variables: # $item (String) - Name of the function that transform applied to. TransformNavigator--focus-function = Foco sur: { $item } +# "Focus self" transform. +# See: https://profiler.firefox.com/docs/#/./guide-filtering-call-trees?id=focus-on-function-self +# Also see the translation note above CallNodeContextMenu--transform-focus-self. +# Variables: +# $item (String) - Name of the function that transform applied to. +TransformNavigator--focus-self = Foco proprie: { $item } # "Focus category" transform. The word "Focus" has the meaning of an adjective here. # See: https://profiler.firefox.com/docs/#/./guide-filtering-call-trees?id=focus-category # Variables: From 23cab85eafc8e8966779a84841be52b848397a3f Mon Sep 17 00:00:00 2001 From: Markus Stange Date: Thu, 29 Jan 2026 18:16:40 -0500 Subject: [PATCH 25/59] Make the argument-values.json profile fixture go through profile upgrading. Otherwise we'll have to change it whenever we change the format. --- src/test/components/StackChart.test.tsx | 9 ++++++--- src/test/fixtures/stores.ts | 8 ++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/test/components/StackChart.test.tsx b/src/test/components/StackChart.test.tsx index b4968c2458..e8a2769361 100644 --- a/src/test/components/StackChart.test.tsx +++ b/src/test/components/StackChart.test.tsx @@ -63,6 +63,7 @@ import { import { autoMockElementSize } from '../fixtures/mocks/element-size'; import type { CssPixels, Store } from 'firefox-profiler/types'; +import { unserializeProfileOfArbitraryFormat } from 'firefox-profiler/profile-logic/process-profile'; jest.useFakeTimers(); @@ -315,9 +316,11 @@ describe('StackChart', function () { }); describe('ArgumentValues', () => { - it('shows argument values when a profile has them', () => { - const profile = require('../fixtures/upgrades/argument-values.json'); - const store = storeWithProfile(profile); + it('shows argument values when a profile has them', async () => { + const profiler = await unserializeProfileOfArbitraryFormat( + require('../fixtures/upgrades/argument-values.json') + ); + const store = storeWithProfile(profiler); const { getTooltip, moveMouse, findFillTextPosition } = setup(store); moveMouse(findFillTextPosition('bar')); diff --git a/src/test/fixtures/stores.ts b/src/test/fixtures/stores.ts index 17e39b86b4..1c225d03f7 100644 --- a/src/test/fixtures/stores.ts +++ b/src/test/fixtures/stores.ts @@ -8,6 +8,7 @@ import { processGeckoProfile } from '../../profile-logic/process-profile'; import { getProfileFromTextSamples } from './profiles/processed-profile'; import type { Store, Profile } from 'firefox-profiler/types'; +import { PROCESSED_PROFILE_VERSION } from 'firefox-profiler/app-logic/constants'; export function blankStore() { return createStore(); @@ -18,6 +19,13 @@ export function storeWithProfile(profile?: Profile): Store { profile = processGeckoProfile(createGeckoProfileWithJsTimings()); profile.meta.symbolicated = true; } + + if (profile.meta.preprocessedProfileVersion !== PROCESSED_PROFILE_VERSION) { + throw new Error( + `storeWithProfile called with something that's not a fully-uprgaded processed profile!` + ); + } + const store = createStore(); store.dispatch(viewProfile(profile)); return store; From 3717fbe3f37f37fd7142d1d3654f2b8b825ac046 Mon Sep 17 00:00:00 2001 From: Markus Stange Date: Thu, 8 Jan 2026 20:46:32 +0100 Subject: [PATCH 26/59] Add thread.usedInnerWindowIDs to the processed profile format. This is a prerequisite for sharing the frameTable across threads. --- docs-developer/CHANGELOG-formats.md | 6 +++ src/app-logic/constants.ts | 2 +- src/profile-logic/process-profile.ts | 27 ++++++++++ .../processed-profile-versioning.ts | 33 ++++++++++++ src/profile-logic/profile-data.ts | 50 ++++--------------- src/test/components/TabSelectorMenu.test.tsx | 10 ++++ src/test/components/TooltipCallnode.test.tsx | 6 ++- .../fixtures/profiles/processed-profile.ts | 6 +++ .../symbolicator-cli.test.ts.snap | 4 +- .../__snapshots__/profile-view.test.ts.snap | 8 ++- src/test/store/profile-view.test.ts | 1 + .../profile-conversion.test.ts.snap | 36 ++++++------- .../profile-upgrading.test.ts.snap | 17 +++++-- src/types/profile.ts | 4 ++ 14 files changed, 142 insertions(+), 68 deletions(-) diff --git a/docs-developer/CHANGELOG-formats.md b/docs-developer/CHANGELOG-formats.md index 21c7185a5a..551f33a9a9 100644 --- a/docs-developer/CHANGELOG-formats.md +++ b/docs-developer/CHANGELOG-formats.md @@ -6,6 +6,12 @@ Note that this is not an exhaustive list. Processed profile format upgraders can ## Processed profile format +### Version 59 + +A new optional `usedInnerWindowIDs` field was added to the `Thread` type. This field contains an array of inner window IDs. It is used for the tab selector dropdown in the profiler UI, together with the information from `profile.pages`. When a tab is selected in this dropdown, threads that don't have an inner window ID for the selected tab in their `usedInnerWindowIDs` field are hidden. The array is treated as a set - the order of items in it has no meaning. + +Profiles which don't use `profile.pages` also don't need to use the `thread.usedInnerWindowIDs` field. + ### Version 58 A new `SourceTable` has been added to `profile.shared.sources` to centralize all source file information. The `FuncTable.fileName` field has been replaced with `FuncTable.source`, which references indices in the shared sources table. This change allows storing a UUID per JS source, which will be used for fetching sources. diff --git a/src/app-logic/constants.ts b/src/app-logic/constants.ts index be047a25b2..4679913070 100644 --- a/src/app-logic/constants.ts +++ b/src/app-logic/constants.ts @@ -12,7 +12,7 @@ export const GECKO_PROFILE_VERSION = 32; // The current version of the "processed" profile format. // Please don't forget to update the processed profile format changelog in // `docs-developer/CHANGELOG-formats.md`. -export const PROCESSED_PROFILE_VERSION = 58; +export const PROCESSED_PROFILE_VERSION = 59; // The following are the margin sizes for the left and right of the timeline. Independent // components need to share these values. diff --git a/src/profile-logic/process-profile.ts b/src/profile-logic/process-profile.ts index 41e0070787..755c639813 100644 --- a/src/profile-logic/process-profile.ts +++ b/src/profile-logic/process-profile.ts @@ -1214,6 +1214,29 @@ function _processThread( ); const samples = _processSamples(geckoSamples); + // Compute usedInnerWindowIDs from the frameTable and the markers. + let usedInnerWindowIDs: number[] | undefined; + const usedInnerWindowIDsSet = new Set(); + for (let i = 0; i < frameTable.length; i++) { + const innerWindowID = frameTable.innerWindowID[i]; + if (innerWindowID !== null && innerWindowID !== 0) { + usedInnerWindowIDsSet.add(innerWindowID); + } + } + for (let i = 0; i < markers.length; i++) { + const data = markers.data[i]; + if (!data || !('innerWindowID' in data)) { + continue; + } + const innerWindowID = data.innerWindowID; + if (typeof innerWindowID === 'number' && innerWindowID !== 0) { + usedInnerWindowIDsSet.add(innerWindowID); + } + } + if (usedInnerWindowIDsSet.size !== 0) { + usedInnerWindowIDs = Array.from(usedInnerWindowIDsSet); + } + const newThread: RawThread = { name: thread.name, isMainThread: thread.name === 'GeckoMain', @@ -1249,6 +1272,10 @@ function _processThread( newThread.userContextId = thread.userContextId; } + if (usedInnerWindowIDs !== undefined) { + newThread.usedInnerWindowIDs = usedInnerWindowIDs; + } + if (jsAllocations) { // Only add the JS allocations if they exist. newThread.jsAllocations = jsAllocations; diff --git a/src/profile-logic/processed-profile-versioning.ts b/src/profile-logic/processed-profile-versioning.ts index 2448b486e7..8e341a4fab 100644 --- a/src/profile-logic/processed-profile-versioning.ts +++ b/src/profile-logic/processed-profile-versioning.ts @@ -2693,6 +2693,39 @@ const _upgraders: { } profile.shared.sources = sourceTable; }, + [59]: (profile) => { + // Add usedInnerWindowIDs to each thread by computing it from the frameTable and the markers. + // This changes the profile format from having innerWindowIDs implicitly + // defined by the frameTable / markers to having them explicitly listed in + // thread.usedInnerWindowIDs. + for (const thread of profile.threads) { + const { frameTable, markers } = thread; + const usedInnerWindowIDsSet = new Set(); + + // Collect all unique innerWindowIDs from the frameTable + for (let i = 0; i < frameTable.length; i++) { + const innerWindowID = frameTable.innerWindowID[i]; + if (innerWindowID !== null && innerWindowID !== 0) { + usedInnerWindowIDsSet.add(innerWindowID); + } + } + for (let i = 0; i < markers.length; i++) { + const data = markers.data[i]; + if (!data || !('innerWindowID' in data)) { + continue; + } + const innerWindowID = data.innerWindowID; + if (typeof innerWindowID === 'number' && innerWindowID !== 0) { + usedInnerWindowIDsSet.add(innerWindowID); + } + } + + // Convert the set to an array and store it on the thread, if non-empty + if (usedInnerWindowIDsSet.size !== 0) { + thread.usedInnerWindowIDs = Array.from(usedInnerWindowIDsSet); + } + } + }, // If you add a new upgrader here, please document the change in // `docs-developer/CHANGELOG-formats.md`. }; diff --git a/src/profile-logic/profile-data.ts b/src/profile-logic/profile-data.ts index 4f710f21b4..6e247fad55 100644 --- a/src/profile-logic/profile-data.ts +++ b/src/profile-logic/profile-data.ts @@ -4121,17 +4121,19 @@ export function computeTabToThreadIndexesMap( return tabToThreadIndexesMap; } - // We need to iterate over all the samples and markers once to figure out - // which innerWindowIDs are present in each thread. This is probably not - // very cheap, but it'll allow us to not compute this information every - // time when we need it. + // Iterate over the usedInnerWindowIDs for each thread to figure out + // which threads are involved for each tab. for (let threadIdx = 0; threadIdx < threads.length; threadIdx++) { const thread = threads[threadIdx]; + const { usedInnerWindowIDs } = thread; - // First go over the innerWindowIDs of the samples. - for (let i = 0; i < thread.frameTable.length; i++) { - const innerWindowID = thread.frameTable.innerWindowID[i]; - if (innerWindowID === null || innerWindowID === 0) { + if (!usedInnerWindowIDs) { + // No innerWindowIDs for this thread + continue; + } + + for (const innerWindowID of usedInnerWindowIDs) { + if (innerWindowID === 0) { // Zero value also means null for innerWindowID. continue; } @@ -4150,38 +4152,6 @@ export function computeTabToThreadIndexesMap( } threadIndexes.add(threadIdx); } - - // Then go over the markers to find their innerWindowIDs. - for (let i = 0; i < thread.markers.length; i++) { - const markerData = thread.markers.data[i]; - - if (!markerData) { - continue; - } - - if ( - 'innerWindowID' in markerData && - markerData.innerWindowID !== null && - markerData.innerWindowID !== undefined && - // Zero value also means null for innerWindowID. - markerData.innerWindowID !== 0 - ) { - const innerWindowID = markerData.innerWindowID; - const tabID = innerWindowIDToTabMap.get(innerWindowID); - if (tabID === undefined) { - // We couldn't find the tab of this innerWindowID, this should - // never happen, it might indicate a bug in Firefox. - continue; - } - - let threadIndexes = tabToThreadIndexesMap.get(tabID); - if (!threadIndexes) { - threadIndexes = new Set(); - tabToThreadIndexesMap.set(tabID, threadIndexes); - } - threadIndexes.add(threadIdx); - } - } } return tabToThreadIndexesMap; diff --git a/src/test/components/TabSelectorMenu.test.tsx b/src/test/components/TabSelectorMenu.test.tsx index b8689d3b41..3df42bdce7 100644 --- a/src/test/components/TabSelectorMenu.test.tsx +++ b/src/test/components/TabSelectorMenu.test.tsx @@ -40,6 +40,9 @@ describe('app/TabSelectorMenu', () => { profile.threads[0].frameTable.innerWindowID[0] = extraPageData.parentInnerWindowIDsWithChildren; profile.threads[0].frameTable.length++; + profile.threads[0].usedInnerWindowIDs = [ + extraPageData.parentInnerWindowIDsWithChildren, + ]; // Add a threadCPUDelta value for thread activity score. profile.threads[0].samples.threadCPUDelta = [1]; @@ -47,6 +50,9 @@ describe('app/TabSelectorMenu', () => { profile.threads[1].frameTable.innerWindowID[0] = extraPageData.secondTabInnerWindowIDs[0]; profile.threads[1].frameTable.length++; + profile.threads[1].usedInnerWindowIDs = [ + extraPageData.secondTabInnerWindowIDs[0], + ]; // Add a threadCPUDelta value for thread activity score. This thread // should stay above the first thread. profile.threads[0].samples.threadCPUDelta = [2]; @@ -203,6 +209,10 @@ describe('app/TabSelectorMenu', () => { profile.threads[0].frameTable.innerWindowID[1] = extraPageData.secondTabInnerWindowIDs[0]; profile.threads[0].frameTable.length++; + profile.threads[0].usedInnerWindowIDs = [ + extraPageData.parentInnerWindowIDsWithChildren, + extraPageData.secondTabInnerWindowIDs[0], + ]; const store = storeWithProfile(profile); render( diff --git a/src/test/components/TooltipCallnode.test.tsx b/src/test/components/TooltipCallnode.test.tsx index 86a4029112..ddf6f409b8 100644 --- a/src/test/components/TooltipCallnode.test.tsx +++ b/src/test/components/TooltipCallnode.test.tsx @@ -117,10 +117,12 @@ describe('TooltipCallNode', function () { const { frameTable } = profile.threads[threadIndex]; + const innerWindowID = + profile.pages[profile.pages.length - 1].innerWindowID; for (let i = 1; i < frameTable.length; i++) { - frameTable.innerWindowID[i] = - profile.pages[profile.pages.length - 1].innerWindowID; + frameTable.innerWindowID[i] = innerWindowID; } + profile.threads[threadIndex].usedInnerWindowIDs = [innerWindowID]; const callNodePath = [A, Bjs, Cjs]; const { dispatch, renderTooltip } = setup(profile); diff --git a/src/test/fixtures/profiles/processed-profile.ts b/src/test/fixtures/profiles/processed-profile.ts index b5054bc200..9d58ed0571 100644 --- a/src/test/fixtures/profiles/processed-profile.ts +++ b/src/test/fixtures/profiles/processed-profile.ts @@ -2046,8 +2046,10 @@ export function addInnerWindowIdToStacks( callNodesToDupe?: CallNodePath[] ) { const { stackTable, frameTable, samples } = thread; + const usedInnerWindowIDsSet = new Set(); for (const { innerWindowID, callNodes } of listOfOperations) { + usedInnerWindowIDsSet.add(innerWindowID); for (const callNode of callNodes) { const stackIndex = getStackIndexForCallNodePath(thread, callNode); const foundFrameIndex = stackTable.frame[stackIndex]; @@ -2132,6 +2134,10 @@ export function addInnerWindowIdToStacks( samples.length++; } } + + if (usedInnerWindowIDsSet.size !== 0) { + thread.usedInnerWindowIDs = Array.from(usedInnerWindowIDsSet); + } } /** diff --git a/src/test/integration/symbolicator-cli/__snapshots__/symbolicator-cli.test.ts.snap b/src/test/integration/symbolicator-cli/__snapshots__/symbolicator-cli.test.ts.snap index 2e547d427a..afca09a6e0 100644 --- a/src/test/integration/symbolicator-cli/__snapshots__/symbolicator-cli.test.ts.snap +++ b/src/test/integration/symbolicator-cli/__snapshots__/symbolicator-cli.test.ts.snap @@ -87,7 +87,7 @@ Object { "markerSchema": Array [], "oscpu": "macOS 14.6.1", "pausedRanges": Array [], - "preprocessedProfileVersion": 58, + "preprocessedProfileVersion": 59, "processType": 0, "product": "a.out", "sampleUnits": Object { @@ -2108,7 +2108,7 @@ Object { "markerSchema": Array [], "oscpu": "macOS 14.6.1", "pausedRanges": Array [], - "preprocessedProfileVersion": 58, + "preprocessedProfileVersion": 59, "processType": 0, "product": "a.out", "sampleUnits": Object { diff --git a/src/test/store/__snapshots__/profile-view.test.ts.snap b/src/test/store/__snapshots__/profile-view.test.ts.snap index 974ee20b96..585ec17655 100644 --- a/src/test/store/__snapshots__/profile-view.test.ts.snap +++ b/src/test/store/__snapshots__/profile-view.test.ts.snap @@ -418,7 +418,7 @@ Object { "oscpu": "", "physicalCPUs": 0, "platform": "", - "preprocessedProfileVersion": 58, + "preprocessedProfileVersion": 59, "processType": 0, "product": "Firefox", "sourceURL": "", @@ -880,6 +880,9 @@ Object { }, "tid": 0, "unregisterTime": null, + "usedInnerWindowIDs": Array [ + 2, + ], }, Object { "frameTable": Object { @@ -1492,6 +1495,9 @@ Array [ }, "tid": 0, "unregisterTime": null, + "usedInnerWindowIDs": Array [ + 2, + ], }, Object { "frameTable": Object { diff --git a/src/test/store/profile-view.test.ts b/src/test/store/profile-view.test.ts index e69d5d8acc..9c52905225 100644 --- a/src/test/store/profile-view.test.ts +++ b/src/test/store/profile-view.test.ts @@ -1979,6 +1979,7 @@ describe('snapshots of selectors/profile', function () { samplesThread.frameTable.innerWindowID[frameIdx] = innerWindowID; } } + samplesThread.usedInnerWindowIDs = [innerWindowID]; // Add in a thread with markers const markersThread = getThreadWithMarkers(profile.shared, [ diff --git a/src/test/unit/__snapshots__/profile-conversion.test.ts.snap b/src/test/unit/__snapshots__/profile-conversion.test.ts.snap index 4ed44b5784..d0b166b1e9 100644 --- a/src/test/unit/__snapshots__/profile-conversion.test.ts.snap +++ b/src/test/unit/__snapshots__/profile-conversion.test.ts.snap @@ -591,7 +591,7 @@ Object { "oscpu": undefined, "physicalCPUs": undefined, "platform": undefined, - "preprocessedProfileVersion": 58, + "preprocessedProfileVersion": 59, "processType": 0, "product": "ART Trace (Android)", "sampleUnits": undefined, @@ -85564,7 +85564,7 @@ Object { "oscpu": undefined, "physicalCPUs": undefined, "platform": undefined, - "preprocessedProfileVersion": 58, + "preprocessedProfileVersion": 59, "processType": 0, "product": "ART Trace (Android)", "sampleUnits": undefined, @@ -334698,7 +334698,7 @@ Object { "oscpu": "", "physicalCPUs": 0, "platform": "", - "preprocessedProfileVersion": 58, + "preprocessedProfileVersion": 59, "processType": 0, "product": "Chrome Trace", "profilingEndTime": 119159778.026, @@ -370180,7 +370180,7 @@ Object { "oscpu": "", "physicalCPUs": 0, "platform": "", - "preprocessedProfileVersion": 58, + "preprocessedProfileVersion": 59, "processType": 0, "product": "Chrome Trace", "profilingEndTime": 119159778.026, @@ -405641,7 +405641,7 @@ Object { "oscpu": "", "physicalCPUs": 0, "platform": "", - "preprocessedProfileVersion": 58, + "preprocessedProfileVersion": 59, "processType": 0, "product": "Chrome Trace", "profilingEndTime": 66155012.423, @@ -408272,7 +408272,7 @@ Object { "oscpu": "", "physicalCPUs": 0, "platform": "", - "preprocessedProfileVersion": 58, + "preprocessedProfileVersion": 59, "processType": 0, "product": "Chrome Trace", "sourceURL": "", @@ -410631,7 +410631,7 @@ Object { "oscpu": "", "physicalCPUs": 0, "platform": "", - "preprocessedProfileVersion": 58, + "preprocessedProfileVersion": 59, "processType": 0, "product": "Chrome Trace", "profilingEndTime": 355035987.653, @@ -414163,7 +414163,7 @@ Object { "oscpu": "", "physicalCPUs": 0, "platform": "", - "preprocessedProfileVersion": 58, + "preprocessedProfileVersion": 59, "processType": 0, "product": "Chrome Trace", "sourceURL": "", @@ -419426,7 +419426,7 @@ Object { "oscpu": "", "physicalCPUs": 0, "platform": "", - "preprocessedProfileVersion": 58, + "preprocessedProfileVersion": 59, "processType": 0, "product": "Chrome Trace", "profilingEndTime": 66155012.423, @@ -420453,7 +420453,7 @@ Object { "oscpu": undefined, "physicalCPUs": undefined, "platform": undefined, - "preprocessedProfileVersion": 58, + "preprocessedProfileVersion": 59, "processType": 0, "product": "Firefox", "sampleUnits": undefined, @@ -426392,7 +426392,7 @@ Object { "oscpu": undefined, "physicalCPUs": undefined, "platform": undefined, - "preprocessedProfileVersion": 58, + "preprocessedProfileVersion": 59, "processType": 0, "product": "Firefox", "sampleUnits": undefined, @@ -441287,7 +441287,7 @@ Object { "oscpu": undefined, "physicalCPUs": undefined, "platform": undefined, - "preprocessedProfileVersion": 58, + "preprocessedProfileVersion": 59, "processType": 0, "product": "Firefox", "sampleUnits": undefined, @@ -447491,7 +447491,7 @@ Object { "oscpu": undefined, "physicalCPUs": undefined, "platform": undefined, - "preprocessedProfileVersion": 58, + "preprocessedProfileVersion": 59, "processType": 0, "product": "Firefox", "sampleUnits": undefined, @@ -465899,7 +465899,7 @@ Object { "keepProfileThreadOrder": true, "markerSchema": Array [], "platform": "Android", - "preprocessedProfileVersion": 58, + "preprocessedProfileVersion": 59, "processType": 0, "product": "com.example.sampleapplication", "sourceCodeIsNotOnSearchfox": true, @@ -534973,7 +534973,7 @@ Object { "keepProfileThreadOrder": true, "markerSchema": Array [], "platform": "Android", - "preprocessedProfileVersion": 58, + "preprocessedProfileVersion": 59, "processType": 0, "product": "com.example.sampleapplication", "sourceCodeIsNotOnSearchfox": true, @@ -606870,7 +606870,7 @@ Object { "oscpu": "", "physicalCPUs": 0, "platform": "", - "preprocessedProfileVersion": 58, + "preprocessedProfileVersion": 59, "processType": 0, "product": "target/debug/examples/work_log (dhat)", "sourceURL": "", @@ -630465,7 +630465,7 @@ Object { "oscpu": "", "physicalCPUs": 0, "platform": "", - "preprocessedProfileVersion": 58, + "preprocessedProfileVersion": 59, "processType": 0, "product": "Flamegraph", "sourceURL": "", @@ -938348,7 +938348,7 @@ Object { "oscpu": "", "physicalCPUs": 0, "platform": "", - "preprocessedProfileVersion": 58, + "preprocessedProfileVersion": 59, "processType": 0, "product": "Flamegraph", "sourceURL": "", diff --git a/src/test/unit/__snapshots__/profile-upgrading.test.ts.snap b/src/test/unit/__snapshots__/profile-upgrading.test.ts.snap index 03f2801ef5..b45e81d8d4 100644 --- a/src/test/unit/__snapshots__/profile-upgrading.test.ts.snap +++ b/src/test/unit/__snapshots__/profile-upgrading.test.ts.snap @@ -40,7 +40,7 @@ Object { "oscpu": undefined, "physicalCPUs": undefined, "platform": undefined, - "preprocessedProfileVersion": 58, + "preprocessedProfileVersion": 59, "processType": 0, "product": "Firefox", "sampleUnits": undefined, @@ -9382,7 +9382,7 @@ Object { "misc": "rv:48.0", "oscpu": "Intel Mac OS X 10.11", "platform": "Macintosh", - "preprocessedProfileVersion": 58, + "preprocessedProfileVersion": 59, "processType": 0, "product": "Firefox", "stackwalk": 1, @@ -10924,7 +10924,7 @@ Object { "misc": "rv:48.0", "oscpu": "Intel Mac OS X 10.11", "platform": "Macintosh", - "preprocessedProfileVersion": 58, + "preprocessedProfileVersion": 59, "processType": 0, "product": "Firefox", "stackwalk": 1, @@ -12599,7 +12599,7 @@ Object { "misc": "rv:48.0", "oscpu": "Intel Mac OS X 10.11", "platform": "Macintosh", - "preprocessedProfileVersion": 58, + "preprocessedProfileVersion": 59, "processType": 0, "product": "Firefox", "stackwalk": 1, @@ -13057,6 +13057,9 @@ Object { ], }, "unregisterTime": null, + "usedInnerWindowIDs": Array [ + 3, + ], }, Object { "frameTable": Object { @@ -13417,6 +13420,9 @@ Object { ], }, "unregisterTime": null, + "usedInnerWindowIDs": Array [ + 1, + ], }, Object { "frameTable": Object { @@ -13844,6 +13850,9 @@ Object { ], }, "unregisterTime": null, + "usedInnerWindowIDs": Array [ + 2, + ], }, ], } diff --git a/src/types/profile.ts b/src/types/profile.ts index d463ca724d..be184c51ac 100644 --- a/src/types/profile.ts +++ b/src/types/profile.ts @@ -675,6 +675,10 @@ export type RawThread = { userContextId?: number; tracedValuesBuffer?: string; tracedObjectShapes?: Array; + // If present, contains the list of innerWindowIDs for pages that this thread is + // related to (or, in practice, whose code may be executing in this thread). + // It's absent in profiles that don't use inner window IDs. + usedInnerWindowIDs?: InnerWindowID[]; }; export type ExtensionTable = { From 1e386d26b14f8e3f98dfa98edf6ab91dc2e498d7 Mon Sep 17 00:00:00 2001 From: fatadel Date: Fri, 30 Jan 2026 19:47:41 +0100 Subject: [PATCH 27/59] Fix context menu and hover preview z-index (#5797) * fix hover preview appearing above context menu * consolidate z-index --- res/css/global.css | 31 +++++++++++++++++++ src/components/app/BottomBox.css | 2 +- src/components/app/KeyboardShortcut.css | 1 + src/components/app/ProfileViewer.css | 8 ++--- src/components/app/ProfileViewer.tsx | 2 +- src/components/app/Root.css | 2 +- src/components/app/ServiceWorkerManager.css | 2 +- src/components/app/TabBar.css | 2 +- .../shared/ButtonWithPanel/ArrowPanel.css | 2 +- src/components/shared/ContextMenu.css | 2 +- src/components/shared/MarkerSettings.css | 2 +- src/components/shared/PanelSearch.css | 7 +++-- src/components/shared/TreeView.css | 4 +-- src/components/shared/Warning.css | 2 +- src/components/shared/chart/Viewport.css | 2 +- .../timeline/OverflowEdgeIndicator.css | 2 +- src/components/timeline/Selection.css | 8 ++--- src/components/timeline/Track.css | 4 +-- src/components/timeline/TrackScreenshots.css | 2 +- src/components/timeline/TrackThread.css | 2 +- src/components/timeline/index.css | 5 ++- 21 files changed, 63 insertions(+), 31 deletions(-) diff --git a/res/css/global.css b/res/css/global.css index 80e3ccb15d..8ac01e02d1 100644 --- a/res/css/global.css +++ b/res/css/global.css @@ -3,6 +3,37 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ :root { + /* ===================== + * Z-index scale + * ===================== */ + --z-tab-selected: 1; + --z-warning: 4; + --z-root: 0; + --z-bottom-box: 1; + --z-serviceworker-notice: 4; + --z-arrow-panel: 5; + --z-copy-table-warning: 4; + --z-tree-view: 0; + --z-tree-view-inner: 2; + --z-overflow-edge-indicator: 3; + --z-chart-viewport-expanded: 1; + --z-timeline-selection: 1; + --z-timeline-selection-overlay: 2; + --z-timeline-selection-overlay-time: 1; + --z-timeline-selection-grippy: 3; + --z-timeline-track-local: 3; + --z-timeline-track-thread: 1; + --z-timeline-track-screenshot: 4; /* should be higher than any other timeline element */ + --z-keyboard-shortcuts: 11; /* should be highest possible value */ + --z-context-menu: 5; /* should be higher than --z-overflow-edge-indicator and --z-timeline-track-screenshot */ + --z-panel-search: 1; + --z-panel-search-introduction: -1; + --z-timeline-header: 1; + --z-tracks-container: 0; /* should be below --z-timeline-header */ + + /* ===================== + * Colors & theming + * ===================== */ --base-foreground-color: #000; --base-background-color: #fff; --base-border-color: var(--grey-30); diff --git a/src/components/app/BottomBox.css b/src/components/app/BottomBox.css index d337882d52..dc80d0345b 100644 --- a/src/components/app/BottomBox.css +++ b/src/components/app/BottomBox.css @@ -33,7 +33,7 @@ /* Provide 3px extra grabbable surface on each side of the splitter */ .bottom-box .layout-splitter::before { position: absolute; - z-index: 1; + z-index: var(--z-bottom-box); display: block; content: ''; inset: 0 -3px; diff --git a/src/components/app/KeyboardShortcut.css b/src/components/app/KeyboardShortcut.css index 3898f85c5a..8b9ba8fab2 100644 --- a/src/components/app/KeyboardShortcut.css +++ b/src/components/app/KeyboardShortcut.css @@ -8,6 +8,7 @@ --internal-shadow-color: rgb(0 0 0 / 0.4); position: absolute; + z-index: var(--z-keyboard-shortcuts); top: 0; left: 0; display: none; diff --git a/src/components/app/ProfileViewer.css b/src/components/app/ProfileViewer.css index 36b349703b..1fdf52b78f 100644 --- a/src/components/app/ProfileViewer.css +++ b/src/components/app/ProfileViewer.css @@ -110,11 +110,11 @@ } .profileViewerSplitter { - /* Create a stacking context, so that the KeyboardShortcut can overlay the profile - viewer. In addition, the built-in class uses position: absolute, which is not - appropriate here. */ + /* Position relative for layout. Don't set z-index here to avoid creating a + stacking context that would trap the context menu below the screenshot hover. + This allows both the screenshot hover and context menu to be compared + at the .profileViewer level, ensuring the context menu appears on top. */ position: relative; - z-index: 0; } .profileViewerSplitter > .layout-pane:not(.layout-pane-primary) { diff --git a/src/components/app/ProfileViewer.tsx b/src/components/app/ProfileViewer.tsx index dcfeafcb9c..e00a87117d 100644 --- a/src/components/app/ProfileViewer.tsx +++ b/src/components/app/ProfileViewer.tsx @@ -103,7 +103,6 @@ class ProfileViewerImpl extends PureComponent { } as React.CSSProperties) } > -
{hasZipFile ? ( + + ); + } + + override render() { + return ( +
+ {this._renderIconButton( + 'light', + 'ThemeToggle--light', + , + this._handleLightClick + )} + {this._renderIconButton( + 'system', + 'ThemeToggle--system', + , + this._handleSystemClick + )} + {this._renderIconButton( + 'dark', + 'ThemeToggle--dark', + , + this._handleDarkClick + )} +
+ ); + } +} + +export { ThemeToggle }; diff --git a/src/test/components/__snapshots__/CompareHome.test.tsx.snap b/src/test/components/__snapshots__/CompareHome.test.tsx.snap index b2f257169e..fb4484b036 100644 --- a/src/test/components/__snapshots__/CompareHome.test.tsx.snap +++ b/src/test/components/__snapshots__/CompareHome.test.tsx.snap @@ -24,27 +24,65 @@ exports[`app/CompareHome matches the snapshot 1`] = ` Web app for ⁨Firefox⁩ performance analysis - - - - - + + + +
+ + + + + +

- - - - - +
+ + + +
+ + + + + +

- - - - - +
+ + + +
+ + + + + +
- - - - - +
+ + + +
+ + + + + +
- - - - - + + + +
+ + + + + +

- - - - - + + + + + + + + + +

Date: Wed, 28 Jan 2026 12:22:27 +0100 Subject: [PATCH 56/59] Change the theme when the user color scheme preference changes --- src/index.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/index.tsx b/src/index.tsx index f242c80306..30658b2910 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -94,7 +94,11 @@ if (svgFiltersElement) { const forcedColorsMql = window.matchMedia('(forced-colors: active)'); const darkSchemeMql = window.matchMedia('(prefers-color-scheme: dark)'); forcedColorsMql.addEventListener('change', defineSvgFiltersForColors); - darkSchemeMql.addEventListener('change', defineSvgFiltersForColors); + darkSchemeMql.addEventListener('change', () => { + defineSvgFiltersForColors(); + // Re-apply theme when system preference changes (for 'system' mode users) + initTheme(); + }); } const store = createStore(); From 5445a1830435e30ee5f56caed5b8f1cb3df9b17a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Naz=C4=B1m=20Can=20Alt=C4=B1nova?= Date: Wed, 28 Jan 2026 12:22:27 +0100 Subject: [PATCH 57/59] Update the toggleDarkMode function to cycle through 3 of them --- .../__snapshots__/window-console.test.ts.snap | 2 +- src/utils/window-console.ts | 32 +++++++++++-------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/test/unit/__snapshots__/window-console.test.ts.snap b/src/test/unit/__snapshots__/window-console.test.ts.snap index 1bfd7bad1c..d55758c4da 100644 --- a/src/test/unit/__snapshots__/window-console.test.ts.snap +++ b/src/test/unit/__snapshots__/window-console.test.ts.snap @@ -36,7 +36,7 @@ Array [ %cwindow.experimental%c - The object that holds flags of all the experimental features. %cwindow.togglePseudoLocalization%c - Enable pseudo localizations by passing \\"accented\\" or \\"bidi\\" to this function, or disable using no parameters. %cwindow.toggleTimelineType%c - Toggle timeline graph type by passing \\"cpu-category\\", \\"category\\", or \\"stack\\". -%cwindow.toggleDarkMode%c - Toggle between dark mode and light mode. +%cwindow.toggleDarkMode%c - Cycle through theme preferences: system, light, dark. %cwindow.retrieveRawProfileDataFromBrowser%c - Retrieve the profile attached to the current tab and returns it. Use \\"await\\" to call it, and use saveToDisk to save it. %cwindow.extractGeckoLogs%c - Retrieve recorded logs in the current range, using the MOZ_LOG format. Use with \\"copy\\" or \\"saveToDisk\\". %cwindow.saveToDisk%c - Saves to a file the parameter passed to it, with an optional filename parameter. You can use that to save the profile returned by \\"retrieveRawProfileDataFromBrowser\\" or the data returned by \\"extractGeckoLogs\\". diff --git a/src/utils/window-console.ts b/src/utils/window-console.ts index e9f77877a7..12ad179482 100644 --- a/src/utils/window-console.ts +++ b/src/utils/window-console.ts @@ -16,10 +16,10 @@ import { shortenUrl } from 'firefox-profiler/utils/shorten-url'; import { createBrowserConnection } from 'firefox-profiler/app-logic/browser-connection'; import { formatTimestamp } from 'firefox-profiler/utils/format-numbers'; import { togglePseudoStrategy } from 'firefox-profiler/components/app/AppLocalizationProvider'; +import type { ThemePreference } from 'firefox-profiler/utils/dark-mode'; import { - isDarkMode, - setDarkMode, - setLightMode, + getThemePreference, + setThemePreference, } from 'firefox-profiler/utils/dark-mode'; import type { CallTree } from 'firefox-profiler/profile-logic/call-tree'; @@ -200,17 +200,23 @@ export function addDataToWindowObject( }; target.toggleDarkMode = function () { - if (isDarkMode()) { - setLightMode(); - console.log(stripIndent` - ✅ Light mode is now enabled. - `); + const current = getThemePreference(); + let next: ThemePreference; + let message: string; + + if (current === 'system') { + next = 'light'; + message = '✅ Theme set to: light'; + } else if (current === 'light') { + next = 'dark'; + message = '✅ Theme set to: dark'; } else { - setDarkMode(); - console.log(stripIndent` - ✅ Dark mode is now enabled. - `); + next = 'system'; + message = '✅ Theme set to: system (follows OS preference)'; } + + setThemePreference(next); + console.log(message); }; target.retrieveRawProfileDataFromBrowser = async function (): Promise< @@ -412,7 +418,7 @@ export function logFriendlyPreamble() { %cwindow.experimental%c - The object that holds flags of all the experimental features. %cwindow.togglePseudoLocalization%c - Enable pseudo localizations by passing "accented" or "bidi" to this function, or disable using no parameters. %cwindow.toggleTimelineType%c - Toggle timeline graph type by passing "cpu-category", "category", or "stack". - %cwindow.toggleDarkMode%c - Toggle between dark mode and light mode. + %cwindow.toggleDarkMode%c - Cycle through theme preferences: system, light, dark. %cwindow.retrieveRawProfileDataFromBrowser%c - Retrieve the profile attached to the current tab and returns it. Use "await" to call it, and use saveToDisk to save it. %cwindow.extractGeckoLogs%c - Retrieve recorded logs in the current range, using the MOZ_LOG format. Use with "copy" or "saveToDisk". %cwindow.saveToDisk%c - Saves to a file the parameter passed to it, with an optional filename parameter. You can use that to save the profile returned by "retrieveRawProfileDataFromBrowser" or the data returned by "extractGeckoLogs". From eb4840f37877c500f5a8d6c9d63bab5a35f7d79b Mon Sep 17 00:00:00 2001 From: Pontoon Date: Wed, 4 Feb 2026 15:10:31 +0000 Subject: [PATCH 58/59] Pontoon/Firefox Profiler: Update German (de) Co-authored-by: Ger (de) --- locales/de/app.ftl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/locales/de/app.ftl b/locales/de/app.ftl index df1a92d699..6c360285f7 100644 --- a/locales/de/app.ftl +++ b/locales/de/app.ftl @@ -561,6 +561,8 @@ MenuButtons--metaInfo--buffer-duration-seconds = MenuButtons--metaInfo--buffer-duration-unlimited = Unbegrenzt MenuButtons--metaInfo--application = Anwendung MenuButtons--metaInfo--name-and-version = Name und Version: +# The time between application startup and when the profiler was started +MenuButtons--metaInfo--application-uptime2 = Verfügbarkeit: MenuButtons--metaInfo--update-channel = Update-Kanal: MenuButtons--metaInfo--build-id = Build-ID: MenuButtons--metaInfo--build-type = Build-Typ: From 3a2161af1b539a560a3a4a1a57451b5543702969 Mon Sep 17 00:00:00 2001 From: Pontoon Date: Wed, 4 Feb 2026 16:45:09 +0000 Subject: [PATCH 59/59] Pontoon/Firefox Profiler: Update German (de) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Nazım Can Altınova (de) --- locales/de/app.ftl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/de/app.ftl b/locales/de/app.ftl index 6c360285f7..62e72d124f 100644 --- a/locales/de/app.ftl +++ b/locales/de/app.ftl @@ -562,7 +562,7 @@ MenuButtons--metaInfo--buffer-duration-unlimited = Unbegrenzt MenuButtons--metaInfo--application = Anwendung MenuButtons--metaInfo--name-and-version = Name und Version: # The time between application startup and when the profiler was started -MenuButtons--metaInfo--application-uptime2 = Verfügbarkeit: +MenuButtons--metaInfo--application-uptime2 = Uptime: MenuButtons--metaInfo--update-channel = Update-Kanal: MenuButtons--metaInfo--build-id = Build-ID: MenuButtons--metaInfo--build-type = Build-Typ: