惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

W
WeLiveSecurity
T
Threatpost
C
CXSECURITY Database RSS Feed - CXSecurity.com
T
Threat Research - Cisco Blogs
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Know Your Adversary
Know Your Adversary
Scott Helme
Scott Helme
S
Schneier on Security
T
The Exploit Database - CXSecurity.com
Latest news
Latest news
雷峰网
雷峰网
T
Tor Project blog
T
Tenable Blog
Spread Privacy
Spread Privacy
博客园 - 叶小钗
D
DataBreaches.Net
美团技术团队
A
Arctic Wolf
Project Zero
Project Zero
L
Lohrmann on Cybersecurity
The GitHub Blog
The GitHub Blog
博客园 - 司徒正美
Security Latest
Security Latest
D
Docker
月光博客
月光博客
C
Cyber Attacks, Cyber Crime and Cyber Security
S
Secure Thoughts
T
Troy Hunt's Blog
U
Unit 42
WordPress大学
WordPress大学
I
Intezer
Forbes - Security
Forbes - Security
Microsoft Azure Blog
Microsoft Azure Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
SecWiki News
SecWiki News
罗磊的独立博客
The Last Watchdog
The Last Watchdog
人人都是产品经理
人人都是产品经理
Y
Y Combinator Blog
aimingoo的专栏
aimingoo的专栏
B
Blog
博客园 - 【当耐特】
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Help Net Security
Help Net Security
S
Security @ Cisco Blogs
Microsoft Security Blog
Microsoft Security Blog
S
SegmentFault 最新的问题
The Cloudflare Blog
V
V2EX

Lobsters

CIFSwitch: a non-universal Linux local root vulnerability RIPE NCC session fixation: poaching logins with an Atlas probe GNOME 2.20 but its Web Components Agentic Search for Context Engineering – Leonie Monigatti Garnix is shutting down [not OC] akashina.tngl.sh/jjc Concerning Emacs (and Jazz) Nitpicking the shell history scene in ‘Tron: Legacy’ What's cooking on SourceHut? Q2 2026 The tenth OpenPGP email summit Package managers that package package managers Clojure on Fennel part three: parsing WordPress at 23 Finding Miscompiles for Fun, Not Profit GitHub - creusot-rs/creusot: Creusot helps you prove your Rust code is correct. Announcing Rust 1.96.0 | Rust Blog A Love Letter to Neovim sqlite AGENTS.md Am I a Bad Friend? CSS vs. JavaScript • Josh W. Comeau Erlang Ecosystem Foundation - Supporting the BEAM community A brief note about slot access cost in Common Lisp Keyboard latency probe Rethinking the GNOME clipboard issues Back to the Building Blocks’ Building Blocks Tech Notes: Theseus: translating win32 to wasm Fast is better than slow Content-addressed Rust builds (or, what kache actually caches) Intent to Prototype: Embedding API Canada’s Bill C-22 and the security cost of collecting more data 5 PostgreSQL locking behaviors that trip people up okmij.org Stop advertising in your commits! | AksDev GitHub - mplsllc/macsurf: A modern web browser for Classic Mac OS 9 PowerPC. Real CSS3, ES5 JavaScript, native HTTPS — built with CodeWarrior on the Carbon API. Introducing DoomBench - Can Your Data Stack Run DOOM? What are some of your favourite developer tools? Building a Scalable Ingestion Pipeline with Temporal (Part 1) Converting shallow Git bundles into normal repositories Are you a member of any professional associations? What is a harmonic? An interactive comic about additive synthesis How Virtual Tables Work in the Itanium C++ ABI Using SwiftUI to Build a Mac-assed App in 2026 Rust (and Slint) on a jailbroken Kindle. ~jack/lambda-on-lambda - Serverless Haskell on AWS - sourcehut git Human proof for FOSS contributions Extremely simple internet radio controlled via IRC Announcing BABLR Splitting Konsole views from Helix to run tools | AksDev GitHub - yugr/rust-slides Serving files over HTTP three ways: synchronous, epoll, and io_uring update docs with information about building with build.py (#979) · astral-sh/python-build-standalone@c9c40c5 A Simple Makefile Tutorial On C extensions, portability, and alternative compilers Switching to Colemak | Pedro Alves Just How Bad Was The Intel IAPX432? Nix's Substituter List Is Not a Routing Table Accelerating copy_if using SIMD Lambda on Lambda: Serverless Haskell on AWS | Blog Announcing feed-repeat v1.0 Scaling Akvorado BMP RIB with sharding EYG news: A host of CLI improvements, new guides and new effects The social contract of writing JS Crossword C array types are weird; and related topics Flatpak will depend on systemd – OSnews Migrating from Go to Rust | corrode Rust Consulting A portentous reunion Vivado Licensing Options How my minimal, memory-safe Go rsync steers clear of vulnerabilities the entropy layer of a wavelet codec, on its own GitHub - nferhat/fht-compositor: A dynamic tiling Wayland compositor. Debian SE Linux and PinTheft Does bulk memmove speed up std::remove_if? (No.) 声明式部分更新 | Blog | Chrome for Developers Fully in-browser container builds Dianne Skoll's Web Site - Remind The Architecture of Open Source Applications (Volume 1)Berkeley DB Pardon MIE? - ironPeak Blog “Long-Term Support” doesn’t mean what you think Jira IS Turing-Complete May I recommend thinking of Emacs as your Fortress of Solitude hershey Floodgap Gopher-HTTP gateway gopher://thelambdalab.xyz/1cuneiforth/ HP QuickWeb, Singular And Pointless That one time I used Go panics for flow control A new suite of modern tools coming for editing and publishing RFCs From the Tabletop… The Digital Antiquarian Building a Host-Tuned GCC to Make GCC Compile Faster Are we self-sovereign PKI yet? Claw Patrol: an open-source security firewall for agents | Deno Revised^7 Report on Scheme, Large: Procedural Fascicle Draft is now public A Network Allow-List Won't Stop Exfiltration — André Graf From AFSK to Goertzel – µArt.cz Software For My New Home Server Introducing Neptune: Direct3D virtualization for QEMU AI Agent Bankrupted Their Operator While Trying to Scan DN42 - Lan Tian @ Blog mimalloc: A new, high-performance, scalable memory allocator for the modern era Making wl_shm fast The Soul of Maintaining a New Machine - Third Draft | Books in Progress What is Git made of?
What can wonky APIs tell us about the web?
alexwlchan.n · 2026-06-22 · via Lobsters

