blob: 8d39dfc2ee0033bb4fe3fdd7759ef2268902eec0 [file] [log] [blame]
// Copyright 2019 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 <fuzzer/FuzzedDataProvider.h>
#include <algorithm>
#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/i18n/icu_util.h"
#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/task_environment.h"
#include "base/test/test_timeouts.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/render_text.h"
// TODO(crbug.com/1052397): Revisit once build flag switch of lacros-chrome is
// complete.
#if defined(OS_ANDROID) || (defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS))
#include "base/test/test_discardable_memory_allocator.h"
#endif
namespace {
#if defined(OS_WIN)
const char kFontDescription[] = "Segoe UI, 13px";
#elif defined(OS_ANDROID)
const char kFontDescription[] = "serif, 13px";
#else
const char kFontDescription[] = "sans, 13px";
#endif
struct Environment {
Environment()
: task_environment((base::CommandLine::Init(0, nullptr),
TestTimeouts::Initialize(),
base::test::TaskEnvironment::MainThreadType::UI)) {
logging::SetMinLogLevel(logging::LOG_FATAL);
// TODO(crbug.com/1052397): Revisit once build flag switch of lacros-chrome is
// complete.
#if defined(OS_ANDROID) || (defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS))
// Some platforms require discardable memory to use bitmap fonts.
base::DiscardableMemoryAllocator::SetInstance(
&discardable_memory_allocator);
#endif
CHECK(base::i18n::InitializeICU());
gfx::FontList::SetDefaultFontDescription(kFontDescription);
}
// TODO(crbug.com/1052397): Revisit once build flag switch of lacros-chrome is
// complete.
#if defined(OS_ANDROID) || (defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS))
base::TestDiscardableMemoryAllocator discardable_memory_allocator;
#endif
base::AtExitManager at_exit_manager;
base::test::TaskEnvironment task_environment;
};
// Commands recognized to drive the API calls on RenderText.
enum class RenderTextAPI {
kDraw,
kSetText,
kAppendText,
kSetHorizontalAlignment,
kSetVerticalAlignment,
kSetCursorEnabled,
kSetSelectionColor,
kSetSelectionBackgroundFocusedColor,
kSetSymmetricSelectionVisualBounds,
kSetFocused,
kSetClipToDisplayRect,
kSetObscured,
kSetObscuredRevealIndex,
kSetMultiline,
kSetMaxLines,
kSetWordWrapBehavior,
kSetWhitespaceElision,
kSetSubpixelRenderingSuppressed,
kSetColor,
kApplyColor,
kSetStyle,
kApplyStyle,
kSetWeight,
kApplyWeight,
kSetDirectionalityMode,
kSetElideBehavior,
kIsGraphemeBoundary,
kIndexOfAdjacentGrapheme,
kSetObscuredGlyphSpacing,
kSetDisplayRect,
kGetSubstringBounds,
kGetCursorSpan,
kMaxValue = kGetCursorSpan
};
gfx::DirectionalityMode ConsumeDirectionalityMode(FuzzedDataProvider* fdp) {
if (fdp->ConsumeBool())
return gfx::DIRECTIONALITY_FORCE_RTL;
return gfx::DIRECTIONALITY_FORCE_LTR;
}
gfx::HorizontalAlignment ConsumeHorizontalAlignment(FuzzedDataProvider* fdp) {
switch (fdp->ConsumeIntegralInRange(0, 4)) {
case 0:
return gfx::ALIGN_LEFT;
case 1:
return gfx::ALIGN_CENTER;
case 2:
return gfx::ALIGN_RIGHT;
case 3:
return gfx::ALIGN_TO_HEAD;
default:
return gfx::ALIGN_LEFT;
}
}
gfx::VerticalAlignment ConsumeVerticalAlignment(FuzzedDataProvider* fdp) {
switch (fdp->ConsumeIntegralInRange(0, 3)) {
case 0:
return gfx::ALIGN_TOP;
case 1:
return gfx::ALIGN_MIDDLE;
case 2:
return gfx::ALIGN_BOTTOM;
default:
return gfx::ALIGN_TOP;
}
}
gfx::TextStyle ConsumeStyle(FuzzedDataProvider* fdp) {
switch (fdp->ConsumeIntegralInRange(0, 4)) {
case 0:
return gfx::TEXT_STYLE_ITALIC;
case 1:
return gfx::TEXT_STYLE_STRIKE;
case 2:
return gfx::TEXT_STYLE_UNDERLINE;
case 3:
return gfx::TEXT_STYLE_HEAVY_UNDERLINE;
default:
return gfx::TEXT_STYLE_ITALIC;
}
}
gfx::WordWrapBehavior ConsumeWordWrap(FuzzedDataProvider* fdp) {
// TODO(1150235): ELIDE_LONG_WORDS is not supported.
switch (fdp->ConsumeIntegralInRange(0, 3)) {
case 0:
return gfx::IGNORE_LONG_WORDS;
case 1:
return gfx::TRUNCATE_LONG_WORDS;
case 2:
return gfx::WRAP_LONG_WORDS;
default:
return gfx::IGNORE_LONG_WORDS;
}
}
gfx::ElideBehavior ConsumeElideBehavior(FuzzedDataProvider* fdp) {
switch (fdp->ConsumeIntegralInRange(0, 7)) {
case 0:
return gfx::NO_ELIDE;
case 1:
return gfx::TRUNCATE;
case 2:
return gfx::ELIDE_HEAD;
case 3:
return gfx::ELIDE_MIDDLE;
case 4:
return gfx::ELIDE_TAIL;
case 5:
return gfx::ELIDE_EMAIL;
case 6:
return gfx::FADE_TAIL;
default:
return gfx::NO_ELIDE;
}
}
gfx::LogicalCursorDirection ConsumeLogicalCursorDirection(
FuzzedDataProvider* fdp) {
switch (fdp->ConsumeIntegralInRange(0, 1)) {
case 0:
return gfx::CURSOR_BACKWARD;
default:
return gfx::CURSOR_FORWARD;
}
}
gfx::Font::Weight ConsumeWeight(FuzzedDataProvider* fdp) {
if (fdp->ConsumeBool())
return gfx::Font::Weight::BOLD;
return gfx::Font::Weight::NORMAL;
}
SkColor ConsumeSkColor(FuzzedDataProvider* fdp) {
return static_cast<SkColor>(fdp->ConsumeIntegral<uint32_t>());
}
gfx::Range ConsumeRange(FuzzedDataProvider* fdp, size_t max) {
size_t start = fdp->ConsumeIntegralInRange<size_t>(0, max);
size_t end = fdp->ConsumeIntegralInRange<size_t>(start, max);
return gfx::Range(start, end);
}
const int kMaxStringLength = 128;
} // anonymous namespace
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
static Environment env;
std::unique_ptr<gfx::RenderText> render_text =
gfx::RenderText::CreateRenderText();
gfx::Canvas canvas;
FuzzedDataProvider fdp(data, size);
while (fdp.remaining_bytes() != 0) {
const RenderTextAPI command = fdp.ConsumeEnum<RenderTextAPI>();
switch (command) {
case RenderTextAPI::kDraw:
render_text->Draw(&canvas);
break;
case RenderTextAPI::kSetText:
render_text->SetText(
base::UTF8ToUTF16(fdp.ConsumeRandomLengthString(kMaxStringLength)));
break;
case RenderTextAPI::kAppendText:
render_text->AppendText(
base::UTF8ToUTF16(fdp.ConsumeRandomLengthString(kMaxStringLength)));
break;
case RenderTextAPI::kSetHorizontalAlignment:
render_text->SetHorizontalAlignment(ConsumeHorizontalAlignment(&fdp));
break;
case RenderTextAPI::kSetVerticalAlignment:
render_text->SetVerticalAlignment(ConsumeVerticalAlignment(&fdp));
break;
case RenderTextAPI::kSetCursorEnabled:
render_text->SetCursorEnabled(fdp.ConsumeBool());
break;
case RenderTextAPI::kSetSelectionColor:
render_text->set_selection_color(ConsumeSkColor(&fdp));
break;
case RenderTextAPI::kSetSelectionBackgroundFocusedColor:
render_text->set_selection_background_focused_color(
ConsumeSkColor(&fdp));
break;
case RenderTextAPI::kSetSymmetricSelectionVisualBounds:
render_text->set_symmetric_selection_visual_bounds(fdp.ConsumeBool());
break;
case RenderTextAPI::kSetFocused:
render_text->set_focused(fdp.ConsumeBool());
break;
case RenderTextAPI::kSetClipToDisplayRect:
render_text->set_clip_to_display_rect(fdp.ConsumeBool());
break;
case RenderTextAPI::kSetObscured:
render_text->SetObscured(fdp.ConsumeBool());
break;
case RenderTextAPI::kSetObscuredRevealIndex:
render_text->SetObscuredRevealIndex(fdp.ConsumeIntegralInRange<size_t>(
0, render_text->text().length()));
break;
case RenderTextAPI::kSetMultiline:
render_text->SetMultiline(fdp.ConsumeBool());
break;
case RenderTextAPI::kSetMaxLines:
render_text->SetMaxLines(fdp.ConsumeIntegralInRange<size_t>(0, 5));
break;
case RenderTextAPI::kSetWordWrapBehavior:
render_text->SetWordWrapBehavior(ConsumeWordWrap(&fdp));
break;
case RenderTextAPI::kSetWhitespaceElision:
render_text->SetWhitespaceElision(fdp.ConsumeBool());
break;
case RenderTextAPI::kSetSubpixelRenderingSuppressed:
render_text->set_subpixel_rendering_suppressed(fdp.ConsumeBool());
break;
case RenderTextAPI::kSetColor:
render_text->SetColor(ConsumeSkColor(&fdp));
break;
case RenderTextAPI::kApplyColor:
render_text->ApplyColor(
ConsumeSkColor(&fdp),
ConsumeRange(&fdp, render_text->text().length()));
break;
case RenderTextAPI::kSetStyle:
render_text->SetStyle(ConsumeStyle(&fdp), fdp.ConsumeBool());
break;
case RenderTextAPI::kApplyStyle:
render_text->ApplyStyle(
ConsumeStyle(&fdp), fdp.ConsumeBool(),
ConsumeRange(&fdp, render_text->text().length()));
break;
case RenderTextAPI::kSetWeight:
render_text->SetWeight(ConsumeWeight(&fdp));
break;
case RenderTextAPI::kApplyWeight:
render_text->ApplyWeight(
ConsumeWeight(&fdp),
ConsumeRange(&fdp, render_text->text().length()));
break;
case RenderTextAPI::kSetDirectionalityMode:
render_text->SetDirectionalityMode(ConsumeDirectionalityMode(&fdp));
break;
case RenderTextAPI::kSetElideBehavior:
render_text->SetElideBehavior(ConsumeElideBehavior(&fdp));
break;
case RenderTextAPI::kIsGraphemeBoundary:
render_text->IsGraphemeBoundary(fdp.ConsumeIntegralInRange<size_t>(
0, render_text->text().length()));
break;
case RenderTextAPI::kIndexOfAdjacentGrapheme: {
size_t index = render_text->IndexOfAdjacentGrapheme(
fdp.ConsumeIntegralInRange<size_t>(0, render_text->text().length()),
ConsumeLogicalCursorDirection(&fdp));
bool is_grapheme = render_text->IsGraphemeBoundary(index);
DCHECK(is_grapheme);
break;
}
case RenderTextAPI::kSetObscuredGlyphSpacing:
render_text->SetObscuredGlyphSpacing(
fdp.ConsumeIntegralInRange<size_t>(0, 10));
break;
case RenderTextAPI::kSetDisplayRect:
render_text->SetDisplayRect(
gfx::Rect(fdp.ConsumeIntegralInRange<int>(-30, 30),
fdp.ConsumeIntegralInRange<int>(-30, 30),
fdp.ConsumeIntegralInRange<int>(0, 200),
fdp.ConsumeIntegralInRange<int>(0, 30)));
break;
case RenderTextAPI::kGetSubstringBounds:
render_text->GetSubstringBounds(
ConsumeRange(&fdp, render_text->text().length()));
break;
case RenderTextAPI::kGetCursorSpan:
render_text->GetCursorSpan(
ConsumeRange(&fdp, render_text->text().length()));
break;
}
}
return 0;
}