Bug 1267614 - Update meta refresh parsing to match the new spec. r=farre

Differential Revision: https://phabricator.services.mozilla.com/D134225
This commit is contained in:
Peter Van der Beken 2021-12-26 10:09:44 +00:00
Родитель 829f90aa36
Коммит 9435610906
7 изменённых файлов: 227 добавлений и 527 удалений

Просмотреть файл

@ -5171,247 +5171,216 @@ nsDocShell::ForceRefreshURI(nsIURI* aURI, nsIPrincipal* aPrincipal,
return NS_OK;
}
nsresult nsDocShell::SetupRefreshURIFromHeader(nsIURI* aBaseURI,
nsIPrincipal* aPrincipal,
uint64_t aInnerWindowID,
const nsACString& aHeader) {
static const char16_t* SkipASCIIWhitespace(const char16_t* aStart,
const char16_t* aEnd) {
const char16_t* iter = aStart;
while (iter != aEnd && mozilla::IsAsciiWhitespace(*iter)) {
++iter;
}
return iter;
}
static Tuple<const char16_t*, const char16_t*> ExtractURLString(
const char16_t* aPosition, const char16_t* aEnd) {
MOZ_ASSERT(aPosition != aEnd);
// 1. Let urlString be the substring of input from the code point at
// position to the end of the string.
const char16_t* urlStart = aPosition;
const char16_t* urlEnd = aEnd;
// 2. If the code point in input pointed to by position is U+0055 (U) or
// U+0075 (u), then advance position to the next code point.
// Otherwise, jump to the step labeled skip quotes.
if (*aPosition == 'U' || *aPosition == 'u') {
++aPosition;
// 3. If the code point in input pointed to by position is U+0052 (R) or
// U+0072 (r), then advance position to the next code point.
// Otherwise, jump to the step labeled parse.
if (aPosition == aEnd || (*aPosition != 'R' && *aPosition != 'r')) {
return MakeTuple(urlStart, urlEnd);
}
++aPosition;
// 4. If the code point in input pointed to by position is U+004C (L) or
// U+006C (l), then advance position to the next code point.
// Otherwise, jump to the step labeled parse.
if (aPosition == aEnd || (*aPosition != 'L' && *aPosition != 'l')) {
return MakeTuple(urlStart, urlEnd);
}
++aPosition;
// 5. Skip ASCII whitespace within input given position.
aPosition = SkipASCIIWhitespace(aPosition, aEnd);
// 6. If the code point in input pointed to by position is U+003D (=),
// then advance position to the next code point. Otherwise, jump to
// the step labeled parse.
if (aPosition == aEnd || *aPosition != '=') {
return MakeTuple(urlStart, urlEnd);
}
++aPosition;
// 7. Skip ASCII whitespace within input given position.
aPosition = SkipASCIIWhitespace(aPosition, aEnd);
}
// 8. Skip quotes: If the code point in input pointed to by position is
// U+0027 (') or U+0022 ("), then let quote be that code point, and
// advance position to the next code point. Otherwise, let quote be
// the empty string.
Maybe<char> quote;
if (aPosition != aEnd && (*aPosition == '\'' || *aPosition == '"')) {
quote.emplace(*aPosition);
++aPosition;
}
// 9. Set urlString to the substring of input from the code point at
// position to the end of the string.
urlStart = aPosition;
urlEnd = aEnd;
// 10. If quote is not the empty string, and there is a code point in
// urlString equal to quote, then truncate urlString at that code
// point, so that it and all subsequent code points are removed.
const char16_t* quotePos;
if (quote.isSome() &&
(quotePos = nsCharTraits<char16_t>::find(
urlStart, std::distance(urlStart, aEnd), quote.value()))) {
urlEnd = quotePos;
}
return MakeTuple(urlStart, urlEnd);
}
void nsDocShell::SetupRefreshURIFromHeader(Document* aDocument,
const nsAString& aHeader) {
if (mIsBeingDestroyed) {
return NS_ERROR_FAILURE;
return;
}
// Refresh headers are parsed with the following format in mind
// <META HTTP-EQUIV=REFRESH CONTENT="5; URL=http://uri">
// By the time we are here, the following is true:
// header = "REFRESH"
// content = "5; URL=http://uri" // note the URL attribute is
// optional, if it is absent, the currently loaded url is used.
// Also note that the seconds and URL separator can be either
// a ';' or a ','. The ',' separator should be illegal but CNN
// is using it.
//
// We need to handle the following strings, where
// - X is a set of digits
// - URI is either a relative or absolute URI
//
// Note that URI should start with "url=" but we allow omission
//
// "" || ";" || ","
// empty string. use the currently loaded URI
// and refresh immediately.
// "X" || "X;" || "X,"
// Refresh the currently loaded URI in X seconds.
// "X; URI" || "X, URI"
// Refresh using URI as the destination in X seconds.
// "URI" || "; URI" || ", URI"
// Refresh immediately using URI as the destination.
//
// Currently, anything immediately following the URI, if
// separated by any char in the set "'\"\t\r\n " will be
// ignored. So "10; url=go.html ; foo=bar" will work,
// and so will "10; url='go.html'; foo=bar". However,
// "10; url=go.html; foo=bar" will result in the uri
// "go.html;" since ';' and ',' are valid uri characters.
//
// Note that we need to remove any tokens wrapping the URI.
// These tokens currently include spaces, double and single
// quotes.
const char16_t* position = aHeader.BeginReading();
const char16_t* end = aHeader.EndReading();
// when done, seconds is 0 or the given number of seconds
// uriAttrib is empty or the URI specified
MOZ_ASSERT(aPrincipal);
// See
// https://html.spec.whatwg.org/#pragma-directives:shared-declarative-refresh-steps.
nsAutoCString uriAttrib;
CheckedInt<uint32_t> seconds(0);
bool specifiesSeconds = false;
// 3. Skip ASCII whitespace
position = SkipASCIIWhitespace(position, end);
nsACString::const_iterator iter, tokenStart, doneIterating;
// 4. Let time be 0.
CheckedInt<uint32_t> milliSeconds;
aHeader.BeginReading(iter);
aHeader.EndReading(doneIterating);
// skip leading whitespace
while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter)) {
++iter;
// 5. Collect a sequence of code points that are ASCII digits
const char16_t* digitsStart = position;
while (position != end && mozilla::IsAsciiDigit(*position)) {
++position;
}
tokenStart = iter;
if (iter != doneIterating) {
if (*iter == '-') {
return NS_ERROR_FAILURE;
if (position == digitsStart) {
// 6. If timeString is the empty string, then:
// 1. If the code point in input pointed to by position is not U+002E
// (.), then return.
if (position == end || *position != '.') {
return;
}
// skip leading +
if (*iter == '+') {
++iter;
}
}
// parse number
while (iter != doneIterating && (*iter >= '0' && *iter <= '9')) {
seconds = seconds * 10 + (*iter - '0');
if (!seconds.isValid()) {
return NS_ERROR_FAILURE;
}
specifiesSeconds = true;
++iter;
}
CheckedInt<uint32_t> milliSeconds(seconds * 1000);
if (!milliSeconds.isValid()) {
return NS_ERROR_FAILURE;
}
if (iter != doneIterating) {
// skip to next ';' or ','
nsACString::const_iterator iterAfterDigit = iter;
while (iter != doneIterating && !(*iter == ';' || *iter == ',')) {
if (specifiesSeconds) {
// Non-whitespace characters here mean that the string is
// malformed but tolerate sites that specify a decimal point,
// even though meta refresh only works on whole seconds.
if (iter == iterAfterDigit && !nsCRT::IsAsciiSpace(*iter) &&
*iter != '.') {
// The characters between the seconds and the next
// section are just garbage!
// e.g. content="2a0z+,URL=http://www.mozilla.org/"
// Just ignore this redirect.
return NS_ERROR_FAILURE;
} else if (nsCRT::IsAsciiSpace(*iter)) {
// We've had at least one whitespace so tolerate the mistake
// and drop through.
// e.g. content="10 foo"
++iter;
break;
}
}
++iter;
}
// skip any remaining whitespace
while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter)) {
++iter;
}
// skip ';' or ','
if (iter != doneIterating && (*iter == ';' || *iter == ',')) {
++iter;
}
// skip whitespace
while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter)) {
++iter;
}
}
// possible start of URI
tokenStart = iter;
// skip "url = " to real start of URI
if (iter != doneIterating && (*iter == 'u' || *iter == 'U')) {
++iter;
if (iter != doneIterating && (*iter == 'r' || *iter == 'R')) {
++iter;
if (iter != doneIterating && (*iter == 'l' || *iter == 'L')) {
++iter;
// skip whitespace
while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter)) {
++iter;
}
if (iter != doneIterating && *iter == '=') {
++iter;
// skip whitespace
while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter)) {
++iter;
}
// found real start of URI
tokenStart = iter;
}
}
}
}
// skip a leading '"' or '\''.
bool isQuotedURI = false;
if (tokenStart != doneIterating &&
(*tokenStart == '"' || *tokenStart == '\'')) {
isQuotedURI = true;
++tokenStart;
}
// set iter to start of URI
iter = tokenStart;
// tokenStart here points to the beginning of URI
// grab the rest of the URI
while (iter != doneIterating) {
if (isQuotedURI && (*iter == '"' || *iter == '\'')) {
break;
}
++iter;
}
// move iter one back if the last character is a '"' or '\''
if (iter != tokenStart && isQuotedURI) {
--iter;
if (!(*iter == '"' || *iter == '\'')) {
++iter;
}
}
// URI is whatever's contained from tokenStart to iter.
// note: if tokenStart == doneIterating, so is iter.
nsresult rv = NS_OK;
nsCOMPtr<nsIURI> uri;
bool specifiesURI = false;
if (tokenStart == iter) {
uri = aBaseURI;
} else {
uriAttrib = Substring(tokenStart, iter);
// NS_NewURI takes care of any whitespace surrounding the URL
rv = NS_NewURI(getter_AddRefs(uri), uriAttrib, nullptr, aBaseURI);
specifiesURI = true;
// 7. Otherwise, set time to the result of parsing timeString using the
// rules for parsing non-negative integers.
nsContentUtils::ParseHTMLIntegerResultFlags result;
uint32_t seconds =
nsContentUtils::ParseHTMLInteger(digitsStart, position, &result);
MOZ_ASSERT(!(result & nsContentUtils::eParseHTMLInteger_Negative));
if (result & nsContentUtils::eParseHTMLInteger_Error) {
// The spec assumes no errors here (since we only pass ASCII digits in),
// but we can still overflow, so this block should deal with that (and
// only that).
MOZ_ASSERT(!(result & nsContentUtils::eParseHTMLInteger_ErrorOverflow));
return;
}
MOZ_ASSERT(
!(result & nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput));
milliSeconds = seconds;
milliSeconds *= 1000;
if (!milliSeconds.isValid()) {
return;
}
}
// No URI or seconds were specified
if (!specifiesSeconds && !specifiesURI) {
// Do nothing because the alternative is to spin around in a refresh
// loop forever!
return NS_ERROR_FAILURE;
// 8. Collect a sequence of code points that are ASCII digits and U+002E FULL
// STOP characters (.) from input given position. Ignore any collected
// characters.
while (position != end &&
(mozilla::IsAsciiDigit(*position) || *position == '.')) {
++position;
}
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIScriptSecurityManager> securityManager(
do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv));
if (NS_SUCCEEDED(rv)) {
rv = securityManager->CheckLoadURIWithPrincipal(
aPrincipal, uri,
// 9. Let urlRecord be document's URL.
nsCOMPtr<nsIURI> urlRecord(aDocument->GetDocumentURI());
// 10. If position is not past the end of input
if (position != end) {
// 1. If the code point in input pointed to by position is not U+003B (;),
// U+002C (,), or ASCII whitespace, then return.
if (*position != ';' && *position != ',' &&
!mozilla::IsAsciiWhitespace(*position)) {
return;
}
// 2. Skip ASCII whitespace within input given position.
position = SkipASCIIWhitespace(position, end);
// 3. If the code point in input pointed to by position is U+003B (;) or
// U+002C (,), then advance position to the next code point.
if (position != end && (*position == ';' || *position == ',')) {
++position;
// 4. Skip ASCII whitespace within input given position.
position = SkipASCIIWhitespace(position, end);
}
// 11. If position is not past the end of input, then:
if (position != end) {
const char16_t* urlStart;
const char16_t* urlEnd;
// 1-10. See ExtractURLString.
Tie(urlStart, urlEnd) = ExtractURLString(position, end);
// 11. Parse: Parse urlString relative to document. If that fails, return.
// Otherwise, set urlRecord to the resulting URL record.
nsresult rv = NS_NewURI(
getter_AddRefs(urlRecord),
Substring(urlStart, std::distance(urlStart, urlEnd)),
aDocument->GetDocumentCharacterSet(), aDocument->GetDocBaseURI());
NS_ENSURE_SUCCESS_VOID(rv);
}
}
nsIPrincipal* principal = aDocument->NodePrincipal();
nsCOMPtr<nsIScriptSecurityManager> securityManager =
nsContentUtils::GetSecurityManager();
nsresult rv = securityManager->CheckLoadURIWithPrincipal(
principal, urlRecord,
nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT,
aInnerWindowID);
aDocument->InnerWindowID());
NS_ENSURE_SUCCESS_VOID(rv);
if (NS_SUCCEEDED(rv)) {
bool isjs = true;
rv = NS_URIChainHasFlags(
uri, nsIProtocolHandler::URI_OPENING_EXECUTES_SCRIPT, &isjs);
NS_ENSURE_SUCCESS(rv, rv);
urlRecord, nsIProtocolHandler::URI_OPENING_EXECUTES_SCRIPT, &isjs);
NS_ENSURE_SUCCESS_VOID(rv);
if (isjs) {
return NS_ERROR_FAILURE;
}
return;
}
rv = RefreshURI(uri, aPrincipal, milliSeconds.value());
}
}
return rv;
RefreshURI(urlRecord, principal, milliSeconds.value());
}
static void DoCancelRefreshURITimers(nsIMutableArray* aTimerList) {

Просмотреть файл

@ -293,16 +293,11 @@ class nsDocShell final : public nsDocLoader,
* header is found. If docshell is busy loading a page currently, the request
* will be queued and executed when the current page finishes loading.
*
* @param aBaseURI base URI to resolve refresh uri with.
* @param aPrincipal The triggeringPrincipal for the refresh load
* May be null, in which case the principal of current document will be
* applied.
* @param aInnerWindowID The window id to use for error reporting.
* @param aDocument document to which the refresh header applies.
* @param aHeader The meta refresh header string.
*/
nsresult SetupRefreshURIFromHeader(nsIURI* aBaseURI, nsIPrincipal* aPrincipal,
uint64_t aInnerWindowID,
const nsACString& aHeader);
void SetupRefreshURIFromHeader(mozilla::dom::Document* aDocument,
const nsAString& aHeader);
// Perform a URI load from a refresh timer. This is just like the
// ForceRefreshURI method on nsIRefreshURI, but makes sure to take

Просмотреть файл

@ -6760,9 +6760,7 @@ void Document::SetHeaderData(nsAtom* aHeaderField, const nsAString& aData) {
// should really be the same thing). Note that this code can run
// before the current URI of the webnavigation has been updated, so we
// can't assert equality here.
mDocumentContainer->SetupRefreshURIFromHeader(
mDocumentURI, NodePrincipal(), InnerWindowID(),
NS_ConvertUTF16toUTF8(aData));
mDocumentContainer->SetupRefreshURIFromHeader(this, aData);
}
}

