@paulirish/trace_engine 0.0.58 → 0.0.59

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/.tmp/tsbuildinfo/tsconfig.tsbuildinfo +1 -1
  2. package/generated/protocol.d.ts +38 -0
  3. package/models/trace/LanternComputationData.js +1 -0
  4. package/models/trace/LanternComputationData.js.map +1 -1
  5. package/models/trace/handlers/ExtensionTraceDataHandler.js +2 -25
  6. package/models/trace/handlers/ExtensionTraceDataHandler.js.map +1 -1
  7. package/models/trace/handlers/LayoutShiftsHandler.js +6 -2
  8. package/models/trace/handlers/LayoutShiftsHandler.js.map +1 -1
  9. package/models/trace/handlers/MetaHandler.d.ts +6 -1
  10. package/models/trace/handlers/MetaHandler.js +1 -1
  11. package/models/trace/handlers/MetaHandler.js.map +1 -1
  12. package/models/trace/handlers/UserTimingsHandler.d.ts +21 -0
  13. package/models/trace/handlers/UserTimingsHandler.js +52 -23
  14. package/models/trace/handlers/UserTimingsHandler.js.map +1 -1
  15. package/models/trace/helpers/Trace.d.ts +3 -4
  16. package/models/trace/helpers/Trace.js +40 -7
  17. package/models/trace/helpers/Trace.js.map +1 -1
  18. package/models/trace/insights/Common.d.ts +1 -0
  19. package/models/trace/insights/Common.js +17 -0
  20. package/models/trace/insights/Common.js.map +1 -1
  21. package/models/trace/insights/LCPBreakdown.js +4 -8
  22. package/models/trace/insights/LCPBreakdown.js.map +1 -1
  23. package/models/trace/insights/LCPDiscovery.js +3 -5
  24. package/models/trace/insights/LCPDiscovery.js.map +1 -1
  25. package/models/trace/types/File.d.ts +1 -2
  26. package/models/trace/types/File.js.map +1 -1
  27. package/models/trace/types/Overlays.d.ts +4 -2
  28. package/models/trace/types/Overlays.js.map +1 -1
  29. package/models/trace/types/TraceEvents.d.ts +6 -5
  30. package/models/trace/types/TraceEvents.js.map +1 -1
  31. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"Common.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/insights/Common.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,yEAAyE;AACzE,6BAA6B;AAK7B,OAAO,KAAK,OAAO,MAAM,uBAAuB,CAAC;AAEjD,OAAO,KAAK,KAAK,MAAM,mBAAmB,CAAC;AAE3C,OAAO,EAAC,iBAAiB,EAAC,MAAM,iBAAiB,CAAC;AAClD,OAAO,EACL,WAAW,GAKZ,MAAM,YAAY,CAAC;AAEpB,MAAM,uBAAuB,GAAG,EAAE,CAAC;AAEnC,MAAM,UAAU,UAAU,CACtB,WAAwB,EAAE,UAAsB;IAClD,MAAM,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAC9C,IAAI,OAAO,YAAY,KAAK,EAAE,CAAC;QAC7B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,yFAAyF;IACzF,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,UAAsB;IAE3C,MAAM,OAAO,GAAG,UAAU,CAAC,WAAW,CAAC,aAAa,EAAE,UAAU,CAAC,CAAC;IAClE,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;QACpD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACzD,OAAO,EAAC,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,QAAQ,EAAC,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,UAAsB;IAE3C,MAAM,OAAO,GAAG,UAAU,CAAC,WAAW,CAAC,aAAa,EAAE,UAAU,CAAC,CAAC;IAClE,IAAI,CAAC,OAAO,EAAE,uBAAuB,EAAE,GAAG,EAAE,CAAC;QAC3C,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,KAAK,GAAG,OAAO,CAAC,uBAAuB,CAAC,GAAG,CAAC;IAClD,OAAO,EAAC,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,uBAAuB,EAAC,CAAC;AACzD,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,UAAsB;IAC3C,MAAM,OAAO,GAAG,UAAU,CAAC,WAAW,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;IACjE,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,+EAA+E;QAC/E,OAAO,EAAC,KAAK,EAAE,CAAC,EAAE,iBAAiB,EAAE,IAAI,EAAC,CAAC;IAC7C,CAAC;IAED,6DAA6D;IAC7D,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,IAAI,YAAY,CAAC;IACjB,KAAK,MAAM,OAAO,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QACvC,IAAI,OAAO,CAAC,sBAAsB,GAAG,QAAQ,EAAE,CAAC;YAC9C,QAAQ,GAAG,OAAO,CAAC,sBAAsB,CAAC;YAC1C,YAAY,GAAG,OAAO,CAAC;QACzB,CAAC;IACH,CAAC;IAED,OAAO,EAAC,KAAK,EAAE,QAAQ,EAAE,iBAAiB,EAAE,YAAY,IAAI,IAAI,EAAC,CAAC;AACpE,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,KAAyB;IAC9D,OAAO,iBAAiB,CAAC,EAAC,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAC,EAAE,KAAK,CAAC,CAAC;AAC7D,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,KAAyB;IAC9D,OAAO,iBAAiB,CAAC,EAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAC,EAAE,KAAK,CAAC,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,KAAa;IAClD,OAAO,iBAAiB,CAAC,EAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAC,EAAE,KAAK,CAAC,CAAC;AAC5D,CAAC;AAuBD,SAAS,aAAa,CAClB,aAAuC,EAAE,GAAW,EAAE,MAAc,EACpE,QAAgC,IAAI;IACtC,OAAO,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE;QACjC,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,WAAW,EAAE,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;YAC/D,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,MAAM,CAAC,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC;QAC5E,OAAO,CAAC,GAAG,EAAE,GAAG,IAAI,GAAG,CAAC,GAAG,KAAK,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC;IACjF,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,eAAe,CACpB,UAAkC,EAAE,IAAqC,EACzE,QAAgC,IAAI;IACtC,MAAM,MAAM,GAAoF,EAAE,CAAC;IACnG,IAAI,KAAK,EAAE,CAAC;QACV,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACrB,CAAC;SAAM,CAAC;QACN,MAAM,CAAC,IAAI,CAAC,EAAC,SAAS,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAC,CAAC,CAAC;QACpD,MAAM,CAAC,IAAI,CAAC,EAAC,SAAS,EAAE,QAAQ,EAAE,WAAW,EAAE,KAAK,EAAC,CAAC,CAAC;IACzD,CAAC;IAED,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,GAAG,GAAG,GAAG,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,WAAW,EAAW,CAAC;QAC/D,IAAI,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,WAAW,EAAE,GAAG,CAAC;QACpE,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC9B,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QACxB,CAAC;QACD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YACxD,OAAO,EAAC,KAAK,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAC,CAAC;QAC7C,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,qBAAqB,CAC1B,UAAkC,EAAE,IAAqC,EACzE,QAAgC,IAAI;IACtC,MAAM,MAAM,GAAG,eAAe,CAAC,UAAU,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;IACxD,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,OAAO,GAAG,MAAM,CAAC,KAA2B,CAAC;QACnD,OAAO,EAAC,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAC,CAAC;IACpF,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,4BAA4B,CACxC,UAAsB,EAAE,QAAkC,EAC1D,QAAgC,IAAI;IACtC,MAAM,aAAa,GAAG,QAAQ,EAAE,aAAa,CAAC;IAC9C,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,UAAU,GAAG,aAAa,CAAC,aAAa,EAAE,UAAU,CAAC,GAAG,CAAC,IAAI,EAAE,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACnG,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO;QACL,GAAG,EAAE,qBAAqB,CAAC,UAAU,EAAE,wBAAwB,EAAE,KAAK,CAAC;QACvE,GAAG,EAAE,qBAAqB,CAAC,UAAU,EAAE,0BAA0B,EAAE,KAAK,CAAC;QACzE,GAAG,EAAE,qBAAqB,CAAC,UAAU,EAAE,2BAA2B,EAAE,KAAK,CAAC;QAC1E,GAAG,EAAE,eAAe,CAAC,UAAU,EAAE,yBAAyB,EAAE,KAAK,CAAC;QAClE,YAAY,EAAE;YACZ,IAAI,EAAE,qBAAqB,CAAC,UAAU,EAAE,mDAAmD,EAAE,KAAK,CAAC;YACnG,SAAS,EAAE,qBAAqB,CAAC,UAAU,EAAE,oDAAoD,EAAE,KAAK,CAAC;YACzG,YAAY,EAAE,qBAAqB,CAAC,UAAU,EAAE,uDAAuD,EAAE,KAAK,CAAC;YAC/G,WAAW,EAAE,qBAAqB,CAAC,UAAU,EAAE,qDAAqD,EAAE,KAAK,CAAC;SAC7G;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,gCAAgC,CAC5C,UAAsB,EAAE,QAAkC;IAC5D,MAAM,OAAO,GAAG;QACd,GAAG,EAAE,CAAC,GAAG,CAAC;QACV,GAAG,EAAE,CAAC,GAAG,CAAC;QACV,GAAG,EAAE,CAAC,GAAG,CAAC;KACX,CAAC;IAEF,MAAM,aAAa,GAAG,QAAQ,EAAE,aAAa,CAAC;IAC9C,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,MAAM,YAAY,GAAG,4BAA4B,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IACxE,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,MAAM,QAAQ,GAAG,YAAY,CAAC,GAAG,EAAE,KAAK,IAAI,IAAI,CAAC;IACjD,MAAM,QAAQ,GAAG,YAAY,CAAC,GAAG,EAAE,KAAK,IAAI,IAAI,CAAC;IACjD,MAAM,QAAQ,GAAG,YAAY,CAAC,GAAG,EAAE,KAAK,IAAI,IAAI,CAAC;IACjD,MAAM,aAAa,GAAG,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,sBAAsB,CAAC,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5G,MAAM,aAAa,GAAG,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,sBAAsB,CAAC,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5G,MAAM,aAAa,GAAG,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,sBAAsB,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/E,MAAM,qBAAqB,GAAG,CAAC,GAAG,aAAa,CAAC;IAChD,MAAM,qBAAqB,GAAG,CAAC,GAAG,aAAa,CAAC;IAChD,MAAM,qBAAqB,GAAG,CAAC,GAAG,aAAa,CAAC;IAChD,MAAM,WAAW,GAAG,qBAAqB,GAAG,qBAAqB,GAAG,qBAAqB,CAAC;IAC1F,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,OAAO,CAAC,GAAG,GAAG,qBAAqB,GAAG,WAAW,CAAC;IAClD,OAAO,CAAC,GAAG,GAAG,qBAAqB,GAAG,WAAW,CAAC;IAClD,OAAO,CAAC,GAAG,GAAG,qBAAqB,GAAG,WAAW,CAAC;IAElD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,SAAS,yBAAyB,CAC9B,sBAA2C,EAAE,SAAuC,EACpF,KAAyB;IAC3B,MAAM,uBAAuB,GAAG,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAE1D,MAAM,qBAAqB,GAAG,IAAI,GAAG,EAAkB,CAAC;IACxD,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE;QACpB,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC5B,OAAO;QACT,CAAC;QACD,MAAM,WAAW,GAAG,sBAAsB,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QACvE,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,OAAO;QACT,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC;QAC3C,qBAAqB,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;QAE5D,IAAI,CAAC,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,GAAG,WAAW,EAAE,CAAC,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;IAEH,MAAM,sBAAsB,GAAG,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAEzD,qEAAqE;IACrE,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE;QACpB,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC5B,OAAO;QACT,CAAC;QACD,MAAM,oBAAoB,GAAG,qBAAqB,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAC/E,IAAI,oBAAoB,KAAK,SAAS,EAAE,CAAC;YACvC,OAAO;QACT,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,YAAY,GAAG,oBAAoB,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,IAAI,OAAO,GAAG,uBAAuB,CAAC,QAAQ,GAAG,sBAAsB,CAAC,QAAQ,CAAC;IACjF,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,uBAAuB,CAAC,GAAG,uBAAuB,CAAC;IAClF,OAAO,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;AACrC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,2BAA2B,CACvC,sBAA2C,EAAE,OAA0B;IACzE,IAAI,CAAC,OAAO,CAAC,UAAU,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QAC5C,OAAO;IACT,CAAC;IAED,IAAI,CAAC,sBAAsB,CAAC,IAAI,EAAE,CAAC;QACjC,OAAO,EAAC,GAAG,EAAE,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAC,CAAC;IAClE,CAAC;IAED,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC;IAC5C,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,oBAAoB,CAAC,eAAe,CAAC;IAC9E,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,sBAAsB,CAAC,eAAe,CAAC;IAEhF,OAAO;QACL,GAAG,EAAE,yBAAyB,CAAC,sBAAsB,EAAE,SAAS,EAAE,QAAQ,CAAC;QAC3E,GAAG,EAAE,yBAAyB,CAAC,sBAAsB,EAAE,SAAS,EAAE,QAAQ,CAAC;KAC5E,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAA6C;IAC/E,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC;QACvC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,+EAA+E;IAC/E,qDAAqD;IACrD,MAAM,QAAQ,GAAG;QACf,qBAAqB,EAAE,oCAAoC;QAC3D,gCAAgC,EAAG,cAAc;KAClD,CAAC;IACF,MAAM,gBAAgB,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;IAC3D,OAAO,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,CACzC,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,gBAAgB,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;AACrG,CAAC;AAED,MAAM,UAAU,+BAA+B,CAAC,OAA6C;IAC3F,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;QACnE,OAAO,KAAK,CAAC;IACf,CAAC;IAED,gBAAgB;IAChB,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,KAAK,GAAG,EAAE,CAAC;QACzC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,+EAA+E;IAC/E,iFAAiF;IACjF,iFAAiF;IACjF,oDAAoD;IAEpD,MAAM,EAAC,YAAY,EAAE,YAAY,EAAC,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;IAC9D,MAAM,KAAK,GAAG,YAAY,CAAC,CAAC,CAAC,YAAY,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7D,IAAI,KAAK,GAAG,IAAI,EAAE,CAAC;QACjB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,eAAe,CAAC,OAA6C;IACpE,MAAM,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC;IACzD,MAAM,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC;IACzD,OAAO,EAAC,YAAY,EAAE,YAAY,EAAC,CAAC;AACtC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,6BAA6B,CACzC,OAAuD,EAAE,UAAkB,EAC3E,YAA2C;IAC7C,IAAI,CAAC,OAAO,IAAI,+BAA+B,CAAC,OAAO,CAAC,EAAE,CAAC;QACzD,uFAAuF;QACvF,2CAA2C;QAC3C,+JAA+J;QAC/J,uGAAuG;QACvG,QAAQ,YAAY,EAAE,CAAC;YACrB,KAAK,YAAY;gBACf,+CAA+C;gBAC/C,OAAO,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,GAAG,CAAC,CAAC;YACtC,KAAK,QAAQ,CAAC;YACd,KAAK,UAAU;gBACb,6CAA6C;gBAC7C,OAAO,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;YACvC;gBACE,sEAAsE;gBACtE,OAAO,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,GAAG,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;IAED,oDAAoD;IACpD,MAAM,EAAC,YAAY,EAAE,YAAY,EAAC,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;IAC9D,IAAI,mBAAmB,GAAG,YAAY,CAAC;IACvC,IAAI,CAAC,mBAAmB,CAAC,OAAO,CAAC,EAAE,CAAC;QAClC,+DAA+D;QAC/D,2FAA2F;QAC3F,iDAAiD;QACjD,mBAAmB,GAAG,YAAY,CAAC;IACrC,CAAC;IACD,sEAAsE;IACtE,kDAAkD;IAClD,iDAAiD;IACjD,0BAA0B;IAC1B,8EAA8E;IAC9E,IAAI;IAEJ,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,KAAK,YAAY,EAAE,CAAC;QACpD,mEAAmE;QACnE,OAAO,mBAAmB,CAAC;IAC7B,CAAC;IAED,wFAAwF;IACxF,qFAAqF;IACrF,+EAA+E;IAC/E,MAAM,gBAAgB,GAAG,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC,IAAI,YAAY,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,mBAAmB,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACtH,OAAO,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,gBAAgB,CAAC,CAAC;AACnD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iCAAiC,CAAC,MAA6C;IAC7F,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,qCAAqC;QACrC,OAAO,CAAC,CAAC;IACX,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;IAC/B,MAAM,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,iBAAiB,IAAI,MAAM,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC,CAAC;IACzF,MAAM,cAAc,GAAG,6BAA6B,CAAC,OAAO,EAAE,aAAa,sDAAuC,CAAC;IACnH,IAAI,aAAa,KAAK,CAAC,IAAI,cAAc,KAAK,CAAC,EAAE,CAAC;QAChD,OAAO,CAAC,CAAC;IACX,CAAC;IAED,MAAM,gBAAgB,GAAG,cAAc,GAAG,aAAa,CAAC;IACxD,OAAO,gBAAgB,CAAC;AAC1B,CAAC","sourcesContent":["// Copyright 2024 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\nimport * as Protocol from '../../../generated/protocol.js';\nimport type * as CrUXManager from '../../crux-manager/crux-manager.js';\nimport type * as Handlers from '../handlers/handlers.js';\nimport * as Helpers from '../helpers/helpers.js';\nimport type * as Lantern from '../lantern/lantern.js';\nimport * as Types from '../types/types.js';\n\nimport {getLogNormalScore} from './Statistics.js';\nimport {\n InsightKeys,\n type InsightModels,\n type InsightSet,\n type InsightSetContext,\n type MetricSavings,\n} from './types.js';\n\nconst GRAPH_SAVINGS_PRECISION = 50;\n\nexport function getInsight<InsightName extends keyof InsightModels>(\n insightName: InsightName, insightSet: InsightSet): InsightModels[InsightName]|null {\n const insight = insightSet.model[insightName];\n if (insight instanceof Error) {\n return null;\n }\n\n // For some reason typescript won't narrow the type by removing Error, so do it manually.\n return insight;\n}\n\nexport function getLCP(insightSet: InsightSet):\n {value: Types.Timing.Micro, event: Types.Events.LargestContentfulPaintCandidate}|null {\n const insight = getInsight(InsightKeys.LCP_BREAKDOWN, insightSet);\n if (!insight || !insight.lcpMs || !insight.lcpEvent) {\n return null;\n }\n\n const value = Helpers.Timing.milliToMicro(insight.lcpMs);\n return {value, event: insight.lcpEvent};\n}\n\nexport function getINP(insightSet: InsightSet):\n {value: Types.Timing.Micro, event: Types.Events.SyntheticInteractionPair}|null {\n const insight = getInsight(InsightKeys.INP_BREAKDOWN, insightSet);\n if (!insight?.longestInteractionEvent?.dur) {\n return null;\n }\n\n const value = insight.longestInteractionEvent.dur;\n return {value, event: insight.longestInteractionEvent};\n}\n\nexport function getCLS(insightSet: InsightSet): {value: number, worstClusterEvent: Types.Events.Event|null} {\n const insight = getInsight(InsightKeys.CLS_CULPRITS, insightSet);\n if (!insight) {\n // Unlike the other metrics, there is always a value for CLS even with no data.\n return {value: 0, worstClusterEvent: null};\n }\n\n // TODO(cjamcl): the CLS insight should be doing this for us.\n let maxScore = 0;\n let worstCluster;\n for (const cluster of insight.clusters) {\n if (cluster.clusterCumulativeScore > maxScore) {\n maxScore = cluster.clusterCumulativeScore;\n worstCluster = cluster;\n }\n }\n\n return {value: maxScore, worstClusterEvent: worstCluster ?? null};\n}\n\nexport function evaluateLCPMetricScore(value: Types.Timing.Milli): number {\n return getLogNormalScore({p10: 2500, median: 4000}, value);\n}\n\nexport function evaluateINPMetricScore(value: Types.Timing.Milli): number {\n return getLogNormalScore({p10: 200, median: 500}, value);\n}\n\nexport function evaluateCLSMetricScore(value: number): number {\n return getLogNormalScore({p10: 0.1, median: 0.25}, value);\n}\n\nexport interface CrUXFieldMetricTimingResult {\n value: Types.Timing.Micro;\n pageScope: CrUXManager.PageScope;\n}\nexport interface CrUXFieldMetricNumberResult {\n value: number;\n pageScope: CrUXManager.PageScope;\n}\nexport interface CrUXFieldMetricResults {\n fcp: CrUXFieldMetricTimingResult|null;\n lcp: CrUXFieldMetricTimingResult|null;\n inp: CrUXFieldMetricTimingResult|null;\n cls: CrUXFieldMetricNumberResult|null;\n lcpBreakdown: {\n ttfb: CrUXFieldMetricTimingResult|null,\n loadDelay: CrUXFieldMetricTimingResult|null,\n loadDuration: CrUXFieldMetricTimingResult|null,\n renderDelay: CrUXFieldMetricTimingResult|null,\n };\n}\n\nfunction getPageResult(\n cruxFieldData: CrUXManager.PageResult[], url: string, origin: string,\n scope: CrUXManager.Scope|null = null): CrUXManager.PageResult|undefined {\n return cruxFieldData.find(result => {\n const key = scope ? result[`${scope.pageScope}-${scope.deviceScope}`]?.record.key :\n (result['url-ALL'] || result['origin-ALL'])?.record.key;\n return (key?.url && key.url === url) || (key?.origin && key.origin === origin);\n });\n}\n\nfunction getMetricResult(\n pageResult: CrUXManager.PageResult, name: CrUXManager.StandardMetricNames,\n scope: CrUXManager.Scope|null = null): CrUXFieldMetricNumberResult|null {\n const scopes: Array<{pageScope: CrUXManager.PageScope, deviceScope: CrUXManager.DeviceScope}> = [];\n if (scope) {\n scopes.push(scope);\n } else {\n scopes.push({pageScope: 'url', deviceScope: 'ALL'});\n scopes.push({pageScope: 'origin', deviceScope: 'ALL'});\n }\n\n for (const scope of scopes) {\n const key = `${scope.pageScope}-${scope.deviceScope}` as const;\n let value = pageResult[key]?.record.metrics[name]?.percentiles?.p75;\n if (typeof value === 'string') {\n value = Number(value);\n }\n if (typeof value === 'number' && Number.isFinite(value)) {\n return {value, pageScope: scope.pageScope};\n }\n }\n\n return null;\n}\n\nfunction getMetricTimingResult(\n pageResult: CrUXManager.PageResult, name: CrUXManager.StandardMetricNames,\n scope: CrUXManager.Scope|null = null): CrUXFieldMetricTimingResult|null {\n const result = getMetricResult(pageResult, name, scope);\n if (result) {\n const valueMs = result.value as Types.Timing.Milli;\n return {value: Helpers.Timing.milliToMicro(valueMs), pageScope: result.pageScope};\n }\n\n return null;\n}\n\nexport function getFieldMetricsForInsightSet(\n insightSet: InsightSet, metadata: Types.File.MetaData|null,\n scope: CrUXManager.Scope|null = null): CrUXFieldMetricResults|null {\n const cruxFieldData = metadata?.cruxFieldData;\n if (!cruxFieldData) {\n return null;\n }\n\n const pageResult = getPageResult(cruxFieldData, insightSet.url.href, insightSet.url.origin, scope);\n if (!pageResult) {\n return null;\n }\n\n return {\n fcp: getMetricTimingResult(pageResult, 'first_contentful_paint', scope),\n lcp: getMetricTimingResult(pageResult, 'largest_contentful_paint', scope),\n inp: getMetricTimingResult(pageResult, 'interaction_to_next_paint', scope),\n cls: getMetricResult(pageResult, 'cumulative_layout_shift', scope),\n lcpBreakdown: {\n ttfb: getMetricTimingResult(pageResult, 'largest_contentful_paint_image_time_to_first_byte', scope),\n loadDelay: getMetricTimingResult(pageResult, 'largest_contentful_paint_image_resource_load_delay', scope),\n loadDuration: getMetricTimingResult(pageResult, 'largest_contentful_paint_image_resource_load_duration', scope),\n renderDelay: getMetricTimingResult(pageResult, 'largest_contentful_paint_image_element_render_delay', scope),\n }\n };\n}\n\nexport function calculateMetricWeightsForSorting(\n insightSet: InsightSet, metadata: Types.File.MetaData|null): {lcp: number, inp: number, cls: number} {\n const weights = {\n lcp: 1 / 3,\n inp: 1 / 3,\n cls: 1 / 3,\n };\n\n const cruxFieldData = metadata?.cruxFieldData;\n if (!cruxFieldData) {\n return weights;\n }\n\n const fieldMetrics = getFieldMetricsForInsightSet(insightSet, metadata);\n if (!fieldMetrics) {\n return weights;\n }\n\n const fieldLcp = fieldMetrics.lcp?.value ?? null;\n const fieldInp = fieldMetrics.inp?.value ?? null;\n const fieldCls = fieldMetrics.cls?.value ?? null;\n const fieldLcpScore = fieldLcp !== null ? evaluateLCPMetricScore(Helpers.Timing.microToMilli(fieldLcp)) : 0;\n const fieldInpScore = fieldInp !== null ? evaluateINPMetricScore(Helpers.Timing.microToMilli(fieldInp)) : 0;\n const fieldClsScore = fieldCls !== null ? evaluateCLSMetricScore(fieldCls) : 0;\n const fieldLcpScoreInverted = 1 - fieldLcpScore;\n const fieldInpScoreInverted = 1 - fieldInpScore;\n const fieldClsScoreInverted = 1 - fieldClsScore;\n const invertedSum = fieldLcpScoreInverted + fieldInpScoreInverted + fieldClsScoreInverted;\n if (!invertedSum) {\n return weights;\n }\n\n weights.lcp = fieldLcpScoreInverted / invertedSum;\n weights.inp = fieldInpScoreInverted / invertedSum;\n weights.cls = fieldClsScoreInverted / invertedSum;\n\n return weights;\n}\n\n/**\n * Simulates the provided graph before and after the byte savings from `wastedBytesByRequestId` are applied.\n */\nfunction estimateSavingsWithGraphs(\n wastedBytesByRequestId: Map<string, number>, simulator: Lantern.Simulation.Simulator,\n graph: Lantern.Graph.Node): Types.Timing.Milli {\n const simulationBeforeChanges = simulator.simulate(graph);\n\n const originalTransferSizes = new Map<string, number>();\n graph.traverse(node => {\n if (node.type !== 'network') {\n return;\n }\n const wastedBytes = wastedBytesByRequestId.get(node.request.requestId);\n if (!wastedBytes) {\n return;\n }\n\n const original = node.request.transferSize;\n originalTransferSizes.set(node.request.requestId, original);\n\n node.request.transferSize = Math.max(original - wastedBytes, 0);\n });\n\n const simulationAfterChanges = simulator.simulate(graph);\n\n // Restore the original transfer size after we've done our simulation\n graph.traverse(node => {\n if (node.type !== 'network') {\n return;\n }\n const originalTransferSize = originalTransferSizes.get(node.request.requestId);\n if (originalTransferSize === undefined) {\n return;\n }\n node.request.transferSize = originalTransferSize;\n });\n\n let savings = simulationBeforeChanges.timeInMs - simulationAfterChanges.timeInMs;\n savings = Math.round(savings / GRAPH_SAVINGS_PRECISION) * GRAPH_SAVINGS_PRECISION;\n return Types.Timing.Milli(savings);\n}\n\n/**\n * Estimates the FCP & LCP savings for wasted bytes in `wastedBytesByRequestId`.\n */\nexport function metricSavingsForWastedBytes(\n wastedBytesByRequestId: Map<string, number>, context: InsightSetContext): MetricSavings|undefined {\n if (!context.navigation || !context.lantern) {\n return;\n }\n\n if (!wastedBytesByRequestId.size) {\n return {FCP: Types.Timing.Milli(0), LCP: Types.Timing.Milli(0)};\n }\n\n const simulator = context.lantern.simulator;\n const fcpGraph = context.lantern.metrics.firstContentfulPaint.optimisticGraph;\n const lcpGraph = context.lantern.metrics.largestContentfulPaint.optimisticGraph;\n\n return {\n FCP: estimateSavingsWithGraphs(wastedBytesByRequestId, simulator, fcpGraph),\n LCP: estimateSavingsWithGraphs(wastedBytesByRequestId, simulator, lcpGraph),\n };\n}\n\n/**\n * Returns whether the network request was sent encoded.\n */\nexport function isRequestCompressed(request: Types.Events.SyntheticNetworkRequest): boolean {\n if (!request.args.data.responseHeaders) {\n return false;\n }\n\n // FYI: In Lighthouse, older devtools logs (like our test fixtures) seems to be\n // lower case, while modern logs are Cased-Like-This.\n const patterns = [\n /^content-encoding$/i, /^x-content-encoding-over-network$/i,\n /^x-original-content-encoding$/i, // Lightrider.\n ];\n const compressionTypes = ['gzip', 'br', 'deflate', 'zstd'];\n return request.args.data.responseHeaders.some(\n header => patterns.some(p => header.name.match(p)) && compressionTypes.includes(header.value));\n}\n\nexport function isRequestServedFromBrowserCache(request: Types.Events.SyntheticNetworkRequest): boolean {\n if (!request.args.data.responseHeaders || request.args.data.failed) {\n return false;\n }\n\n // Not Modified?\n if (request.args.data.statusCode === 304) {\n return true;\n }\n\n // TODO: for some reason ResourceReceiveResponse events never show a 304 status\n // code, so the above is never gonna work. For now, fall back to a dirty check of\n // looking at the ratio of transfer size and resource size. If it's really small,\n // we certainly did not use the network to fetch it.\n\n const {transferSize, resourceSize} = getRequestSizes(request);\n const ratio = resourceSize ? transferSize / resourceSize : 0;\n if (ratio < 0.01) {\n return true;\n }\n\n return false;\n}\n\nfunction getRequestSizes(request: Types.Events.SyntheticNetworkRequest): {resourceSize: number, transferSize: number} {\n const resourceSize = request.args.data.decodedBodyLength;\n const transferSize = request.args.data.encodedDataLength;\n return {resourceSize, transferSize};\n}\n\n/**\n * Estimates the number of bytes the content of this network record would have consumed on the network based on the\n * uncompressed size (totalBytes). Uses the actual transfer size from the network record if applicable,\n * minus the size of the response headers.\n *\n * @param totalBytes Uncompressed size of the resource\n */\nexport function estimateCompressedContentSize(\n request: Types.Events.SyntheticNetworkRequest|undefined, totalBytes: number,\n resourceType: Protocol.Network.ResourceType): number {\n if (!request || isRequestServedFromBrowserCache(request)) {\n // We don't know how many bytes this asset used on the network, but we can guess it was\n // roughly the size of the content gzipped.\n // See https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/optimize-encoding-and-transfer for specific CSS/Script examples\n // See https://discuss.httparchive.org/t/file-size-and-compression-savings/145 for fallback multipliers\n switch (resourceType) {\n case 'Stylesheet':\n // Stylesheets tend to compress extremely well.\n return Math.round(totalBytes * 0.2);\n case 'Script':\n case 'Document':\n // Scripts and HTML compress fairly well too.\n return Math.round(totalBytes * 0.33);\n default:\n // Otherwise we'll just fallback to the average savings in HTTPArchive\n return Math.round(totalBytes * 0.5);\n }\n }\n\n // Get the size of the response body on the network.\n const {transferSize, resourceSize} = getRequestSizes(request);\n let contentTransferSize = transferSize;\n if (!isRequestCompressed(request)) {\n // This is not compressed, so we can use resourceSize directly.\n // This would be equivalent to transfer size minus headers transfer size, but transfer size\n // may also include bytes for SSL connection etc.\n contentTransferSize = resourceSize;\n }\n // TODO(cjamcl): Get \"responseHeadersTransferSize\" in Network handler.\n // else if (request.responseHeadersTransferSize) {\n // // Subtract the size of the encoded headers.\n // contentTransferSize =\n // Math.max(0, contentTransferSize - request.responseHeadersTransferSize);\n // }\n\n if (request.args.data.resourceType === resourceType) {\n // This was a regular standalone asset, just use the transfer size.\n return contentTransferSize;\n }\n\n // This was an asset that was inlined in a different resource type (e.g. HTML document).\n // Use the compression ratio of the resource to estimate the total transferred bytes.\n // Get the compression ratio, if it's an invalid number, assume no compression.\n const compressionRatio = Number.isFinite(resourceSize) && resourceSize > 0 ? (contentTransferSize / resourceSize) : 1;\n return Math.round(totalBytes * compressionRatio);\n}\n\n/**\n * Utility function to estimate the ratio of the compression of a script.\n * This excludes the size of the response headers.\n */\nexport function estimateCompressionRatioForScript(script: Handlers.ModelHandlers.Scripts.Script): number {\n if (!script.request) {\n // Can't find request, so just use 1.\n return 1;\n }\n\n const request = script.request;\n const contentLength = request.args.data.decodedBodyLength ?? script.content?.length ?? 0;\n const compressedSize = estimateCompressedContentSize(request, contentLength, Protocol.Network.ResourceType.Script);\n if (contentLength === 0 || compressedSize === 0) {\n return 1;\n }\n\n const compressionRatio = compressedSize / contentLength;\n return compressionRatio;\n}\n"]}
