whatwg / html

HTML Standard
https://html.spec.whatwg.org/multipage/
Other
8.03k stars 2.63k forks source link

Need a way to specify base url of pure fragment urls #1781

Open duanyao opened 8 years ago

duanyao commented 8 years ago

Currently pure fragment urls ("#foo") are resolved in the same way as normal relative urls, i.e. against base url. However sometimes we want to resolve pure fragment urls against current document regardless of the base url. For example:

Currently, if the base url is not identical to the document url, those pure fragment urls are broken.

Someone suggested changing links to absolute ones via JS to workaround this issue. However this doesn't work for css clip-path, because the absolute url is pointing to a html document, not a svg. Additionally, if the document url is a special one (e.g. about:blank), it is not even possible to figure out the absolute form of a pure fragment url.

The following test case demonstrates the "fragment in about:blank" problem:

<!DOCTYPE html>
<iframe style="width:400px; height:400px"></iframe>
<script>
var frame = document.querySelector('iframe');
if (frame.contentDocument.readyState == 'complete') {
  renderFrame();
} else frame.onload = renderFrame;

function renderFrame() {
  var content = '';
  for (var i = 0; i < 50; i++) {
    content += '<p id="' + i + '">' + i + '</p>';
  }
  content += '<p><a href="#25">go to the 25th</a></p>';
  // content += '<p><a href="' + document.baseURI + '#25">go to the 25th, absolute url</a></p>';
  frame.contentDocument.body.innerHTML = content;
}
console.log('load time:' + Date.now());
</script>

What should a browser do when the link "go to the 25th" is clicked? Chrome 53 creates a nested iframe, while Firefox 51 and Edge 13 scroll to the the 25th line. I think Chrome's behavior is "technically correct" but is not what we want, while Firefox and Edge are the opposite.

So I suggest reusing <base> tag to specify base url of pure fragment urls, E.g.:

<base fragment-resolution="self|base" href="...">

Value "self" and "base" of fragment-resolution means pure fragment urls are resolved against the document itself, and the base url, respectively.

zcorpan commented 8 years ago

I don't think we need to add something to base, it seems to me it should be possible to just fix this (i.e. specify the Firefox/Edge behavior).

For navigate I think the relevant step is

If this is not a reload-triggered navigation, resource is a request, resource's url equals browsingContext's active document's URL with exclude fragments flag set, and resource's url's fragment is non-null, then navigate to that fragment and abort these steps.

https://html.spec.whatwg.org/multipage/browsers.html#navigate

CSS/SVG needs to be handled in CSS and SVG specs, but I think there has been some work already to try to deal with this problem... cc @AmeliaBR @tabatkins

duanyao commented 8 years ago

But what to do if there is a valid <base> in a about:blank doc, and we still want to resolve pure fragment urls against the doc? It seems about:blank#foo works in Firefox and Chrome, but not in Edge. Even if about:blank#foo works, it is still very nasty to change every pure fragment urls.

Test case:

<!DOCTYPE html>
<iframe style="width:400px; height:400px"></iframe>
<script>
var frame = document.querySelector('iframe');
if (frame.contentDocument.readyState == 'complete') {
  renderFrame();
} else frame.onload = renderFrame;

function renderFrame() {
  if (!frame.contentDocument || frame.contentDocument.URL !== 'about:blank') {
    return;
  }
  frame.contentDocument.head.innerHTML = '<base href="http://example.com" >';
  var content = '';
  for (var i = 0; i < 50; i++) {
    content += '<p id="' + i + '">' + i + '</p>';
  }
  // go to http://example.com/#25 in all browsers
  content += '<p><a href="#25">go to #25</a></p>';
  // go to #25 in Chrome & Firefox, fail in Edge
  content += '<p><a href="about:blank#25">go to about:blank#25</a></p>';
  frame.contentDocument.body.innerHTML = content;
}
</script>
duanyao commented 8 years ago

Just found that CSS has special behavior for fragment-only urls: always treat as same-document (rather than cross-document): https://drafts.csswg.org/css-values-3/#local-urls

Should HTML spec do the same thing?

AmeliaBR commented 8 years ago

I handled this in SVG 2 by requiring that, for SVG cross-references

For links, I reference the HTML rules (or W3C snapshot thereof), so I don't explicitly require the same comparison against document base URL before navigation. But if HTML makes it explicit, SVG will be expected to get the matching behavior.

In CSS it's a little more complicated, because they also wanted to enable same-document cross references (for properties like clip-path) with CSS set in a .css file. So CSS Values and Units 4 adds a special rule that target-only URLs are always to be treated as same-document URLs, relative to whatever document contains the styled element.

PS, These are both recent spec changes; implementations have not been universally updated to match.

duanyao commented 8 years ago

Thanks for the detailed explanation!

Maybe it's better to just fix the navigation rule of HTML: treat #foo and document.baseURI + '#foo' as same-document navigation. But I doubt whether this is web compatible.

annevk commented 8 years ago

URLs are only made absolute when needed

How does this work? Making a URL absolute is not something https://url.spec.whatwg.org/ supports. But assuming you mean parsing a URL string into a URL record, how can you only do that sometimes? And if do not do it at a deterministic point in time, you break blob URLs.

tabatkins commented 7 years ago

CSS, in particular, explicitly calls out hash-only urls (that is, values of the url() function that start with a hash) as always being local. They still resolve to absolute URLs as normal, but they have an internal "local url" flag set, forcing them to serialize as hash-only, and making them always be interpreted as references to the current document only.

duanyao commented 7 years ago

Now that this issue was closed, what was the resolution?

annevk commented 7 years ago

No change, basically. We're not going to add new features for this and changing the way fragment identifiers work seems extremely risky.

duanyao commented 7 years ago

How about the different behaviors between Chrome and Firefox/Edge when navigating to #foo or about:blank#foo mentioned in this issue?

annevk commented 7 years ago

I'll reopen it for that since it does seem worth to add tests if we don't have any yet. I don't really understand how you get a new nested frame by following a link.

duanyao commented 7 years ago

Which browser are you testing? Chrome 53 should load a nested iframe, not sure newer versions. Update: Chrome 60 still loads a nested iframe when click "go to the 25th" for the first time, but does nothing for following clicks.

annevk commented 7 years ago

It doesn't load a nested iframe here. It just navigates the iframe the link is in.

duanyao commented 7 years ago

So you mean Chrome and Firefox have same behavior? Strange.

duanyao commented 7 years ago

Can you try html this in Chrome? fragment-url-base-link-dynamic.htm.zip

The screenshot after clicking "go to the 25th" looks like 20170504201351

annevk commented 7 years ago

I think this is a duplicate of #421. I don't think I get different results from you, I'm just describing it differently.

duanyao commented 7 years ago

I see. Is about:blank#25 case mentioned in https://github.com/whatwg/html/issues/1781#issuecomment-247039442 also covered by #421 ?

annevk commented 7 years ago

Yeah, it's basically that bug. But we can leave this open and close them together.