This seems like the only viable solution but you don't have "opt in". WebGL already supports it today. You just set the backbuffer to the size you want like you do now and let CSS scale it also like you do now. There are plenty of samples that already set the size of the backbuffer to something different than the size it's displayed.
If you want it 1x1 with an HD-DPI display you set it
canvas.width = desiredWidth * window.devicePixelRatio
canvas.height = desiredHeight * window.devicePixelRatio
and everything just magically works.
That seems the far saner way to go.
This seems like the ideal solution to me; with this method users can choose to use HD-DPI if they want to but are forced into it. It is also the converse of what we are already doing to get better performance on slower machines, so it is a natural extension of our existing code. Also, as mentioned, the WebGL functions would all take dimensions in a consistent space, which makes for a much less confusing API.
And just as a data point, our app is already passing drawingBufferWidth and drawingBufferHeight to gl.viewport, so not EVERY application is doing it wrong... =)