1
+ {"version":3,"file":"Common.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/insights/Common.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\nimport * as Protocol from '../../../generated/protocol.js';\nimport type * as CrUXManager from '../../crux-manager/crux-manager.js';\nimport type * as Handlers from '../handlers/handlers.js';\nimport * as Helpers from '../helpers/helpers.js';\nimport type * as Lantern from '../lantern/lantern.js';\nimport * as Types from '../types/types.js';\n\nimport {getLogNormalScore} from './Statistics.js';\nimport {\n InsightKeys,\n type InsightModels,\n type InsightSet,\n type InsightSetContext,\n type MetricSavings,\n} from './types.js';\n\nconst GRAPH_SAVINGS_PRECISION = 50;\n\nexport function getInsight<InsightName extends keyof InsightModels>(\n insightName: InsightName, insightSet: InsightSet): InsightModels[InsightName]|null {\n const insight = insightSet.model[insightName];\n if (insight instanceof Error) {\n return null;\n }\n\n // For some reason typescript won't narrow the type by removing Error, so do it manually.\n return insight;\n}\n\nexport function getLCP(insightSet: InsightSet):\n {value: Types.Timing.Micro, event: Types.Events.LargestContentfulPaintCandidate}|null {\n const insight = getInsight(InsightKeys.LCP_BREAKDOWN, insightSet);\n if (!insight || !insight.lcpMs || !insight.lcpEvent) {\n return null;\n }\n\n const value = Helpers.Timing.milliToMicro(insight.lcpMs);\n return {value, event: insight.lcpEvent};\n}\n\nexport function getINP(insightSet: InsightSet):\n {value: Types.Timing.Micro, event: Types.Events.SyntheticInteractionPair}|null {\n const insight = getInsight(InsightKeys.INP_BREAKDOWN, insightSet);\n if (!insight?.longestInteractionEvent?.dur) {\n return null;\n }\n\n const value = insight.longestInteractionEvent.dur;\n return {value, event: insight.longestInteractionEvent};\n}\n\nexport function getCLS(insightSet: InsightSet): {value: number, worstClusterEvent: Types.Events.Event|null} {\n const insight = getInsight(InsightKeys.CLS_CULPRITS, insightSet);\n if (!insight) {\n // Unlike the other metrics, there is always a value for CLS even with no data.\n return {value: 0, worstClusterEvent: null};\n }\n\n // TODO(cjamcl): the CLS insight should be doing this for us.\n let maxScore = 0;\n let worstCluster;\n for (const cluster of insight.clusters) {\n if (cluster.clusterCumulativeScore > maxScore) {\n maxScore = cluster.clusterCumulativeScore;\n worstCluster = cluster;\n }\n }\n\n return {value: maxScore, worstClusterEvent: worstCluster ?? null};\n}\n\nexport function evaluateLCPMetricScore(value: Types.Timing.Milli): number {\n return getLogNormalScore({p10: 2500, median: 4000}, value);\n}\n\nexport function evaluateINPMetricScore(value: Types.Timing.Milli): number {\n return getLogNormalScore({p10: 200, median: 500}, value);\n}\n\nexport function evaluateCLSMetricScore(value: number): number {\n return getLogNormalScore({p10: 0.1, median: 0.25}, value);\n}\n\nexport interface CrUXFieldMetricTimingResult {\n value: Types.Timing.Micro;\n pageScope: CrUXManager.PageScope;\n}\nexport interface CrUXFieldMetricNumberResult {\n value: number;\n pageScope: CrUXManager.PageScope;\n}\nexport interface CrUXFieldMetricResults {\n fcp: CrUXFieldMetricTimingResult|null;\n lcp: CrUXFieldMetricTimingResult|null;\n inp: CrUXFieldMetricTimingResult|null;\n cls: CrUXFieldMetricNumberResult|null;\n lcpBreakdown: {\n ttfb: CrUXFieldMetricTimingResult|null,\n loadDelay: CrUXFieldMetricTimingResult|null,\n loadDuration: CrUXFieldMetricTimingResult|null,\n renderDelay: CrUXFieldMetricTimingResult|null,\n };\n}\n\nfunction getPageResult(\n cruxFieldData: CrUXManager.PageResult[], url: string, origin: string,\n scope: CrUXManager.Scope|null = null): CrUXManager.PageResult|undefined {\n return cruxFieldData.find(result => {\n const key = scope ? result[`${scope.pageScope}-${scope.deviceScope}`]?.record.key :\n (result['url-ALL'] || result['origin-ALL'])?.record.key;\n return (key?.url && key.url === url) || (key?.origin && key.origin === origin);\n });\n}\n\nfunction getMetricResult(\n pageResult: CrUXManager.PageResult, name: CrUXManager.StandardMetricNames,\n scope: CrUXManager.Scope|null = null): CrUXFieldMetricNumberResult|null {\n const scopes: Array<{pageScope: CrUXManager.PageScope, deviceScope: CrUXManager.DeviceScope}> = [];\n if (scope) {\n scopes.push(scope);\n } else {\n scopes.push({pageScope: 'url', deviceScope: 'ALL'});\n scopes.push({pageScope: 'origin', deviceScope: 'ALL'});\n }\n\n for (const scope of scopes) {\n const key = `${scope.pageScope}-${scope.deviceScope}` as const;\n let value = pageResult[key]?.record.metrics[name]?.percentiles?.p75;\n if (typeof value === 'string') {\n value = Number(value);\n }\n if (typeof value === 'number' && Number.isFinite(value)) {\n return {value, pageScope: scope.pageScope};\n }\n }\n\n return null;\n}\n\nfunction getMetricTimingResult(\n pageResult: CrUXManager.PageResult, name: CrUXManager.StandardMetricNames,\n scope: CrUXManager.Scope|null = null): CrUXFieldMetricTimingResult|null {\n const result = getMetricResult(pageResult, name, scope);\n if (result) {\n const valueMs = result.value as Types.Timing.Milli;\n return {value: Helpers.Timing.milliToMicro(valueMs), pageScope: result.pageScope};\n }\n\n return null;\n}\n\nexport function getFieldMetricsForInsightSet(\n insightSet: InsightSet, metadata: Types.File.MetaData|null,\n scope: CrUXManager.Scope|null = null): CrUXFieldMetricResults|null {\n const cruxFieldData = metadata?.cruxFieldData;\n if (!cruxFieldData) {\n return null;\n }\n\n const pageResult = getPageResult(cruxFieldData, insightSet.url.href, insightSet.url.origin, scope);\n if (!pageResult) {\n return null;\n }\n\n return {\n fcp: getMetricTimingResult(pageResult, 'first_contentful_paint', scope),\n lcp: getMetricTimingResult(pageResult, 'largest_contentful_paint', scope),\n inp: getMetricTimingResult(pageResult, 'interaction_to_next_paint', scope),\n cls: getMetricResult(pageResult, 'cumulative_layout_shift', scope),\n lcpBreakdown: {\n ttfb: getMetricTimingResult(pageResult, 'largest_contentful_paint_image_time_to_first_byte', scope),\n loadDelay: getMetricTimingResult(pageResult, 'largest_contentful_paint_image_resource_load_delay', scope),\n loadDuration: getMetricTimingResult(pageResult, 'largest_contentful_paint_image_resource_load_duration', scope),\n renderDelay: getMetricTimingResult(pageResult, 'largest_contentful_paint_image_element_render_delay', scope),\n }\n };\n}\n\nexport function calculateMetricWeightsForSorting(\n insightSet: InsightSet, metadata: Types.File.MetaData|null): {lcp: number, inp: number, cls: number} {\n const weights = {\n lcp: 1 / 3,\n inp: 1 / 3,\n cls: 1 / 3,\n };\n\n const cruxFieldData = metadata?.cruxFieldData;\n if (!cruxFieldData) {\n return weights;\n }\n\n const fieldMetrics = getFieldMetricsForInsightSet(insightSet, metadata);\n if (!fieldMetrics) {\n return weights;\n }\n\n const fieldLcp = fieldMetrics.lcp?.value ?? null;\n const fieldInp = fieldMetrics.inp?.value ?? null;\n const fieldCls = fieldMetrics.cls?.value ?? null;\n const fieldLcpScore = fieldLcp !== null ? evaluateLCPMetricScore(Helpers.Timing.microToMilli(fieldLcp)) : 0;\n const fieldInpScore = fieldInp !== null ? evaluateINPMetricScore(Helpers.Timing.microToMilli(fieldInp)) : 0;\n const fieldClsScore = fieldCls !== null ? evaluateCLSMetricScore(fieldCls) : 0;\n const fieldLcpScoreInverted = 1 - fieldLcpScore;\n const fieldInpScoreInverted = 1 - fieldInpScore;\n const fieldClsScoreInverted = 1 - fieldClsScore;\n const invertedSum = fieldLcpScoreInverted + fieldInpScoreInverted + fieldClsScoreInverted;\n if (!invertedSum) {\n return weights;\n }\n\n weights.lcp = fieldLcpScoreInverted / invertedSum;\n weights.inp = fieldInpScoreInverted / invertedSum;\n weights.cls = fieldClsScoreInverted / invertedSum;\n\n return weights;\n}\n\n/**\n * Simulates the provided graph before and after the byte savings from `wastedBytesByRequestId` are applied.\n */\nfunction estimateSavingsWithGraphs(\n wastedBytesByRequestId: Map<string, number>, simulator: Lantern.Simulation.Simulator,\n graph: Lantern.Graph.Node): Types.Timing.Milli {\n const simulationBeforeChanges = simulator.simulate(graph);\n\n const originalTransferSizes = new Map<string, number>();\n graph.traverse(node => {\n if (node.type !== 'network') {\n return;\n }\n const wastedBytes = wastedBytesByRequestId.get(node.request.requestId);\n if (!wastedBytes) {\n return;\n }\n\n const original = node.request.transferSize;\n originalTransferSizes.set(node.request.requestId, original);\n\n node.request.transferSize = Math.max(original - wastedBytes, 0);\n });\n\n const simulationAfterChanges = simulator.simulate(graph);\n\n // Restore the original transfer size after we've done our simulation\n graph.traverse(node => {\n if (node.type !== 'network') {\n return;\n }\n const originalTransferSize = originalTransferSizes.get(node.request.requestId);\n if (originalTransferSize === undefined) {\n return;\n }\n node.request.transferSize = originalTransferSize;\n });\n\n let savings = simulationBeforeChanges.timeInMs - simulationAfterChanges.timeInMs;\n savings = Math.round(savings / GRAPH_SAVINGS_PRECISION) * GRAPH_SAVINGS_PRECISION;\n return Types.Timing.Milli(savings);\n}\n\n/**\n * Estimates the FCP & LCP savings for wasted bytes in `wastedBytesByRequestId`.\n */\nexport function metricSavingsForWastedBytes(\n wastedBytesByRequestId: Map<string, number>, context: InsightSetContext): MetricSavings|undefined {\n if (!context.navigation || !context.lantern) {\n return;\n }\n\n if (!wastedBytesByRequestId.size) {\n return {FCP: Types.Timing.Milli(0), LCP: Types.Timing.Milli(0)};\n }\n\n const simulator = context.lantern.simulator;\n const fcpGraph = context.lantern.metrics.firstContentfulPaint.optimisticGraph;\n const lcpGraph = context.lantern.metrics.largestContentfulPaint.optimisticGraph;\n\n return {\n FCP: estimateSavingsWithGraphs(wastedBytesByRequestId, simulator, fcpGraph),\n LCP: estimateSavingsWithGraphs(wastedBytesByRequestId, simulator, lcpGraph),\n };\n}\n\n/**\n * Returns whether the network request was sent encoded.\n */\nexport function isRequestCompressed(request: Types.Events.SyntheticNetworkRequest): boolean {\n if (!request.args.data.responseHeaders) {\n return false;\n }\n\n // FYI: In Lighthouse, older devtools logs (like our test fixtures) seems to be\n // lower case, while modern logs are Cased-Like-This.\n const patterns = [\n /^content-encoding$/i, /^x-content-encoding-over-network$/i,\n /^x-original-content-encoding$/i, // Lightrider.\n ];\n const compressionTypes = ['gzip', 'br', 'deflate', 'zstd'];\n return request.args.data.responseHeaders.some(\n header => patterns.some(p => header.name.match(p)) && compressionTypes.includes(header.value));\n}\n\nexport function isRequestServedFromBrowserCache(request: Types.Events.SyntheticNetworkRequest): boolean {\n if (!request.args.data.responseHeaders || request.args.data.failed) {\n return false;\n }\n\n // Not Modified?\n if (request.args.data.statusCode === 304) {\n return true;\n }\n\n // TODO: for some reason ResourceReceiveResponse events never show a 304 status\n // code, so the above is never gonna work. For now, fall back to a dirty check of\n // looking at the ratio of transfer size and resource size. If it's really small,\n // we certainly did not use the network to fetch it.\n\n const {transferSize, resourceSize} = getRequestSizes(request);\n const ratio = resourceSize ? transferSize / resourceSize : 0;\n if (ratio < 0.01) {\n return true;\n }\n\n return false;\n}\n\nfunction getRequestSizes(request: Types.Events.SyntheticNetworkRequest): {resourceSize: number, transferSize: number} {\n const resourceSize = request.args.data.decodedBodyLength;\n const transferSize = request.args.data.encodedDataLength;\n return {resourceSize, transferSize};\n}\n\n/**\n * Estimates the number of bytes the content of this network record would have consumed on the network based on the\n * uncompressed size (totalBytes). Uses the actual transfer size from the network record if applicable,\n * minus the size of the response headers.\n *\n * @param totalBytes Uncompressed size of the resource\n */\nexport function estimateCompressedContentSize(\n request: Types.Events.SyntheticNetworkRequest|undefined, totalBytes: number,\n resourceType: Protocol.Network.ResourceType): number {\n if (!request || isRequestServedFromBrowserCache(request)) {\n // We don't know how many bytes this asset used on the network, but we can guess it was\n // roughly the size of the content gzipped.\n // See https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/optimize-encoding-and-transfer for specific CSS/Script examples\n // See https://discuss.httparchive.org/t/file-size-and-compression-savings/145 for fallback multipliers\n switch (resourceType) {\n case 'Stylesheet':\n // Stylesheets tend to compress extremely well.\n return Math.round(totalBytes * 0.2);\n case 'Script':\n case 'Document':\n // Scripts and HTML compress fairly well too.\n return Math.round(totalBytes * 0.33);\n default:\n // Otherwise we'll just fallback to the average savings in HTTPArchive\n return Math.round(totalBytes * 0.5);\n }\n }\n\n // Get the size of the response body on the network.\n const {transferSize, resourceSize} = getRequestSizes(request);\n let contentTransferSize = transferSize;\n if (!isRequestCompressed(request)) {\n // This is not compressed, so we can use resourceSize directly.\n // This would be equivalent to transfer size minus headers transfer size, but transfer size\n // may also include bytes for SSL connection etc.\n contentTransferSize = resourceSize;\n }\n // TODO(cjamcl): Get \"responseHeadersTransferSize\" in Network handler.\n // else if (request.responseHeadersTransferSize) {\n // // Subtract the size of the encoded headers.\n // contentTransferSize =\n // Math.max(0, contentTransferSize - request.responseHeadersTransferSize);\n // }\n\n if (request.args.data.resourceType === resourceType) {\n // This was a regular standalone asset, just use the transfer size.\n return contentTransferSize;\n }\n\n // This was an asset that was inlined in a different resource type (e.g. HTML document).\n // Use the compression ratio of the resource to estimate the total transferred bytes.\n // Get the compression ratio, if it's an invalid number, assume no compression.\n const compressionRatio = Number.isFinite(resourceSize) && resourceSize > 0 ? (contentTransferSize / resourceSize) : 1;\n return Math.round(totalBytes * compressionRatio);\n}\n\n/**\n * Utility function to estimate the ratio of the compression of a script.\n * This excludes the size of the response headers.\n */\nexport function estimateCompressionRatioForScript(script: Handlers.ModelHandlers.Scripts.Script): number {\n if (!script.request) {\n // Can't find request, so just use 1.\n return 1;\n }\n\n const request = script.request;\n const contentLength = request.args.data.decodedBodyLength ?? script.content?.length ?? 0;\n const compressedSize = estimateCompressedContentSize(request, contentLength, Protocol.Network.ResourceType.Script);\n if (contentLength === 0 || compressedSize === 0) {\n return 1;\n }\n\n const compressionRatio = compressedSize / contentLength;\n return compressionRatio;\n}\n\nexport function calculateDocFirstByteTs(docRequest: Types.Events.SyntheticNetworkRequest): Types.Timing.Micro|null {\n if (docRequest.args.data.protocol === 'file') {\n // file: requests do not have timings\n return docRequest.ts;\n }\n\n const timing = docRequest.args.data.timing;\n if (!timing) {\n // Older traces do not have timings.\n return null;\n }\n\n // Time that first byte (headers) are received.\n // For older traces, receiveHeadersStart can be missing (ex: web.dev.json.gz).\n // In that case use the headers end timing, which should be pretty close to when\n // the headers start.\n return Types.Timing.Micro(\n Helpers.Timing.secondsToMicro(timing.requestTime) +\n Helpers.Timing.milliToMicro(timing.receiveHeadersStart ?? timing.receiveHeadersEnd));\n}\n"]}
@@ -5,6 +5,7 @@
5
5
  import * as Handlers from '../handlers/handlers.js';
