blob: 2d6802add07a56d01edd39dceacc871cdd81d5ba [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "core/svg/SVGParsingError.h"
#include "core/dom/QualifiedName.h"
#include "platform/json/JSONValues.h"
#include "platform/wtf/text/CharacterNames.h"
#include "platform/wtf/text/StringBuilder.h"
#include <utility>
namespace blink {
namespace {
void AppendErrorContextInfo(StringBuilder& builder,
const String& tag_name,
const QualifiedName& name) {
builder.Append('<');
builder.Append(tag_name);
builder.Append("> attribute ");
builder.Append(name.ToString());
}
std::pair<const char*, const char*> MessageForStatus(SVGParseStatus status) {
switch (status) {
case SVGParseStatus::kTrailingGarbage:
return std::make_pair("Trailing garbage, ", ".");
case SVGParseStatus::kExpectedAngle:
return std::make_pair("Expected angle, ", ".");
case SVGParseStatus::kExpectedArcFlag:
return std::make_pair("Expected arc flag ('0' or '1'), ", ".");
case SVGParseStatus::kExpectedBoolean:
return std::make_pair("Expected 'true' or 'false', ", ".");
case SVGParseStatus::kExpectedEndOfArguments:
return std::make_pair("Expected ')', ", ".");
case SVGParseStatus::kExpectedEnumeration:
return std::make_pair("Unrecognized enumerated value, ", ".");
case SVGParseStatus::kExpectedInteger:
return std::make_pair("Expected integer, ", ".");
case SVGParseStatus::kExpectedLength:
return std::make_pair("Expected length, ", ".");
case SVGParseStatus::kExpectedMoveToCommand:
return std::make_pair("Expected moveto path command ('M' or 'm'), ", ".");
case SVGParseStatus::kExpectedNumber:
return std::make_pair("Expected number, ", ".");
case SVGParseStatus::kExpectedNumberOrPercentage:
return std::make_pair("Expected number or percentage, ", ".");
case SVGParseStatus::kExpectedPathCommand:
return std::make_pair("Expected path command, ", ".");
case SVGParseStatus::kExpectedStartOfArguments:
return std::make_pair("Expected '(', ", ".");
case SVGParseStatus::kExpectedTransformFunction:
return std::make_pair("Expected transform function, ", ".");
case SVGParseStatus::kNegativeValue:
return std::make_pair("A negative value is not valid. (", ")");
case SVGParseStatus::kZeroValue:
return std::make_pair("A value of zero is not valid. (", ")");
case SVGParseStatus::kParsingFailed:
return std::make_pair("Invalid value, ", ".");
default:
NOTREACHED();
break;
}
return std::make_pair("", "");
}
bool DisableLocus(SVGParseStatus status) {
// Disable locus for semantic errors and generic errors (see TODO below).
return status == SVGParseStatus::kNegativeValue ||
status == SVGParseStatus::kZeroValue ||
status == SVGParseStatus::kParsingFailed;
}
void AppendValue(StringBuilder& builder,
SVGParsingError error,
const AtomicString& value) {
builder.Append('"');
if (!error.HasLocus() || DisableLocus(error.Status())) {
EscapeStringForJSON(value.GetString(), &builder);
} else {
// Emit a string on the form: '"[...]<before><after>[...]"'
unsigned locus = error.Locus();
DCHECK_LE(locus, value.length());
// Amount of context to show before/after the error.
const unsigned kContext = 16;
unsigned context_start = std::max(locus, kContext) - kContext;
unsigned context_end = std::min(locus + kContext, value.length());
DCHECK_LE(context_start, context_end);
DCHECK_LE(context_end, value.length());
if (context_start != 0)
builder.Append(kHorizontalEllipsisCharacter);
EscapeStringForJSON(
value.GetString().Substring(context_start, context_end - context_start),
&builder);
if (context_end != value.length())
builder.Append(kHorizontalEllipsisCharacter);
}
builder.Append('"');
}
} // namespace
String SVGParsingError::Format(const String& tag_name,
const QualifiedName& name,
const AtomicString& value) const {
StringBuilder builder;
AppendErrorContextInfo(builder, tag_name, name);
builder.Append(": ");
if (HasLocus() && Locus() == value.length())
builder.Append("Unexpected end of attribute. ");
auto message = MessageForStatus(Status());
builder.Append(message.first);
AppendValue(builder, *this, value);
builder.Append(message.second);
return builder.ToString();
}
} // namespace blink