Hi fellow reader,
I'm ririxi, core maintainer of spicetify. Some people asked me to do a technical explanation of what spotify shipped with macOS Intel versions. This would be my first time writing a post like this, so I apologize for any inscriptions.
Spotify under the hood uses CEF (Chromium Embedded Framework) which is a framework around chromium, essentially electron but for C++. Chromium uses a V8 which is a Javascript engine created by Google. V8 can generate serialized representation (bytecode) of the V8 engine's heap. It contains pre-initialized JavaScript objects and functions.
In simple words; V8 analyzes what provided blob (content of javascript file) uses, initializes needed functions and objects while compiling and then puts its heap into context. Since all of this is pre-initialized, app on the startup can get this context snapshot and very quickly load it into the webview resulting in better startup performance.
My first thought was to modify the snapshot because it includes clear-text javascript file (that's because the blob is still included, only the executed functions and objects are pre-generated into bytecode). So... I made quick PoC to modify this by looking for start marker, and end marker, however this resulted in no results.
It turns out that this file is in UTF-16LE
and not UTF-8
. Quick change and file was modified with our patches! But to my surprise or not, app started crashing. From what I found out while writing this post, V8 Context Snapshot contains a header with number of contexts, version (chromium related), rehashibility (no idea what's that for) and most importantly, checksum.
We also found a reply from V8 developer on stack overflow saying that this format is undocumented and internal and that V8 will disregard any context not generated for right chromium version. Kinda wild I must say.
I dropped the idea of modifying the snapshot because it was too much work at this point.
Spotify uses webpack like most of web apps nowadays with React & React Router. Since 1.1.56 their "codebase" is called "xpui" which essentially holds everything, from mini player to all pages etc. This change was made to make their code as cross-platform as possible (desktop / web).
Spotify still left most of the code in its own files, including something called xpui-snapshot.js
. This file is a start point for webpack. It expects __webpack_modules__
variable to be available and load (most) webpack modules from it.
This is where our v8 context comes in. This context holds that __webpack_modules__
variable and loads it into webview. This includes xpui.js
& vendor~xpui.js
(that's how Spotify bundled their code in previous versions)
So, instead of modifying the binary file, I extracted the content from it, and saved it to xpui-modules.js
file which then we applied our patches on.
After doing so, I made spicetify to load xpui-modules.js
(our patched one) before xpui-snapshot.js
and that worked perfectly.
We now have working spicetify on Spotify version(s) that put xpui into v8_context_snapshot.bin
!
We obviously lose some performance gains that Spotify made with this from this approach, but this ensures that spicetify continue working with new versions if they decide to go with this approach on all platforms :)
nice read 🚀