6
6
  import * as Helpers from '../helpers/helpers.js';
7
7
  import * as Types from '../types/types.js';
8
+ import { calculateDocFirstByteTs } from './Common.js';
8
9
  import { InsightCategory, InsightKeys, InsightWarning, } from './types.js';
9
10
  export const UIStrings = {
10
11
  /**
@@ -64,14 +65,9 @@ function anyValuesNaN(...values) {
64
65
  * data we rely on, so we want to handle that case.
65
66
  */
66
67
  function determineSubparts(nav, docRequest, lcpEvent, lcpRequest) {
67
- const docReqTiming = docRequest.args.data.timing;
68
- let firstDocByteTs;
69
- if (docReqTiming) {
70
- firstDocByteTs = Types.Timing.Micro(Helpers.Timing.secondsToMicro(docReqTiming.requestTime) +
71
- Helpers.Timing.milliToMicro(docReqTiming.receiveHeadersStart));
72
- }
73
- else {
74
- firstDocByteTs = docRequest.ts; // file:
68
+ const firstDocByteTs = calculateDocFirstByteTs(docRequest);
69
+ if (firstDocByteTs === null) {
70
+ return null;
75
71
  }
76
72
  const ttfb = Helpers.Timing.traceWindowFromMicroSeconds(nav.ts, firstDocByteTs);
77
73
  ttfb.label = i18nString(UIStrings.timeToFirstByte);
@@ -1 +1 @@
1
- {"version":3,"file":"LCPBreakdown.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/insights/LCPBreakdown.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\nimport type * as Common from '../../../core/common/common.js';\nimport * as i18n from '../../../core/i18n/i18n.js';\nimport * as Handlers from '../handlers/handlers.js';\nimport * as Helpers from '../helpers/helpers.js';\nimport * as Types from '../types/types.js';\n\nimport {\n InsightCategory,\n InsightKeys,\n type InsightModel,\n type InsightSetContext,\n InsightWarning,\n type PartialInsightModel,\n} from './types.js';\n\nexport const UIStrings = {\n /**\n * @description Title of an insight that provides details about the LCP metric, broken down by parts.\n */\n title: 'LCP breakdown',\n /**\n * @description Description of a DevTools insight that presents a breakdown for the LCP metric by subparts.\n * This is displayed after a user expands the section to see more. No character length limits.\n */\n description:\n 'Each [subpart has specific improvement strategies](https://web.dev/articles/optimize-lcp#lcp-breakdown). Ideally, most of the LCP time should be spent on loading the resources, not within delays.',\n /**\n * @description Time to first byte title for the Largest Contentful Paint's subparts timespan breakdown.\n */\n timeToFirstByte: 'Time to first byte',\n /**\n * @description Resource load delay title for the Largest Contentful Paint subparts timespan breakdown.\n */\n resourceLoadDelay: 'Resource load delay',\n /**\n * @description Resource load duration title for the Largest Contentful Paint subparts timespan breakdown.\n */\n resourceLoadDuration: 'Resource load duration',\n /**\n * @description Element render delay title for the Largest Contentful Paint subparts timespan breakdown.\n */\n elementRenderDelay: 'Element render delay',\n /**\n * @description Label used for the subpart (section) of a larger duration.\n */\n subpart: 'Subpart',\n /**\n * @description Label used for the duration a single subpart (section) takes up of a larger duration.\n */\n duration: 'Duration',\n /**\n * @description Label used for the duration a single subpart (section) takes up of a larger duration. The value will be the 75th percentile of aggregate data. \"Field\" means that the data was collected from real users in the field as opposed to the developers local environment. \"Field\" is synonymous with \"Real user data\".\n */\n fieldDuration: 'Field p75',\n /**\n * @description Text status indicating that the the Largest Contentful Paint (LCP) metric timing was not found. \"LCP\" is an acronym and should not be translated.\n */\n noLcp: 'No LCP detected',\n} as const;\nconst str_ = i18n.i18n.registerUIStrings('models/trace/insights/LCPBreakdown.ts', UIStrings);\nexport const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);\n\n// A TraceWindow plus its UIString.\nexport type Subpart = Types.Timing.TraceWindowMicro&{label: Common.UIString.LocalizedString};\ninterface LCPSubparts {\n /**\n * The time between when the user initiates loading the page until when\n * the browser receives the first byte of the html response.\n */\n ttfb: Subpart;\n /**\n * The time between ttfb and the LCP request request being started.\n * For a text LCP, this is undefined given no request is loaded.\n */\n loadDelay?: Subpart;\n /**\n * The time it takes to load the LCP request.\n */\n loadDuration?: Subpart;\n /**\n * The time between when the LCP request finishes loading and when\n * the LCP element is rendered.\n */\n renderDelay: Subpart;\n}\n\nexport function isLCPBreakdown(model: InsightModel): model is LCPBreakdownInsightModel {\n return model.insightKey === 'LCPBreakdown';\n}\nexport type LCPBreakdownInsightModel = InsightModel<typeof UIStrings, {\n lcpMs?: Types.Timing.Milli,\n lcpTs?: Types.Timing.Milli,\n lcpEvent?: Types.Events.LargestContentfulPaintCandidate,\n /** The network request for the LCP image, if there was one. */\n lcpRequest?: Types.Events.SyntheticNetworkRequest,\n subparts?: LCPSubparts,\n}>;\n\nfunction anyValuesNaN(...values: number[]): boolean {\n return values.some(v => Number.isNaN(v));\n}\n/**\n * Calculates the 2–4 subparts of an LCP as bounds.\n * Will return `null` if any required values were missing. We don't ever expect\n * them to be missing on newer traces, but old trace files may lack some of the\n * data we rely on, so we want to handle that case.\n */\nfunction determineSubparts(\n nav: Types.Events.NavigationStart, docRequest: Types.Events.SyntheticNetworkRequest,\n lcpEvent: Types.Events.LargestContentfulPaintCandidate,\n lcpRequest: Types.Events.SyntheticNetworkRequest|undefined): LCPSubparts|null {\n const docReqTiming = docRequest.args.data.timing;\n\n let firstDocByteTs;\n if (docReqTiming) {\n firstDocByteTs = Types.Timing.Micro(\n Helpers.Timing.secondsToMicro(docReqTiming.requestTime) +\n Helpers.Timing.milliToMicro(docReqTiming.receiveHeadersStart));\n } else {\n firstDocByteTs = docRequest.ts; // file:\n }\n\n const ttfb = Helpers.Timing.traceWindowFromMicroSeconds(nav.ts, firstDocByteTs) as Subpart;\n ttfb.label = i18nString(UIStrings.timeToFirstByte);\n\n let renderDelay = Helpers.Timing.traceWindowFromMicroSeconds(ttfb.max, lcpEvent.ts) as Subpart;\n renderDelay.label = i18nString(UIStrings.elementRenderDelay);\n\n // If the LCP is text, we don't have a request, so just 2 subparts.\n if (!lcpRequest) {\n /**\n * Text LCP. 2 subparts, thus 3 timestamps\n *\n * | ttfb | renderDelay |\n * ^ lcpEvent.ts\n * ^ firstDocByteTs\n * ^ navStartTs\n */\n if (anyValuesNaN(ttfb.range, renderDelay.range)) {\n return null;\n }\n return {ttfb, renderDelay};\n }\n\n /**\n * Image LCP. 4 subparts means 5 timestamps\n *\n * | ttfb | loadDelay | loadTime | renderDelay |\n * ^ lcpEvent.ts\n * ^ lcpReqEndTs\n * ^ lcpStartTs\n * ^ ttfbTs\n * ^ navStartTs\n */\n const lcpStartTs = lcpRequest.ts;\n const lcpReqEndTs = lcpRequest.args.data.syntheticData.finishTime;\n\n const loadDelay = Helpers.Timing.traceWindowFromMicroSeconds(ttfb.max, lcpStartTs) as Subpart;\n const loadDuration = Helpers.Timing.traceWindowFromMicroSeconds(lcpStartTs, lcpReqEndTs) as Subpart;\n renderDelay = Helpers.Timing.traceWindowFromMicroSeconds(lcpReqEndTs, lcpEvent.ts) as Subpart;\n loadDelay.label = i18nString(UIStrings.resourceLoadDelay);\n loadDuration.label = i18nString(UIStrings.resourceLoadDuration);\n renderDelay.label = i18nString(UIStrings.elementRenderDelay);\n if (anyValuesNaN(ttfb.range, loadDelay.range, loadDuration.range, renderDelay.range)) {\n return null;\n }\n\n return {\n ttfb,\n loadDelay,\n loadDuration,\n renderDelay,\n };\n}\n\nfunction finalize(partialModel: PartialInsightModel<LCPBreakdownInsightModel>): LCPBreakdownInsightModel {\n const relatedEvents = [];\n if (partialModel.lcpEvent) {\n relatedEvents.push(partialModel.lcpEvent);\n }\n if (partialModel.lcpRequest) {\n relatedEvents.push(partialModel.lcpRequest);\n }\n return {\n insightKey: InsightKeys.LCP_BREAKDOWN,\n strings: UIStrings,\n title: i18nString(UIStrings.title),\n description: i18nString(UIStrings.description),\n category: InsightCategory.LCP,\n state: partialModel.lcpEvent || partialModel.lcpRequest ? 'informative' : 'pass',\n ...partialModel,\n relatedEvents,\n };\n}\n\nexport function generateInsight(\n parsedTrace: Handlers.Types.ParsedTrace, context: InsightSetContext): LCPBreakdownInsightModel {\n if (!context.navigation) {\n return finalize({});\n }\n\n const networkRequests = parsedTrace.NetworkRequests;\n\n const frameMetrics = parsedTrace.PageLoadMetrics.metricScoresByFrameId.get(context.frameId);\n if (!frameMetrics) {\n throw new Error('no frame metrics');\n }\n\n const navMetrics = frameMetrics.get(context.navigationId);\n if (!navMetrics) {\n throw new Error('no navigation metrics');\n }\n const metricScore = navMetrics.get(Handlers.ModelHandlers.PageLoadMetrics.MetricName.LCP);\n const lcpEvent = metricScore?.event;\n if (!lcpEvent || !Types.Events.isLargestContentfulPaintCandidate(lcpEvent)) {\n return finalize({warnings: [InsightWarning.NO_LCP]});\n }\n\n // This helps calculate the subparts.\n const lcpMs = Helpers.Timing.microToMilli(metricScore.timing);\n // This helps position things on the timeline's UI accurately for a trace.\n const lcpTs = metricScore.event?.ts ? Helpers.Timing.microToMilli(metricScore.event?.ts) : undefined;\n const lcpRequest = parsedTrace.LargestImagePaint.lcpRequestByNavigationId.get(context.navigationId);\n\n const docRequest = networkRequests.byId.get(context.navigationId);\n if (!docRequest) {\n return finalize({lcpMs, lcpTs, lcpEvent, lcpRequest, warnings: [InsightWarning.NO_DOCUMENT_REQUEST]});\n }\n\n return finalize({\n lcpMs,\n lcpTs,\n lcpEvent,\n lcpRequest,\n subparts: determineSubparts(context.navigation, docRequest, lcpEvent, lcpRequest) ?? undefined,\n });\n}\n\nexport function createOverlays(model: LCPBreakdownInsightModel): Types.Overlays.Overlay[] {\n if (!model.subparts || !model.lcpTs) {\n return [];\n }\n\n const overlays: Types.Overlays.Overlay[] = [\n {\n type: 'TIMESPAN_BREAKDOWN',\n sections: Object.values(model.subparts)\n .map((subpart: Subpart) => ({bounds: subpart, label: subpart.label, showDuration: true})),\n },\n ];\n if (model.lcpRequest) {\n overlays.push({type: 'ENTRY_OUTLINE', entry: model.lcpRequest, outlineReason: 'INFO'});\n }\n\n return overlays;\n}\n"]}
1
+ {"version":3,"file":"LCPBreakdown.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/insights/LCPBreakdown.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\nimport type * as Common from '../../../core/common/common.js';\nimport * as i18n from '../../../core/i18n/i18n.js';\nimport * as Handlers from '../handlers/handlers.js';\nimport * as Helpers from '../helpers/helpers.js';\nimport * as Types from '../types/types.js';\n\nimport {calculateDocFirstByteTs} from './Common.js';\nimport {\n InsightCategory,\n InsightKeys,\n type InsightModel,\n type InsightSetContext,\n InsightWarning,\n type PartialInsightModel,\n} from './types.js';\n\nexport const UIStrings = {\n /**\n * @description Title of an insight that provides details about the LCP metric, broken down by parts.\n */\n title: 'LCP breakdown',\n /**\n * @description Description of a DevTools insight that presents a breakdown for the LCP metric by subparts.\n * This is displayed after a user expands the section to see more. No character length limits.\n */\n description:\n 'Each [subpart has specific improvement strategies](https://web.dev/articles/optimize-lcp#lcp-breakdown). Ideally, most of the LCP time should be spent on loading the resources, not within delays.',\n /**\n * @description Time to first byte title for the Largest Contentful Paint's subparts timespan breakdown.\n */\n timeToFirstByte: 'Time to first byte',\n /**\n * @description Resource load delay title for the Largest Contentful Paint subparts timespan breakdown.\n */\n resourceLoadDelay: 'Resource load delay',\n /**\n * @description Resource load duration title for the Largest Contentful Paint subparts timespan breakdown.\n */\n resourceLoadDuration: 'Resource load duration',\n /**\n * @description Element render delay title for the Largest Contentful Paint subparts timespan breakdown.\n */\n elementRenderDelay: 'Element render delay',\n /**\n * @description Label used for the subpart (section) of a larger duration.\n */\n subpart: 'Subpart',\n /**\n * @description Label used for the duration a single subpart (section) takes up of a larger duration.\n */\n duration: 'Duration',\n /**\n * @description Label used for the duration a single subpart (section) takes up of a larger duration. The value will be the 75th percentile of aggregate data. \"Field\" means that the data was collected from real users in the field as opposed to the developers local environment. \"Field\" is synonymous with \"Real user data\".\n */\n fieldDuration: 'Field p75',\n /**\n * @description Text status indicating that the the Largest Contentful Paint (LCP) metric timing was not found. \"LCP\" is an acronym and should not be translated.\n */\n noLcp: 'No LCP detected',\n} as const;\nconst str_ = i18n.i18n.registerUIStrings('models/trace/insights/LCPBreakdown.ts', UIStrings);\nexport const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);\n\n// A TraceWindow plus its UIString.\nexport type Subpart = Types.Timing.TraceWindowMicro&{label: Common.UIString.LocalizedString};\ninterface LCPSubparts {\n /**\n * The time between when the user initiates loading the page until when\n * the browser receives the first byte of the html response.\n */\n ttfb: Subpart;\n /**\n * The time between ttfb and the LCP request request being started.\n * For a text LCP, this is undefined given no request is loaded.\n */\n loadDelay?: Subpart;\n /**\n * The time it takes to load the LCP request.\n */\n loadDuration?: Subpart;\n /**\n * The time between when the LCP request finishes loading and when\n * the LCP element is rendered.\n */\n renderDelay: Subpart;\n}\n\nexport function isLCPBreakdown(model: InsightModel): model is LCPBreakdownInsightModel {\n return model.insightKey === 'LCPBreakdown';\n}\nexport type LCPBreakdownInsightModel = InsightModel<typeof UIStrings, {\n lcpMs?: Types.Timing.Milli,\n lcpTs?: Types.Timing.Milli,\n lcpEvent?: Types.Events.LargestContentfulPaintCandidate,\n /** The network request for the LCP image, if there was one. */\n lcpRequest?: Types.Events.SyntheticNetworkRequest,\n subparts?: LCPSubparts,\n}>;\n\nfunction anyValuesNaN(...values: number[]): boolean {\n return values.some(v => Number.isNaN(v));\n}\n/**\n * Calculates the 2–4 subparts of an LCP as bounds.\n * Will return `null` if any required values were missing. We don't ever expect\n * them to be missing on newer traces, but old trace files may lack some of the\n * data we rely on, so we want to handle that case.\n */\nfunction determineSubparts(\n nav: Types.Events.NavigationStart, docRequest: Types.Events.SyntheticNetworkRequest,\n lcpEvent: Types.Events.LargestContentfulPaintCandidate,\n lcpRequest: Types.Events.SyntheticNetworkRequest|undefined): LCPSubparts|null {\n const firstDocByteTs = calculateDocFirstByteTs(docRequest);\n if (firstDocByteTs === null) {\n return null;\n }\n\n const ttfb = Helpers.Timing.traceWindowFromMicroSeconds(nav.ts, firstDocByteTs) as Subpart;\n ttfb.label = i18nString(UIStrings.timeToFirstByte);\n\n let renderDelay = Helpers.Timing.traceWindowFromMicroSeconds(ttfb.max, lcpEvent.ts) as Subpart;\n renderDelay.label = i18nString(UIStrings.elementRenderDelay);\n\n // If the LCP is text, we don't have a request, so just 2 subparts.\n if (!lcpRequest) {\n /**\n * Text LCP. 2 subparts, thus 3 timestamps\n *\n * | ttfb | renderDelay |\n * ^ lcpEvent.ts\n * ^ firstDocByteTs\n * ^ navStartTs\n */\n if (anyValuesNaN(ttfb.range, renderDelay.range)) {\n return null;\n }\n return {ttfb, renderDelay};\n }\n\n /**\n * Image LCP. 4 subparts means 5 timestamps\n *\n * | ttfb | loadDelay | loadTime | renderDelay |\n * ^ lcpEvent.ts\n * ^ lcpReqEndTs\n * ^ lcpStartTs\n * ^ ttfbTs\n * ^ navStartTs\n */\n const lcpStartTs = lcpRequest.ts;\n const lcpReqEndTs = lcpRequest.args.data.syntheticData.finishTime;\n\n const loadDelay = Helpers.Timing.traceWindowFromMicroSeconds(ttfb.max, lcpStartTs) as Subpart;\n const loadDuration = Helpers.Timing.traceWindowFromMicroSeconds(lcpStartTs, lcpReqEndTs) as Subpart;\n renderDelay = Helpers.Timing.traceWindowFromMicroSeconds(lcpReqEndTs, lcpEvent.ts) as Subpart;\n loadDelay.label = i18nString(UIStrings.resourceLoadDelay);\n loadDuration.label = i18nString(UIStrings.resourceLoadDuration);\n renderDelay.label = i18nString(UIStrings.elementRenderDelay);\n if (anyValuesNaN(ttfb.range, loadDelay.range, loadDuration.range, renderDelay.range)) {\n return null;\n }\n\n return {\n ttfb,\n loadDelay,\n loadDuration,\n renderDelay,\n };\n}\n\nfunction finalize(partialModel: PartialInsightModel<LCPBreakdownInsightModel>): LCPBreakdownInsightModel {\n const relatedEvents = [];\n if (partialModel.lcpEvent) {\n relatedEvents.push(partialModel.lcpEvent);\n }\n if (partialModel.lcpRequest) {\n relatedEvents.push(partialModel.lcpRequest);\n }\n return {\n insightKey: InsightKeys.LCP_BREAKDOWN,\n strings: UIStrings,\n title: i18nString(UIStrings.title),\n description: i18nString(UIStrings.description),\n category: InsightCategory.LCP,\n state: partialModel.lcpEvent || partialModel.lcpRequest ? 'informative' : 'pass',\n ...partialModel,\n relatedEvents,\n };\n}\n\nexport function generateInsight(\n parsedTrace: Handlers.Types.ParsedTrace, context: InsightSetContext): LCPBreakdownInsightModel {\n if (!context.navigation) {\n return finalize({});\n }\n\n const networkRequests = parsedTrace.NetworkRequests;\n\n const frameMetrics = parsedTrace.PageLoadMetrics.metricScoresByFrameId.get(context.frameId);\n if (!frameMetrics) {\n throw new Error('no frame metrics');\n }\n\n const navMetrics = frameMetrics.get(context.navigationId);\n if (!navMetrics) {\n throw new Error('no navigation metrics');\n }\n const metricScore = navMetrics.get(Handlers.ModelHandlers.PageLoadMetrics.MetricName.LCP);\n const lcpEvent = metricScore?.event;\n if (!lcpEvent || !Types.Events.isLargestContentfulPaintCandidate(lcpEvent)) {\n return finalize({warnings: [InsightWarning.NO_LCP]});\n }\n\n // This helps calculate the subparts.\n const lcpMs = Helpers.Timing.microToMilli(metricScore.timing);\n // This helps position things on the timeline's UI accurately for a trace.\n const lcpTs = metricScore.event?.ts ? Helpers.Timing.microToMilli(metricScore.event?.ts) : undefined;\n const lcpRequest = parsedTrace.LargestImagePaint.lcpRequestByNavigationId.get(context.navigationId);\n\n const docRequest = networkRequests.byId.get(context.navigationId);\n if (!docRequest) {\n return finalize({lcpMs, lcpTs, lcpEvent, lcpRequest, warnings: [InsightWarning.NO_DOCUMENT_REQUEST]});\n }\n\n return finalize({\n lcpMs,\n lcpTs,\n lcpEvent,\n lcpRequest,\n subparts: determineSubparts(context.navigation, docRequest, lcpEvent, lcpRequest) ?? undefined,\n });\n}\n\nexport function createOverlays(model: LCPBreakdownInsightModel): Types.Overlays.Overlay[] {\n if (!model.subparts || !model.lcpTs) {\n return [];\n }\n\n const overlays: Types.Overlays.Overlay[] = [\n {\n type: 'TIMESPAN_BREAKDOWN',\n sections: Object.values(model.subparts)\n .map((subpart: Subpart) => ({bounds: subpart, label: subpart.label, showDuration: true})),\n },\n ];\n if (model.lcpRequest) {\n overlays.push({type: 'ENTRY_OUTLINE', entry: model.lcpRequest, outlineReason: 'INFO'});\n }\n\n return overlays;\n}\n"]}
@@ -5,6 +5,7 @@
5
5
  import * as Handlers from '../handlers/handlers.js';
6
6
  import * as Helpers from '../helpers/helpers.js';
7
7
  import * as Types from '../types/types.js';
8
+ import { calculateDocFirstByteTs } from './Common.js';
8
9
  import { InsightCategory, InsightKeys, InsightWarning, } from './types.js';
9
10
  export const UIStrings = {
10
11
  /**
@@ -103,11 +104,8 @@ export function generateInsight(parsedTrace, context) {
103
104
  const imgPreloadedOrFoundInHTML = lcpRequest?.args.data.isLinkPreload || initiatedByMainDoc;
104
105
  const imageLoadingAttr = lcpEvent.args.data?.loadingAttr;
105
106
  const imageFetchPriorityHint = lcpRequest?.args.data.fetchPriorityHint;
106
- // This is the earliest discovery time an LCP request could have - it's TTFB.
107
- const earliestDiscoveryTime = docRequest?.args.data.timing ?
108
- Helpers.Timing.secondsToMicro(docRequest.args.data.timing.requestTime) +
109
- Helpers.Timing.milliToMicro(docRequest.args.data.timing.receiveHeadersStart) :
110
- undefined;
107
+ // This is the earliest discovery time an LCP request could have - it's TTFB (as an absolute timestamp).
108
+ const earliestDiscoveryTime = calculateDocFirstByteTs(docRequest);
111
109
  const priorityHintFound = imageFetchPriorityHint === 'high';
112
110
  return finalize({
113
111
  lcpEvent,
@@ -1 +1 @@
1
- {"version":3,"file":"LCPDiscovery.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/insights/LCPDiscovery.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\nimport * as i18n from '../../../core/i18n/i18n.js';\nimport * as Handlers from '../handlers/handlers.js';\nimport * as Helpers from '../helpers/helpers.js';\nimport * as Types from '../types/types.js';\n\nimport {\n type Checklist,\n InsightCategory,\n InsightKeys,\n type InsightModel,\n type InsightSetContext,\n InsightWarning,\n type PartialInsightModel,\n} from './types.js';\n\nexport const UIStrings = {\n /**\n * @description Title of an insight that provides details about the LCP metric, and the network requests necessary to load it. Details how the LCP request was discoverable - in other words, the path necessary to load it (ex: network requests, JavaScript)\n */\n title: 'LCP request discovery',\n /**\n * @description Description of an insight that provides details about the LCP metric, and the network requests necessary to load it.\n */\n description:\n 'Optimize LCP by making the LCP image [discoverable](https://web.dev/articles/optimize-lcp#1_eliminate_resource_load_delay) from the HTML immediately, and [avoiding lazy-loading](https://web.dev/articles/lcp-lazy-loading)',\n /**\n * @description Text to tell the user how long after the earliest discovery time their LCP element loaded.\n * @example {401ms} PH1\n */\n lcpLoadDelay: 'LCP image loaded {PH1} after earliest start point.',\n /**\n * @description Text to tell the user that a fetchpriority property value of \"high\" is applied to the LCP request.\n */\n fetchPriorityApplied: 'fetchpriority=high applied',\n /**\n * @description Text to tell the user that a fetchpriority property value of \"high\" should be applied to the LCP request.\n */\n fetchPriorityShouldBeApplied: 'fetchpriority=high should be applied',\n /**\n * @description Text to tell the user that the LCP request is discoverable in the initial document.\n */\n requestDiscoverable: 'Request is discoverable in initial document',\n /**\n * @description Text to tell the user that the LCP request does not have the lazy load property applied.\n */\n lazyLoadNotApplied: 'lazy load not applied',\n /**\n * @description Text status indicating that the the Largest Contentful Paint (LCP) metric timing was not found. \"LCP\" is an acronym and should not be translated.\n */\n noLcp: 'No LCP detected',\n /**\n * @description Text status indicating that the Largest Contentful Paint (LCP) metric was text rather than an image. \"LCP\" is an acronym and should not be translated.\n */\n noLcpResource: 'No LCP resource detected because the LCP is not an image',\n} as const;\n\nconst str_ = i18n.i18n.registerUIStrings('models/trace/insights/LCPDiscovery.ts', UIStrings);\nexport const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);\n\nexport function isLCPDiscovery(model: InsightModel): model is LCPDiscoveryInsightModel {\n return model.insightKey === 'LCPDiscovery';\n}\nexport type LCPDiscoveryInsightModel = InsightModel<typeof UIStrings, {\n lcpEvent?: Types.Events.LargestContentfulPaintCandidate,\n /** The network request for the LCP image, if there was one. */\n lcpRequest?: Types.Events.SyntheticNetworkRequest,\n earliestDiscoveryTimeTs?: Types.Timing.Micro,\n checklist?: Checklist<'priorityHinted'|'requestDiscoverable'|'eagerlyLoaded'>,\n}>;\n\nfunction finalize(partialModel: PartialInsightModel<LCPDiscoveryInsightModel>): LCPDiscoveryInsightModel {\n const relatedEvents = partialModel.lcpEvent && partialModel.lcpRequest ?\n // TODO: add entire request initiator chain?\n [partialModel.lcpEvent, partialModel.lcpRequest] :\n [];\n return {\n insightKey: InsightKeys.LCP_DISCOVERY,\n strings: UIStrings,\n title: i18nString(UIStrings.title),\n description: i18nString(UIStrings.description),\n category: InsightCategory.LCP,\n state: partialModel.lcpRequest && partialModel.checklist &&\n (!partialModel.checklist.eagerlyLoaded.value || !partialModel.checklist.requestDiscoverable.value ||\n !partialModel.checklist.priorityHinted.value) ?\n 'fail' :\n 'pass',\n ...partialModel,\n relatedEvents,\n };\n}\n\nexport function generateInsight(\n parsedTrace: Handlers.Types.ParsedTrace, context: InsightSetContext): LCPDiscoveryInsightModel {\n if (!context.navigation) {\n return finalize({});\n }\n\n const networkRequests = parsedTrace.NetworkRequests;\n\n const frameMetrics = parsedTrace.PageLoadMetrics.metricScoresByFrameId.get(context.frameId);\n if (!frameMetrics) {\n throw new Error('no frame metrics');\n }\n\n const navMetrics = frameMetrics.get(context.navigationId);\n if (!navMetrics) {\n throw new Error('no navigation metrics');\n }\n const metricScore = navMetrics.get(Handlers.ModelHandlers.PageLoadMetrics.MetricName.LCP);\n const lcpEvent = metricScore?.event;\n if (!lcpEvent || !Types.Events.isLargestContentfulPaintCandidate(lcpEvent)) {\n return finalize({warnings: [InsightWarning.NO_LCP]});\n }\n\n const docRequest = networkRequests.byId.get(context.navigationId);\n if (!docRequest) {\n return finalize({warnings: [InsightWarning.NO_DOCUMENT_REQUEST]});\n }\n\n const lcpRequest = parsedTrace.LargestImagePaint.lcpRequestByNavigationId.get(context.navigationId);\n if (!lcpRequest) {\n return finalize({lcpEvent});\n }\n\n const initiatorUrl = lcpRequest.args.data.initiator?.url;\n // TODO(b/372319476): Explore using trace event HTMLDocumentParser::FetchQueuedPreloads to determine if the request\n // is discovered by the preload scanner.\n const initiatedByMainDoc =\n lcpRequest?.args.data.initiator?.type === 'parser' && docRequest.args.data.url === initiatorUrl;\n const imgPreloadedOrFoundInHTML = lcpRequest?.args.data.isLinkPreload || initiatedByMainDoc;\n\n const imageLoadingAttr = lcpEvent.args.data?.loadingAttr;\n const imageFetchPriorityHint = lcpRequest?.args.data.fetchPriorityHint;\n // This is the earliest discovery time an LCP request could have - it's TTFB.\n const earliestDiscoveryTime = docRequest?.args.data.timing ?\n Helpers.Timing.secondsToMicro(docRequest.args.data.timing.requestTime) +\n Helpers.Timing.milliToMicro(docRequest.args.data.timing.receiveHeadersStart) :\n undefined;\n\n const priorityHintFound = imageFetchPriorityHint === 'high';\n\n return finalize({\n lcpEvent,\n lcpRequest,\n earliestDiscoveryTimeTs: earliestDiscoveryTime ? Types.Timing.Micro(earliestDiscoveryTime) : undefined,\n checklist: {\n priorityHinted: {\n label: priorityHintFound ? i18nString(UIStrings.fetchPriorityApplied) :\n i18nString(UIStrings.fetchPriorityShouldBeApplied),\n value: priorityHintFound\n },\n requestDiscoverable: {label: i18nString(UIStrings.requestDiscoverable), value: imgPreloadedOrFoundInHTML},\n eagerlyLoaded: {label: i18nString(UIStrings.lazyLoadNotApplied), value: imageLoadingAttr !== 'lazy'},\n },\n });\n}\n\ninterface LCPImageDiscoveryData {\n checklist: Exclude<LCPDiscoveryInsightModel['checklist'], undefined>;\n request: Types.Events.SyntheticNetworkRequest;\n discoveryDelay: Types.Timing.Micro|null;\n estimatedSavings: Types.Timing.Milli|null;\n}\n\n/**\n * TODO: this extra transformation (getImageData) should not be necessary.\n */\nexport function getImageData(model: LCPDiscoveryInsightModel): LCPImageDiscoveryData|null {\n if (!model.lcpRequest || !model.checklist) {\n return null;\n }\n\n const shouldIncreasePriorityHint = !model.checklist.priorityHinted.value;\n const shouldPreloadImage = !model.checklist.requestDiscoverable.value;\n const shouldRemoveLazyLoading = !model.checklist.eagerlyLoaded.value;\n\n const imageLCP = shouldIncreasePriorityHint !== undefined && shouldPreloadImage !== undefined &&\n shouldRemoveLazyLoading !== undefined;\n\n // Shouldn't render anything if lcp insight is null or lcp is text.\n if (!imageLCP) {\n return null;\n }\n\n const data: LCPImageDiscoveryData = {\n checklist: model.checklist,\n request: model.lcpRequest,\n discoveryDelay: null,\n estimatedSavings: model.metricSavings?.LCP ?? null,\n };\n\n if (model.earliestDiscoveryTimeTs && model.lcpRequest) {\n const discoveryDelay = model.lcpRequest.ts - model.earliestDiscoveryTimeTs;\n data.discoveryDelay = Types.Timing.Micro(discoveryDelay);\n }\n\n return data;\n}\n\nexport function createOverlays(model: LCPDiscoveryInsightModel): Types.Overlays.Overlay[] {\n const imageResults = getImageData(model);\n if (!imageResults?.discoveryDelay) {\n return [];\n }\n\n const delay = Helpers.Timing.traceWindowFromMicroSeconds(\n Types.Timing.Micro(imageResults.request.ts - imageResults.discoveryDelay),\n imageResults.request.ts,\n );\n\n return [\n {\n type: 'ENTRY_OUTLINE',\n entry: imageResults.request,\n outlineReason: 'ERROR',\n },\n {\n type: 'CANDY_STRIPED_TIME_RANGE',\n bounds: delay,\n entry: imageResults.request,\n },\n {\n type: 'TIMESPAN_BREAKDOWN',\n sections: [{\n bounds: delay,\n // This is overridden in the component.\n label: `${imageResults.discoveryDelay} microseconds`,\n showDuration: false,\n }],\n entry: imageResults.request,\n renderLocation: 'ABOVE_EVENT',\n },\n ];\n}\n"]}
1
+ {"version":3,"file":"LCPDiscovery.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/insights/LCPDiscovery.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\nimport * as i18n from '../../../core/i18n/i18n.js';\nimport * as Handlers from '../handlers/handlers.js';\nimport * as Helpers from '../helpers/helpers.js';\nimport * as Types from '../types/types.js';\n\nimport {calculateDocFirstByteTs} from './Common.js';\nimport {\n type Checklist,\n InsightCategory,\n InsightKeys,\n type InsightModel,\n type InsightSetContext,\n InsightWarning,\n type PartialInsightModel,\n} from './types.js';\n\nexport const UIStrings = {\n /**\n * @description Title of an insight that provides details about the LCP metric, and the network requests necessary to load it. Details how the LCP request was discoverable - in other words, the path necessary to load it (ex: network requests, JavaScript)\n */\n title: 'LCP request discovery',\n /**\n * @description Description of an insight that provides details about the LCP metric, and the network requests necessary to load it.\n */\n description:\n 'Optimize LCP by making the LCP image [discoverable](https://web.dev/articles/optimize-lcp#1_eliminate_resource_load_delay) from the HTML immediately, and [avoiding lazy-loading](https://web.dev/articles/lcp-lazy-loading)',\n /**\n * @description Text to tell the user how long after the earliest discovery time their LCP element loaded.\n * @example {401ms} PH1\n */\n lcpLoadDelay: 'LCP image loaded {PH1} after earliest start point.',\n /**\n * @description Text to tell the user that a fetchpriority property value of \"high\" is applied to the LCP request.\n */\n fetchPriorityApplied: 'fetchpriority=high applied',\n /**\n * @description Text to tell the user that a fetchpriority property value of \"high\" should be applied to the LCP request.\n */\n fetchPriorityShouldBeApplied: 'fetchpriority=high should be applied',\n /**\n * @description Text to tell the user that the LCP request is discoverable in the initial document.\n */\n requestDiscoverable: 'Request is discoverable in initial document',\n /**\n * @description Text to tell the user that the LCP request does not have the lazy load property applied.\n */\n lazyLoadNotApplied: 'lazy load not applied',\n /**\n * @description Text status indicating that the the Largest Contentful Paint (LCP) metric timing was not found. \"LCP\" is an acronym and should not be translated.\n */\n noLcp: 'No LCP detected',\n /**\n * @description Text status indicating that the Largest Contentful Paint (LCP) metric was text rather than an image. \"LCP\" is an acronym and should not be translated.\n */\n noLcpResource: 'No LCP resource detected because the LCP is not an image',\n} as const;\n\nconst str_ = i18n.i18n.registerUIStrings('models/trace/insights/LCPDiscovery.ts', UIStrings);\nexport const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);\n\nexport function isLCPDiscovery(model: InsightModel): model is LCPDiscoveryInsightModel {\n return model.insightKey === 'LCPDiscovery';\n}\nexport type LCPDiscoveryInsightModel = InsightModel<typeof UIStrings, {\n lcpEvent?: Types.Events.LargestContentfulPaintCandidate,\n /** The network request for the LCP image, if there was one. */\n lcpRequest?: Types.Events.SyntheticNetworkRequest,\n earliestDiscoveryTimeTs?: Types.Timing.Micro,\n checklist?: Checklist<'priorityHinted'|'requestDiscoverable'|'eagerlyLoaded'>,\n}>;\n\nfunction finalize(partialModel: PartialInsightModel<LCPDiscoveryInsightModel>): LCPDiscoveryInsightModel {\n const relatedEvents = partialModel.lcpEvent && partialModel.lcpRequest ?\n // TODO: add entire request initiator chain?\n [partialModel.lcpEvent, partialModel.lcpRequest] :\n [];\n return {\n insightKey: InsightKeys.LCP_DISCOVERY,\n strings: UIStrings,\n title: i18nString(UIStrings.title),\n description: i18nString(UIStrings.description),\n category: InsightCategory.LCP,\n state: partialModel.lcpRequest && partialModel.checklist &&\n (!partialModel.checklist.eagerlyLoaded.value || !partialModel.checklist.requestDiscoverable.value ||\n !partialModel.checklist.priorityHinted.value) ?\n 'fail' :\n 'pass',\n ...partialModel,\n relatedEvents,\n };\n}\n\nexport function generateInsight(\n parsedTrace: Handlers.Types.ParsedTrace, context: InsightSetContext): LCPDiscoveryInsightModel {\n if (!context.navigation) {\n return finalize({});\n }\n\n const networkRequests = parsedTrace.NetworkRequests;\n\n const frameMetrics = parsedTrace.PageLoadMetrics.metricScoresByFrameId.get(context.frameId);\n if (!frameMetrics) {\n throw new Error('no frame metrics');\n }\n\n const navMetrics = frameMetrics.get(context.navigationId);\n if (!navMetrics) {\n throw new Error('no navigation metrics');\n }\n const metricScore = navMetrics.get(Handlers.ModelHandlers.PageLoadMetrics.MetricName.LCP);\n const lcpEvent = metricScore?.event;\n if (!lcpEvent || !Types.Events.isLargestContentfulPaintCandidate(lcpEvent)) {\n return finalize({warnings: [InsightWarning.NO_LCP]});\n }\n\n const docRequest = networkRequests.byId.get(context.navigationId);\n if (!docRequest) {\n return finalize({warnings: [InsightWarning.NO_DOCUMENT_REQUEST]});\n }\n\n const lcpRequest = parsedTrace.LargestImagePaint.lcpRequestByNavigationId.get(context.navigationId);\n if (!lcpRequest) {\n return finalize({lcpEvent});\n }\n\n const initiatorUrl = lcpRequest.args.data.initiator?.url;\n // TODO(b/372319476): Explore using trace event HTMLDocumentParser::FetchQueuedPreloads to determine if the request\n // is discovered by the preload scanner.\n const initiatedByMainDoc =\n lcpRequest?.args.data.initiator?.type === 'parser' && docRequest.args.data.url === initiatorUrl;\n const imgPreloadedOrFoundInHTML = lcpRequest?.args.data.isLinkPreload || initiatedByMainDoc;\n\n const imageLoadingAttr = lcpEvent.args.data?.loadingAttr;\n const imageFetchPriorityHint = lcpRequest?.args.data.fetchPriorityHint;\n // This is the earliest discovery time an LCP request could have - it's TTFB (as an absolute timestamp).\n const earliestDiscoveryTime = calculateDocFirstByteTs(docRequest);\n\n const priorityHintFound = imageFetchPriorityHint === 'high';\n\n return finalize({\n lcpEvent,\n lcpRequest,\n earliestDiscoveryTimeTs: earliestDiscoveryTime ? Types.Timing.Micro(earliestDiscoveryTime) : undefined,\n checklist: {\n priorityHinted: {\n label: priorityHintFound ? i18nString(UIStrings.fetchPriorityApplied) :\n i18nString(UIStrings.fetchPriorityShouldBeApplied),\n value: priorityHintFound\n },\n requestDiscoverable: {label: i18nString(UIStrings.requestDiscoverable), value: imgPreloadedOrFoundInHTML},\n eagerlyLoaded: {label: i18nString(UIStrings.lazyLoadNotApplied), value: imageLoadingAttr !== 'lazy'},\n },\n });\n}\n\ninterface LCPImageDiscoveryData {\n checklist: Exclude<LCPDiscoveryInsightModel['checklist'], undefined>;\n request: Types.Events.SyntheticNetworkRequest;\n discoveryDelay: Types.Timing.Micro|null;\n estimatedSavings: Types.Timing.Milli|null;\n}\n\n/**\n * TODO: this extra transformation (getImageData) should not be necessary.\n */\nexport function getImageData(model: LCPDiscoveryInsightModel): LCPImageDiscoveryData|null {\n if (!model.lcpRequest || !model.checklist) {\n return null;\n }\n\n const shouldIncreasePriorityHint = !model.checklist.priorityHinted.value;\n const shouldPreloadImage = !model.checklist.requestDiscoverable.value;\n const shouldRemoveLazyLoading = !model.checklist.eagerlyLoaded.value;\n\n const imageLCP = shouldIncreasePriorityHint !== undefined && shouldPreloadImage !== undefined &&\n shouldRemoveLazyLoading !== undefined;\n\n // Shouldn't render anything if lcp insight is null or lcp is text.\n if (!imageLCP) {\n return null;\n }\n\n const data: LCPImageDiscoveryData = {\n checklist: model.checklist,\n request: model.lcpRequest,\n discoveryDelay: null,\n estimatedSavings: model.metricSavings?.LCP ?? null,\n };\n\n if (model.earliestDiscoveryTimeTs && model.lcpRequest) {\n const discoveryDelay = model.lcpRequest.ts - model.earliestDiscoveryTimeTs;\n data.discoveryDelay = Types.Timing.Micro(discoveryDelay);\n }\n\n return data;\n}\n\nexport function createOverlays(model: LCPDiscoveryInsightModel): Types.Overlays.Overlay[] {\n const imageResults = getImageData(model);\n if (!imageResults?.discoveryDelay) {\n return [];\n }\n\n const delay = Helpers.Timing.traceWindowFromMicroSeconds(\n Types.Timing.Micro(imageResults.request.ts - imageResults.discoveryDelay),\n imageResults.request.ts,\n );\n\n return [\n {\n type: 'ENTRY_OUTLINE',\n entry: imageResults.request,\n outlineReason: 'ERROR',\n },\n {\n type: 'CANDY_STRIPED_TIME_RANGE',\n bounds: delay,\n entry: imageResults.request,\n },\n {\n type: 'TIMESPAN_BREAKDOWN',\n sections: [{\n bounds: delay,\n // This is overridden in the component.\n label: `${imageResults.discoveryDelay} microseconds`,\n showDuration: false,\n }],\n entry: imageResults.request,\n renderLocation: 'ABOVE_EVENT',\n },\n ];\n}\n"]}
@@ -167,7 +167,7 @@ export interface MetaData {
167
167
  visualTrackConfig?: PersistedTraceVisualConfig;
168
168
  hostDPR?: number;
169
169
  }