Просмотреть файл

@ -1309,23 +1309,20 @@ nsContentUtils::InternalSerializeAutocompleteAttribute(
}
// Parse an integer according to HTML spec
template <class StringT>
template <class CharT>
int32_t nsContentUtils::ParseHTMLIntegerImpl(
const StringT& aValue, ParseHTMLIntegerResultFlags* aResult) {
using CharT = typename StringT::char_type;
const CharT* aStart, const CharT* aEnd,
ParseHTMLIntegerResultFlags* aResult) {
int result = eParseHTMLInteger_NoFlags;
typename StringT::const_iterator iter, end;
aValue.BeginReading(iter);
aValue.EndReading(end);
const CharT* iter = aStart;
while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
while (iter != aEnd && nsContentUtils::IsHTMLWhitespace(*iter)) {
result |= eParseHTMLInteger_NonStandard;
++iter;
}
if (iter == end) {
if (iter == aEnd) {
result |= eParseHTMLInteger_Error | eParseHTMLInteger_ErrorNoValue;
*aResult = (ParseHTMLIntegerResultFlags)result;
return 0;
@ -1346,7 +1343,7 @@ int32_t nsContentUtils::ParseHTMLIntegerImpl(
// Check for leading zeros first.
uint64_t leadingZeros = 0;
while (iter != end) {
while (iter != aEnd) {
if (*iter != CharT('0')) {
break;
}
@ -1356,7 +1353,7 @@ int32_t nsContentUtils::ParseHTMLIntegerImpl(
++iter;
}
while (iter != end) {
while (iter != aEnd) {
if (*iter >= CharT('0') && *iter <= CharT('9')) {
value = (value * 10) + (*iter - CharT('0')) * sign;
++iter;
@ -1380,7 +1377,7 @@ int32_t nsContentUtils::ParseHTMLIntegerImpl(
result |= eParseHTMLInteger_NonStandard;
}
if (iter != end) {
if (iter != aEnd) {
result |= eParseHTMLInteger_DidNotConsumeAllInput;
}
@ -1389,14 +1386,15 @@ int32_t nsContentUtils::ParseHTMLIntegerImpl(
}
// Parse an integer according to HTML spec
int32_t nsContentUtils::ParseHTMLInteger(const nsAString& aValue,
int32_t nsContentUtils::ParseHTMLInteger(const char16_t* aStart,
const char16_t* aEnd,
ParseHTMLIntegerResultFlags* aResult) {
return ParseHTMLIntegerImpl(aValue, aResult);
return ParseHTMLIntegerImpl(aStart, aEnd, aResult);
}
int32_t nsContentUtils::ParseHTMLInteger(const nsACString& aValue,
int32_t nsContentUtils::ParseHTMLInteger(const char* aStart, const char* aEnd,
ParseHTMLIntegerResultFlags* aResult) {
return ParseHTMLIntegerImpl(aValue, aResult);
return ParseHTMLIntegerImpl(aStart, aEnd, aResult);
}
#define SKIP_WHITESPACE(iter, end_iter, end_res) \

Просмотреть файл

@ -702,13 +702,23 @@ class nsContentUtils {
eParseHTMLInteger_Negative = 1 << 5,
};
static int32_t ParseHTMLInteger(const nsAString& aValue,
ParseHTMLIntegerResultFlags* aResult) {
return ParseHTMLInteger(aValue.BeginReading(), aValue.EndReading(),
aResult);
}
static int32_t ParseHTMLInteger(const char16_t* aStart, const char16_t* aEnd,
ParseHTMLIntegerResultFlags* aResult);
static int32_t ParseHTMLInteger(const nsACString& aValue,
ParseHTMLIntegerResultFlags* aResult) {
return ParseHTMLInteger(aValue.BeginReading(), aValue.EndReading(),
aResult);
}
static int32_t ParseHTMLInteger(const char* aStart, const char* aEnd,
ParseHTMLIntegerResultFlags* aResult);
private:
template <class StringT>
static int32_t ParseHTMLIntegerImpl(const StringT& aValue,
template <class CharT>
static int32_t ParseHTMLIntegerImpl(const CharT* aStart, const CharT* aEnd,
ParseHTMLIntegerResultFlags* aResult);
public:

Просмотреть файл

@ -1,270 +0,0 @@
[parsing.html]
expected: TIMEOUT
["1\\f"]
expected: TIMEOUT
["1;\\furl=foo"]
expected: FAIL
["1,\\furl=foo"]
expected: FAIL
["1\\furl=foo"]
expected: TIMEOUT
["1; url=\\"foo'bar"]
expected: FAIL
["; foo"]
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1230909
expected:
if not debug and (os == "mac") and (processor == "x86") and (bits == 32): FAIL
if debug and (os == "mac"): FAIL
if not debug and (os == "win") and (version == "6.1.7601"): FAIL
[", foo"]
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1230909
expected:
if not debug and (os == "mac") and (processor == "x86") and (bits == 32): FAIL
if debug and (os == "mac"): FAIL
if not debug and (os == "win") and (version == "6.1.7601"): FAIL
["+1; url=foo"]
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1230909
expected:
if not debug and (os == "mac") and (processor == "x86") and (bits == 32): FAIL
if debug and (os == "mac"): FAIL
if not debug and (os == "win") and (version == "6.1.7601"): FAIL
["+0; url=foo"]
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1230909
expected:
if not debug and (os == "mac") and (processor == "x86") and (bits == 32): FAIL
if debug and (os == "mac"): FAIL
if not debug and (os == "win") and (version == "6.1.7601"): FAIL
["+1; foo"]
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1230909
expected:
if not debug and (os == "mac") and (processor == "x86") and (bits == 32): FAIL
if debug and (os == "mac"): FAIL
if not debug and (os == "win") and (version == "6.1.7601"): FAIL
["+0; foo"]
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1230909
expected:
if not debug and (os == "mac") and (processor == "x86") and (bits == 32): FAIL
if debug and (os == "mac"): FAIL
if not debug and (os == "win") and (version == "6.1.7601"): FAIL
["+1"]
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1230909
expected:
if not debug and (os == "mac") and (processor == "x86") and (bits == 32): FAIL
if debug and (os == "mac"): FAIL
if not debug and (os == "win") and (version == "6.1.7601"): FAIL
if (processor == "x86_64") and (bits == 64): FAIL
["+0"]
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1230909
expected:
if not debug and (os == "mac") and (processor == "x86") and (bits == 32): FAIL
if debug and (os == "mac"): FAIL
if not debug and (os == "win") and (version == "6.1.7601"): FAIL
if (processor == "x86_64") and (bits == 64): FAIL
[".9; url=foo"]
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1230909
expected:
if not debug and (os == "mac") and (processor == "x86") and (bits == 32): FAIL
if debug and (os == "mac"): FAIL
if not debug and (os == "win") and (version == "6.1.7601"): FAIL
[";foo"]
expected: FAIL
[",foo"]
expected: FAIL
[<meta>: "1\\f"]
expected: TIMEOUT
[<meta>: "1;\\furl=foo"]
expected: FAIL
[<meta>: "1,\\furl=foo"]
expected: FAIL
[<meta>: "1\\furl=foo"]
expected: TIMEOUT
[<meta>: "1; url=\\"foo'bar"]
expected: FAIL
[Refresh header: "1; url=\\"foo'bar"]
expected: FAIL
[<meta>: "; foo"]
expected: FAIL
[Refresh header: "; foo"]
expected: FAIL
[<meta>: ";foo"]
expected: FAIL
[Refresh header: ";foo"]
expected: FAIL
[<meta>: ", foo"]
expected: FAIL
[Refresh header: ", foo"]
expected: FAIL
[<meta>: ",foo"]
expected: FAIL
[Refresh header: ",foo"]
expected: FAIL
[<meta>: "+1; url=foo"]
expected: FAIL
[Refresh header: "+1; url=foo"]
expected: FAIL
[<meta>: "+0; url=foo"]
expected: FAIL
[Refresh header: "+0; url=foo"]
expected: FAIL
[<meta>: "+1; foo"]
expected: FAIL
[Refresh header: "+1; foo"]
expected: FAIL
[<meta>: "+0; foo"]
expected: FAIL
[Refresh header: "+0; foo"]
expected: FAIL
[<meta>: "+1"]
expected: FAIL
[Refresh header: "+1"]
expected: FAIL
[<meta>: "+0"]
expected: FAIL
[Refresh header: "+0"]
expected: FAIL
[parsing.html?11-20]
expected: TIMEOUT
[<meta>: "1\\f"]
expected: TIMEOUT
[parsing.html?71-80]
[<meta>: "; foo"]
expected: FAIL
[Refresh header: "; foo"]
expected: FAIL
[<meta>: ";foo"]
expected: FAIL
[Refresh header: ";foo"]
expected: FAIL
[<meta>: ", foo"]
expected: FAIL
[Refresh header: ", foo"]
expected: FAIL
[<meta>: ",foo"]
expected: FAIL
[parsing.html?31-40]
expected: TIMEOUT
[<meta>: "1;\\furl=foo"]
expected: FAIL
[<meta>: "1,\\furl=foo"]
expected: FAIL
[<meta>: "1\\furl=foo"]
expected: TIMEOUT
[parsing.html?91-100]
[<meta>: "+1; foo"]
expected: FAIL
[Refresh header: "+1; foo"]
expected: FAIL
[<meta>: "+0; foo"]
expected: FAIL
[Refresh header: "+0; foo"]
expected: FAIL
[parsing.html?61-70]
[<meta>: "1; url=\\"foo'bar"]
expected: FAIL
[Refresh header: "1; url=\\"foo'bar"]
expected: FAIL
[parsing.html?81-90]
[Refresh header: ",foo"]
expected: FAIL
[<meta>: "+1; url=foo"]
expected: FAIL
[Refresh header: "+1; url=foo"]
expected: FAIL
[<meta>: "+0; url=foo"]
expected: FAIL
[Refresh header: "+0; url=foo"]
expected: FAIL
[parsing.html?101-110]
[<meta>: "+1"]
expected: FAIL
[Refresh header: "+1"]
expected: FAIL
[<meta>: "+0"]
expected: FAIL
[Refresh header: "+0"]
expected: FAIL
[parsing.html?1-10]
[parsing.html?121-130]
[parsing.html?51-60]
[parsing.html?21-30]
[parsing.html?41-50]