blob: 3eda1bf3c1d980f895dc30faafe9e80ec488da05 [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 "third_party/blink/renderer/core/events/input_event.h"
#include "base/stl_util.h"
#include "third_party/blink/renderer/core/dom/events/event_dispatcher.h"
#include "third_party/blink/renderer/core/dom/range.h"
#include "third_party/blink/renderer/core/editing/commands/editing_command_type.h"
namespace blink {
namespace {
const struct {
InputEvent::InputType input_type;
const char* string_name;
} kInputTypeStringNameMap[] = {
{InputEvent::InputType::kNone, ""},
{InputEvent::InputType::kInsertText, "insertText"},
{InputEvent::InputType::kInsertLineBreak, "insertLineBreak"},
{InputEvent::InputType::kInsertParagraph, "insertParagraph"},
{InputEvent::InputType::kInsertOrderedList, "insertOrderedList"},
{InputEvent::InputType::kInsertUnorderedList, "insertUnorderedList"},
{InputEvent::InputType::kInsertHorizontalRule, "insertHorizontalRule"},
{InputEvent::InputType::kInsertFromPaste, "insertFromPaste"},
{InputEvent::InputType::kInsertFromDrop, "insertFromDrop"},
{InputEvent::InputType::kInsertFromYank, "insertFromYank"},
{InputEvent::InputType::kInsertTranspose, "insertTranspose"},
{InputEvent::InputType::kInsertReplacementText, "insertReplacementText"},
{InputEvent::InputType::kInsertCompositionText, "insertCompositionText"},
{InputEvent::InputType::kDeleteWordBackward, "deleteWordBackward"},
{InputEvent::InputType::kDeleteWordForward, "deleteWordForward"},
{InputEvent::InputType::kDeleteSoftLineBackward, "deleteSoftLineBackward"},
{InputEvent::InputType::kDeleteSoftLineForward, "deleteSoftLineForward"},
{InputEvent::InputType::kDeleteHardLineBackward, "deleteHardLineBackward"},
{InputEvent::InputType::kDeleteHardLineForward, "deleteHardLineForward"},
{InputEvent::InputType::kDeleteContentBackward, "deleteContentBackward"},
{InputEvent::InputType::kDeleteContentForward, "deleteContentForward"},
{InputEvent::InputType::kDeleteByCut, "deleteByCut"},
{InputEvent::InputType::kDeleteByDrag, "deleteByDrag"},
{InputEvent::InputType::kHistoryUndo, "historyUndo"},
{InputEvent::InputType::kHistoryRedo, "historyRedo"},
{InputEvent::InputType::kFormatBold, "formatBold"},
{InputEvent::InputType::kFormatItalic, "formatItalic"},
{InputEvent::InputType::kFormatUnderline, "formatUnderline"},
{InputEvent::InputType::kFormatStrikeThrough, "formatStrikeThrough"},
{InputEvent::InputType::kFormatSuperscript, "formatSuperscript"},
{InputEvent::InputType::kFormatSubscript, "formatSubscript"},
{InputEvent::InputType::kFormatJustifyCenter, "formatJustifyCenter"},
{InputEvent::InputType::kFormatJustifyFull, "formatJustifyFull"},
{InputEvent::InputType::kFormatJustifyRight, "formatJustifyRight"},
{InputEvent::InputType::kFormatJustifyLeft, "formatJustifyLeft"},
{InputEvent::InputType::kFormatIndent, "formatIndent"},
{InputEvent::InputType::kFormatOutdent, "formatOutdent"},
{InputEvent::InputType::kFormatRemove, "formatRemove"},
{InputEvent::InputType::kFormatSetBlockTextDirection,
"formatSetBlockTextDirection"},
};
static_assert(
base::size(kInputTypeStringNameMap) ==
static_cast<size_t>(InputEvent::InputType::kNumberOfInputTypes),
"must handle all InputEvent::InputType");
String ConvertInputTypeToString(InputEvent::InputType input_type) {
auto* const it =
std::begin(kInputTypeStringNameMap) + static_cast<size_t>(input_type);
if (it >= std::begin(kInputTypeStringNameMap) &&
it < std::end(kInputTypeStringNameMap))
return AtomicString(it->string_name);
return g_empty_string;
}
InputEvent::InputType ConvertStringToInputType(const String& string_name) {
// TODO(input-dev): Use binary search if the map goes larger.
for (const auto& entry : kInputTypeStringNameMap) {
if (string_name == entry.string_name)
return entry.input_type;
}
return InputEvent::InputType::kNone;
}
} // anonymous namespace
InputEvent::InputEvent(const AtomicString& type,
const InputEventInit* initializer)
: UIEvent(type, initializer) {
// TODO(ojan): We should find a way to prevent conversion like
// String->enum->String just in order to use initializer.
// See InputEvent::createBeforeInput() for the first conversion.
if (initializer->hasInputType())
input_type_ = ConvertStringToInputType(initializer->inputType());
if (initializer->hasData())
data_ = initializer->data();
if (initializer->hasDataTransfer())
data_transfer_ = initializer->dataTransfer();
if (initializer->hasIsComposing())
is_composing_ = initializer->isComposing();
if (!initializer->hasTargetRanges())
return;
for (const auto& range : initializer->targetRanges())
ranges_.push_back(range->toRange());
}
/* static */
InputEvent* InputEvent::CreateBeforeInput(InputType input_type,
const String& data,
EventCancelable cancelable,
EventIsComposing is_composing,
const StaticRangeVector* ranges) {
InputEventInit* input_event_init = InputEventInit::Create();
input_event_init->setBubbles(true);
input_event_init->setCancelable(cancelable == kIsCancelable);
// TODO(ojan): We should find a way to prevent conversion like
// String->enum->String just in order to use initializer.
// See InputEvent::InputEvent() for the second conversion.
input_event_init->setInputType(ConvertInputTypeToString(input_type));
input_event_init->setData(data);
input_event_init->setIsComposing(is_composing == kIsComposing);
if (ranges)
input_event_init->setTargetRanges(*ranges);
input_event_init->setComposed(true);
return InputEvent::Create(event_type_names::kBeforeinput, input_event_init);
}
/* static */
InputEvent* InputEvent::CreateBeforeInput(InputType input_type,
DataTransfer* data_transfer,
EventCancelable cancelable,
EventIsComposing is_composing,
const StaticRangeVector* ranges) {
InputEventInit* input_event_init = InputEventInit::Create();
input_event_init->setBubbles(true);
input_event_init->setCancelable(cancelable == kIsCancelable);
input_event_init->setInputType(ConvertInputTypeToString(input_type));
input_event_init->setDataTransfer(data_transfer);
input_event_init->setIsComposing(is_composing == kIsComposing);
if (ranges)
input_event_init->setTargetRanges(*ranges);
input_event_init->setComposed(true);
return InputEvent::Create(event_type_names::kBeforeinput, input_event_init);
}
/* static */
InputEvent* InputEvent::CreateInput(InputType input_type,
const String& data,
EventIsComposing is_composing,
const StaticRangeVector* ranges) {
InputEventInit* input_event_init = InputEventInit::Create();
input_event_init->setBubbles(true);
input_event_init->setCancelable(false);
// TODO(ojan): We should find a way to prevent conversion like
// String->enum->String just in order to use initializer.
// See InputEvent::InputEvent() for the second conversion.
input_event_init->setInputType(ConvertInputTypeToString(input_type));
input_event_init->setData(data);
input_event_init->setIsComposing(is_composing == kIsComposing);
if (ranges)
input_event_init->setTargetRanges(*ranges);
input_event_init->setComposed(true);
return InputEvent::Create(event_type_names::kInput, input_event_init);
}
String InputEvent::inputType() const {
return ConvertInputTypeToString(input_type_);
}
StaticRangeVector InputEvent::getTargetRanges() const {
StaticRangeVector static_ranges;
for (const auto& range : ranges_)
static_ranges.push_back(StaticRange::Create(range));
return static_ranges;
}
bool InputEvent::IsInputEvent() const {
return true;
}
void InputEvent::Trace(Visitor* visitor) const {
UIEvent::Trace(visitor);
visitor->Trace(data_transfer_);
visitor->Trace(ranges_);
}
DispatchEventResult InputEvent::DispatchEvent(EventDispatcher& dispatcher) {
DispatchEventResult result = dispatcher.Dispatch();
// It's weird to hold and clear live |Range| objects internally, and only
// expose |StaticRange| through |getTargetRanges()|. However there is no
// better solutions due to the following issues:
// 1. We don't want to expose live |Range| objects for the author to hold as
// it will slow down all DOM operations. So we just expose |StaticRange|.
// 2. Event handlers in chain might modify DOM, which means we have to keep
// a copy of live |Range| internally and return snapshots.
// 3. We don't want authors to hold live |Range| indefinitely by holding
// |InputEvent|, so we clear them after dispatch.
// Authors should explicitly call |getTargetRanges()|->|toRange()| if they
// want to keep a copy of |Range|. See Editing TF meeting notes:
// https://docs.google.com/document/d/1hCj6QX77NYIVY0RWrMHT1Yra6t8_Qu8PopaWLG0AM58/edit?usp=sharing
//
// This is the only Event::DispatchEvent() that modifies the event.
ranges_.clear();
return result;
}
} // namespace blink