170
- interface MetadataSourceMap {
170
+ export interface MetadataSourceMap {
171
171
  url: string;
172
172
  /** If not defined, then this was a data url. */
173
173
  sourceMapUrl?: string;
@@ -175,4 +175,3 @@ interface MetadataSourceMap {
175
175
  }
176
176
  export type Contents = TraceFile | Event[];
177
177
  export declare function traceEventKeyToValues(key: SerializableKey): SerializableKeyValues;
178
- export {};
@@ -1 +1 @@
1
- {"version":3,"file":"File.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/types/File.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2023 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\nimport type * as SDK from '../../../core/sdk/sdk.js';\nimport type * as Protocol from '../../../generated/protocol.js';\nimport type * as CrUXManager from '../../../models/crux-manager/crux-manager.js';\n\nimport type {TraceWindowMicro} from './Timing.js';\nimport type {Event, LegacyTimelineFrame, ProcessID, SampleIndex, ThreadID} from './TraceEvents.js';\n\nexport interface TraceFile {\n traceEvents: readonly Event[];\n metadata: MetaData;\n}\n\nexport interface Breadcrumb {\n window: TraceWindowMicro;\n child: Breadcrumb|null;\n}\n\nexport enum DataOrigin {\n CPU_PROFILE = 'CPUProfile',\n TRACE_EVENTS = 'TraceEvents',\n}\n\n/**\n * The Entries link can have 3 stated:\n * 1. The Link creation is not started yet, meaning only the button that needs to be clicked to start creating the link is visible.\n * 2. Pending to event - the creation is started, but the entry that the link points to has not been chosen yet\n * 3. Link connected - final state, both entries present\n */\nexport enum EntriesLinkState {\n CREATION_NOT_STARTED = 'creation_not_started',\n PENDING_TO_EVENT = 'pending_to_event',\n CONNECTED = 'connected',\n}\n\nexport enum EventKeyType {\n RAW_EVENT = 'r',\n SYNTHETIC_EVENT = 's',\n PROFILE_CALL = 'p',\n LEGACY_TIMELINE_FRAME = 'l',\n}\n\n/**\n * Represents an object that is saved in the file when user created annotations in the timeline.\n *\n * Expected to add more annotations.\n */\nexport interface SerializedAnnotations {\n entryLabels: EntryLabelAnnotationSerialized[];\n labelledTimeRanges: TimeRangeAnnotationSerialized[];\n linksBetweenEntries: EntriesLinkAnnotationSerialized[];\n}\n\n/**\n * Represents an object that is used to store the Entry Label annotation that is created when a user creates a label for an entry in the timeline.\n */\nexport interface EntryLabelAnnotation {\n type: 'ENTRY_LABEL';\n entry: Event|LegacyTimelineFrame;\n label: string;\n}\n\n/**\n * Represents an object that is used to store the Labelled Time Range Annotation that is created when a user creates a Time Range Selection in the timeline.\n */\nexport interface TimeRangeAnnotation {\n type: 'TIME_RANGE';\n label: string;\n bounds: TraceWindowMicro;\n}\n\nexport interface EntriesLinkAnnotation {\n type: 'ENTRIES_LINK';\n state: EntriesLinkState;\n entryFrom: Event;\n entryTo?: Event;\n}\n\n/**\n * Represents an object that is saved in the file when a user creates a label for an entry in the timeline.\n */\nexport interface EntryLabelAnnotationSerialized {\n entry: SerializableKey;\n label: string;\n}\n\n/**\n * Represents an object that is saved in the file when a user creates a time range with a label in the timeline.\n */\nexport interface TimeRangeAnnotationSerialized {\n bounds: TraceWindowMicro;\n label: string;\n}\n\n/**\n * Represents an object that is saved in the file when a user creates a link between entries in the timeline.\n */\nexport interface EntriesLinkAnnotationSerialized {\n entryFrom: SerializableKey;\n entryTo: SerializableKey;\n}\n\n/**\n * `Annotation` are the user-created annotations that are saved into the metadata.\n * Those annotations are rendered on the timeline by `Overlays.ts`\n *\n * TODO: Implement other OverlayAnnotations (annotated time ranges, links between entries).\n * TODO: Save/load overlay annotations to/from the trace file.\n */\nexport type Annotation = EntryLabelAnnotation|TimeRangeAnnotation|EntriesLinkAnnotation;\n\nexport function isTimeRangeAnnotation(annotation: Annotation): annotation is TimeRangeAnnotation {\n return annotation.type === 'TIME_RANGE';\n}\n\nexport function isEntryLabelAnnotation(annotation: Annotation): annotation is EntryLabelAnnotation {\n return annotation.type === 'ENTRY_LABEL';\n}\n\nexport function isEntriesLinkAnnotation(annotation: Annotation): annotation is EntriesLinkAnnotation {\n return annotation.type === 'ENTRIES_LINK';\n}\n\n// Serializable keys are created for trace events to be able to save\n// references to timeline events in a trace file. These keys enable\n// user modifications that can be saved. See go/cpq:event-data-json for\n// more details on the key format.\nexport type RawEventKey = `${EventKeyType.RAW_EVENT}-${number}`;\nexport type SyntheticEventKey = `${EventKeyType.SYNTHETIC_EVENT}-${number}`;\nexport type ProfileCallKey = `${EventKeyType.PROFILE_CALL}-${ProcessID}-${ThreadID}-${SampleIndex}-${Protocol.integer}`;\nexport type LegacyTimelineFrameKey = `${EventKeyType.LEGACY_TIMELINE_FRAME}-${number}`;\nexport type SerializableKey = RawEventKey|ProfileCallKey|SyntheticEventKey|LegacyTimelineFrameKey;\n\n// Serializable keys values objects contain data that maps the keys to original Trace Events\nexport interface RawEventKeyValues {\n type: EventKeyType.RAW_EVENT;\n rawIndex: number;\n}\n\nexport interface SyntheticEventKeyValues {\n type: EventKeyType.SYNTHETIC_EVENT;\n rawIndex: number;\n}\n\nexport interface ProfileCallKeyValues {\n type: EventKeyType.PROFILE_CALL;\n processID: ProcessID;\n threadID: ThreadID;\n sampleIndex: SampleIndex;\n protocol: Protocol.integer;\n}\n\nexport interface LegacyTimelineFrameKeyValues {\n type: EventKeyType.LEGACY_TIMELINE_FRAME;\n rawIndex: number;\n}\n\nexport type SerializableKeyValues =\n RawEventKeyValues|ProfileCallKeyValues|SyntheticEventKeyValues|LegacyTimelineFrameKeyValues;\n\nexport interface Modifications {\n entriesModifications: {\n // Entries hidden by the user\n hiddenEntries: SerializableKey[],\n // Entries that parent a hiddenEntry\n expandableEntries: SerializableKey[],\n };\n initialBreadcrumb: Breadcrumb;\n annotations: SerializedAnnotations;\n}\n\n// IMPORTANT: this is the same as PerfUI.FlameChart.PersistedGroupConfig\n// However, the PerfUI code should not depend on the model/trace, and similarly\n// this model cannot depend on that code, so we duplicate it.\nexport interface TrackVisualConfig {\n hidden: boolean;\n expanded: boolean;\n originalIndex: number;\n visualIndex: number;\n trackName: string;\n}\n\n/**\n * Stores the visual config if the user has modified it. Split into \"main\" and\n * \"network\" so we can pass the relevant config into the right data provider.\n * NOTE: as of August 2025 (M141) we currently do not export this in new\n * traces, or use it if an existing trace is imported with it.\n */\nexport interface PersistedTraceVisualConfig {\n main: TrackVisualConfig[]|null;\n network: TrackVisualConfig[]|null;\n}\n\n/**\n * Trace metadata that we persist to the file. This will allow us to\n * store specifics for the trace, e.g., which tracks should be visible\n * on load.\n */\nexport interface MetaData {\n source?: 'DevTools';\n startTime?: string;\n emulatedDeviceTitle?: string;\n // Only set if network throttling is active.\n networkThrottling?: string;\n // Only set if network throttling is active.\n networkThrottlingConditions?: Omit<SDK.NetworkManager.Conditions, 'title'>;\n // Only set if CPU throttling is active.\n cpuThrottling?: number;\n dataOrigin?: DataOrigin;\n enhancedTraceVersion?: number;\n modifications?: Modifications;\n cruxFieldData?: CrUXManager.PageResult[];\n /** Currently only stores JS maps, not CSS. This never stores data url source maps. */\n sourceMaps?: MetadataSourceMap[];\n visualTrackConfig?: PersistedTraceVisualConfig;\n hostDPR?: number;\n}\n\ninterface MetadataSourceMap {\n url: string;\n /** If not defined, then this was a data url. */\n sourceMapUrl?: string;\n sourceMap: SDK.SourceMap.SourceMapV3;\n}\n\nexport type Contents = TraceFile|Event[];\n\nexport function traceEventKeyToValues(key: SerializableKey): SerializableKeyValues {\n const parts = key.split('-');\n const type = parts[0];\n\n switch (type) {\n case EventKeyType.PROFILE_CALL:\n if (parts.length !== 5 ||\n !(parts.every((part, i) => i === 0 || typeof part === 'number' || !isNaN(parseInt(part, 10))))) {\n throw new Error(`Invalid ProfileCallKey: ${key}`);\n }\n return {\n type: parts[0],\n processID: parseInt(parts[1], 10),\n threadID: parseInt(parts[2], 10),\n sampleIndex: parseInt(parts[3], 10),\n protocol: parseInt(parts[4], 10),\n } as ProfileCallKeyValues;\n case EventKeyType.RAW_EVENT:\n if (parts.length !== 2 || !(typeof parts[1] === 'number' || !isNaN(parseInt(parts[1], 10)))) {\n throw new Error(`Invalid RawEvent Key: ${key}`);\n }\n return {\n type: parts[0],\n rawIndex: parseInt(parts[1], 10),\n } as RawEventKeyValues;\n case EventKeyType.SYNTHETIC_EVENT:\n if (parts.length !== 2 || !(typeof parts[1] === 'number' || !isNaN(parseInt(parts[1], 10)))) {\n throw new Error(`Invalid SyntheticEvent Key: ${key}`);\n }\n return {\n type: parts[0],\n rawIndex: parseInt(parts[1], 10),\n } as SyntheticEventKeyValues;\n case EventKeyType.LEGACY_TIMELINE_FRAME: {\n if (parts.length !== 2 || Number.isNaN(parseInt(parts[1], 10))) {\n throw new Error(`Invalid LegacyTimelineFrame Key: ${key}`);\n }\n return {\n type,\n rawIndex: parseInt(parts[1], 10),\n };\n }\n\n default:\n throw new Error(`Unknown trace event key: ${key}`);\n }\n}\n"]}
1
+ {"version":3,"file":"File.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/types/File.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2023 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\nimport type * as SDK from '../../../core/sdk/sdk.js';\nimport type * as Protocol from '../../../generated/protocol.js';\nimport type * as CrUXManager from '../../../models/crux-manager/crux-manager.js';\n\nimport type {TraceWindowMicro} from './Timing.js';\nimport type {Event, LegacyTimelineFrame, ProcessID, SampleIndex, ThreadID} from './TraceEvents.js';\n\nexport interface TraceFile {\n traceEvents: readonly Event[];\n metadata: MetaData;\n}\n\nexport interface Breadcrumb {\n window: TraceWindowMicro;\n child: Breadcrumb|null;\n}\n\nexport enum DataOrigin {\n CPU_PROFILE = 'CPUProfile',\n TRACE_EVENTS = 'TraceEvents',\n}\n\n/**\n * The Entries link can have 3 stated:\n * 1. The Link creation is not started yet, meaning only the button that needs to be clicked to start creating the link is visible.\n * 2. Pending to event - the creation is started, but the entry that the link points to has not been chosen yet\n * 3. Link connected - final state, both entries present\n */\nexport enum EntriesLinkState {\n CREATION_NOT_STARTED = 'creation_not_started',\n PENDING_TO_EVENT = 'pending_to_event',\n CONNECTED = 'connected',\n}\n\nexport enum EventKeyType {\n RAW_EVENT = 'r',\n SYNTHETIC_EVENT = 's',\n PROFILE_CALL = 'p',\n LEGACY_TIMELINE_FRAME = 'l',\n}\n\n/**\n * Represents an object that is saved in the file when user created annotations in the timeline.\n *\n * Expected to add more annotations.\n */\nexport interface SerializedAnnotations {\n entryLabels: EntryLabelAnnotationSerialized[];\n labelledTimeRanges: TimeRangeAnnotationSerialized[];\n linksBetweenEntries: EntriesLinkAnnotationSerialized[];\n}\n\n/**\n * Represents an object that is used to store the Entry Label annotation that is created when a user creates a label for an entry in the timeline.\n */\nexport interface EntryLabelAnnotation {\n type: 'ENTRY_LABEL';\n entry: Event|LegacyTimelineFrame;\n label: string;\n}\n\n/**\n * Represents an object that is used to store the Labelled Time Range Annotation that is created when a user creates a Time Range Selection in the timeline.\n */\nexport interface TimeRangeAnnotation {\n type: 'TIME_RANGE';\n label: string;\n bounds: TraceWindowMicro;\n}\n\nexport interface EntriesLinkAnnotation {\n type: 'ENTRIES_LINK';\n state: EntriesLinkState;\n entryFrom: Event;\n entryTo?: Event;\n}\n\n/**\n * Represents an object that is saved in the file when a user creates a label for an entry in the timeline.\n */\nexport interface EntryLabelAnnotationSerialized {\n entry: SerializableKey;\n label: string;\n}\n\n/**\n * Represents an object that is saved in the file when a user creates a time range with a label in the timeline.\n */\nexport interface TimeRangeAnnotationSerialized {\n bounds: TraceWindowMicro;\n label: string;\n}\n\n/**\n * Represents an object that is saved in the file when a user creates a link between entries in the timeline.\n */\nexport interface EntriesLinkAnnotationSerialized {\n entryFrom: SerializableKey;\n entryTo: SerializableKey;\n}\n\n/**\n * `Annotation` are the user-created annotations that are saved into the metadata.\n * Those annotations are rendered on the timeline by `Overlays.ts`\n *\n * TODO: Implement other OverlayAnnotations (annotated time ranges, links between entries).\n * TODO: Save/load overlay annotations to/from the trace file.\n */\nexport type Annotation = EntryLabelAnnotation|TimeRangeAnnotation|EntriesLinkAnnotation;\n\nexport function isTimeRangeAnnotation(annotation: Annotation): annotation is TimeRangeAnnotation {\n return annotation.type === 'TIME_RANGE';\n}\n\nexport function isEntryLabelAnnotation(annotation: Annotation): annotation is EntryLabelAnnotation {\n return annotation.type === 'ENTRY_LABEL';\n}\n\nexport function isEntriesLinkAnnotation(annotation: Annotation): annotation is EntriesLinkAnnotation {\n return annotation.type === 'ENTRIES_LINK';\n}\n\n// Serializable keys are created for trace events to be able to save\n// references to timeline events in a trace file. These keys enable\n// user modifications that can be saved. See go/cpq:event-data-json for\n// more details on the key format.\nexport type RawEventKey = `${EventKeyType.RAW_EVENT}-${number}`;\nexport type SyntheticEventKey = `${EventKeyType.SYNTHETIC_EVENT}-${number}`;\nexport type ProfileCallKey = `${EventKeyType.PROFILE_CALL}-${ProcessID}-${ThreadID}-${SampleIndex}-${Protocol.integer}`;\nexport type LegacyTimelineFrameKey = `${EventKeyType.LEGACY_TIMELINE_FRAME}-${number}`;\nexport type SerializableKey = RawEventKey|ProfileCallKey|SyntheticEventKey|LegacyTimelineFrameKey;\n\n// Serializable keys values objects contain data that maps the keys to original Trace Events\nexport interface RawEventKeyValues {\n type: EventKeyType.RAW_EVENT;\n rawIndex: number;\n}\n\nexport interface SyntheticEventKeyValues {\n type: EventKeyType.SYNTHETIC_EVENT;\n rawIndex: number;\n}\n\nexport interface ProfileCallKeyValues {\n type: EventKeyType.PROFILE_CALL;\n processID: ProcessID;\n threadID: ThreadID;\n sampleIndex: SampleIndex;\n protocol: Protocol.integer;\n}\n\nexport interface LegacyTimelineFrameKeyValues {\n type: EventKeyType.LEGACY_TIMELINE_FRAME;\n rawIndex: number;\n}\n\nexport type SerializableKeyValues =\n RawEventKeyValues|ProfileCallKeyValues|SyntheticEventKeyValues|LegacyTimelineFrameKeyValues;\n\nexport interface Modifications {\n entriesModifications: {\n // Entries hidden by the user\n hiddenEntries: SerializableKey[],\n // Entries that parent a hiddenEntry\n expandableEntries: SerializableKey[],\n };\n initialBreadcrumb: Breadcrumb;\n annotations: SerializedAnnotations;\n}\n\n// IMPORTANT: this is the same as PerfUI.FlameChart.PersistedGroupConfig\n// However, the PerfUI code should not depend on the model/trace, and similarly\n// this model cannot depend on that code, so we duplicate it.\nexport interface TrackVisualConfig {\n hidden: boolean;\n expanded: boolean;\n originalIndex: number;\n visualIndex: number;\n trackName: string;\n}\n\n/**\n * Stores the visual config if the user has modified it. Split into \"main\" and\n * \"network\" so we can pass the relevant config into the right data provider.\n * NOTE: as of August 2025 (M141) we currently do not export this in new\n * traces, or use it if an existing trace is imported with it.\n */\nexport interface PersistedTraceVisualConfig {\n main: TrackVisualConfig[]|null;\n network: TrackVisualConfig[]|null;\n}\n\n/**\n * Trace metadata that we persist to the file. This will allow us to\n * store specifics for the trace, e.g., which tracks should be visible\n * on load.\n */\nexport interface MetaData {\n source?: 'DevTools';\n startTime?: string;\n emulatedDeviceTitle?: string;\n // Only set if network throttling is active.\n networkThrottling?: string;\n // Only set if network throttling is active.\n networkThrottlingConditions?: Omit<SDK.NetworkManager.Conditions, 'title'>;\n // Only set if CPU throttling is active.\n cpuThrottling?: number;\n dataOrigin?: DataOrigin;\n enhancedTraceVersion?: number;\n modifications?: Modifications;\n cruxFieldData?: CrUXManager.PageResult[];\n /** Currently only stores JS maps, not CSS. This never stores data url source maps. */\n sourceMaps?: MetadataSourceMap[];\n visualTrackConfig?: PersistedTraceVisualConfig;\n hostDPR?: number;\n}\n\nexport interface MetadataSourceMap {\n url: string;\n /** If not defined, then this was a data url. */\n sourceMapUrl?: string;\n sourceMap: SDK.SourceMap.SourceMapV3;\n}\n\nexport type Contents = TraceFile|Event[];\n\nexport function traceEventKeyToValues(key: SerializableKey): SerializableKeyValues {\n const parts = key.split('-');\n const type = parts[0];\n\n switch (type) {\n case EventKeyType.PROFILE_CALL:\n if (parts.length !== 5 ||\n !(parts.every((part, i) => i === 0 || typeof part === 'number' || !isNaN(parseInt(part, 10))))) {\n throw new Error(`Invalid ProfileCallKey: ${key}`);\n }\n return {\n type: parts[0],\n processID: parseInt(parts[1], 10),\n threadID: parseInt(parts[2], 10),\n sampleIndex: parseInt(parts[3], 10),\n protocol: parseInt(parts[4], 10),\n } as ProfileCallKeyValues;\n case EventKeyType.RAW_EVENT:\n if (parts.length !== 2 || !(typeof parts[1] === 'number' || !isNaN(parseInt(parts[1], 10)))) {\n throw new Error(`Invalid RawEvent Key: ${key}`);\n }\n return {\n type: parts[0],\n rawIndex: parseInt(parts[1], 10),\n } as RawEventKeyValues;\n case EventKeyType.SYNTHETIC_EVENT:\n if (parts.length !== 2 || !(typeof parts[1] === 'number' || !isNaN(parseInt(parts[1], 10)))) {\n throw new Error(`Invalid SyntheticEvent Key: ${key}`);\n }\n return {\n type: parts[0],\n rawIndex: parseInt(parts[1], 10),\n } as SyntheticEventKeyValues;\n case EventKeyType.LEGACY_TIMELINE_FRAME: {\n if (parts.length !== 2 || Number.isNaN(parseInt(parts[1], 10))) {\n throw new Error(`Invalid LegacyTimelineFrame Key: ${key}`);\n }\n return {\n type,\n rawIndex: parseInt(parts[1], 10),\n };\n }\n\n default:\n throw new Error(`Unknown trace event key: ${key}`);\n }\n}\n"]}
@@ -1,4 +1,3 @@
1
- // import type * as UI from '../../../ui/legacy/legacy.js';
2
1
  // import type * as Lit from '../../../ui/lit/lit.js';
