Previous: My way to prevent the macOS Tahoe update with Little Snitch Articles index Jeff Johnson (My apps, PayPal.Me, Mastodon)
January 22 2026
This may not be news to some web developers, but it was news to me. I’m not much of a web developer, aside from my retro (euphemism for primitive) web sites, which eschew JavaScript. However, I am a web browser extension developer, most notably of StopTheMadness Pro. In general, my extension’s…
Previous: My way to prevent the macOS Tahoe update with Little Snitch Articles index Jeff Johnson (My apps, PayPal.Me, Mastodon)
January 22 2026
This may not be news to some web developers, but it was news to me. I’m not much of a web developer, aside from my retro (euphemism for primitive) web sites, which eschew JavaScript. However, I am a web browser extension developer, most notably of StopTheMadness Pro. In general, my extension’s JavaScript doesn’t consume a lot of memory as far as I can tell, and it certainly doesn’t cause web pages to crash. What led me to investigate memory usage in mobile Safari was the failure of a specific StopTheMadness Pro feature, font replacements, or even more specifically, replacement of web fonts in Safari with user-installed fonts. Safari refuses to display user-installed fonts, I believe to avoid fingerprinting, but Safari will display data: URL fonts, so StopTheMadness Pro converts user-supplied font files into data: URLs for display. Although this usually works great, I got a bug report from a customer on iOS trying to use a relatively large font file, 50 MB. I could reproduce the bug in mobile Safari, not in desktop Safari. My hypothesis was that the extension API runtime.sendMessage() has a memory limit in mobile Safari, but I experienced a lot of problems testing the hypothesis, because I kept making the web page crash! The irony is that the web page I was using for testing purposes kept hitting its own memory limit, which prevented me from producing a message large enough to test the extension API memory limit.
You can test this yourself with the example I’ve provided below. Just input the number of megabytes you want to use, then press the Fill Memory button, which triggers some simple JavaScript:
const longArray = []; for (let i = 0; i < size * 1024 * 1024; ++i) longArray.push("a");
In other words, it adds a little over a million characters to an array for every megabyte you’ve selected.
MB:
Status: Unfilled
There’s no magic number that makes the web page crash, but I can consistently crash a web page in mobile Safari on my 3rd generation iPhone SE at around 100 MB and on my 8th generation iPad at around 200 MB, both devices running iOS 26.2.
A couple of times, my memory test entirely froze my iPad, such that not even the home button worked, and eventually the device appeared to reboot, after spending some time at a dark screen with a spinner in the center. This seems very bad, right? A web page DoS!
You can see how uploading a 50 MB file (via the FileReader API) and creating a data: URL from it could easily crash the web page on iPhone, and I’m not sure there’s anything I can do about it, except to warn customers. Using try {} catch {} blocks in JavaScript doesn’t help at all; there’s no JavaScript exception to catch.
By the way, it turns out that the extension API runtime.sendMessage() also has a memory limit: 64 MB, on both iOS and Mac! My initial testing was on my iPhone, but I had better luck narrowing down the problem on my iPad because the latter apparently has more RAM, so I was able to hit the API memory limit without hitting the web page memory limit. The only good news is that this API does provide a JavaScript exception to catch when the memory limit is exceeded. In my opinion, the extension API and implementation, with arbitrary size limits, was poorly designed, originally by Google and Chrome, later copied by the other web browsers for compatibility with Chrome extensions. The dumbest part is that the API limits only the size of individual messages but does not limit the frequency of messages, so extensions can send a rapid series of messages at the size limit, mostly defeating the purpose of the limit. What the browsers should have done is automatically chunk the messages when necessary, as an implementation detail. The failure to do so left the worst of all worlds to extension developers. There’s not a great way for extensions to determine the exact size of a message before attempting to send it, and if the size limit happens to be exceeded, it’s up to every individual extension developer to implement their own custom chunking implementation to work around the limit. Sigh. I’m reminded of the infamous claim, “640K ought to be enough for anyone.”
Jeff Johnson (My apps, PayPal.Me, Mastodon) Articles index Previous: My way to prevent the macOS Tahoe update with Little Snitch