Thanks Jody. I prefer option (b) as the default. It ensures that the user will get something based on their data even if they forget to set bounds explicitly. I guess you could require them to set the bounds by making that field null initially, but that seems unfriendly.
It's not always the case that shared viewports will have been hand-made. The DefaultRenderingExecutor class in gt-swing deals with rendering layers into separate graphics by creating temp MapContents which all share the viewport of the original, and that viewport can easily be the default one.
Meanwhile, we seem to have circled back to taking the CRS from the first Layer with a non-null CRS (if present). I think it would be good for MapContent to do that (rather than a client like JMapPane for instance). To prevent automagic changes to a viewport's CRS when it is shared I've added MapViewport.setEditable( boolean ) as per the sub-task