blob: aaa4a38eb6744c096d0e2675de0fee8510e8c00d [file] [log] [blame]
// Copyright 2017 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/editing/commands/undo_step.h"
#include "third_party/blink/renderer/core/dom/events/scoped_event_queue.h"
#include "third_party/blink/renderer/core/editing/commands/edit_command.h"
#include "third_party/blink/renderer/core/editing/commands/editing_commands_utilities.h"
#include "third_party/blink/renderer/core/editing/commands/undo_stack.h"
#include "third_party/blink/renderer/core/editing/editing_utilities.h"
#include "third_party/blink/renderer/core/editing/editor.h"
#include "third_party/blink/renderer/core/editing/selection_template.h"
#include "third_party/blink/renderer/core/editing/set_selection_options.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
namespace blink {
namespace {
uint64_t g_current_sequence_number = 0;
}
UndoStep::UndoStep(Document* document,
const SelectionForUndoStep& starting_selection,
const SelectionForUndoStep& ending_selection,
InputEvent::InputType input_type)
: document_(document),
starting_selection_(starting_selection),
ending_selection_(ending_selection),
input_type_(input_type),
sequence_number_(++g_current_sequence_number) {}
void UndoStep::Unapply() {
DCHECK(document_);
LocalFrame* frame = document_->GetFrame();
DCHECK(frame);
// Changes to the document may have been made since the last editing operation
// that require a layout, as in <rdar://problem/5658603>. Low level
// operations, like RemoveNodeCommand, don't require a layout because the high
// level operations that use them perform one if one is necessary (like for
// the creation of VisiblePositions).
document_->UpdateStyleAndLayout(DocumentUpdateReason::kEditing);
{
wtf_size_t size = commands_.size();
for (wtf_size_t i = size; i; --i)
commands_[i - 1]->DoUnapply();
}
EventQueueScope scope;
DispatchEditableContentChangedEvents(StartingRootEditableElement(),
EndingRootEditableElement());
DispatchInputEventEditableContentChanged(
StartingRootEditableElement(), EndingRootEditableElement(),
InputEvent::InputType::kHistoryUndo, g_null_atom,
InputEvent::EventIsComposing::kNotComposing);
const SelectionInDOMTree& new_selection =
CorrectedSelectionAfterCommand(StartingSelection(), document_);
ChangeSelectionAfterCommand(frame, new_selection,
SetSelectionOptions::Builder()
.SetShouldCloseTyping(true)
.SetShouldClearTypingStyle(true)
.SetIsDirectional(SelectionIsDirectional())
.Build());
Editor& editor = frame->GetEditor();
editor.SetLastEditCommand(nullptr);
editor.GetUndoStack().RegisterRedoStep(this);
editor.RespondToChangedContents(new_selection.Base());
}
void UndoStep::Reapply() {
DCHECK(document_);
LocalFrame* frame = document_->GetFrame();
DCHECK(frame);
// Changes to the document may have been made since the last editing operation
// that require a layout, as in <rdar://problem/5658603>. Low level
// operations, like RemoveNodeCommand, don't require a layout because the high
// level operations that use them perform one if one is necessary (like for
// the creation of VisiblePositions).
document_->UpdateStyleAndLayout(DocumentUpdateReason::kEditing);
{
for (const auto& command : commands_)
command->DoReapply();
}
EventQueueScope scope;
DispatchEditableContentChangedEvents(StartingRootEditableElement(),
EndingRootEditableElement());
DispatchInputEventEditableContentChanged(
StartingRootEditableElement(), EndingRootEditableElement(),
InputEvent::InputType::kHistoryRedo, g_null_atom,
InputEvent::EventIsComposing::kNotComposing);
const SelectionInDOMTree& new_selection =
CorrectedSelectionAfterCommand(EndingSelection(), document_);
ChangeSelectionAfterCommand(frame, new_selection,
SetSelectionOptions::Builder()
.SetShouldCloseTyping(true)
.SetShouldClearTypingStyle(true)
.SetIsDirectional(SelectionIsDirectional())
.Build());
Editor& editor = frame->GetEditor();
editor.SetLastEditCommand(nullptr);
editor.GetUndoStack().RegisterUndoStep(this);
editor.RespondToChangedContents(new_selection.Base());
}
InputEvent::InputType UndoStep::GetInputType() const {
return input_type_;
}
void UndoStep::Append(SimpleEditCommand* command) {
commands_.push_back(command);
}
void UndoStep::Append(UndoStep* undo_step) {
commands_.AppendVector(undo_step->commands_);
}
void UndoStep::SetStartingSelection(const SelectionForUndoStep& selection) {
starting_selection_ = selection;
}
void UndoStep::SetEndingSelection(const SelectionForUndoStep& selection) {
ending_selection_ = selection;
}
void UndoStep::Trace(Visitor* visitor) const {
visitor->Trace(document_);
visitor->Trace(starting_selection_);
visitor->Trace(ending_selection_);
visitor->Trace(commands_);
}
} // namespace blink