зеркало из https://github.com/mozilla/gecko-dev.git
Bug 582361 - Align scrolling to a fragment with HTML spec. r=emilio
Differential Revision: https://phabricator.services.mozilla.com/D187362
This commit is contained in:
Родитель
f5107ef981
Коммит
9b4650bb45
|
@ -10850,6 +10850,7 @@ nsresult nsDocShell::OpenRedirectedChannel(nsDocShellLoadState* aLoadState) {
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/#scrolling-to-a-fragment
|
||||||
nsresult nsDocShell::ScrollToAnchor(bool aCurHasRef, bool aNewHasRef,
|
nsresult nsDocShell::ScrollToAnchor(bool aCurHasRef, bool aNewHasRef,
|
||||||
nsACString& aNewHash, uint32_t aLoadType) {
|
nsACString& aNewHash, uint32_t aLoadType) {
|
||||||
if (!mCurrentURI) {
|
if (!mCurrentURI) {
|
||||||
|
@ -10879,88 +10880,71 @@ nsresult nsDocShell::ScrollToAnchor(bool aCurHasRef, bool aNewHasRef,
|
||||||
// Both the new and current URIs refer to the same page. We can now
|
// Both the new and current URIs refer to the same page. We can now
|
||||||
// browse to the hash stored in the new URI.
|
// browse to the hash stored in the new URI.
|
||||||
|
|
||||||
if (!aNewHash.IsEmpty()) {
|
// If it's a load from history, we don't have any anchor jumping to do.
|
||||||
// anchor is there, but if it's a load from history,
|
// Scrollbar position will be restored by the caller based on positions stored
|
||||||
// we don't have any anchor jumping to do
|
// in session history.
|
||||||
bool scroll = aLoadType != LOAD_HISTORY && aLoadType != LOAD_RELOAD_NORMAL;
|
bool scroll = aLoadType != LOAD_HISTORY && aLoadType != LOAD_RELOAD_NORMAL;
|
||||||
|
|
||||||
// We assume that the bytes are in UTF-8, as it says in the
|
if (aNewHash.IsEmpty()) {
|
||||||
// spec:
|
// 2. If fragment is the empty string, then return the special value top of
|
||||||
// http://www.w3.org/TR/html4/appendix/notes.html#h-B.2.1
|
// the document.
|
||||||
|
|
||||||
// We try the UTF-8 string first, and then try the document's
|
|
||||||
// charset (see below). If the string is not UTF-8,
|
|
||||||
// conversion will fail and give us an empty Unicode string.
|
|
||||||
// In that case, we should just fall through to using the
|
|
||||||
// page's charset.
|
|
||||||
nsresult rv = NS_ERROR_FAILURE;
|
|
||||||
NS_ConvertUTF8toUTF16 uStr(aNewHash);
|
|
||||||
if (!uStr.IsEmpty()) {
|
|
||||||
rv = presShell->GoToAnchor(uStr, scroll, ScrollFlags::ScrollSmoothAuto);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (NS_FAILED(rv)) {
|
|
||||||
char* str = ToNewCString(aNewHash, mozilla::fallible);
|
|
||||||
if (!str) {
|
|
||||||
return NS_ERROR_OUT_OF_MEMORY;
|
|
||||||
}
|
|
||||||
nsUnescape(str);
|
|
||||||
NS_ConvertUTF8toUTF16 utf16Str(str);
|
|
||||||
if (!utf16Str.IsEmpty()) {
|
|
||||||
rv = presShell->GoToAnchor(utf16Str, scroll,
|
|
||||||
ScrollFlags::ScrollSmoothAuto);
|
|
||||||
}
|
|
||||||
free(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Above will fail if the anchor name is not UTF-8. Need to
|
|
||||||
// convert from document charset to unicode.
|
|
||||||
if (NS_FAILED(rv)) {
|
|
||||||
// Get a document charset
|
|
||||||
NS_ENSURE_TRUE(mContentViewer, NS_ERROR_FAILURE);
|
|
||||||
Document* doc = mContentViewer->GetDocument();
|
|
||||||
NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
|
|
||||||
nsAutoCString charset;
|
|
||||||
doc->GetDocumentCharacterSet()->Name(charset);
|
|
||||||
|
|
||||||
nsCOMPtr<nsITextToSubURI> textToSubURI =
|
|
||||||
do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
|
|
||||||
// Unescape and convert to unicode
|
|
||||||
nsAutoString uStr;
|
|
||||||
|
|
||||||
rv = textToSubURI->UnEscapeAndConvert(charset, aNewHash, uStr);
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
|
|
||||||
// Ignore return value of GoToAnchor, since it will return an error
|
|
||||||
// if there is no such anchor in the document, which is actually a
|
|
||||||
// success condition for us (we want to update the session history
|
|
||||||
// with the new URI no matter whether we actually scrolled
|
|
||||||
// somewhere).
|
|
||||||
//
|
//
|
||||||
// When aNewHash contains "%00", unescaped string may be empty.
|
// Tell the shell it's at an anchor without scrolling.
|
||||||
// And GoToAnchor asserts if we ask it to scroll to an empty ref.
|
|
||||||
presShell->GoToAnchor(uStr, scroll && !uStr.IsEmpty(),
|
|
||||||
ScrollFlags::ScrollSmoothAuto);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Tell the shell it's at an anchor, without scrolling.
|
|
||||||
presShell->GoToAnchor(u""_ns, false);
|
presShell->GoToAnchor(u""_ns, false);
|
||||||
|
|
||||||
// An empty anchor was found, but if it's a load from history,
|
if (scroll) {
|
||||||
// we don't have to jump to the top of the page. Scrollbar
|
// Scroll to the top of the page. Ignore the return value; failure to
|
||||||
// position will be restored by the caller, based on positions
|
// scroll here (e.g. if there is no root scrollframe) is not grounds for
|
||||||
// stored in session history.
|
// canceling the load!
|
||||||
if (aLoadType == LOAD_HISTORY || aLoadType == LOAD_RELOAD_NORMAL) {
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
// An empty anchor. Scroll to the top of the page. Ignore the
|
|
||||||
// return value; failure to scroll here (e.g. if there is no
|
|
||||||
// root scrollframe) is not grounds for canceling the load!
|
|
||||||
SetCurScrollPosEx(0, 0);
|
SetCurScrollPosEx(0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Let potentialIndicatedElement be the result of finding a potential
|
||||||
|
// indicated element given document and fragment.
|
||||||
|
NS_ConvertUTF8toUTF16 uStr(aNewHash);
|
||||||
|
auto rv = presShell->GoToAnchor(uStr, scroll, ScrollFlags::ScrollSmoothAuto);
|
||||||
|
|
||||||
|
// 4. If potentialIndicatedElement is not null, then return
|
||||||
|
// potentialIndicatedElement.
|
||||||
|
if (NS_SUCCEEDED(rv)) {
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Let fragmentBytes be the result of percent-decoding fragment.
|
||||||
|
nsAutoCString fragmentBytes;
|
||||||
|
const bool unescaped = NS_UnescapeURL(aNewHash.Data(), aNewHash.Length(),
|
||||||
|
/* aFlags = */ 0, fragmentBytes);
|
||||||
|
|
||||||
|
if (!unescaped) {
|
||||||
|
// Another attempt is only necessary if characters were unescaped.
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fragmentBytes.IsEmpty()) {
|
||||||
|
// When aNewHash contains "%00", the unescaped string may be empty, and
|
||||||
|
// GoToAnchor asserts if we ask it to scroll to an empty ref.
|
||||||
|
presShell->GoToAnchor(u""_ns, false);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. Let decodedFragment be the result of running UTF-8 decode without BOM on
|
||||||
|
// fragmentBytes.
|
||||||
|
nsAutoString decodedFragment;
|
||||||
|
rv = UTF_8_ENCODING->DecodeWithoutBOMHandling(fragmentBytes, decodedFragment);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
// 7. Set potentialIndicatedElement to the result of finding a potential
|
||||||
|
// indicated element given document and decodedFragment.
|
||||||
|
//
|
||||||
|
// Ignore the return value of GoToAnchor, since it will return an error if
|
||||||
|
// there is no such anchor in the document, which is actually a success
|
||||||
|
// condition for us (we want to update the session history with the new URI no
|
||||||
|
// matter whether we actually scrolled somewhere).
|
||||||
|
presShell->GoToAnchor(decodedFragment, scroll, ScrollFlags::ScrollSmoothAuto);
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13108,6 +13108,7 @@ void Document::SetScrollToRef(nsIURI* aDocumentURI) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/#scrolling-to-a-fragment
|
||||||
void Document::ScrollToRef() {
|
void Document::ScrollToRef() {
|
||||||
if (mScrolledToRefAlready) {
|
if (mScrolledToRefAlready) {
|
||||||
RefPtr<PresShell> presShell = GetPresShell();
|
RefPtr<PresShell> presShell = GetPresShell();
|
||||||
|
@ -13117,53 +13118,52 @@ void Document::ScrollToRef() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 2. If fragment is the empty string, then return the special value top of
|
||||||
|
// the document.
|
||||||
if (mScrollToRef.IsEmpty()) {
|
if (mScrollToRef.IsEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
RefPtr<PresShell> presShell = GetPresShell();
|
RefPtr<PresShell> presShell = GetPresShell();
|
||||||
if (presShell) {
|
if (!presShell) {
|
||||||
nsresult rv = NS_ERROR_FAILURE;
|
return;
|
||||||
// We assume that the bytes are in UTF-8, as it says in the spec:
|
}
|
||||||
// http://www.w3.org/TR/html4/appendix/notes.html#h-B.2.1
|
|
||||||
|
// 3. Let potentialIndicatedElement be the result of finding a potential
|
||||||
|
// indicated element given document and fragment.
|
||||||
NS_ConvertUTF8toUTF16 ref(mScrollToRef);
|
NS_ConvertUTF8toUTF16 ref(mScrollToRef);
|
||||||
// Check an empty string which might be caused by the UTF-8 conversion
|
auto rv = presShell->GoToAnchor(ref, mChangeScrollPosWhenScrollingToRef);
|
||||||
if (!ref.IsEmpty()) {
|
|
||||||
// Note that GoToAnchor will handle flushing layout as needed.
|
|
||||||
rv = presShell->GoToAnchor(ref, mChangeScrollPosWhenScrollingToRef);
|
|
||||||
} else {
|
|
||||||
rv = NS_ERROR_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (NS_FAILED(rv)) {
|
// 4. If potentialIndicatedElement is not null, then return
|
||||||
nsAutoCString buff;
|
// potentialIndicatedElement.
|
||||||
const bool unescaped =
|
|
||||||
NS_UnescapeURL(mScrollToRef.BeginReading(), mScrollToRef.Length(),
|
|
||||||
/*aFlags =*/0, buff);
|
|
||||||
|
|
||||||
// This attempt is only necessary if characters were unescaped.
|
|
||||||
if (unescaped) {
|
|
||||||
NS_ConvertUTF8toUTF16 utf16Str(buff);
|
|
||||||
if (!utf16Str.IsEmpty()) {
|
|
||||||
rv = presShell->GoToAnchor(utf16Str,
|
|
||||||
mChangeScrollPosWhenScrollingToRef);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If UTF-8 URI failed then try to assume the string as a
|
|
||||||
// document's charset.
|
|
||||||
if (NS_FAILED(rv)) {
|
|
||||||
const Encoding* encoding = GetDocumentCharacterSet();
|
|
||||||
rv = encoding->DecodeWithoutBOMHandling(unescaped ? buff : mScrollToRef,
|
|
||||||
ref);
|
|
||||||
if (NS_SUCCEEDED(rv) && !ref.IsEmpty()) {
|
|
||||||
rv = presShell->GoToAnchor(ref, mChangeScrollPosWhenScrollingToRef);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (NS_SUCCEEDED(rv)) {
|
if (NS_SUCCEEDED(rv)) {
|
||||||
mScrolledToRefAlready = true;
|
mScrolledToRefAlready = true;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 5. Let fragmentBytes be the result of percent-decoding fragment.
|
||||||
|
nsAutoCString fragmentBytes;
|
||||||
|
const bool unescaped =
|
||||||
|
NS_UnescapeURL(mScrollToRef.Data(), mScrollToRef.Length(),
|
||||||
|
/* aFlags = */ 0, fragmentBytes);
|
||||||
|
|
||||||
|
if (!unescaped || fragmentBytes.IsEmpty()) {
|
||||||
|
// Another attempt is only necessary if characters were unescaped.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. Let decodedFragment be the result of running UTF-8 decode without BOM on
|
||||||
|
// fragmentBytes.
|
||||||
|
nsAutoString decodedFragment;
|
||||||
|
rv = UTF_8_ENCODING->DecodeWithoutBOMHandling(fragmentBytes, decodedFragment);
|
||||||
|
NS_ENSURE_SUCCESS_VOID(rv);
|
||||||
|
|
||||||
|
// 7. Set potentialIndicatedElement to the result of finding a potential
|
||||||
|
// indicated element given document and decodedFragment.
|
||||||
|
rv = presShell->GoToAnchor(decodedFragment,
|
||||||
|
mChangeScrollPosWhenScrollingToRef);
|
||||||
|
if (NS_SUCCEEDED(rv)) {
|
||||||
|
mScrolledToRefAlready = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -416,20 +416,11 @@ void nsHtml5TreeOperation::SetHTMLElementAttributes(
|
||||||
nsAtom* localName = aAttributes->getLocalNameNoBoundsCheck(i);
|
nsAtom* localName = aAttributes->getLocalNameNoBoundsCheck(i);
|
||||||
nsAtom* prefix = aAttributes->getPrefixNoBoundsCheck(i);
|
nsAtom* prefix = aAttributes->getPrefixNoBoundsCheck(i);
|
||||||
int32_t nsuri = aAttributes->getURINoBoundsCheck(i);
|
int32_t nsuri = aAttributes->getURINoBoundsCheck(i);
|
||||||
|
|
||||||
nsString value; // Not Auto, because using it to hold nsStringBuffer*
|
nsString value; // Not Auto, because using it to hold nsStringBuffer*
|
||||||
val.ToString(value);
|
val.ToString(value);
|
||||||
if (nsGkAtoms::a == aName && nsGkAtoms::name == localName) {
|
|
||||||
// This is an HTML5-incompliant Geckoism.
|
|
||||||
// Remove when fixing bug 582361
|
|
||||||
NS_ConvertUTF16toUTF8 cname(value);
|
|
||||||
NS_ConvertUTF8toUTF16 uv(nsUnescape(cname.BeginWriting()));
|
|
||||||
aElement->SetAttr(nsuri, localName, prefix, uv, false);
|
|
||||||
} else {
|
|
||||||
aElement->SetAttr(nsuri, localName, prefix, value, false);
|
aElement->SetAttr(nsuri, localName, prefix, value, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nsIContent* nsHtml5TreeOperation::CreateHTMLElement(
|
nsIContent* nsHtml5TreeOperation::CreateHTMLElement(
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
[fragment-and-encoding-2.html]
|
|
||||||
max-asserts: 4
|
|
||||||
expected:
|
|
||||||
if (os == "android") and fission: [OK, TIMEOUT]
|
|
|
@ -1,6 +0,0 @@
|
||||||
[fragment-and-encoding.html]
|
|
||||||
max-asserts: 5
|
|
||||||
expected:
|
|
||||||
if (os == "android") and fission: [OK, TIMEOUT]
|
|
||||||
[%FF should not find U+00FF as decoding it gives U+FFFD]
|
|
||||||
expected: FAIL
|
|
|
@ -1,5 +0,0 @@
|
||||||
[scroll-frag-non-utf8-encoded-document.html]
|
|
||||||
expected:
|
|
||||||
if (os == "android") and fission: [OK, TIMEOUT]
|
|
||||||
[Fragment Navigation: fragment id should not be found in non UTF8 document]
|
|
||||||
expected: FAIL
|
|
|
@ -1,6 +0,0 @@
|
||||||
[scroll-frag-percent-encoded.html]
|
|
||||||
expected:
|
|
||||||
if (os == "android") and fission: [OK, TIMEOUT]
|
|
||||||
[Fragment Navigation: fragment id should be percent-decoded]
|
|
||||||
expected:
|
|
||||||
if os == "android": FAIL
|
|
Загрузка…
Ссылка в новой задаче