3
2
  import type { EntriesLinkState } from './File.js';
4
3
  import type { Micro, TraceWindowMicro } from './Timing.js';
@@ -107,7 +106,10 @@ export interface TimingsMarkerFieldResult {
107
106
  }
108
107
  export interface BottomInfoBar {
109
108
  type: 'BOTTOM_INFO_BAR';
110
- infobar: any;
109
+ infobar: {
110
+ element: HTMLElement;
111
+ dispose: () => void;
112
+ };
111
113
  }
112
114
  /**
113
115
  * All supported overlay types.
@@ -1 +1 @@
1
- {"version":3,"file":"Overlays.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/types/Overlays.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,yEAAyE;AACzE,6BAA6B","sourcesContent":["// Copyright 2025 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\nimport type * as UI from '../../../ui/legacy/legacy.js';\nimport type * as Lit from '../../../ui/lit/lit.js';\n\nimport type {EntriesLinkState} from './File.js';\nimport type {Micro, TraceWindowMicro} from './Timing.js';\nimport type {Event, LegacyTimelineFrame, PageLoadEvent} from './TraceEvents.js';\n\n/**\n * Represents which flamechart an entry is rendered in.\n * We need to know this because when we place an overlay for an entry we need\n * to adjust its Y value if it's in the main chart which is drawn below the\n * network chart\n */\nexport type EntryChartLocation = 'main'|'network';\n\n/**\n * You can add overlays to trace events, but also right now frames are drawn on\n * the timeline but they are not trace events, so we need to allow for that.\n * In the future when the frames track has been migrated to be powered by\n * animation frames (crbug.com/345144583), we can remove the requirement to\n * support TimelineFrame instances (which themselves will be removed from the\n * codebase.)\n */\nexport type OverlayEntry = Event|LegacyTimelineFrame;\n\n/**\n * Represents when a user has selected an entry in the timeline\n */\nexport interface EntrySelected {\n type: 'ENTRY_SELECTED';\n entry: OverlayEntry;\n}\n\n/**\n * Drawn around an entry when we want to highlight it to the user.\n */\nexport interface EntryOutline {\n type: 'ENTRY_OUTLINE';\n entry: OverlayEntry;\n outlineReason: 'ERROR'|'INFO';\n}\n\n/**\n * Represents an object created when a user creates a label for an entry in the timeline.\n */\nexport interface EntryLabel {\n type: 'ENTRY_LABEL';\n entry: OverlayEntry;\n label: string;\n}\n\nexport interface EntriesLink {\n type: 'ENTRIES_LINK';\n state: EntriesLinkState;\n entryFrom: OverlayEntry;\n entryTo?: OverlayEntry;\n}\n\n/**\n * Represents a time range on the trace. Also used when the user shift+clicks\n * and drags to create a time range.\n */\nexport interface TimeRangeLabel {\n type: 'TIME_RANGE';\n bounds: TraceWindowMicro;\n label: string;\n showDuration: boolean;\n}\n\n/**\n * Used to highlight with a red-candy stripe a time range. It takes an entry\n * because this entry is the row that will be used to place the candy stripe,\n * and its height will be set to the height of that row.\n */\nexport interface CandyStripedTimeRange {\n type: 'CANDY_STRIPED_TIME_RANGE';\n bounds: TraceWindowMicro;\n entry: Event;\n}\n\n/**\n * An EntryBreakdown, or section, that makes up a TimespanBreakdown.\n */\nexport interface TimespanBreakdownEntryBreakdown {\n bounds: TraceWindowMicro;\n label: string|Lit.LitTemplate;\n showDuration: boolean;\n}\n\n/**\n * Represents a timespan on a trace broken down into parts. Each part has a label to it.\n * If an entry is defined, the breakdown will be vertically positioned based on it.\n */\nexport interface TimespanBreakdown {\n type: 'TIMESPAN_BREAKDOWN';\n sections: TimespanBreakdownEntryBreakdown[];\n entry?: Event;\n renderLocation?: 'BOTTOM_OF_TIMELINE'|'BELOW_EVENT'|'ABOVE_EVENT';\n}\n\nexport interface TimestampMarker {\n type: 'TIMESTAMP_MARKER';\n timestamp: Micro;\n}\n\n/**\n * Represents a timings marker. This has a line that runs up the whole canvas.\n * We can hold an array of entries, in the case we want to hold more than one with the same timestamp.\n * The adjusted timestamp being the timestamp for the event adjusted by closest navigation.\n */\nexport interface TimingsMarker {\n type: 'TIMINGS_MARKER';\n entries: PageLoadEvent[];\n entryToFieldResult: Map<PageLoadEvent, TimingsMarkerFieldResult>;\n adjustedTimestamp: Micro;\n}\n\nexport interface TimingsMarkerFieldResult {\n value: Micro;\n pageScope: 'url'|'origin';\n}\n\nexport interface BottomInfoBar {\n type: 'BOTTOM_INFO_BAR';\n infobar: UI.Infobar.Infobar;\n}\n\n/**\n * All supported overlay types.\n */\nexport type Overlay = EntrySelected|EntryOutline|TimeRangeLabel|EntryLabel|EntriesLink|TimespanBreakdown|\n TimestampMarker|CandyStripedTimeRange|TimingsMarker|BottomInfoBar;\n"]}
1
+ {"version":3,"file":"Overlays.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/types/Overlays.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,yEAAyE;AACzE,6BAA6B","sourcesContent":["// Copyright 2025 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\nimport type * as Lit from '../../../ui/lit/lit.js';\n\nimport type {EntriesLinkState} from './File.js';\nimport type {Micro, TraceWindowMicro} from './Timing.js';\nimport type {Event, LegacyTimelineFrame, PageLoadEvent} from './TraceEvents.js';\n\n/**\n * Represents which flamechart an entry is rendered in.\n * We need to know this because when we place an overlay for an entry we need\n * to adjust its Y value if it's in the main chart which is drawn below the\n * network chart\n */\nexport type EntryChartLocation = 'main'|'network';\n\n/**\n * You can add overlays to trace events, but also right now frames are drawn on\n * the timeline but they are not trace events, so we need to allow for that.\n * In the future when the frames track has been migrated to be powered by\n * animation frames (crbug.com/345144583), we can remove the requirement to\n * support TimelineFrame instances (which themselves will be removed from the\n * codebase.)\n */\nexport type OverlayEntry = Event|LegacyTimelineFrame;\n\n/**\n * Represents when a user has selected an entry in the timeline\n */\nexport interface EntrySelected {\n type: 'ENTRY_SELECTED';\n entry: OverlayEntry;\n}\n\n/**\n * Drawn around an entry when we want to highlight it to the user.\n */\nexport interface EntryOutline {\n type: 'ENTRY_OUTLINE';\n entry: OverlayEntry;\n outlineReason: 'ERROR'|'INFO';\n}\n\n/**\n * Represents an object created when a user creates a label for an entry in the timeline.\n */\nexport interface EntryLabel {\n type: 'ENTRY_LABEL';\n entry: OverlayEntry;\n label: string;\n}\n\nexport interface EntriesLink {\n type: 'ENTRIES_LINK';\n state: EntriesLinkState;\n entryFrom: OverlayEntry;\n entryTo?: OverlayEntry;\n}\n\n/**\n * Represents a time range on the trace. Also used when the user shift+clicks\n * and drags to create a time range.\n */\nexport interface TimeRangeLabel {\n type: 'TIME_RANGE';\n bounds: TraceWindowMicro;\n label: string;\n showDuration: boolean;\n}\n\n/**\n * Used to highlight with a red-candy stripe a time range. It takes an entry\n * because this entry is the row that will be used to place the candy stripe,\n * and its height will be set to the height of that row.\n */\nexport interface CandyStripedTimeRange {\n type: 'CANDY_STRIPED_TIME_RANGE';\n bounds: TraceWindowMicro;\n entry: Event;\n}\n\n/**\n * An EntryBreakdown, or section, that makes up a TimespanBreakdown.\n */\nexport interface TimespanBreakdownEntryBreakdown {\n bounds: TraceWindowMicro;\n label: string|Lit.LitTemplate;\n showDuration: boolean;\n}\n\n/**\n * Represents a timespan on a trace broken down into parts. Each part has a label to it.\n * If an entry is defined, the breakdown will be vertically positioned based on it.\n */\nexport interface TimespanBreakdown {\n type: 'TIMESPAN_BREAKDOWN';\n sections: TimespanBreakdownEntryBreakdown[];\n entry?: Event;\n renderLocation?: 'BOTTOM_OF_TIMELINE'|'BELOW_EVENT'|'ABOVE_EVENT';\n}\n\nexport interface TimestampMarker {\n type: 'TIMESTAMP_MARKER';\n timestamp: Micro;\n}\n\n/**\n * Represents a timings marker. This has a line that runs up the whole canvas.\n * We can hold an array of entries, in the case we want to hold more than one with the same timestamp.\n * The adjusted timestamp being the timestamp for the event adjusted by closest navigation.\n */\nexport interface TimingsMarker {\n type: 'TIMINGS_MARKER';\n entries: PageLoadEvent[];\n entryToFieldResult: Map<PageLoadEvent, TimingsMarkerFieldResult>;\n adjustedTimestamp: Micro;\n}\n\nexport interface TimingsMarkerFieldResult {\n value: Micro;\n pageScope: 'url'|'origin';\n}\n\nexport interface BottomInfoBar {\n type: 'BOTTOM_INFO_BAR';\n // In DevTools, this infobar is a UI.Infobar.Infobar but we can't refer to\n // the type here.\n infobar: {\n element: HTMLElement,\n dispose: () => void,\n };\n}\n\n/**\n * All supported overlay types.\n */\nexport type Overlay = EntrySelected|EntryOutline|TimeRangeLabel|EntryLabel|EntriesLink|TimespanBreakdown|\n TimestampMarker|CandyStripedTimeRange|TimingsMarker|BottomInfoBar;\n"]}
@@ -295,7 +295,7 @@ export interface SyntheticNetworkRequest extends Complete, SyntheticBased<Phase.
295
295
  *
