Repository: jlfwong/speedscope
Pull request #313 (2020-09-29 21:19:42)

Add support for Safari profiles
This is just a copy of #300 with prettier run so I can land this without having a commit on master that's red

coveralls: 
[![Coverage Status](https://coveralls.io/builds/33812394/badge)](https://coveralls.io/builds/33812394)

Coverage increased (+0.3%) to 47.113% when pulling **2347a8fcd5c79007684729c71db7f100be7490b2 on jlfwong/safari-formatted** into **069c0194a6d3fd10f9cabce24735e3bd91fc1556 on master**.


Patch:
diff --git a/README.md b/README.md
index 39c05d2..471bdc0 100644
--- a/README.md
+++ b/README.md
@@ -36,6 +36,7 @@ speedscope is designed to ingest profiles from a variety of different profilers
 - JavaScript
   - [Importing from Chrome](https://github.com/jlfwong/speedscope/wiki/Importing-from-Chrome)
   - [Importing from Firefox](https://github.com/jlfwong/speedscope/wiki/Importing-from-Firefox)
+  - [Importing from Safari](https://github.com/jlfwong/speedscope/wiki/Importing-from-Safari)
   - [Importing from Node.js](https://github.com/jlfwong/speedscope/wiki/Importing-from-Node.js)
 - Ruby
   - [Importing from stackprof](https://github.com/jlfwong/speedscope/wiki/Importing-from-stackprof-(ruby))
diff --git a/sample/profiles/Safari/13.1/simple.html-recording.json b/sample/profiles/Safari/13.1/simple.html-recording.json
new file mode 100644
index 0000000..44f081f
--- /dev/null
+++ b/sample/profiles/Safari/13.1/simple.html-recording.json
@@ -0,0 +1,1 @@
+{"version":1,"recording":{"displayName":"Grabación de Control temporal 1","startTime":4.163978071999736,"endTime":6.692470462992787,"discontinuities":[],"instrumentTypes":["timeline-record-type-network","timeline-record-type-layout","timeline-record-type-script","timeline-record-type-rendering-frame","timeline-record-type-cpu"],"records":[{"type":"timeline-record-type-cpu","timestamp":4.174319899990223,"usage":0,"threads":[{"name":"Main Thread","usage":0,"type":"main"},{"name":"JavaScriptCore bmalloc scavenger","usage":0,"type":"webkit"},{"name":"","usage":0},{"name":"","usage":0},{"name":"WebCore: Scrolling","usage":0,"type":"webkit"},{"name":"","usage":0},{"name":"","usage":0},{"name":"","usage":0},{"name":"JIT Worklist Helper Thread","usage":0,"type":"webkit"},{"name":"DFG Worklist Worker Thread","usage":0,"type":"webkit"},{"name":"FTL Worklist Worker Thread","usage":0,"type":"webkit"},{"name":"DFG Worklist Worker Thread","usage":0,"type":"webkit"},{"name":"FTL Worklist Worker Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"}]},{"type":"timeline-record-type-network","archiveStartTime":1595926028.6345506,"entry":{"pageref":"page_0","startedDateTime":"2020-07-28T08:47:13.097Z","time":0,"request":{"method":"GET","url":"file:///speedscope/sample/programs/javascript/simple.html","httpVersion":"","cookies":[],"headers":[{"name":"Upgrade-Insecure-Requests","value":"1"},{"name":"Accept","value":"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"},{"name":"User-Agent","value":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15"}],"queryString":[],"headersSize":0,"bodySize":0},"response":{"status":0,"statusText":"","httpVersion":"","cookies":[],"headers":[],"content":{"size":0,"compression":0,"mimeType":"text/html"},"redirectURL":"","headersSize":0,"bodySize":0,"_transferSize":0},"cache":{},"timings":{"blocked":0,"dns":-1,"connect":-1,"ssl":-1,"send":0,"wait":0,"receive":0},"_fetchType":"Network Load","_priority":"high"}},{"type":"timeline-record-type-network","archiveStartTime":1595926028.6345935,"entry":{"pageref":"page_0","startedDateTime":"2020-07-28T08:47:13.126Z","time":0,"request":{"method":"GET","url":"file:///speedscope/sample/programs/javascript/simple.js","httpVersion":"","cookies":[],"headers":[{"name":"User-Agent","value":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15"},{"name":"Accept","value":"*/*"}],"queryString":[],"headersSize":0,"bodySize":0},"response":{"status":0,"statusText":"","httpVersion":"","cookies":[],"headers":[],"content":{"size":0,"compression":0,"mimeType":"text/javascript"},"redirectURL":"","headersSize":0,"bodySize":0,"_transferSize":0},"cache":{},"timings":{"blocked":0,"dns":-1,"connect":-1,"ssl":-1,"send":0,"wait":0,"receive":0},"_fetchType":"Network Load","_priority":"high"}},{"type":"timeline-record-type-rendering-frame","startTime":4.467359638976632,"endTime":4.492321582976729},{"type":"timeline-record-type-layout","eventType":"recalculate-styles","startTime":4.471605148981325,"endTime":4.471845255000517},{"type":"timeline-record-type-layout","eventType":"invalidate-styles","startTime":4.474205192993395,"endTime":4.474205192993395},{"type":"timeline-record-type-layout","eventType":"recalculate-styles","startTime":4.474220764997881,"endTime":4.4884890760004055},{"type":"timeline-record-type-layout","eventType":"invalidate-styles","startTime":4.491480771976057,"endTime":4.491480771976057},{"type":"timeline-record-type-rendering-frame","startTime":4.492437198990956,"endTime":4.507455001003109},{"type":"timeline-record-type-layout","eventType":"recalculate-styles","startTime":4.492771623976296,"endTime":4.507425039977534},{"type":"timeline-record-type-rendering-frame","startTime":4.507473898003809,"endTime":4.5526633959962055},{"type":"timeline-record-type-script","eventType":"script-evaluated","startTime":4.513065324979834,"endTime":4.545594235998578,"details":"","extraDetails":null},{"type":"timeline-record-type-layout","eventType":"invalidate-styles","startTime":4.545641086995602,"endTime":4.545641086995602},{"type":"timeline-record-type-layout","eventType":"recalculate-styles","startTime":4.545677282992983,"endTime":4.545750883989967},{"type":"timeline-record-type-layout","eventType":"invalidate-layout","startTime":4.545747961994493,"endTime":4.545747961994493},{"type":"timeline-record-type-layout","eventType":"layout","startTime":4.545889647997683,"endTime":4.546186422987375,"quad":[0,0,1153,0,1153,814,0,814]},{"type":"timeline-record-type-layout","eventType":"paint","startTime":4.547691310988739,"endTime":4.5477759249915835,"quad":[1024,575,1153,575,1153,877,1024,877]},{"type":"timeline-record-type-layout","eventType":"paint","startTime":4.5483486430021,"endTime":4.548382615990704,"quad":[512,575,1024,575,1024,877,512,877]},{"type":"timeline-record-type-layout","eventType":"paint","startTime":4.54878898098832,"endTime":4.548809086001711,"quad":[0,575,512,575,512,877,0,877]},{"type":"timeline-record-type-layout","eventType":"paint","startTime":4.549124169978313,"endTime":4.549146869976539,"quad":[1024,63,1153,63,1153,575,1024,575]},{"type":"timeline-record-type-layout","eventType":"paint","startTime":4.549656325980322,"endTime":4.5496779779787175,"quad":[512,63,1024,63,1024,575,512,575]},{"type":"timeline-record-type-layout","eventType":"paint","startTime":4.5500254239887,"endTime":4.5500440479954705,"quad":[0,63,512,63,512,575,0,575]},{"type":"timeline-record-type-layout","eventType":"composite","startTime":4.550935890991241,"endTime":4.551078663003864},{"type":"timeline-record-type-script","eventType":"api-script-evaluated","startTime":4.555242645990802,"endTime":4.555461437994381,"details":"","extraDetails":null},{"type":"timeline-record-type-script","eventType":"api-script-evaluated","startTime":4.572847526986152,"endTime":4.573226868000347,"details":"","extraDetails":null},{"type":"timeline-record-type-rendering-frame","startTime":4.621030936978059,"endTime":4.621331342990743},{"type":"timeline-record-type-layout","eventType":"paint","startTime":4.62123295100173,"endTime":4.621266011003172,"quad":[0,63,1153,63,1153,877,0,877]},{"type":"timeline-record-type-cpu","timestamp":4.676897135999752,"usage":21.69999885559082,"threads":[{"name":"Main Thread","usage":18.600000381469727,"type":"main"},{"name":"JavaScriptCore bmalloc scavenger","usage":0,"type":"webkit"},{"name":"","usage":0.30000001192092896},{"name":"","usage":0.6000000238418579},{"name":"WebCore: Scrolling","usage":0,"type":"webkit"},{"name":"","usage":0.6000000238418579},{"name":"","usage":0.10000000149011612},{"name":"","usage":0},{"name":"JIT Worklist Helper Thread","usage":0,"type":"webkit"},{"name":"DFG Worklist Worker Thread","usage":0.30000001192092896,"type":"webkit"},{"name":"FTL Worklist Worker Thread","usage":0.800000011920929,"type":"webkit"},{"name":"DFG Worklist Worker Thread","usage":0,"type":"webkit"},{"name":"FTL Worklist Worker Thread","usage":0.4000000059604645,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"}]},{"type":"timeline-record-type-script","eventType":"api-script-evaluated","startTime":4.79833571598283,"endTime":4.798853140004212,"details":"","extraDetails":null},{"type":"timeline-record-type-script","eventType":"api-script-evaluated","startTime":4.8038428709842265,"endTime":4.803945658000885,"details":"","extraDetails":null},{"type":"timeline-record-type-script","eventType":"api-script-evaluated","startTime":4.807004723988939,"endTime":4.807019564992515,"details":"","extraDetails":null},{"type":"timeline-record-type-rendering-frame","startTime":4.92319532399415,"endTime":4.923449570982484},{"type":"timeline-record-type-layout","eventType":"paint","startTime":4.923351720004575,"endTime":4.923385464993771,"quad":[0,63,1153,63,1153,877,0,877]},{"type":"timeline-record-type-cpu","timestamp":5.17931115499232,"usage":3.999999761581421,"threads":[{"name":"Main Thread","usage":3.700000047683716,"type":"main"},{"name":"JavaScriptCore bmalloc scavenger","usage":0,"type":"webkit"},{"name":"","usage":0.10000000149011612},{"name":"","usage":0.10000000149011612},{"name":"WebCore: Scrolling","usage":0,"type":"webkit"},{"name":"","usage":0},{"name":"","usage":0},{"name":"","usage":0},{"name":"JIT Worklist Helper Thread","usage":0,"type":"webkit"},{"name":"DFG Worklist Worker Thread","usage":0,"type":"webkit"},{"name":"FTL Worklist Worker Thread","usage":0.10000000149011612,"type":"webkit"},{"name":"DFG Worklist Worker Thread","usage":0,"type":"webkit"},{"name":"FTL Worklist Worker Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"}]},{"type":"timeline-record-type-cpu","timestamp":5.683525055996142,"usage":0.6000000238418579,"threads":[{"name":"Main Thread","usage":0.6000000238418579,"type":"main"},{"name":"JavaScriptCore bmalloc scavenger","usage":0,"type":"webkit"},{"name":"","usage":0},{"name":"","usage":0},{"name":"WebCore: Scrolling","usage":0,"type":"webkit"},{"name":"","usage":0},{"name":"","usage":0},{"name":"","usage":0},{"name":"JIT Worklist Helper Thread","usage":0,"type":"webkit"},{"name":"DFG Worklist Worker Thread","usage":0,"type":"webkit"},{"name":"FTL Worklist Worker Thread","usage":0,"type":"webkit"},{"name":"DFG Worklist Worker Thread","usage":0,"type":"webkit"},{"name":"FTL Worklist Worker Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"}]},{"type":"timeline-record-type-cpu","timestamp":6.188329044991406,"usage":0,"threads":[{"name":"Main Thread","usage":0,"type":"main"},{"name":"JavaScriptCore bmalloc scavenger","usage":0,"type":"webkit"},{"name":"","usage":0},{"name":"","usage":0},{"name":"WebCore: Scrolling","usage":0,"type":"webkit"},{"name":"","usage":0},{"name":"","usage":0},{"name":"","usage":0},{"name":"JIT Worklist Helper Thread","usage":0,"type":"webkit"},{"name":"DFG Worklist Worker Thread","usage":0,"type":"webkit"},{"name":"FTL Worklist Worker Thread","usage":0,"type":"webkit"},{"name":"DFG Worklist Worker Thread","usage":0,"type":"webkit"},{"name":"FTL Worklist Worker Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"}]},{"type":"timeline-record-type-cpu","timestamp":6.692470462992787,"usage":0,"threads":[{"name":"Main Thread","usage":0,"type":"main"},{"name":"JavaScriptCore bmalloc scavenger","usage":0,"type":"webkit"},{"name":"","usage":0},{"name":"","usage":0},{"name":"WebCore: Scrolling","usage":0,"type":"webkit"},{"name":"","usage":0},{"name":"","usage":0},{"name":"","usage":0},{"name":"JIT Worklist Helper Thread","usage":0,"type":"webkit"},{"name":"DFG Worklist Worker Thread","usage":0,"type":"webkit"},{"name":"FTL Worklist Worker Thread","usage":0,"type":"webkit"},{"name":"DFG Worklist Worker Thread","usage":0,"type":"webkit"},{"name":"FTL Worklist Worker Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"}]}],"markers":[{"time":4.545802743989043,"type":"dom-content-event"},{"time":4.545883116981713,"type":"load-event"}],"memoryPressureEvents":[],"sampleStackTraces":[{"timestamp":4.472667262976756,"stackFrames":[{"sourceID":"7","name":"","line":2,"column":10,"url":"__InjectedScript_CommandLineAPIModuleSource.js","expressionLocation":{"line":34,"column":13}},{"sourceID":"6","name":"injectModule","line":109,"column":13,"url":"__InjectedScript_InjectedScriptSource.js","expressionLocation":{"line":111,"column":85}}]},{"timestamp":4.514140182000119,"stackFrames":[{"sourceID":"8","name":"gamma","line":20,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":22,"column":19}},{"sourceID":"8","name":"delta","line":14,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":16,"column":10}},{"sourceID":"8","name":"alpha","line":1,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":4,"column":10}},{"sourceID":"8","name":"(program)","line":1,"column":1,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":28,"column":6}}]},{"timestamp":4.516020926996134,"stackFrames":[{"sourceID":"8","name":"gamma","line":20,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":22,"column":19}},{"sourceID":"8","name":"delta","line":14,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":16,"column":10}},{"sourceID":"8","name":"alpha","line":1,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":4,"column":10}},{"sourceID":"8","name":"(program)","line":1,"column":1,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":28,"column":6}}]},{"timestamp":4.5172975349996705,"stackFrames":[{"sourceID":"8","name":"gamma","line":20,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":22,"column":19}},{"sourceID":"8","name":"delta","line":14,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":16,"column":10}},{"sourceID":"8","name":"alpha","line":1,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":4,"column":10}},{"sourceID":"8","name":"(program)","line":1,"column":1,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":28,"column":6}}]},{"timestamp":4.518430622003507,"stackFrames":[{"sourceID":"8","name":"gamma","line":20,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":22,"column":19}},{"sourceID":"8","name":"delta","line":14,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":16,"column":10}},{"sourceID":"8","name":"alpha","line":1,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":4,"column":10}},{"sourceID":"8","name":"(program)","line":1,"column":1,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":28,"column":6}}]},{"timestamp":4.519961087004049,"stackFrames":[{"sourceID":"8","name":"gamma","line":20,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":22,"column":19}},{"sourceID":"8","name":"delta","line":14,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":16,"column":10}},{"sourceID":"8","name":"alpha","line":1,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":4,"column":10}},{"sourceID":"8","name":"(program)","line":1,"column":1,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":28,"column":6}}]},{"timestamp":4.521057823993033,"stackFrames":[{"sourceID":"8","name":"gamma","line":20,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":22,"column":19}},{"sourceID":"8","name":"delta","line":14,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":16,"column":10}},{"sourceID":"8","name":"alpha","line":1,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":4,"column":10}},{"sourceID":"8","name":"(program)","line":1,"column":1,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":28,"column":6}}]},{"timestamp":4.523174777976237,"stackFrames":[{"sourceID":"8","name":"gamma","line":20,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":22,"column":19}},{"sourceID":"8","name":"delta","line":14,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":16,"column":10}},{"sourceID":"8","name":"alpha","line":1,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":4,"column":10}},{"sourceID":"8","name":"(program)","line":1,"column":1,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":28,"column":6}}]},{"timestamp":4.527184398990357,"stackFrames":[{"sourceID":"8","name":"delta","line":14,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":15,"column":19}},{"sourceID":"8","name":"alpha","line":1,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":4,"column":10}},{"sourceID":"8","name":"(program)","line":1,"column":1,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":28,"column":6}}]},{"timestamp":4.528929433989106,"stackFrames":[{"sourceID":"8","name":"delta","line":14,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":15,"column":19}},{"sourceID":"8","name":"alpha","line":1,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":4,"column":10}},{"sourceID":"8","name":"(program)","line":1,"column":1,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":28,"column":6}}]},{"timestamp":4.529900363995694,"stackFrames":[{"sourceID":"8","name":"gamma","line":20,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":22,"column":19}},{"sourceID":"8","name":"beta","line":8,"column":14,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":10,"column":10}},{"sourceID":"8","name":"alpha","line":1,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":3,"column":9}},{"sourceID":"8","name":"(program)","line":1,"column":1,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":28,"column":6}}]},{"timestamp":4.530873391981004,"stackFrames":[{"sourceID":"8","name":"gamma","line":20,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":22,"column":19}},{"sourceID":"8","name":"beta","line":8,"column":14,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":10,"column":10}},{"sourceID":"8","name":"alpha","line":1,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":3,"column":9}},{"sourceID":"8","name":"(program)","line":1,"column":1,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":28,"column":6}}]},{"timestamp":4.532054496987257,"stackFrames":[{"sourceID":"8","name":"gamma","line":20,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":22,"column":19}},{"sourceID":"8","name":"beta","line":8,"column":14,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":10,"column":10}},{"sourceID":"8","name":"alpha","line":1,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":3,"column":9}},{"sourceID":"8","name":"(program)","line":1,"column":1,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":28,"column":6}}]},{"timestamp":4.533258385985391,"stackFrames":[{"sourceID":"8","name":"gamma","line":20,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":22,"column":19}},{"sourceID":"8","name":"delta","line":14,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":16,"column":10}},{"sourceID":"8","name":"alpha","line":1,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":4,"column":10}},{"sourceID":"8","name":"(program)","line":1,"column":1,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":28,"column":6}}]},{"timestamp":4.534531568002421,"stackFrames":[{"sourceID":"8","name":"gamma","line":20,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":22,"column":19}},{"sourceID":"8","name":"beta","line":8,"column":14,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":10,"column":10}},{"sourceID":"8","name":"alpha","line":1,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":3,"column":9}},{"sourceID":"8","name":"(program)","line":1,"column":1,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":28,"column":6}}]},{"timestamp":4.536061600985704,"stackFrames":[{"sourceID":"8","name":"gamma","line":20,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":22,"column":19}},{"sourceID":"8","name":"delta","line":14,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":16,"column":10}},{"sourceID":"8","name":"alpha","line":1,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":4,"column":10}},{"sourceID":"8","name":"(program)","line":1,"column":1,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":28,"column":6}}]},{"timestamp":4.537377392989583,"stackFrames":[{"sourceID":"8","name":"gamma","line":20,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":22,"column":19}},{"sourceID":"8","name":"delta","line":14,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":16,"column":10}},{"sourceID":"8","name":"alpha","line":1,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":4,"column":10}},{"sourceID":"8","name":"(program)","line":1,"column":1,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":28,"column":6}}]},{"timestamp":4.538861920998897,"stackFrames":[{"sourceID":"8","name":"gamma","line":20,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":22,"column":19}},{"sourceID":"8","name":"beta","line":8,"column":14,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":10,"column":10}},{"sourceID":"8","name":"alpha","line":1,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":3,"column":9}},{"sourceID":"8","name":"(program)","line":1,"column":1,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":28,"column":6}}]},{"timestamp":4.540356789977523,"stackFrames":[{"sourceID":"8","name":"gamma","line":20,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":22,"column":19}},{"sourceID":"8","name":"beta","line":8,"column":14,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":10,"column":10}},{"sourceID":"8","name":"alpha","line":1,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":3,"column":9}},{"sourceID":"8","name":"(program)","line":1,"column":1,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":28,"column":6}}]},{"timestamp":4.541859452001518,"stackFrames":[{"sourceID":"8","name":"gamma","line":20,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":22,"column":19}},{"sourceID":"8","name":"beta","line":8,"column":14,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":10,"column":10}},{"sourceID":"8","name":"alpha","line":1,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":3,"column":9}},{"sourceID":"8","name":"(program)","line":1,"column":1,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":28,"column":6}}]},{"timestamp":4.543215062993113,"stackFrames":[{"sourceID":"8","name":"gamma","line":20,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":22,"column":19}},{"sourceID":"8","name":"delta","line":14,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":16,"column":10}},{"sourceID":"8","name":"alpha","line":1,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":4,"column":10}},{"sourceID":"8","name":"(program)","line":1,"column":1,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":28,"column":6}}]},{"timestamp":4.5444030729995575,"stackFrames":[{"sourceID":"8","name":"gamma","line":20,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":22,"column":19}},{"sourceID":"8","name":"beta","line":8,"column":14,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":10,"column":10}},{"sourceID":"8","name":"alpha","line":1,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":3,"column":9}},{"sourceID":"8","name":"(program)","line":1,"column":1,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":28,"column":6}}]},{"timestamp":4.798422646999825,"stackFrames":[{"sourceID":"10","name":"firstOpenSearchURLString","line":4,"column":102,"url":"","expressionLocation":{"line":4,"column":117}}]}],"sampleDurations":[0.001,0.001547111237111191,0.001547111237111191,0.001547111237111191,0.001547111237111191,0.001547111237111191,0.001547111237111191,0.001547111237111191,0.001547111237111191,0.001547111237111191,0.001547111237111191,0.001547111237111191,0.001547111237111191,0.001547111237111191,0.001547111237111191,0.001547111237111191,0.001547111237111191,0.001547111237111191,0.001547111237111191,0.001547111237111191,0.001547111237111191,0.001547111237111191,0.0005174240213818848]},"overview":{"secondsPerPixel":0.01,"scrollStartTime":2.887978071999762,"selectionStartTime":4.163978071999736,"selectionDuration":15}}
diff --git a/src/import/index.ts b/src/import/index.ts
index d9f09da..3f46d52 100644
--- a/src/import/index.ts
+++ b/src/import/index.ts
@@ -15,6 +15,7 @@ import {importSpeedscopeProfiles} from '../lib/file-format'
 import {importFromV8ProfLog} from './v8proflog'
 import {importFromLinuxPerf} from './linux-tools-perf'
 import {importFromHaskell} from './haskell'
+import {importFromSafari} from './safari'
 import {ProfileDataSource, TextProfileDataSource, MaybeCompressedDataReader} from './utils'
 import {importAsPprofProfile} from './pprof'
 import {decodeBase64} from '../lib/utils'
@@ -131,6 +132,9 @@ async function _importProfileGroup(dataSource: ProfileDataSource): Promise<Profi
   } else if (fileName.endsWith('.heapprofile')) {
     console.log('Importing as Chrome Heap Profile')
     return toGroup(importFromChromeHeapProfile(JSON.parse(contents)))
+  } else if (fileName.endsWith('-recording.json')) {
+    console.log('Importing as Safari profile')
+    return toGroup(importFromSafari(JSON.parse(contents)))
   }
 
   // Second pass: Try to guess what file format it is based on structure
@@ -169,6 +173,9 @@ async function _importProfileGroup(dataSource: ProfileDataSource): Promise<Profi
     } else if ('rts_arguments' in parsed && 'initial_capabilities' in parsed) {
       console.log('Importing as Haskell GHC JSON Profile')
       return importFromHaskell(parsed)
+    } else if ('recording' in parsed && 'sampleStackTraces' in parsed.recording) {
+      console.log('Importing as Safari profile')
+      return toGroup(importFromSafari(JSON.parse(contents)))
     }
   } else {
     // Format is not JSON
diff --git a/src/import/safari.ts b/src/import/safari.ts
new file mode 100644
index 0000000..d83fc32
--- /dev/null
+++ b/src/import/safari.ts
@@ -0,0 +1,120 @@
+import {Profile, FrameInfo, StackListProfileBuilder} from '../lib/profile'
+import {TimeFormatter} from '../lib/value-formatters'
+
+interface Record {
+  type: string
+  eventType?: string
+  startTime?: number
+  endTime?: number
+  // timeline-record-type-cpu
+  timestamp?: number
+  usage?: number
+  threads?: any[]
+  // timeline-record-type-script
+  details?: number | string | any
+  extraDetails?: null | any
+  // timeline-record-type-network
+  archiveStartTime?: number
+  entry?: any
+  // timeline-record-type-layout
+  quad?: number[]
+}
+
+interface ExprLocation {
+  line: number
+  column: number
+}
+
+interface StackFrame {
+  sourceID: string
+  name: string
+  line: number
+  column: number
+  url: string
+  expressionLocation?: ExprLocation
+}
+
+interface Sample {
+  timestamp: number
+  stackFrames: StackFrame[]
+}
+
+interface Recording {
+  displayName: string
+  startTime: number
+  endTime: number
+  discontinuities: any[]
+  instrumentTypes: string[]
+  records: Record[]
+  markers: any[]
+  memoryPressureEvents: any[]
+  sampleStackTraces: Sample[]
+  sampleDurations: number[]
+}
+
+interface Overview {
+  secondsPerPixel: number
+  scrollStartTime: number
+  selectionStartTime: number
+  selectionDuration: number
+}
+
+interface SafariProfile {
+  version: number
+  recording: Recording
+  overview: Overview
+}
+
+function makeStack(frames: StackFrame[]): FrameInfo[] {
+  return frames
+    .map(({name, url, line, column}) => ({
+      key: `${name}:${url}:${line}:${column}`,
+      file: url,
+      line,
+      col: column,
+      name: name || '(anonymous)',
+    }))
+    .reverse()
+}
+
+export function importFromSafari(contents: SafariProfile): Profile | null {
+  if (contents.version !== 1) {
+    console.warn(`Unknown Safari profile version ${contents.version}... Might be incompatible.`)
+  }
+
+  const {recording} = contents
+  const {sampleStackTraces, sampleDurations} = recording
+
+  const count = sampleStackTraces.length
+  if (count < 1) {
+    console.warn('Empty profile')
+    return null
+  }
+
+  const profileDuration =
+    sampleStackTraces[count - 1].timestamp - sampleStackTraces[0].timestamp + sampleDurations[0]
+  const profile = new StackListProfileBuilder(profileDuration)
+
+  let previousEndTime = Number.MAX_VALUE
+
+  sampleStackTraces.forEach((sample, i) => {
+    const endTime = sample.timestamp
+    const duration = sampleDurations[i]
+    const startTime = endTime - duration
+    const idleDurationBefore = startTime - previousEndTime
+
+    // FIXME: 2ms is a lot, but Safari's timestamps and durations don't line up very well and will create
+    // phantom idle time
+    if (idleDurationBefore > 0.002) {
+      profile.appendSampleWithWeight([], idleDurationBefore)
+    }
+
+    profile.appendSampleWithWeight(makeStack(sample.stackFrames), duration)
+
+    previousEndTime = endTime
+  })
+
+  profile.setValueFormatter(new TimeFormatter('seconds'))
+  profile.setName(recording.displayName)
+  return profile.build()
+}


Test patch:
diff --git a/src/import/__snapshots__/safari.test.ts.snap b/src/import/__snapshots__/safari.test.ts.snap
new file mode 100644
index 0000000..ba6d904
--- /dev/null
+++ b/src/import/__snapshots__/safari.test.ts.snap
@@ -0,0 +1,101 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`importFromSafari 1`] = `
+Object {
+  "frames": Array [
+    Frame {
+      "col": 13,
+      "file": "__InjectedScript_InjectedScriptSource.js",
+      "key": "injectModule:__InjectedScript_InjectedScriptSource.js:109:13",
+      "line": 109,
+      "name": "injectModule",
+      "selfWeight": 0,
+      "totalWeight": 0.001,
+    },
+    Frame {
+      "col": 10,
+      "file": "__InjectedScript_CommandLineAPIModuleSource.js",
+      "key": ":__InjectedScript_CommandLineAPIModuleSource.js:2:10",
+      "line": 2,
+      "name": "(anonymous)",
+      "selfWeight": 0.001,
+      "totalWeight": 0.001,
+    },
+    Frame {
+      "col": 1,
+      "file": "file:///speedscope/sample/programs/javascript/simple.js",
+      "key": "(program):file:///speedscope/sample/programs/javascript/simple.js:1:1",
+      "line": 1,
+      "name": "(program)",
+      "selfWeight": 0,
+      "totalWeight": 0.03248933597933502,
+    },
+    Frame {
+      "col": 15,
+      "file": "file:///speedscope/sample/programs/javascript/simple.js",
+      "key": "alpha:file:///speedscope/sample/programs/javascript/simple.js:1:15",
+      "line": 1,
+      "name": "alpha",
+      "selfWeight": 0,
+      "totalWeight": 0.03248933597933502,
+    },
+    Frame {
+      "col": 15,
+      "file": "file:///speedscope/sample/programs/javascript/simple.js",
+      "key": "delta:file:///speedscope/sample/programs/javascript/simple.js:14:15",
+      "line": 14,
+      "name": "delta",
+      "selfWeight": 0.003094222474222382,
+      "totalWeight": 0.020112446082445484,
+    },
+    Frame {
+      "col": 15,
+      "file": "file:///speedscope/sample/programs/javascript/simple.js",
+      "key": "gamma:file:///speedscope/sample/programs/javascript/simple.js:20:15",
+      "line": 20,
+      "name": "gamma",
+      "selfWeight": 0.029395113505112636,
+      "totalWeight": 0.029395113505112636,
+    },
+    Frame {
+      "col": 14,
+      "file": "file:///speedscope/sample/programs/javascript/simple.js",
+      "key": "beta:file:///speedscope/sample/programs/javascript/simple.js:8:14",
+      "line": 8,
+      "name": "beta",
+      "selfWeight": 0,
+      "totalWeight": 0.012376889896889526,
+    },
+    Frame {
+      "col": 102,
+      "file": "",
+      "key": "firstOpenSearchURLString::4:102",
+      "line": 4,
+      "name": "firstOpenSearchURLString",
+      "selfWeight": 0.0005174240213818848,
+      "totalWeight": 0.0005174240213818848,
+    },
+  ],
+  "name": "GrabaciÃ³n de Control temporal 1",
+  "stacks": Array [
+    "injectModule;(anonymous) 1.00ms",
+    " 39.93ms",
+    "(program);alpha;delta;gamma 10.83ms",
+    " 2.46ms",
+    "(program);alpha;delta 3.09ms",
+    "(program);alpha;beta;gamma 4.64ms",
+    "(program);alpha;delta;gamma 1.55ms",
+    "(program);alpha;beta;gamma 1.55ms",
+    "(program);alpha;delta;gamma 3.09ms",
+    "(program);alpha;beta;gamma 4.64ms",
+    "(program);alpha;delta;gamma 1.55ms",
+    "(program);alpha;beta;gamma 1.55ms",
+    " 253.50ms",
+    "firstOpenSearchURLString 517.42µs",
+  ],
+}
+`;
+
+exports[`importFromSafari: indexToView 1`] = `0`;
+
+exports[`importFromSafari: profileGroup.name 1`] = `"GrabaciÃ³n de Control temporal 1"`;
diff --git a/src/import/safari.test.ts b/src/import/safari.test.ts
new file mode 100644
index 0000000..a912ca1
--- /dev/null
+++ b/src/import/safari.test.ts
@@ -0,0 +1,5 @@
+import {checkProfileSnapshot} from '../lib/test-utils'
+
+test('importFromSafari', async () => {
+  await checkProfileSnapshot('./sample/profiles/Safari/13.1/simple.html-recording.json')
+})