A few days ago, Misty posted something that caught my eye:

Finding myself asking if there's ever been a wonkier official browser API than canPlayType

The HTMLMediaElement.canPlayType API tells you how likely it is that a browser can play media with a given MIME type, but the response is unusual. The word “likely” is important here, because it’s not a simple yes/no answer. The possible responses are:

  • "" – no, the browser can’t play the media
  • "probably" – the browser can probably play the media
  • "maybe" – there isn’t enough information to determine if the media is playable.

A ternary, probabilistic response is already a bit weird; the return values double down on the weirdness. A clearer set of return values would be "no", "probably" and "unknown".

But when thinking of wonky web APIs, my mind went somewhere else: to History.pushState() and replaceState(). These APIs are for manipulating your browser history, and take an unused parameter which you have to pass but do absolutely nothing.

Here’s the description of pushState from MDN (emphasis mine):

The pushState() method of the History interface adds an entry to the browser’s session history stack.

Syntax:

    pushState(state, unused)
    pushState(state, unused, url)

Parameters:

  • state – The state object is a JavaScript object which is associated with the new history entry created by pushState(). […]
  • unusedThis parameter exists for historical reasons, and cannot be omitted; passing an empty string is safe against future changes to the method.
  • url – The new history entry’s URL.

This begs the question: what historical reasons? Why does an API supported by every major browser have a parameter that nobody uses?

The History API was designed for use with single-page applications (SPAs) – sites that only load a single page, then use JavaScript to update the contents of a page, rather than loading a new page every time something changes. Using SPAs can make a site faster, because they only have to load the part of a page that’s changed, but they also break the behaviour of the browser’s “back” and “forward” buttons.

From the user’s point of view, they click links and the page changes, so if they click the “back” button, they expect to go back to their previous state.

But the browser only records a single history event, when the user first loads the SPA – all the in-page updates using JavaScript don’t register as new pages. When the user clicks the “back” button, the browser will take them to whatever page they were looking at before they opened the SPA.

Using pushState() and replaceState() allows an app to create synthetic history entries, so the “back” and “forward” buttons can step the user through the pages they’ve seen within the SPA.

The pushState() API first appeared in a draft HTML5 spec in January 2008, with three parameters:

  • The state object would be attached to the history event, and if the user clicked the “back” or “forward” buttons, your app would get a popstate event with the state value associated with the history event they’d selected.
  • The title parameter allowed apps to set a title for the entry saved in the browser’s session history, which could be different to the title shown in the browser window.
  • The url parameter allowed apps to set a URL for the history entry, which could be different to the URL shown in the browser window. If omitted, the browser would use the current URL.

The title parameter was always “advisory”, and in practice most browsers completely ignored the parameter, to avoid the confusion of mismatched titles in the browser UI and session history.

It soon became clear that the title parameter was pointless, but it was already too late to change. Lots of sites were built as single-page applications and already using the new pushState and replaceState APIs, and breaking those sites was unacceptable.

The argument could be neither removed nor made optional. If you removed it, you’d break sites that used the three-parameter pushState(state, title, url). If you made it optional, its position in the middle of the signature would leave browsers unable to distinguish between pushState(state, url) and pushState(state, title).

Instead, the spec was updated to rename the parameter to unused and clarify it has no effect.

This wonky API reflects the challenge of designing for the web: it’s difficult to design APIs without seeing how they’re used on real world websites, but then it’s too late to make changes in response to feedback. The web goes to incredible lengths to preserve backward compatibility, and it’s the ultimate form of “anything described as a prototype will get shipped in production”.

The longevity of vanilla web technology is why I keep using static websites for my media archives – more than anything else in my career in tech, web technology is what lasts.