296
296
  * Note, this is not the same as URL.protocol.
297
297
  *
298
- * Example values (not exhaustive): http/0.9, http/1.0, http/1.1, http, h2, h3-Q050, data, blob
298
+ * Example values (not exhaustive): http/0.9, http/1.0, http/1.1, http, h2, h3-Q050, data, blob, file
299
299
  */
300
300
  protocol: string;
301
301
  redirects: SyntheticNetworkRedirect[];
@@ -769,10 +769,10 @@ export declare const NO_NAVIGATION = "NO_NAVIGATION";
769
769
  */
770
770
  export type NavigationId = string | typeof NO_NAVIGATION;
771
771
  /**
772
- * This is a synthetic Layout shift cluster. Not based on a raw event as there's no concept
773
- * of this as a trace event.
772
+ * This is a synthetic Layout shift cluster. The rawSourceEvent is the worst layout shift event
773
+ * in the cluster.
774
774
  */
775
- export interface SyntheticLayoutShiftCluster {
775
+ export interface SyntheticLayoutShiftCluster extends SyntheticBased<Phase.COMPLETE> {
776
776
  name: 'SyntheticLayoutShiftCluster';
777
777
  clusterWindow: TraceWindowMicro;
778
778
  clusterCumulativeScore: number;
@@ -870,7 +870,8 @@ interface ResourceReceiveResponseTimingData {
870
870
  pushEnd: Milli;
871
871
  pushStart: Milli;
872
872
  receiveHeadersEnd: Milli;
873
- receiveHeadersStart: Milli;
873
+ /** M116. */
874
+ receiveHeadersStart?: Milli;
874
875
  /** When the network service is about to handle a request, ie. just before going to the HTTP cache or going to the network for DNS/connection setup. */
875
876
  requestTime: Seconds;
876
877
  sendEnd: Milli;