提交 94d9cb25 编写于 作者: H Hixie

Remove editing code from the C++ side of Sky.

This removes the bulk of core/editing/*.
The following files remain, because they might be useful yet:
  EditingBoundary.h
  FindOptions.h
  htmlediting.cpp
  htmlediting.h
  PlainTextRange.cpp
  PlainTextRange.h
  PositionWithAffinity.cpp
  PositionWithAffinity.h
  RenderedPosition.cpp
  RenderedPosition.h
  TextAffinity.h
  TextGranularity.h
  TextIterator.cpp
  TextIterator.h
  VisiblePosition.cpp
  VisiblePosition.h
  VisibleSelection.cpp
  VisibleSelection.h
  VisibleUnits.cpp
  VisibleUnits.h

In addition to remove obviously editing-related stuff like
"ApplyBlockElementCommand.cpp" and "InsertLineBreakCommand.cpp", this
also removes the DOM side of selection, all the caret management and
painting code, composition support (IME) including the relevant
events, spelling checker support, and the undo stack.

Outside the core/editing/* directory, I also deleted the EditorClient,
SpellCheckerClient, and EmptyClients classes. The other changes
outside of editing/ are mostly just about removing mentions of the
selection or carets.

I tried to leave the code for _painting_ selections and composition
runs, though that code is mostly disconnected now.
上级 5bbf40df
......@@ -324,103 +324,29 @@ sky_core_files = [
"dom/shadow/SelectRuleFeatureSet.h",
"dom/shadow/ShadowRoot.cpp",
"dom/shadow/ShadowRoot.h",
"editing/AppendNodeCommand.cpp",
"editing/AppendNodeCommand.h",
"editing/ApplyBlockElementCommand.cpp",
"editing/ApplyBlockElementCommand.h",
"editing/Caret.cpp",
"editing/Caret.h",
"editing/CompositeEditCommand.cpp",
"editing/CompositeEditCommand.h",
"editing/CompositionUnderline.h",
"editing/CompositionUnderlineRangeFilter.cpp",
"editing/CompositionUnderlineRangeFilter.h",
"editing/DOMSelection.cpp",
"editing/DOMSelection.h",
"editing/DeleteFromTextNodeCommand.cpp",
"editing/DeleteFromTextNodeCommand.h",
"editing/DeleteSelectionCommand.cpp",
"editing/DeleteSelectionCommand.h",
"editing/EditAction.h",
"editing/EditCommand.cpp",
"editing/EditCommand.h",
"editing/EditingBehavior.cpp",
"editing/EditingBehavior.h",
"editing/EditingBoundary.h",
"editing/EditingStyle.cpp",
"editing/EditingStyle.h",
"editing/Editor.cpp",
"editing/Editor.h",
"editing/EditorCommand.cpp",
"editing/EditorKeyBindings.cpp",
"editing/FindOptions.h",
"editing/FrameSelection.cpp",
"editing/FrameSelection.h",
"editing/HTMLInterchange.cpp",
"editing/HTMLInterchange.h",
"editing/InputMethodController.cpp",
"editing/InputMethodController.h",
"editing/InsertIntoTextNodeCommand.cpp",
"editing/InsertIntoTextNodeCommand.h",
"editing/InsertLineBreakCommand.cpp",
"editing/InsertLineBreakCommand.h",
"editing/InsertNodeBeforeCommand.cpp",
"editing/InsertNodeBeforeCommand.h",
"editing/InsertParagraphSeparatorCommand.cpp",
"editing/InsertParagraphSeparatorCommand.h",
"editing/InsertTextCommand.cpp",
"editing/InsertTextCommand.h",
"editing/MoveSelectionCommand.cpp",
"editing/MoveSelectionCommand.h",
"editing/htmlediting.cpp",
"editing/htmlediting.h",
"editing/PlainTextRange.cpp",
"editing/PlainTextRange.h",
"editing/PositionWithAffinity.cpp",
"editing/PositionWithAffinity.h",
"editing/RemoveNodeCommand.cpp",
"editing/RemoveNodeCommand.h",
"editing/RemoveNodePreservingChildrenCommand.cpp",
"editing/RemoveNodePreservingChildrenCommand.h",
"editing/RenderedPosition.cpp",
"editing/RenderedPosition.h",
"editing/ReplaceSelectionCommand.cpp",
"editing/ReplaceSelectionCommand.h",
"editing/SelectionType.h",
"editing/SmartReplace.h",
"editing/SmartReplaceICU.cpp",
"editing/SpellCheckRequester.cpp",
"editing/SpellCheckRequester.h",
"editing/SpellChecker.cpp",
"editing/SpellChecker.h",
"editing/SplitElementCommand.cpp",
"editing/SplitElementCommand.h",
"editing/SplitTextNodeCommand.cpp",
"editing/SplitTextNodeCommand.h",
"editing/TextAffinity.h",
"editing/TextCheckingHelper.cpp",
"editing/TextCheckingHelper.h",
"editing/TextGranularity.h",
"editing/TextInsertionBaseCommand.cpp",
"editing/TextInsertionBaseCommand.h",
"editing/TextIterator.cpp",
"editing/TextIterator.h",
"editing/TypingCommand.cpp",
"editing/TypingCommand.h",
"editing/UndoStack.cpp",
"editing/UndoStack.h",
"editing/UndoStep.h",
"editing/VisiblePosition.cpp",
"editing/VisiblePosition.h",
"editing/VisibleSelection.cpp",
"editing/VisibleSelection.h",
"editing/VisibleUnits.cpp",
"editing/VisibleUnits.h",
"editing/WritingDirection.h",
"editing/htmlediting.cpp",
"editing/htmlediting.h",
"events/BeforeTextInsertedEvent.cpp",
"events/BeforeTextInsertedEvent.h",
"events/CompositionEvent.cpp",
"events/CompositionEvent.h",
"events/ErrorEvent.cpp",
"events/ErrorEvent.h",
"events/Event.cpp",
......@@ -500,8 +426,6 @@ sky_core_files = [
"loader/CanvasImageDecoder.h",
"loader/DocumentLoadTiming.cpp",
"loader/DocumentLoadTiming.h",
"loader/EmptyClients.cpp",
"loader/EmptyClients.h",
"loader/FrameLoader.cpp",
"loader/FrameLoader.h",
"loader/FrameLoaderClient.h",
......@@ -511,13 +435,11 @@ sky_core_files = [
"loader/UniqueIdentifier.cpp",
"loader/UniqueIdentifier.h",
"page/ChromeClient.h",
"page/EditorClient.h",
"page/FocusController.cpp",
"page/FocusController.h",
"page/FocusType.h",
"page/Page.cpp",
"page/Page.h",
"page/SpellCheckerClient.h",
"painting/Canvas.cpp",
"painting/Canvas.h",
"painting/CanvasColor.cpp",
......@@ -761,8 +683,6 @@ core_idl_files = get_path_info([
"dom/Text.idl",
"dom/URL.idl",
"dom/shadow/ShadowRoot.idl",
"editing/Selection.idl",
"events/CompositionEvent.idl",
"events/ErrorEvent.idl",
"events/Event.idl",
"events/GestureEvent.idl",
......@@ -836,7 +756,6 @@ core_dependency_idl_files = get_path_info([
# interfaces that inherit from Event, including Event itself
core_event_idl_files = get_path_info([
"css/MediaQueryListEvent.idl",
"events/CompositionEvent.idl",
"events/ErrorEvent.idl",
"events/Event.idl",
"events/GestureEvent.idl",
......
......@@ -31,7 +31,6 @@
#include "sky/engine/core/css/CSSSelectorList.h"
#include "sky/engine/core/dom/Document.h"
#include "sky/engine/core/dom/shadow/ShadowRoot.h"
#include "sky/engine/core/editing/FrameSelection.h"
#include "sky/engine/core/frame/LocalFrame.h"
#include "sky/engine/core/html/parser/HTMLParserIdioms.h"
#include "sky/engine/core/page/FocusController.h"
......@@ -46,8 +45,6 @@ static bool matchesFocusPseudoClass(const Element& element)
LocalFrame* frame = element.document().frame();
if (!frame)
return false;
if (!frame->selection().isFocusedAndActive())
return false;
return true;
}
......
......@@ -27,7 +27,6 @@
#include "sky/engine/core/dom/MutationObserverInterestGroup.h"
#include "sky/engine/core/dom/MutationRecord.h"
#include "sky/engine/core/dom/Text.h"
#include "sky/engine/core/editing/FrameSelection.h"
#include "sky/engine/core/frame/LocalFrame.h"
#include "sky/engine/wtf/CheckedArithmetic.h"
......@@ -164,9 +163,6 @@ void CharacterData::setDataAndUpdate(const String& newData, unsigned offsetOfRep
if (isTextNode())
toText(this)->updateTextRenderer(offsetOfReplacedData, oldLength, recalcStyleBehavior);
if (document().frame())
document().frame()->selection().didUpdateCharacterData(this, offsetOfReplacedData, oldLength, newLength);
didModifyData(oldData);
}
......
......@@ -56,6 +56,7 @@
#include "sky/engine/core/dom/NodeRenderingTraversal.h"
#include "sky/engine/core/dom/NodeTraversal.h"
#include "sky/engine/core/dom/NodeWithIndex.h"
#include "sky/engine/core/dom/Range.h"
#include "sky/engine/core/dom/RequestAnimationFrameCallback.h"
#include "sky/engine/core/dom/ScriptedAnimationController.h"
#include "sky/engine/core/dom/SelectorQuery.h"
......@@ -65,8 +66,7 @@
#include "sky/engine/core/dom/custom/custom_element_registry.h"
#include "sky/engine/core/dom/shadow/ElementShadow.h"
#include "sky/engine/core/dom/shadow/ShadowRoot.h"
#include "sky/engine/core/editing/FrameSelection.h"
#include "sky/engine/core/editing/SpellChecker.h"
#include "sky/engine/core/editing/PositionWithAffinity.h"
#include "sky/engine/core/events/Event.h"
#include "sky/engine/core/events/PageTransitionEvent.h"
#include "sky/engine/core/frame/FrameHost.h"
......@@ -1193,13 +1193,6 @@ void Document::nodeChildrenWillBeRemoved(ContainerNode& container)
for (AttachedRangeSet::const_iterator it = m_ranges.begin(); it != end; ++it)
(*it)->nodeChildrenWillBeRemoved(container);
}
if (LocalFrame* frame = this->frame()) {
for (Node* n = container.firstChild(); n; n = n->nextSibling()) {
frame->selection().nodeWillBeRemoved(*n);
frame->page()->dragCaretController().nodeWillBeRemoved(*n);
}
}
}
void Document::nodeWillBeRemoved(Node& n)
......@@ -1209,11 +1202,6 @@ void Document::nodeWillBeRemoved(Node& n)
for (AttachedRangeSet::const_iterator it = m_ranges.begin(); it != rangesEnd; ++it)
(*it)->nodeWillBeRemoved(n);
}
if (LocalFrame* frame = this->frame()) {
frame->selection().nodeWillBeRemoved(n);
frame->page()->dragCaretController().nodeWillBeRemoved(n);
}
}
void Document::didInsertText(Node* text, unsigned offset, unsigned length)
......@@ -1250,9 +1238,6 @@ void Document::didMergeTextNodes(Text& oldNode, unsigned offset)
(*it)->didMergeTextNodes(oldNodeWithIndex, offset);
}
if (m_frame)
m_frame->selection().didMergeTextNodes(oldNode, offset);
// FIXME: This should update markers for spelling and grammar checking.
}
......@@ -1264,9 +1249,6 @@ void Document::didSplitTextNode(Text& oldNode)
(*it)->didSplitTextNode(oldNode);
}
if (m_frame)
m_frame->selection().didSplitTextNode(oldNode);
// FIXME: This should update markers for spelling and grammar checking.
}
......
......@@ -53,9 +53,6 @@ callback CustomElementConstructor = Element ();
Range caretRangeFromPoint([Default=Undefined] optional long x,
[Default=Undefined] optional long y);
// Mozilla extensions
Selection getSelection();
// HTML 5
readonly attribute Element activeElement;
boolean hasFocus();
......
......@@ -28,6 +28,7 @@
#define SKY_ENGINE_CORE_DOM_DOCUMENTMARKERCONTROLLER_H_
#include "sky/engine/core/dom/DocumentMarker.h"
#include "sky/engine/core/dom/Range.h"
#include "sky/engine/platform/geometry/IntRect.h"
#include "sky/engine/platform/heap/Handle.h"
#include "sky/engine/wtf/HashMap.h"
......
......@@ -56,7 +56,6 @@
#include "sky/engine/core/dom/custom/custom_element.h"
#include "sky/engine/core/dom/shadow/InsertionPoint.h"
#include "sky/engine/core/dom/shadow/ShadowRoot.h"
#include "sky/engine/core/editing/FrameSelection.h"
#include "sky/engine/core/editing/TextIterator.h"
#include "sky/engine/core/editing/htmlediting.h"
#include "sky/engine/core/frame/FrameView.h"
......@@ -1094,17 +1093,6 @@ void Element::updateFocusAppearance(bool /*restorePreviousSelection*/)
RefPtr<LocalFrame> frame(document().frame());
if (!frame)
return;
// When focusing an editable element in an iframe, don't reset the selection if it already contains a selection.
if (this == frame->selection().rootEditableElement())
return;
// FIXME: We should restore the previous selection if there is one.
VisibleSelection newSelection = VisibleSelection(firstPositionInOrBeforeNode(this), DOWNSTREAM);
// Passing DoNotSetFocus as this function is called after FocusController::setFocusedElement()
// and we don't want to change the focus to a new Element.
frame->selection().setSelection(newSelection, FrameSelection::CloseTyping | FrameSelection::ClearTypingStyle | FrameSelection::DoNotSetFocus);
frame->selection().revealSelection();
}
}
......
......@@ -36,7 +36,6 @@
#include "sky/engine/core/dom/TreeScopeAdopter.h"
#include "sky/engine/core/dom/shadow/ElementShadow.h"
#include "sky/engine/core/dom/shadow/ShadowRoot.h"
#include "sky/engine/core/editing/DOMSelection.h"
#include "sky/engine/core/frame/FrameView.h"
#include "sky/engine/core/frame/LocalFrame.h"
#include "sky/engine/core/page/FocusController.h"
......@@ -74,11 +73,6 @@ TreeScope::~TreeScope()
ASSERT(!m_guardRefCount);
m_rootNode->setTreeScope(0);
if (m_selection) {
m_selection->clearTreeScope();
m_selection = nullptr;
}
if (m_parentTreeScope)
m_parentTreeScope->guardDeref();
}
......@@ -192,21 +186,6 @@ Element* TreeScope::elementFromPoint(int x, int y) const
return toElement(node);
}
DOMSelection* TreeScope::getSelection() const
{
if (!rootNode().document().frame())
return 0;
if (m_selection)
return m_selection.get();
// FIXME: The correct selection in Shadow DOM requires that Position can have a ShadowRoot
// as a container.
// See https://bugs.webkit.org/show_bug.cgi?id=82697
m_selection = DOMSelection::create(this);
return m_selection.get();
}
void TreeScope::adoptIfNeeded(Node& node)
{
ASSERT(this);
......
......@@ -34,7 +34,6 @@
namespace blink {
class ContainerNode;
class DOMSelection;
class Document;
class Element;
class HitTestResult;
......@@ -68,8 +67,6 @@ public:
// For accessibility.
bool shouldCacheLabelsByForAttribute() const { return m_labelsByForAttribute; }
DOMSelection* getSelection() const;
// Used by the basic DOM mutation methods (e.g., appendChild()).
void adoptIfNeeded(Node&);
......@@ -156,8 +153,6 @@ private:
OwnPtr<DocumentOrderedMap> m_imageMapsByName;
OwnPtr<DocumentOrderedMap> m_labelsByForAttribute;
mutable RefPtr<DOMSelection> m_selection;
int m_guardRefCount;
};
......
......@@ -28,7 +28,6 @@ interface ShadowRoot : DocumentFragment {
readonly attribute Element activeElement;
[RaisesException] Node cloneNode([Named] optional boolean deep = true);
Selection getSelection();
Element getElementById([Default=Undefined] optional DOMString elementId);
Element elementFromPoint([Default=Undefined] optional long x,
......
/*
* Copyright (C) 2005, 2006, 2008 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "sky/engine/core/editing/AppendNodeCommand.h"
#include "sky/engine/bindings/exception_state.h"
namespace blink {
AppendNodeCommand::AppendNodeCommand(PassRefPtr<ContainerNode> parent, PassRefPtr<Node> node)
: SimpleEditCommand(parent->document())
, m_parent(parent)
, m_node(node)
{
ASSERT(m_parent);
ASSERT(m_node);
ASSERT(!m_node->parentNode());
ASSERT(m_parent->hasEditableStyle() || !m_parent->inActiveDocument());
}
void AppendNodeCommand::doApply()
{
if (!m_parent->hasEditableStyle() && m_parent->inActiveDocument())
return;
m_parent->appendChild(m_node.get(), IGNORE_EXCEPTION);
}
void AppendNodeCommand::doUnapply()
{
if (!m_parent->hasEditableStyle())
return;
m_node->remove(IGNORE_EXCEPTION);
}
} // namespace blink
/*
* Copyright (C) 2005, 2006, 2008 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SKY_ENGINE_CORE_EDITING_APPENDNODECOMMAND_H_
#define SKY_ENGINE_CORE_EDITING_APPENDNODECOMMAND_H_
#include "sky/engine/core/editing/EditCommand.h"
namespace blink {
class AppendNodeCommand final : public SimpleEditCommand {
public:
static PassRefPtr<AppendNodeCommand> create(PassRefPtr<ContainerNode> parent, PassRefPtr<Node> node)
{
return adoptRef(new AppendNodeCommand(parent, node));
}
private:
AppendNodeCommand(PassRefPtr<ContainerNode> parent, PassRefPtr<Node>);
virtual void doApply() override;
virtual void doUnapply() override;
RefPtr<ContainerNode> m_parent;
RefPtr<Node> m_node;
};
} // namespace blink
#endif // SKY_ENGINE_CORE_EDITING_APPENDNODECOMMAND_H_
/*
* Copyright (C) 2006, 2008 Apple Inc. All rights reserved.
* Copyright (C) 2010 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "sky/engine/core/editing/ApplyBlockElementCommand.h"
#include "gen/sky/core/HTMLNames.h"
#include "sky/engine/bindings/exception_state.h"
#include "sky/engine/core/dom/NodeRenderStyle.h"
#include "sky/engine/core/dom/Text.h"
#include "sky/engine/core/editing/VisiblePosition.h"
#include "sky/engine/core/editing/VisibleUnits.h"
#include "sky/engine/core/editing/htmlediting.h"
#include "sky/engine/core/html/HTMLElement.h"
#include "sky/engine/core/rendering/RenderObject.h"
#include "sky/engine/core/rendering/style/RenderStyle.h"
namespace blink {
ApplyBlockElementCommand::ApplyBlockElementCommand(Document& document, const QualifiedName& tagName, const AtomicString& inlineStyle)
: CompositeEditCommand(document)
, m_tagName(tagName)
, m_inlineStyle(inlineStyle)
{
}
ApplyBlockElementCommand::ApplyBlockElementCommand(Document& document, const QualifiedName& tagName)
: CompositeEditCommand(document)
, m_tagName(tagName)
{
}
void ApplyBlockElementCommand::doApply()
{
if (!endingSelection().rootEditableElement())
return;
VisiblePosition visibleEnd = endingSelection().visibleEnd();
VisiblePosition visibleStart = endingSelection().visibleStart();
if (visibleStart.isNull() || visibleStart.isOrphan() || visibleEnd.isNull() || visibleEnd.isOrphan())
return;
// When a selection ends at the start of a paragraph, we rarely paint
// the selection gap before that paragraph, because there often is no gap.
// In a case like this, it's not obvious to the user that the selection
// ends "inside" that paragraph, so it would be confusing if Indent/Outdent
// operated on that paragraph.
// FIXME: We paint the gap before some paragraphs that are indented with left
// margin/padding, but not others. We should make the gap painting more consistent and
// then use a left margin/padding rule here.
if (visibleEnd != visibleStart && isStartOfParagraph(visibleEnd)) {
VisibleSelection newSelection(visibleStart, visibleEnd.previous(CannotCrossEditingBoundary), endingSelection().isDirectional());
if (newSelection.isNone())
return;
setEndingSelection(newSelection);
}
VisibleSelection selection = selectionForParagraphIteration(endingSelection());
VisiblePosition startOfSelection = selection.visibleStart();
VisiblePosition endOfSelection = selection.visibleEnd();
ASSERT(!startOfSelection.isNull());
ASSERT(!endOfSelection.isNull());
RefPtr<ContainerNode> startScope = nullptr;
int startIndex = indexForVisiblePosition(startOfSelection, startScope);
RefPtr<ContainerNode> endScope = nullptr;
int endIndex = indexForVisiblePosition(endOfSelection, endScope);
formatSelection(startOfSelection, endOfSelection);
document().updateLayout();
ASSERT(startScope == endScope);
ASSERT(startIndex >= 0);
ASSERT(startIndex <= endIndex);
if (startScope == endScope && startIndex >= 0 && startIndex <= endIndex) {
VisiblePosition start(visiblePositionForIndex(startIndex, startScope.get()));
VisiblePosition end(visiblePositionForIndex(endIndex, endScope.get()));
if (start.isNotNull() && end.isNotNull())
setEndingSelection(VisibleSelection(start, end, endingSelection().isDirectional()));
}
}
void ApplyBlockElementCommand::formatSelection(const VisiblePosition& startOfSelection, const VisiblePosition& endOfSelection)
{
}
static bool isNewLineAtPosition(const Position& position)
{
Node* textNode = position.containerNode();
int offset = position.offsetInContainerNode();
if (!textNode || !textNode->isTextNode() || offset < 0 || offset >= textNode->maxCharacterOffset())
return false;
TrackExceptionState exceptionState;
String textAtPosition = toText(textNode)->substringData(offset, 1, exceptionState);
if (exceptionState.had_exception())
return false;
return textAtPosition[0] == '\n';
}
static RenderStyle* renderStyleOfEnclosingTextNode(const Position& position)
{
if (position.anchorType() != Position::PositionIsOffsetInAnchor || !position.containerNode() || !position.containerNode()->isTextNode())
return 0;
return position.containerNode()->renderStyle();
}
void ApplyBlockElementCommand::rangeForParagraphSplittingTextNodesIfNeeded(const VisiblePosition& endOfCurrentParagraph, Position& start, Position& end)
{
start = startOfParagraph(endOfCurrentParagraph).deepEquivalent();
end = endOfCurrentParagraph.deepEquivalent();
document().updateRenderTreeIfNeeded();
bool isStartAndEndOnSameNode = false;
if (RenderStyle* startStyle = renderStyleOfEnclosingTextNode(start)) {
isStartAndEndOnSameNode = renderStyleOfEnclosingTextNode(end) && start.containerNode() == end.containerNode();
bool isStartAndEndOfLastParagraphOnSameNode = renderStyleOfEnclosingTextNode(m_endOfLastParagraph) && start.containerNode() == m_endOfLastParagraph.containerNode();
// Avoid obtanining the start of next paragraph for start
if (startStyle->preserveNewline() && isNewLineAtPosition(start) && !isNewLineAtPosition(start.previous()) && start.offsetInContainerNode() > 0)
start = startOfParagraph(VisiblePosition(end.previous())).deepEquivalent();
// If start is in the middle of a text node, split.
if (!startStyle->collapseWhiteSpace() && start.offsetInContainerNode() > 0) {
int startOffset = start.offsetInContainerNode();
Text* startText = start.containerText();
splitTextNode(startText, startOffset);
start = firstPositionInNode(startText);
if (isStartAndEndOnSameNode) {
ASSERT(end.offsetInContainerNode() >= startOffset);
end = Position(startText, end.offsetInContainerNode() - startOffset);
}
if (isStartAndEndOfLastParagraphOnSameNode) {
ASSERT(m_endOfLastParagraph.offsetInContainerNode() >= startOffset);
m_endOfLastParagraph = Position(startText, m_endOfLastParagraph.offsetInContainerNode() - startOffset);
}
}
}
document().updateRenderTreeIfNeeded();
if (RenderStyle* endStyle = renderStyleOfEnclosingTextNode(end)) {
bool isEndAndEndOfLastParagraphOnSameNode = renderStyleOfEnclosingTextNode(m_endOfLastParagraph) && end.deprecatedNode() == m_endOfLastParagraph.deprecatedNode();
// Include \n at the end of line if we're at an empty paragraph
if (endStyle->preserveNewline() && start == end && end.offsetInContainerNode() < end.containerNode()->maxCharacterOffset()) {
int endOffset = end.offsetInContainerNode();
if (!isNewLineAtPosition(end.previous()) && isNewLineAtPosition(end))
end = Position(end.containerText(), endOffset + 1);
if (isEndAndEndOfLastParagraphOnSameNode && end.offsetInContainerNode() >= m_endOfLastParagraph.offsetInContainerNode())
m_endOfLastParagraph = end;
}
// If end is in the middle of a text node, split.
if (!endStyle->collapseWhiteSpace() && end.offsetInContainerNode() && end.offsetInContainerNode() < end.containerNode()->maxCharacterOffset()) {
RefPtr<Text> endContainer = end.containerText();
splitTextNode(endContainer, end.offsetInContainerNode());
if (isStartAndEndOnSameNode)
start = firstPositionInOrBeforeNode(endContainer->previousSibling());
if (isEndAndEndOfLastParagraphOnSameNode) {
if (m_endOfLastParagraph.offsetInContainerNode() == end.offsetInContainerNode())
m_endOfLastParagraph = lastPositionInOrAfterNode(endContainer->previousSibling());
else
m_endOfLastParagraph = Position(endContainer, m_endOfLastParagraph.offsetInContainerNode() - end.offsetInContainerNode());
}
end = lastPositionInNode(endContainer->previousSibling());
}
}
}
VisiblePosition ApplyBlockElementCommand::endOfNextParagrahSplittingTextNodesIfNeeded(VisiblePosition& endOfCurrentParagraph, Position& start, Position& end)
{
VisiblePosition endOfNextParagraph = endOfParagraph(endOfCurrentParagraph.next());
Position position = endOfNextParagraph.deepEquivalent();
RenderStyle* style = renderStyleOfEnclosingTextNode(position);
if (!style)
return endOfNextParagraph;
RefPtr<Text> text = position.containerText();
if (!style->preserveNewline() || !position.offsetInContainerNode() || !isNewLineAtPosition(firstPositionInNode(text.get())))
return endOfNextParagraph;
// \n at the beginning of the text node immediately following the current paragraph is trimmed by moveParagraphWithClones.
// If endOfNextParagraph was pointing at this same text node, endOfNextParagraph will be shifted by one paragraph.
// Avoid this by splitting "\n"
splitTextNode(text, 1);
if (text == start.containerNode() && text->previousSibling() && text->previousSibling()->isTextNode()) {
ASSERT(start.offsetInContainerNode() < position.offsetInContainerNode());
start = Position(toText(text->previousSibling()), start.offsetInContainerNode());
}
if (text == end.containerNode() && text->previousSibling() && text->previousSibling()->isTextNode()) {
ASSERT(end.offsetInContainerNode() < position.offsetInContainerNode());
end = Position(toText(text->previousSibling()), end.offsetInContainerNode());
}
if (text == m_endOfLastParagraph.containerNode()) {
if (m_endOfLastParagraph.offsetInContainerNode() < position.offsetInContainerNode()) {
// We can only fix endOfLastParagraph if the previous node was still text and hasn't been modified by script.
if (text->previousSibling()->isTextNode()
&& static_cast<unsigned>(m_endOfLastParagraph.offsetInContainerNode()) <= toText(text->previousSibling())->length())
m_endOfLastParagraph = Position(toText(text->previousSibling()), m_endOfLastParagraph.offsetInContainerNode());
} else
m_endOfLastParagraph = Position(text.get(), m_endOfLastParagraph.offsetInContainerNode() - 1);
}
return VisiblePosition(Position(text.get(), position.offsetInContainerNode() - 1));
}
}
/*
* Copyright (C) 2010 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SKY_ENGINE_CORE_EDITING_APPLYBLOCKELEMENTCOMMAND_H_
#define SKY_ENGINE_CORE_EDITING_APPLYBLOCKELEMENTCOMMAND_H_
#include "sky/engine/core/dom/QualifiedName.h"
#include "sky/engine/core/editing/CompositeEditCommand.h"
namespace blink {
class ApplyBlockElementCommand : public CompositeEditCommand {
protected:
ApplyBlockElementCommand(Document&, const QualifiedName& tagName, const AtomicString& inlineStyle);
ApplyBlockElementCommand(Document&, const QualifiedName& tagName);
virtual void formatSelection(const VisiblePosition& startOfSelection, const VisiblePosition& endOfSelection);
const QualifiedName tagName() const { return m_tagName; }
private:
virtual void doApply() override final;
virtual void formatRange(const Position& start, const Position& end, const Position& endOfSelection, RefPtr<HTMLElement>&) = 0;
void rangeForParagraphSplittingTextNodesIfNeeded(const VisiblePosition&, Position&, Position&);
VisiblePosition endOfNextParagrahSplittingTextNodesIfNeeded(VisiblePosition&, Position&, Position&);
QualifiedName m_tagName;
AtomicString m_inlineStyle;
Position m_endOfLastParagraph;
};
}
#endif // SKY_ENGINE_CORE_EDITING_APPLYBLOCKELEMENTCOMMAND_H_
/*
* Copyright (C) 2004, 2008, 2009, 2010 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "sky/engine/core/editing/Caret.h"
#include "sky/engine/core/dom/Document.h"
#include "sky/engine/core/editing/VisibleUnits.h"
#include "sky/engine/core/editing/htmlediting.h"
#include "sky/engine/core/frame/LocalFrame.h"
#include "sky/engine/core/frame/Settings.h"
#include "sky/engine/core/rendering/RenderBlock.h"
#include "sky/engine/core/rendering/RenderLayer.h"
#include "sky/engine/core/rendering/RenderView.h"
#include "sky/engine/platform/graphics/GraphicsContext.h"
namespace blink {
CaretBase::CaretBase(CaretVisibility visibility)
: m_caretRectNeedsUpdate(true)
, m_caretVisibility(visibility)
{
}
DragCaretController::DragCaretController()
: CaretBase(Visible)
{
}
PassOwnPtr<DragCaretController> DragCaretController::create()
{
return adoptPtr(new DragCaretController);
}
bool DragCaretController::isContentRichlyEditable() const
{
return isRichlyEditablePosition(m_position.deepEquivalent());
}
void DragCaretController::setCaretPosition(const VisiblePosition& position)
{
m_position = position;
setCaretRectNeedsUpdate();
Document* document = 0;
if (Node* node = m_position.deepEquivalent().deprecatedNode()) {
document = &node->document();
}
if (m_position.isNull() || m_position.isOrphan()) {
clearCaretRect();
} else {
document->updateRenderTreeIfNeeded();
updateCaretRect(document, m_position);
}
}
static bool removingNodeRemovesPosition(Node& node, const Position& position)
{
if (!position.anchorNode())
return false;
if (position.anchorNode() == node)
return true;
if (!node.isElementNode())
return false;
Element& element = toElement(node);
return element.containsIncludingShadowDOM(position.anchorNode());
}
void DragCaretController::nodeWillBeRemoved(Node& node)
{
if (!hasCaret() || !node.inActiveDocument())
return;
if (!removingNodeRemovesPosition(node, m_position.deepEquivalent()))
return;
m_position.deepEquivalent().document()->renderView()->clearSelection();
clear();
}
void CaretBase::clearCaretRect()
{
m_caretLocalRect = LayoutRect();
}
static inline bool caretRendersInsideNode(Node* node)
{
return node && !isRenderedTableElement(node) && !editingIgnoresContent(node);
}
RenderBlock* CaretBase::caretRenderer(Node* node)
{
if (!node)
return 0;
RenderObject* renderer = node->renderer();
if (!renderer)
return 0;
// if caretNode is a block and caret is inside it then caret should be painted by that block
bool paintedByBlock = renderer->isRenderBlock() && caretRendersInsideNode(node);
return paintedByBlock ? toRenderBlock(renderer) : renderer->containingBlock();
}
bool CaretBase::updateCaretRect(Document* document, const PositionWithAffinity& caretPosition)
{
m_caretLocalRect = LayoutRect();
m_caretRectNeedsUpdate = false;
if (caretPosition.position().isNull())
return false;
ASSERT(caretPosition.position().deprecatedNode()->renderer());
// First compute a rect local to the renderer at the selection start.
RenderObject* renderer;
LayoutRect localRect = localCaretRectOfPosition(caretPosition, renderer);
// Get the renderer that will be responsible for painting the caret
// (which is either the renderer we just found, or one of its containers).
RenderBlock* caretPainter = caretRenderer(caretPosition.position().deprecatedNode());
// Compute an offset between the renderer and the caretPainter.
bool unrooted = false;
while (renderer != caretPainter) {
RenderObject* containerObject = renderer->container();
if (!containerObject) {
unrooted = true;
break;
}
localRect.move(renderer->offsetFromContainer(containerObject, localRect.location()));
renderer = containerObject;
}
if (!unrooted)
m_caretLocalRect = localRect;
return true;
}
bool CaretBase::updateCaretRect(Document* document, const VisiblePosition& caretPosition)
{
return updateCaretRect(document, PositionWithAffinity(caretPosition.deepEquivalent(), caretPosition.affinity()));
}
RenderBlock* DragCaretController::caretRenderer() const
{
return CaretBase::caretRenderer(m_position.deepEquivalent().deprecatedNode());
}
IntRect CaretBase::absoluteBoundsForLocalRect(Node* node, const LayoutRect& rect) const
{
RenderBlock* caretPainter = caretRenderer(node);
if (!caretPainter)
return IntRect();
return caretPainter->localToAbsoluteQuad(FloatRect(rect)).enclosingBoundingBox();
}
void CaretBase::paintCaret(Node* node, GraphicsContext* context, const LayoutPoint& paintOffset, const LayoutRect& clipRect) const
{
if (m_caretVisibility == Hidden)
return;
LayoutRect drawingRect = localCaretRectWithoutUpdate();
drawingRect.moveBy(roundedIntPoint(paintOffset));
LayoutRect caret = intersection(drawingRect, clipRect);
if (caret.isEmpty())
return;
Color caretColor = Color::black;
Element* element;
if (node->isElementNode())
element = toElement(node);
else
element = node->parentElement();
if (element && element->renderer())
caretColor = element->renderer()->resolveColor(CSSPropertyColor);
context->fillRect(caret, caretColor);
}
void DragCaretController::paintDragCaret(LocalFrame* frame, GraphicsContext* p, const LayoutPoint& paintOffset, const LayoutRect& clipRect) const
{
if (m_position.deepEquivalent().deprecatedNode()->document().frame() == frame)
paintCaret(m_position.deepEquivalent().deprecatedNode(), p, paintOffset, clipRect);
}
}
/*
* Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SKY_ENGINE_CORE_EDITING_CARET_H_
#define SKY_ENGINE_CORE_EDITING_CARET_H_
#include "sky/engine/core/editing/VisiblePosition.h"
#include "sky/engine/platform/geometry/IntRect.h"
#include "sky/engine/platform/geometry/LayoutRect.h"
#include "sky/engine/wtf/Noncopyable.h"
namespace blink {
class LocalFrame;
class GraphicsContext;
class PositionWithAffinity;
class RenderBlock;
class RenderView;
class CaretBase {
WTF_MAKE_NONCOPYABLE(CaretBase);
WTF_MAKE_FAST_ALLOCATED;
protected:
enum CaretVisibility { Visible, Hidden };
explicit CaretBase(CaretVisibility = Hidden);
void clearCaretRect();
// Creating VisiblePosition causes synchronous layout so we should use the
// PositionWithAffinity version if possible.
// A position in HTMLTextFromControlElement is a typical example.
bool updateCaretRect(Document*, const PositionWithAffinity& caretPosition);
bool updateCaretRect(Document*, const VisiblePosition& caretPosition);
IntRect absoluteBoundsForLocalRect(Node*, const LayoutRect&) const;
void paintCaret(Node*, GraphicsContext*, const LayoutPoint&, const LayoutRect& clipRect) const;
const LayoutRect& localCaretRectWithoutUpdate() const { return m_caretLocalRect; }
bool shouldUpdateCaretRect() const { return m_caretRectNeedsUpdate; }
void setCaretRectNeedsUpdate() { m_caretRectNeedsUpdate = true; }
void setCaretVisibility(CaretVisibility visibility) { m_caretVisibility = visibility; }
bool caretIsVisible() const { return m_caretVisibility == Visible; }
CaretVisibility caretVisibility() const { return m_caretVisibility; }
protected:
static RenderBlock* caretRenderer(Node*);
private:
LayoutRect m_caretLocalRect; // caret rect in coords local to the renderer responsible for painting the caret
bool m_caretRectNeedsUpdate; // true if m_caretRect (and m_absCaretBounds in FrameSelection) need to be calculated
CaretVisibility m_caretVisibility;
};
class DragCaretController final : private CaretBase {
WTF_MAKE_NONCOPYABLE(DragCaretController);
WTF_MAKE_FAST_ALLOCATED;
public:
static PassOwnPtr<DragCaretController> create();
RenderBlock* caretRenderer() const;
void paintDragCaret(LocalFrame*, GraphicsContext*, const LayoutPoint&, const LayoutRect& clipRect) const;
bool isContentEditable() const { return m_position.rootEditableElement(); }
bool isContentRichlyEditable() const;
bool hasCaret() const { return m_position.isNotNull(); }
const VisiblePosition& caretPosition() { return m_position; }
void setCaretPosition(const VisiblePosition&);
void clear() { setCaretPosition(VisiblePosition()); }
void nodeWillBeRemoved(Node&);
private:
DragCaretController();
VisiblePosition m_position;
};
} // namespace blink
#endif // SKY_ENGINE_CORE_EDITING_CARET_H_
/*
* Copyright (C) 2005, 2006, 2008 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SKY_ENGINE_CORE_EDITING_COMPOSITEEDITCOMMAND_H_
#define SKY_ENGINE_CORE_EDITING_COMPOSITEEDITCOMMAND_H_
#include "gen/sky/core/CSSPropertyNames.h"
#include "sky/engine/core/editing/EditCommand.h"
#include "sky/engine/core/editing/UndoStep.h"
#include "sky/engine/wtf/Vector.h"
namespace blink {
class EditingStyle;
class Element;
class HTMLElement;
class Text;
class EditCommandComposition final : public UndoStep {
public:
static PassRefPtr<EditCommandComposition> create(Document*, const VisibleSelection&, const VisibleSelection&, EditAction);
virtual bool belongsTo(const LocalFrame&) const override;
virtual void unapply() override;
virtual void reapply() override;
virtual EditAction editingAction() const override { return m_editAction; }
void append(SimpleEditCommand*);
const VisibleSelection& startingSelection() const { return m_startingSelection; }
const VisibleSelection& endingSelection() const { return m_endingSelection; }
void setStartingSelection(const VisibleSelection&);
void setEndingSelection(const VisibleSelection&);
Element* startingRootEditableElement() const { return m_startingRootEditableElement.get(); }
Element* endingRootEditableElement() const { return m_endingRootEditableElement.get(); }
private:
EditCommandComposition(Document*, const VisibleSelection& startingSelection, const VisibleSelection& endingSelection, EditAction);
RefPtr<Document> m_document;
VisibleSelection m_startingSelection;
VisibleSelection m_endingSelection;
Vector<RefPtr<SimpleEditCommand> > m_commands;
RefPtr<Element> m_startingRootEditableElement;
RefPtr<Element> m_endingRootEditableElement;
EditAction m_editAction;
};
class CompositeEditCommand : public EditCommand {
public:
virtual ~CompositeEditCommand();
void apply();
bool isFirstCommand(EditCommand* command) { return !m_commands.isEmpty() && m_commands.first() == command; }
EditCommandComposition* composition() { return m_composition.get(); }
EditCommandComposition* ensureComposition();
virtual bool isTypingCommand() const;
virtual bool preservesTypingStyle() const;
virtual void setShouldRetainAutocorrectionIndicator(bool);
virtual bool shouldStopCaretBlinking() const { return false; }
protected:
explicit CompositeEditCommand(Document&);
//
// sugary-sweet convenience functions to help create and apply edit commands in composite commands
//
void appendNode(PassRefPtr<Node>, PassRefPtr<ContainerNode> parent);
void applyCommandToComposite(PassRefPtr<EditCommand>);
void applyCommandToComposite(PassRefPtr<CompositeEditCommand>, const VisibleSelection&);
void removeStyledElement(PassRefPtr<Element>);
void deleteSelection(bool smartDelete = false, bool mergeBlocksAfterDelete = true, bool expandForSpecialElements = true, bool sanitizeMarkup = true);
void deleteSelection(const VisibleSelection&, bool smartDelete = false, bool mergeBlocksAfterDelete = true, bool expandForSpecialElements = true, bool sanitizeMarkup = true);
virtual void deleteTextFromNode(PassRefPtr<Text>, unsigned offset, unsigned count);
bool isRemovableBlock(const Node*);
void insertNodeAfter(PassRefPtr<Node>, PassRefPtr<Node> refChild);
void insertNodeAt(PassRefPtr<Node>, const Position&);
void insertNodeAtTabSpanPosition(PassRefPtr<Node>, const Position&);
void insertNodeBefore(PassRefPtr<Node>, PassRefPtr<Node> refChild, ShouldAssumeContentIsAlwaysEditable = DoNotAssumeContentIsAlwaysEditable);
void insertParagraphSeparator(bool useDefaultParagraphElement = false, bool pasteBlockqutoeIntoUnquotedArea = false);
void insertTextIntoNode(PassRefPtr<Text>, unsigned offset, const String& text);
void rebalanceWhitespace();
void rebalanceWhitespaceAt(const Position&);
void rebalanceWhitespaceOnTextSubstring(PassRefPtr<Text>, int startOffset, int endOffset);
void prepareWhitespaceAtPositionForSplit(Position&);
void replaceCollapsibleWhitespaceWithNonBreakingSpaceIfNeeded(const VisiblePosition&);
bool canRebalance(const Position&) const;
bool shouldRebalanceLeadingWhitespaceFor(const String&) const;
void removeChildrenInRange(PassRefPtr<Node>, unsigned from, unsigned to);
virtual void removeNode(PassRefPtr<Node>, ShouldAssumeContentIsAlwaysEditable = DoNotAssumeContentIsAlwaysEditable);
void removeNodePreservingChildren(PassRefPtr<Node>, ShouldAssumeContentIsAlwaysEditable = DoNotAssumeContentIsAlwaysEditable);
void removeNodeAndPruneAncestors(PassRefPtr<Node>, Node* excludeNode = 0);
void moveRemainingSiblingsToNewParent(Node*, Node* pastLastNodeToMove, PassRefPtr<Element> prpNewParent);
void updatePositionForNodeRemovalPreservingChildren(Position&, Node&);
void prune(PassRefPtr<Node>, Node* excludeNode = 0);
void replaceTextInNode(PassRefPtr<Text>, unsigned offset, unsigned count, const String& replacementText);
Position replaceSelectedTextInNode(const String&);
void replaceTextInNodePreservingMarkers(PassRefPtr<Text>, unsigned offset, unsigned count, const String& replacementText);
Position positionOutsideTabSpan(const Position&);
void splitElement(PassRefPtr<Element>, PassRefPtr<Node> atChild);
void splitTextNode(PassRefPtr<Text>, unsigned offset);
void deleteInsignificantText(PassRefPtr<Text>, unsigned start, unsigned end);
void deleteInsignificantText(const Position& start, const Position& end);
void deleteInsignificantTextDownstream(const Position&);
void removePlaceholderAt(const Position&);
void pushAnchorElementDown(Element*);
// FIXME: preserveSelection and preserveStyle should be enums
void moveParagraph(const VisiblePosition&, const VisiblePosition&, const VisiblePosition&, bool preserveSelection = false, bool preserveStyle = true, Node* constrainingAncestor = 0);
void moveParagraphs(const VisiblePosition&, const VisiblePosition&, const VisiblePosition&, bool preserveSelection = false, bool preserveStyle = true, Node* constrainingAncestor = 0);
void cleanupAfterDeletion(VisiblePosition destination = VisiblePosition());
Position positionAvoidingSpecialElementBoundary(const Position&);
PassRefPtr<Node> splitTreeToNode(Node*, Node*, bool splitAncestor = false);
Vector<RefPtr<EditCommand> > m_commands;
private:
virtual bool isCompositeEditCommand() const override final { return true; }
RefPtr<EditCommandComposition> m_composition;
};
DEFINE_TYPE_CASTS(CompositeEditCommand, EditCommand, command, command->isCompositeEditCommand(), command.isCompositeEditCommand());
} // namespace blink
#endif // SKY_ENGINE_CORE_EDITING_COMPOSITEEDITCOMMAND_H_
// Copyright 2014 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 "sky/engine/core/editing/CompositionUnderlineRangeFilter.h"
#include <gtest/gtest.h>
#include "sky/engine/core/editing/CompositionUnderline.h"
#include "sky/engine/platform/graphics/Color.h"
#include "sky/engine/wtf/Vector.h"
#include "sky/engine/wtf/text/IntegerToStringConversion.h"
#include "sky/engine/wtf/text/WTFString.h"
using namespace blink;
namespace {
// Parses test case string and populate |underlines|.
void initUnderlines(const String& testCase, Vector<CompositionUnderline>* underlines)
{
ASSERT(underlines && underlines->size() == 0U);
Vector<String> rangeList;
testCase.split('|', rangeList);
// Intervals are named 'A', 'B', ..., 'Z', so ensure there aren't too many.
ASSERT_LE(rangeList.size(), static_cast<size_t>('Z' - 'A'));
for (unsigned i = 0; i < rangeList.size(); ++i) {
String range = rangeList[i];
Vector<String> toks;
rangeList[i].split(',', toks);
ASSERT_EQ(2U, toks.size());
int startOffset = toks[0].toInt();
int endOffset = toks[1].toInt();
ASSERT_LE(startOffset, endOffset);
// For testing: Store i in red component of |color|, so the intervals
// can be distinguished.
underlines->append(CompositionUnderline(startOffset, endOffset, Color(i, 0, 0), false, 0));
}
}
// Runs the filter and encodes the result into a string, with 'A' as first
// elemnt, 'B' as second, etc.
String filterUnderlines(const Vector<CompositionUnderline>& underlines, int indexLo, int indexHi)
{
CompositionUnderlineRangeFilter filter(underlines, indexLo, indexHi);
String ret = "";
for (CompositionUnderlineRangeFilter::ConstIterator it = filter.begin(); it != filter.end(); ++it) {
int code = (*it).color.red();
ret.append(static_cast<char>('A' + code));
}
return ret;
}
TEST(CompositionUnderlineRangeFilterTest, Empty)
{
Vector<CompositionUnderline> underlines;
EXPECT_EQ("", filterUnderlines(underlines, 0, 10));
EXPECT_EQ("", filterUnderlines(underlines, 5, 5));
}
TEST(CompositionUnderlineRangeFilterTest, Single)
{
String testCase = "10,20"; // Semi-closed interval: {10, 11, ..., 19}.
Vector<CompositionUnderline> underlines;
initUnderlines(testCase, &underlines);
// The query intervals are all closed, e.g., [0, 9] = {0, ..., 9}.
EXPECT_EQ("", filterUnderlines(underlines, 0, 9));
EXPECT_EQ("A", filterUnderlines(underlines, 5, 10));
EXPECT_EQ("A", filterUnderlines(underlines, 10, 20));
EXPECT_EQ("A", filterUnderlines(underlines, 15, 25));
EXPECT_EQ("A", filterUnderlines(underlines, 19, 30));
EXPECT_EQ("", filterUnderlines(underlines, 20, 25));
EXPECT_EQ("A", filterUnderlines(underlines, 5, 25));
}
TEST(CompositionUnderlineRangeFilterTest, Multi)
{
String testCase = "0,2|0,5|1,3|1,10|3,5|5,8|7,8|8,10";
Vector<CompositionUnderline> underlines;
initUnderlines(testCase, &underlines);
EXPECT_EQ("", filterUnderlines(underlines, 11, 11));
EXPECT_EQ("ABCDEFGH", filterUnderlines(underlines, 0, 9));
EXPECT_EQ("BDEF", filterUnderlines(underlines, 4, 5));
EXPECT_EQ("AB", filterUnderlines(underlines, 0, 0));
EXPECT_EQ("BDE", filterUnderlines(underlines, 3, 3));
EXPECT_EQ("DF", filterUnderlines(underlines, 5, 5));
EXPECT_EQ("DFG", filterUnderlines(underlines, 7, 7));
}
} // namespace
/*
* Copyright (C) 2007, 2009 Apple Inc. All rights reserved.
* Copyright (C) 2012 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "sky/engine/core/editing/DOMSelection.h"
#include "sky/engine/bindings/exception_messages.h"
#include "sky/engine/bindings/exception_state.h"
#include "sky/engine/bindings/exception_state_placeholder.h"
#include "sky/engine/core/dom/Document.h"
#include "sky/engine/core/dom/ExceptionCode.h"
#include "sky/engine/core/dom/Node.h"
#include "sky/engine/core/dom/Range.h"
#include "sky/engine/core/dom/TreeScope.h"
#include "sky/engine/core/editing/FrameSelection.h"
#include "sky/engine/core/editing/TextIterator.h"
#include "sky/engine/core/editing/htmlediting.h"
#include "sky/engine/core/frame/LocalFrame.h"
#include "sky/engine/core/inspector/ConsoleMessage.h"
#include "sky/engine/wtf/text/WTFString.h"
namespace blink {
static Node* selectionShadowAncestor(LocalFrame* frame)
{
Node* node = frame->selection().selection().base().anchorNode();
if (!node)
return 0;
if (!node->isInShadowTree())
return 0;
return frame->document()->ancestorInThisScope(node);
}
DOMSelection::DOMSelection(const TreeScope* treeScope)
: DOMWindowProperty(treeScope->rootNode().document().frame())
, m_treeScope(treeScope)
{
}
void DOMSelection::clearTreeScope()
{
m_treeScope = nullptr;
}
const VisibleSelection& DOMSelection::visibleSelection() const
{
ASSERT(m_frame);
return m_frame->selection().selection();
}
static Position anchorPosition(const VisibleSelection& selection)
{
Position anchor = selection.isBaseFirst() ? selection.start() : selection.end();
return anchor.parentAnchoredEquivalent();
}
static Position focusPosition(const VisibleSelection& selection)
{
Position focus = selection.isBaseFirst() ? selection.end() : selection.start();
return focus.parentAnchoredEquivalent();
}
static Position basePosition(const VisibleSelection& selection)
{
return selection.base().parentAnchoredEquivalent();
}
static Position extentPosition(const VisibleSelection& selection)
{
return selection.extent().parentAnchoredEquivalent();
}
Node* DOMSelection::anchorNode() const
{
if (!m_frame)
return 0;
return shadowAdjustedNode(anchorPosition(visibleSelection()));
}
int DOMSelection::anchorOffset() const
{
if (!m_frame)
return 0;
return shadowAdjustedOffset(anchorPosition(visibleSelection()));
}
Node* DOMSelection::focusNode() const
{
if (!m_frame)
return 0;
return shadowAdjustedNode(focusPosition(visibleSelection()));
}
int DOMSelection::focusOffset() const
{
if (!m_frame)
return 0;
return shadowAdjustedOffset(focusPosition(visibleSelection()));
}
Node* DOMSelection::baseNode() const
{
if (!m_frame)
return 0;
return shadowAdjustedNode(basePosition(visibleSelection()));
}
int DOMSelection::baseOffset() const
{
if (!m_frame)
return 0;
return shadowAdjustedOffset(basePosition(visibleSelection()));
}
Node* DOMSelection::extentNode() const
{
if (!m_frame)
return 0;
return shadowAdjustedNode(extentPosition(visibleSelection()));
}
int DOMSelection::extentOffset() const
{
if (!m_frame)
return 0;
return shadowAdjustedOffset(extentPosition(visibleSelection()));
}
bool DOMSelection::isCollapsed() const
{
if (!m_frame || selectionShadowAncestor(m_frame))
return true;
return !m_frame->selection().isRange();
}
String DOMSelection::type() const
{
if (!m_frame)
return String();
FrameSelection& selection = m_frame->selection();
// This is a WebKit DOM extension, incompatible with an IE extension
// IE has this same attribute, but returns "none", "text" and "control"
// http://msdn.microsoft.com/en-us/library/ms534692(VS.85).aspx
if (selection.isNone())
return "None";
if (selection.isCaret())
return "Caret";
return "Range";
}
int DOMSelection::rangeCount() const
{
if (!m_frame)
return 0;
return m_frame->selection().isNone() ? 0 : 1;
}
void DOMSelection::collapse(Node* node, int offset, ExceptionState& exceptionState)
{
ASSERT(node);
if (!m_frame)
return;
if (offset < 0) {
exceptionState.ThrowDOMException(IndexSizeError, String::number(offset) + " is not a valid offset.");
return;
}
if (!isValidForPosition(node))
return;
RefPtr<Range> range = Range::create(node->document());
range->setStart(node, offset, exceptionState);
if (exceptionState.had_exception())
return;
range->setEnd(node, offset, exceptionState);
if (exceptionState.had_exception())
return;
m_frame->selection().setSelectedRange(range.get(), DOWNSTREAM, m_frame->selection().isDirectional() ? FrameSelection::Directional : FrameSelection::NonDirectional);
}
void DOMSelection::collapseToEnd(ExceptionState& exceptionState)
{
if (!m_frame)
return;
const VisibleSelection& selection = m_frame->selection().selection();
if (selection.isNone()) {
exceptionState.ThrowDOMException(InvalidStateError, "there is no selection.");
return;
}
m_frame->selection().moveTo(VisiblePosition(selection.end(), DOWNSTREAM));
}
void DOMSelection::collapseToStart(ExceptionState& exceptionState)
{
if (!m_frame)
return;
const VisibleSelection& selection = m_frame->selection().selection();
if (selection.isNone()) {
exceptionState.ThrowDOMException(InvalidStateError, "there is no selection.");
return;
}
m_frame->selection().moveTo(VisiblePosition(selection.start(), DOWNSTREAM));
}
void DOMSelection::empty()
{
if (!m_frame)
return;
m_frame->selection().clear();
}
void DOMSelection::setBaseAndExtent(Node* baseNode, int baseOffset, Node* extentNode, int extentOffset, ExceptionState& exceptionState)
{
if (!m_frame)
return;
if (baseOffset < 0) {
exceptionState.ThrowDOMException(IndexSizeError, String::number(baseOffset) + " is not a valid base offset.");
return;
}
if (extentOffset < 0) {
exceptionState.ThrowDOMException(IndexSizeError, String::number(extentOffset) + " is not a valid extent offset.");
return;
}
if (!isValidForPosition(baseNode) || !isValidForPosition(extentNode))
return;
// FIXME: Eliminate legacy editing positions
VisiblePosition visibleBase = VisiblePosition(createLegacyEditingPosition(baseNode, baseOffset), DOWNSTREAM);
VisiblePosition visibleExtent = VisiblePosition(createLegacyEditingPosition(extentNode, extentOffset), DOWNSTREAM);
m_frame->selection().moveTo(visibleBase, visibleExtent);
}
void DOMSelection::modify(const String& alterString, const String& directionString, const String& granularityString)
{
if (!m_frame)
return;
FrameSelection::EAlteration alter;
if (equalIgnoringCase(alterString, "extend"))
alter = FrameSelection::AlterationExtend;
else if (equalIgnoringCase(alterString, "move"))
alter = FrameSelection::AlterationMove;
else
return;
SelectionDirection direction;
if (equalIgnoringCase(directionString, "forward"))
direction = DirectionForward;
else if (equalIgnoringCase(directionString, "backward"))
direction = DirectionBackward;
else if (equalIgnoringCase(directionString, "left"))
direction = DirectionLeft;
else if (equalIgnoringCase(directionString, "right"))
direction = DirectionRight;
else
return;
TextGranularity granularity;
if (equalIgnoringCase(granularityString, "character"))
granularity = CharacterGranularity;
else if (equalIgnoringCase(granularityString, "word"))
granularity = WordGranularity;
else if (equalIgnoringCase(granularityString, "sentence"))
granularity = SentenceGranularity;
else if (equalIgnoringCase(granularityString, "line"))
granularity = LineGranularity;
else if (equalIgnoringCase(granularityString, "paragraph"))
granularity = ParagraphGranularity;
else if (equalIgnoringCase(granularityString, "lineboundary"))
granularity = LineBoundary;
else if (equalIgnoringCase(granularityString, "sentenceboundary"))
granularity = SentenceBoundary;
else if (equalIgnoringCase(granularityString, "paragraphboundary"))
granularity = ParagraphBoundary;
else if (equalIgnoringCase(granularityString, "documentboundary"))
granularity = DocumentBoundary;
else
return;
m_frame->selection().modify(alter, direction, granularity);
}
void DOMSelection::extend(Node* node, int offset, ExceptionState& exceptionState)
{
ASSERT(node);
if (!m_frame)
return;
if (offset < 0) {
exceptionState.ThrowDOMException(IndexSizeError, String::number(offset) + " is not a valid offset.");
return;
}
if (offset > (node->offsetInCharacters() ? caretMaxOffset(node) : (int)node->countChildren())) {
exceptionState.ThrowDOMException(IndexSizeError, String::number(offset) + " is larger than the given node's length.");
return;
}
if (!isValidForPosition(node))
return;
// FIXME: Eliminate legacy editing positions
m_frame->selection().setExtent(VisiblePosition(createLegacyEditingPosition(node, offset), DOWNSTREAM));
}
PassRefPtr<Range> DOMSelection::getRangeAt(int index, ExceptionState& exceptionState)
{
if (!m_frame)
return nullptr;
if (index < 0 || index >= rangeCount()) {
exceptionState.ThrowDOMException(IndexSizeError, String::number(index) + " is not a valid index.");
return nullptr;
}
// If you're hitting this, you've added broken multi-range selection support
ASSERT(rangeCount() == 1);
if (Node* shadowAncestor = selectionShadowAncestor(m_frame)) {
ASSERT(!shadowAncestor->isShadowRoot());
ContainerNode* container = shadowAncestor->parentOrShadowHostNode();
int offset = shadowAncestor->nodeIndex();
return Range::create(shadowAncestor->document(), container, offset, container, offset);
}
return m_frame->selection().firstRange();
}
void DOMSelection::removeAllRanges()
{
if (!m_frame)
return;
m_frame->selection().clear();
}
void DOMSelection::addRange(Range* newRange)
{
if (!m_frame)
return;
FrameSelection& selection = m_frame->selection();
if (selection.isNone()) {
selection.setSelectedRange(newRange, VP_DEFAULT_AFFINITY);
return;
}
RefPtr<Range> originalRange = selection.firstRange();
// FIXME: "Merge the ranges if they intersect" is Blink-specific behavior; other browsers supporting discontiguous
// selection (obviously) keep each Range added and return it in getRangeAt(). But it's unclear if we can really
// do the same, since we don't support discontiguous selection. Further discussions at
// <https://code.google.com/p/chromium/issues/detail?id=353069>.
Range* start = originalRange->compareBoundaryPoints(Range::START_TO_START, newRange, ASSERT_NO_EXCEPTION) < 0 ? originalRange.get() : newRange;
Range* end = originalRange->compareBoundaryPoints(Range::END_TO_END, newRange, ASSERT_NO_EXCEPTION) < 0 ? newRange : originalRange.get();
RefPtr<Range> merged = Range::create(originalRange->startContainer()->document(), start->startContainer(), start->startOffset(), end->endContainer(), end->endOffset());
EAffinity affinity = selection.selection().affinity();
selection.setSelectedRange(merged.get(), affinity);
}
void DOMSelection::deleteFromDocument()
{
if (!m_frame)
return;
FrameSelection& selection = m_frame->selection();
if (selection.isNone())
return;
RefPtr<Range> selectedRange = selection.selection().toNormalizedRange();
if (!selectedRange)
return;
selectedRange->deleteContents(ASSERT_NO_EXCEPTION);
setBaseAndExtent(selectedRange->startContainer(), selectedRange->startOffset(), selectedRange->startContainer(), selectedRange->startOffset(), ASSERT_NO_EXCEPTION);
}
bool DOMSelection::containsNode(const Node* n, bool allowPartial) const
{
if (!m_frame)
return false;
FrameSelection& selection = m_frame->selection();
if (!n || m_frame->document() != n->document() || selection.isNone())
return false;
unsigned nodeIndex = n->nodeIndex();
RefPtr<Range> selectedRange = selection.selection().toNormalizedRange();
ContainerNode* parentNode = n->parentNode();
if (!parentNode)
return false;
TrackExceptionState exceptionState;
bool nodeFullySelected = Range::compareBoundaryPoints(parentNode, nodeIndex, selectedRange->startContainer(), selectedRange->startOffset(), exceptionState) >= 0 && !exceptionState.had_exception()
&& Range::compareBoundaryPoints(parentNode, nodeIndex + 1, selectedRange->endContainer(), selectedRange->endOffset(), exceptionState) <= 0 && !exceptionState.had_exception();
if (exceptionState.had_exception())
return false;
if (nodeFullySelected)
return true;
bool nodeFullyUnselected = (Range::compareBoundaryPoints(parentNode, nodeIndex, selectedRange->endContainer(), selectedRange->endOffset(), exceptionState) > 0 && !exceptionState.had_exception())
|| (Range::compareBoundaryPoints(parentNode, nodeIndex + 1, selectedRange->startContainer(), selectedRange->startOffset(), exceptionState) < 0 && !exceptionState.had_exception());
ASSERT(!exceptionState.had_exception());
if (nodeFullyUnselected)
return false;
return allowPartial || n->isTextNode();
}
void DOMSelection::selectAllChildren(Node* n, ExceptionState& exceptionState)
{
if (!n)
return;
// This doesn't (and shouldn't) select text node characters.
setBaseAndExtent(n, 0, n, n->countChildren(), exceptionState);
}
String DOMSelection::toString()
{
if (!m_frame)
return String();
return plainText(m_frame->selection().selection().toNormalizedRange().get());
}
Node* DOMSelection::shadowAdjustedNode(const Position& position) const
{
if (position.isNull())
return 0;
Node* containerNode = position.containerNode();
Node* adjustedNode = m_treeScope->ancestorInThisScope(containerNode);
if (!adjustedNode)
return 0;
if (containerNode == adjustedNode)
return containerNode;
ASSERT(!adjustedNode->isShadowRoot());
return adjustedNode->parentOrShadowHostNode();
}
int DOMSelection::shadowAdjustedOffset(const Position& position) const
{
if (position.isNull())
return 0;
Node* containerNode = position.containerNode();
Node* adjustedNode = m_treeScope->ancestorInThisScope(containerNode);
if (!adjustedNode)
return 0;
if (containerNode == adjustedNode)
return position.computeOffsetInContainerNode();
return adjustedNode->nodeIndex();
}
bool DOMSelection::isValidForPosition(Node* node) const
{
ASSERT(m_frame);
if (!node)
return true;
return node->document() == m_frame->document();
}
} // namespace blink
/*
* Copyright (C) 2007 Apple Inc. All rights reserved.
* Copyright (C) 2012 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SKY_ENGINE_CORE_EDITING_DOMSELECTION_H_
#define SKY_ENGINE_CORE_EDITING_DOMSELECTION_H_
#include "sky/engine/tonic/dart_wrappable.h"
#include "sky/engine/core/frame/DOMWindowProperty.h"
#include "sky/engine/platform/heap/Handle.h"
#include "sky/engine/wtf/Forward.h"
#include "sky/engine/wtf/PassRefPtr.h"
#include "sky/engine/wtf/RefCounted.h"
namespace blink {
class ExceptionState;
class LocalFrame;
class Node;
class Position;
class Range;
class TreeScope;
class VisibleSelection;
class DOMSelection final : public RefCounted<DOMSelection>, public DartWrappable, public DOMWindowProperty {
DEFINE_WRAPPERTYPEINFO();
public:
static PassRefPtr<DOMSelection> create(const TreeScope* treeScope)
{
return adoptRef(new DOMSelection(treeScope));
}
void clearTreeScope();
// Safari Selection Object API
// These methods return the valid equivalents of internal editing positions.
Node* baseNode() const;
int baseOffset() const;
Node* extentNode() const;
int extentOffset() const;
String type() const;
void setBaseAndExtent(Node* baseNode, int baseOffset, Node* extentNode, int extentOffset, ExceptionState&);
void modify(const String& alter, const String& direction, const String& granularity);
// Mozilla Selection Object API
// In Firefox, anchor/focus are the equal to the start/end of the selection,
// but reflect the direction in which the selection was made by the user. That does
// not mean that they are base/extent, since the base/extent don't reflect
// expansion.
// These methods return the valid equivalents of internal editing positions.
Node* anchorNode() const;
int anchorOffset() const;
Node* focusNode() const;
int focusOffset() const;
bool isCollapsed() const;
int rangeCount() const;
void collapse(Node*, int offset, ExceptionState&);
void collapseToEnd(ExceptionState&);
void collapseToStart(ExceptionState&);
void extend(Node*, int offset, ExceptionState&);
PassRefPtr<Range> getRangeAt(int, ExceptionState&);
void removeAllRanges();
void addRange(Range*);
void deleteFromDocument();
bool containsNode(const Node*, bool partlyContained) const;
void selectAllChildren(Node*, ExceptionState&);
String toString();
// Microsoft Selection Object API
void empty();
private:
explicit DOMSelection(const TreeScope*);
// Convenience method for accessors, does not check m_frame present.
const VisibleSelection& visibleSelection() const;
Node* shadowAdjustedNode(const Position&) const;
int shadowAdjustedOffset(const Position&) const;
bool isValidForPosition(Node*) const;
RawPtr<const TreeScope> m_treeScope;
};
} // namespace blink
#endif // SKY_ENGINE_CORE_EDITING_DOMSELECTION_H_
/*
* Copyright (C) 2005, 2008 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "sky/engine/core/editing/DeleteFromTextNodeCommand.h"
#include "sky/engine/bindings/exception_state.h"
#include "sky/engine/bindings/exception_state_placeholder.h"
#include "sky/engine/core/dom/Text.h"
namespace blink {
DeleteFromTextNodeCommand::DeleteFromTextNodeCommand(PassRefPtr<Text> node, unsigned offset, unsigned count)
: SimpleEditCommand(node->document())
, m_node(node)
, m_offset(offset)
, m_count(count)
{
ASSERT(m_node);
ASSERT(m_offset <= m_node->length());
ASSERT(m_offset + m_count <= m_node->length());
}
void DeleteFromTextNodeCommand::doApply()
{
ASSERT(m_node);
if (!m_node->isContentEditable(Node::UserSelectAllIsAlwaysNonEditable))
return;
TrackExceptionState exceptionState;
m_text = m_node->substringData(m_offset, m_count, exceptionState);
if (exceptionState.had_exception())
return;
m_node->deleteData(m_offset, m_count, exceptionState, CharacterData::DeprecatedRecalcStyleImmediatlelyForEditing);
}
void DeleteFromTextNodeCommand::doUnapply()
{
ASSERT(m_node);
if (!m_node->hasEditableStyle())
return;
m_node->insertData(m_offset, m_text, IGNORE_EXCEPTION, CharacterData::DeprecatedRecalcStyleImmediatlelyForEditing);
}
} // namespace blink
/*
* Copyright (C) 2005, 2006, 2008 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SKY_ENGINE_CORE_EDITING_DELETEFROMTEXTNODECOMMAND_H_
#define SKY_ENGINE_CORE_EDITING_DELETEFROMTEXTNODECOMMAND_H_
#include "sky/engine/core/editing/EditCommand.h"
namespace blink {
class Text;
class DeleteFromTextNodeCommand final : public SimpleEditCommand {
public:
static PassRefPtr<DeleteFromTextNodeCommand> create(PassRefPtr<Text> node, unsigned offset, unsigned count)
{
return adoptRef(new DeleteFromTextNodeCommand(node, offset, count));
}
private:
DeleteFromTextNodeCommand(PassRefPtr<Text>, unsigned offset, unsigned count);
virtual void doApply() override;
virtual void doUnapply() override;
RefPtr<Text> m_node;
unsigned m_offset;
unsigned m_count;
String m_text;
};
} // namespace blink
#endif // SKY_ENGINE_CORE_EDITING_DELETEFROMTEXTNODECOMMAND_H_
/*
* Copyright (C) 2005, 2006, 2008 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SKY_ENGINE_CORE_EDITING_DELETESELECTIONCOMMAND_H_
#define SKY_ENGINE_CORE_EDITING_DELETESELECTIONCOMMAND_H_
#include "sky/engine/core/editing/CompositeEditCommand.h"
namespace blink {
class EditingStyle;
class DeleteSelectionCommand final : public CompositeEditCommand {
public:
static PassRefPtr<DeleteSelectionCommand> create(Document& document, bool smartDelete = false, bool mergeBlocksAfterDelete = true, bool expandForSpecialElements = false, bool sanitizeMarkup = true)
{
return adoptRef(new DeleteSelectionCommand(document, smartDelete, mergeBlocksAfterDelete, expandForSpecialElements, sanitizeMarkup));
}
static PassRefPtr<DeleteSelectionCommand> create(const VisibleSelection& selection, bool smartDelete = false, bool mergeBlocksAfterDelete = true, bool expandForSpecialElements = false, bool sanitizeMarkup = true)
{
return adoptRef(new DeleteSelectionCommand(selection, smartDelete, mergeBlocksAfterDelete, expandForSpecialElements, sanitizeMarkup));
}
private:
DeleteSelectionCommand(Document&, bool smartDelete, bool mergeBlocksAfterDelete, bool expandForSpecialElements, bool santizeMarkup);
DeleteSelectionCommand(const VisibleSelection&, bool smartDelete, bool mergeBlocksAfterDelete, bool expandForSpecialElements, bool sanitizeMarkup);
virtual void doApply() override;
virtual EditAction editingAction() const override;
virtual bool preservesTypingStyle() const override;
void initializeStartEnd(Position&, Position&);
void setStartingSelectionOnSmartDelete(const Position&, const Position&);
void initializePositionData();
void saveTypingStyleState();
void handleGeneralDelete();
void fixupWhitespace();
void mergeParagraphs();
void calculateTypingStyleAfterDelete();
void clearTransientState();
void makeStylingElementsDirectChildrenOfEditableRootToPreventStyleLoss();
virtual void removeNode(PassRefPtr<Node>, ShouldAssumeContentIsAlwaysEditable = DoNotAssumeContentIsAlwaysEditable) override;
virtual void deleteTextFromNode(PassRefPtr<Text>, unsigned, unsigned) override;
void removeRedundantBlocks();
bool m_hasSelectionToDelete;
bool m_smartDelete;
bool m_mergeBlocksAfterDelete;
bool m_needPlaceholder;
bool m_expandForSpecialElements;
bool m_pruneStartBlockIfNecessary;
bool m_startsAtEmptyLine;
// This data is transient and should be cleared at the end of the doApply function.
VisibleSelection m_selectionToDelete;
Position m_upstreamStart;
Position m_downstreamStart;
Position m_upstreamEnd;
Position m_downstreamEnd;
Position m_endingPosition;
Position m_leadingWhitespace;
Position m_trailingWhitespace;
RefPtr<Node> m_startBlock;
RefPtr<Node> m_endBlock;
RefPtr<EditingStyle> m_deleteIntoBlockquoteStyle;
RefPtr<Element> m_startRoot;
RefPtr<Element> m_endRoot;
RefPtr<Node> m_temporaryPlaceholder;
};
} // namespace blink
#endif // SKY_ENGINE_CORE_EDITING_DELETESELECTIONCOMMAND_H_
/*
* Copyright (C) 2004 Apple Computer, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SKY_ENGINE_CORE_EDITING_EDITACTION_H_
#define SKY_ENGINE_CORE_EDITING_EDITACTION_H_
namespace blink {
typedef enum {
EditActionUnspecified,
EditActionSetColor,
EditActionSetBackgroundColor,
EditActionTurnOffKerning,
EditActionTightenKerning,
EditActionLoosenKerning,
EditActionUseStandardKerning,
EditActionTurnOffLigatures,
EditActionUseStandardLigatures,
EditActionUseAllLigatures,
EditActionRaiseBaseline,
EditActionLowerBaseline,
EditActionSetTraditionalCharacterShape,
EditActionSetFont,
EditActionChangeAttributes,
EditActionAlignLeft,
EditActionAlignRight,
EditActionCenter,
EditActionJustify,
EditActionSetWritingDirection,
EditActionSubscript,
EditActionSuperscript,
EditActionUnderline,
EditActionOutline,
EditActionUnscript,
EditActionDrag,
EditActionCut,
EditActionBold,
EditActionItalics,
EditActionPaste,
EditActionPasteFont,
EditActionPasteRuler,
EditActionTyping,
EditActionCreateLink,
EditActionUnlink,
EditActionFormatBlock,
EditActionInsertList,
EditActionIndent,
EditActionOutdent
} EditAction;
}
#endif // SKY_ENGINE_CORE_EDITING_EDITACTION_H_
/*
* Copyright (C) 2005, 2006, 2007 Apple, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "sky/engine/core/editing/EditCommand.h"
#include "sky/engine/core/dom/Document.h"
#include "sky/engine/core/dom/NodeTraversal.h"
#include "sky/engine/core/editing/CompositeEditCommand.h"
#include "sky/engine/core/editing/FrameSelection.h"
#include "sky/engine/core/frame/LocalFrame.h"
namespace blink {
EditCommand::EditCommand(Document& document)
: m_document(&document)
, m_parent(nullptr)
{
ASSERT(m_document);
ASSERT(m_document->frame());
setStartingSelection(m_document->frame()->selection().selection());
setEndingSelection(m_startingSelection);
}
EditCommand::EditCommand(Document* document, const VisibleSelection& startingSelection, const VisibleSelection& endingSelection)
: m_document(document)
, m_parent(nullptr)
{
ASSERT(m_document);
ASSERT(m_document->frame());
setStartingSelection(startingSelection);
setEndingSelection(endingSelection);
}
EditCommand::~EditCommand()
{
}
EditAction EditCommand::editingAction() const
{
return EditActionUnspecified;
}
static inline EditCommandComposition* compositionIfPossible(EditCommand* command)
{
if (!command->isCompositeEditCommand())
return 0;
return toCompositeEditCommand(command)->composition();
}
void EditCommand::setStartingSelection(const VisibleSelection& selection)
{
for (EditCommand* command = this; ; command = command->m_parent) {
if (EditCommandComposition* composition = compositionIfPossible(command)) {
ASSERT(command->isTopLevelCommand());
composition->setStartingSelection(selection);
}
command->m_startingSelection = selection;
if (!command->m_parent || command->m_parent->isFirstCommand(command))
break;
}
}
void EditCommand::setStartingSelection(const VisiblePosition& position)
{
setStartingSelection(VisibleSelection(position));
}
void EditCommand::setEndingSelection(const VisibleSelection& selection)
{
for (EditCommand* command = this; command; command = command->m_parent) {
if (EditCommandComposition* composition = compositionIfPossible(command)) {
ASSERT(command->isTopLevelCommand());
composition->setEndingSelection(selection);
}
command->m_endingSelection = selection;
}
}
void EditCommand::setEndingSelection(const VisiblePosition& position)
{
setEndingSelection(VisibleSelection(position));
}
void EditCommand::setParent(CompositeEditCommand* parent)
{
ASSERT((parent && !m_parent) || (!parent && m_parent));
ASSERT(!parent || !isCompositeEditCommand() || !toCompositeEditCommand(this)->composition());
m_parent = parent;
if (parent) {
m_startingSelection = parent->m_endingSelection;
m_endingSelection = parent->m_endingSelection;
}
}
void SimpleEditCommand::doReapply()
{
doApply();
}
} // namespace blink
/*
* Copyright (C) 2005, 2006, 2008 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SKY_ENGINE_CORE_EDITING_EDITCOMMAND_H_
#define SKY_ENGINE_CORE_EDITING_EDITCOMMAND_H_
#include "sky/engine/core/editing/EditAction.h"
#include "sky/engine/core/editing/VisibleSelection.h"
#include "sky/engine/platform/heap/Handle.h"
namespace blink {
class CompositeEditCommand;
class Document;
class Element;
class EditCommand : public RefCounted<EditCommand> {
public:
virtual ~EditCommand();
void setParent(CompositeEditCommand*);
virtual EditAction editingAction() const;
const VisibleSelection& startingSelection() const { return m_startingSelection; }
const VisibleSelection& endingSelection() const { return m_endingSelection; }
virtual bool isSimpleEditCommand() const { return false; }
virtual bool isCompositeEditCommand() const { return false; }
bool isTopLevelCommand() const { return !m_parent; }
virtual void doApply() = 0;
protected:
explicit EditCommand(Document&);
EditCommand(Document*, const VisibleSelection&, const VisibleSelection&);
Document& document() const { return *m_document.get(); }
CompositeEditCommand* parent() const { return m_parent; }
void setStartingSelection(const VisibleSelection&);
void setStartingSelection(const VisiblePosition&);
void setEndingSelection(const VisibleSelection&);
void setEndingSelection(const VisiblePosition&);
private:
RefPtr<Document> m_document;
VisibleSelection m_startingSelection;
VisibleSelection m_endingSelection;
RawPtr<CompositeEditCommand> m_parent;
};
enum ShouldAssumeContentIsAlwaysEditable {
AssumeContentIsAlwaysEditable,
DoNotAssumeContentIsAlwaysEditable,
};
class SimpleEditCommand : public EditCommand {
public:
virtual void doUnapply() = 0;
virtual void doReapply(); // calls doApply()
protected:
explicit SimpleEditCommand(Document& document) : EditCommand(document) { }
private:
virtual bool isSimpleEditCommand() const override final { return true; }
};
DEFINE_TYPE_CASTS(SimpleEditCommand, EditCommand, command, command->isSimpleEditCommand(), command.isSimpleEditCommand());
} // namespace blink
#endif // SKY_ENGINE_CORE_EDITING_EDITCOMMAND_H_
/*
* Copyright (C) 2006, 2007 Apple, Inc. All rights reserved.
* Copyright (C) 2012 Google, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "sky/engine/core/editing/EditingBehavior.h"
#include "gen/sky/core/EventTypeNames.h"
#include "sky/engine/core/events/KeyboardEvent.h"
#include "sky/engine/platform/KeyboardCodes.h"
namespace blink {
//
// The below code was adapted from the WebKit file webview.cpp
//
static const unsigned CtrlKey = 1 << 0;
static const unsigned AltKey = 1 << 1;
static const unsigned ShiftKey = 1 << 2;
static const unsigned MetaKey = 1 << 3;
// Keys with special meaning. These will be delegated to the editor using
// the execCommand() method
struct KeyDownEntry {
unsigned virtualKey;
unsigned modifiers;
const char* name;
};
struct KeyPressEntry {
unsigned charCode;
unsigned modifiers;
const char* name;
};
static const KeyDownEntry keyDownEntries[] = {
{ VKEY_LEFT, 0, "MoveLeft" },
{ VKEY_LEFT, ShiftKey, "MoveLeftAndModifySelection" },
{ VKEY_LEFT, CtrlKey, "MoveWordLeft" },
{ VKEY_LEFT, CtrlKey | ShiftKey,
"MoveWordLeftAndModifySelection" },
{ VKEY_RIGHT, 0, "MoveRight" },
{ VKEY_RIGHT, ShiftKey, "MoveRightAndModifySelection" },
{ VKEY_RIGHT, CtrlKey, "MoveWordRight" },
{ VKEY_RIGHT, CtrlKey | ShiftKey, "MoveWordRightAndModifySelection" },
{ VKEY_UP, 0, "MoveUp" },
{ VKEY_UP, ShiftKey, "MoveUpAndModifySelection" },
{ VKEY_PRIOR, ShiftKey, "MovePageUpAndModifySelection" },
{ VKEY_DOWN, 0, "MoveDown" },
{ VKEY_DOWN, ShiftKey, "MoveDownAndModifySelection" },
{ VKEY_NEXT, ShiftKey, "MovePageDownAndModifySelection" },
{ VKEY_UP, CtrlKey, "MoveParagraphBackward" },
{ VKEY_UP, CtrlKey | ShiftKey, "MoveParagraphBackwardAndModifySelection" },
{ VKEY_DOWN, CtrlKey, "MoveParagraphForward" },
{ VKEY_DOWN, CtrlKey | ShiftKey, "MoveParagraphForwardAndModifySelection" },
{ VKEY_PRIOR, 0, "MovePageUp" },
{ VKEY_NEXT, 0, "MovePageDown" },
{ VKEY_HOME, 0, "MoveToBeginningOfLine" },
{ VKEY_HOME, ShiftKey,
"MoveToBeginningOfLineAndModifySelection" },
{ VKEY_HOME, CtrlKey, "MoveToBeginningOfDocument" },
{ VKEY_HOME, CtrlKey | ShiftKey,
"MoveToBeginningOfDocumentAndModifySelection" },
{ VKEY_END, 0, "MoveToEndOfLine" },
{ VKEY_END, ShiftKey, "MoveToEndOfLineAndModifySelection" },
{ VKEY_END, CtrlKey, "MoveToEndOfDocument" },
{ VKEY_END, CtrlKey | ShiftKey,
"MoveToEndOfDocumentAndModifySelection" },
{ VKEY_BACK, 0, "DeleteBackward" },
{ VKEY_DELETE, 0, "DeleteForward" },
{ VKEY_BACK, CtrlKey, "DeleteWordBackward" },
{ VKEY_DELETE, CtrlKey, "DeleteWordForward" },
{ VKEY_RETURN, 0, "InsertNewline" },
{ 'C', CtrlKey, "Copy" },
{ 'V', CtrlKey, "Paste" },
{ 'V', CtrlKey | ShiftKey, "PasteAndMatchStyle" },
{ 'X', CtrlKey, "Cut" },
{ 'A', CtrlKey, "SelectAll" },
{ VKEY_INSERT, 0, "OverWrite" },
};
static const KeyPressEntry keyPressEntries[] = {
{ '\r', 0, "InsertNewline" },
};
const char* EditingBehavior::interpretKeyEvent(const KeyboardEvent& event) const
{
static HashMap<int, const char*>* keyDownCommandsMap = 0;
static HashMap<int, const char*>* keyPressCommandsMap = 0;
if (!keyDownCommandsMap) {
keyDownCommandsMap = new HashMap<int, const char*>;
keyPressCommandsMap = new HashMap<int, const char*>;
for (unsigned i = 0; i < arraysize(keyDownEntries); i++) {
keyDownCommandsMap->set(keyDownEntries[i].modifiers << 16 | keyDownEntries[i].virtualKey, keyDownEntries[i].name);
}
for (unsigned i = 0; i < arraysize(keyPressEntries); i++) {
keyPressCommandsMap->set(keyPressEntries[i].modifiers << 16 | keyPressEntries[i].charCode, keyPressEntries[i].name);
}
}
unsigned modifiers = 0;
if (event.shiftKey())
modifiers |= ShiftKey;
if (event.altKey())
modifiers |= AltKey;
if (event.ctrlKey())
modifiers |= CtrlKey;
if (event.metaKey())
modifiers |= MetaKey;
if (event.type() == EventTypeNames::keydown) {
int mapKey = modifiers << 16 | event.key();
return mapKey ? keyDownCommandsMap->get(mapKey) : 0;
}
int mapKey = modifiers << 16 | event.charCode();
return mapKey ? keyPressCommandsMap->get(mapKey) : 0;
}
bool EditingBehavior::shouldInsertCharacter(const KeyboardEvent& event) const
{
// On Gtk/Linux, it emits key events with ASCII text and ctrl on for ctrl-<x>.
// In Webkit, EditorClient::handleKeyboardEvent in
// WebKit/gtk/WebCoreSupport/EditorClientGtk.cpp drop such events.
// On Mac, it emits key events with ASCII text and meta on for Command-<x>.
// These key events should not emit text insert event.
// Alt key would be used to insert alternative character, so we should let
// through. Also note that Ctrl-Alt combination equals to AltGr key which is
// also used to insert alternative character.
// http://code.google.com/p/chromium/issues/detail?id=10846
// Windows sets both alt and meta are on when "Alt" key pressed.
// http://code.google.com/p/chromium/issues/detail?id=2215
// Also, we should not rely on an assumption that keyboards don't
// send ASCII characters when pressing a control key on Windows,
// which may be configured to do it so by user.
// See also http://en.wikipedia.org/wiki/Keyboard_Layout
// FIXME(ukai): investigate more detail for various keyboard layout.
UChar ch = event.charCode();
// Don't insert null or control characters as they can result in
// unexpected behaviour
if (ch < ' ')
return false;
#if !OS(WIN)
// Don't insert ASCII character if ctrl w/o alt or meta is on.
// On Mac, we should ignore events when meta is on (Command-<x>).
if (ch < 0x80) {
if (event.ctrlKey() && !event.altKey())
return false;
#if OS(MACOSX)
if (event.metaKey())
return false;
#endif
}
#endif
return true;
}
} // namespace blink
/*
* Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
* Copyright (C) 2010 Apple Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef SKY_ENGINE_CORE_EDITING_EDITINGBEHAVIOR_H_
#define SKY_ENGINE_CORE_EDITING_EDITINGBEHAVIOR_H_
namespace blink {
class KeyboardEvent;
class EditingBehavior {
public:
explicit EditingBehavior()
{
}
// Individual functions for each case where we have more than one style of editing behavior.
// Create a new function for any platform difference so we can control it here.
// When extending a selection beyond the top or bottom boundary of an editable area,
// maintain the horizontal position on Windows and Android but extend it to the boundary of
// the editable content on Mac and Linux.
bool shouldMoveCaretToHorizontalBoundaryWhenPastTopOrBottom() const
{
return false;
}
// On Windows, selections should always be considered as directional, regardless if it is
// mouse-based or keyboard-based.
bool shouldConsiderSelectionAsDirectional() const { return true; }
// On Mac, style is considered present when present at the beginning of selection. On other platforms,
// style has to be present throughout the selection.
bool shouldToggleStyleBasedOnStartOfSelection() const { return false; }
// Standard Mac behavior when extending to a boundary is grow the selection rather than leaving the base
// in place and moving the extent. Matches NSTextView.
bool shouldAlwaysGrowSelectionWhenExtendingToBoundary() const { return false; }
// On Mac, when processing a contextual click, the object being clicked upon should be selected.
bool shouldSelectOnContextualMenuClick() const { return false; }
// On Mac and Windows, pressing backspace (when it isn't handled otherwise) should navigate back.
bool shouldNavigateBackOnBackspace() const
{
return false;
}
// On Mac, selecting backwards by word/line from the middle of a word/line, and then going
// forward leaves the caret back in the middle with no selection, instead of directly selecting
// to the other end of the line/word (Unix/Windows behavior).
bool shouldExtendSelectionByWordOrLineAcrossCaret() const { return true; }
// Based on native behavior, when using ctrl(alt)+arrow to move caret by word, ctrl(alt)+left arrow moves caret to
// immediately before the word in all platforms, for example, the word break positions are: "|abc |def |hij |opq".
// But ctrl+right arrow moves caret to "abc |def |hij |opq" on Windows and "abc| def| hij| opq|" on Mac and Linux.
bool shouldSkipSpaceWhenMovingRight() const { return false; }
// On Mac, undo of delete/forward-delete of text should select the deleted text. On other platforms deleted text
// should not be selected and the cursor should be placed where the deletion started.
bool shouldUndoOfDeleteSelectText() const { return false; }
// Support for global selections, used on platforms like the X Window
// System that treat selection as a type of clipboard.
bool supportsGlobalSelection() const
{
return false;
}
// Convert a KeyboardEvent to a command name like "Copy", "Undo" and so on.
// If nothing, return empty string.
const char* interpretKeyEvent(const KeyboardEvent&) const;
bool shouldInsertCharacter(const KeyboardEvent&) const;
};
} // namespace blink
#endif // SKY_ENGINE_CORE_EDITING_EDITINGBEHAVIOR_H_
此差异已折叠。
/*
* Copyright (C) 2010 Google Inc. All rights reserved.
* Copyright (C) 2013 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SKY_ENGINE_CORE_EDITING_EDITINGSTYLE_H_
#define SKY_ENGINE_CORE_EDITING_EDITINGSTYLE_H_
#include "gen/sky/core/CSSPropertyNames.h"
#include "gen/sky/core/CSSValueKeywords.h"
#include "sky/engine/core/editing/WritingDirection.h"
#include "sky/engine/platform/fonts/FixedPitchFontType.h"
#include "sky/engine/platform/heap/Handle.h"
#include "sky/engine/wtf/Forward.h"
#include "sky/engine/wtf/RefCounted.h"
#include "sky/engine/wtf/RefPtr.h"
#include "sky/engine/wtf/TriState.h"
#include "sky/engine/wtf/Vector.h"
#include "sky/engine/wtf/text/WTFString.h"
namespace blink {
class CSSStyleDeclaration;
class CSSComputedStyleDeclaration;
class CSSPrimitiveValue;
class CSSValue;
class ContainerNode;
class Document;
class Element;
class HTMLElement;
class MutableStylePropertySet;
class Node;
class Position;
class QualifiedName;
class RenderStyle;
class StylePropertySet;
class VisibleSelection;
class EditingStyle final : public RefCounted<EditingStyle> {
public:
enum PropertiesToInclude { AllProperties, OnlyEditingInheritableProperties, EditingPropertiesInEffect };
static float NoFontDelta;
static PassRefPtr<EditingStyle> create()
{
return adoptRef(new EditingStyle());
}
static PassRefPtr<EditingStyle> create(ContainerNode* node, PropertiesToInclude propertiesToInclude = OnlyEditingInheritableProperties)
{
return adoptRef(new EditingStyle(node, propertiesToInclude));
}
static PassRefPtr<EditingStyle> create(const Position& position, PropertiesToInclude propertiesToInclude = OnlyEditingInheritableProperties)
{
return adoptRef(new EditingStyle(position, propertiesToInclude));
}
static PassRefPtr<EditingStyle> create(const StylePropertySet* style)
{
return adoptRef(new EditingStyle(style));
}
static PassRefPtr<EditingStyle> create(CSSPropertyID propertyID, const String& value)
{
return adoptRef(new EditingStyle(propertyID, value));
}
~EditingStyle();
MutableStylePropertySet* style() { return m_mutableStyle.get(); }
bool isEmpty() const;
void clear();
PassRefPtr<EditingStyle> copy() const;
void removeBlockProperties();
static bool elementIsStyledSpanOrHTMLEquivalent(const Element*);
void mergeTypingStyle(Document*);
private:
EditingStyle();
EditingStyle(ContainerNode*, PropertiesToInclude);
EditingStyle(const Position&, PropertiesToInclude);
explicit EditingStyle(const StylePropertySet*);
EditingStyle(CSSPropertyID, const String& value);
void init(Node*, PropertiesToInclude);
void removeTextFillAndStrokeColorsIfNeeded(RenderStyle*);
void setProperty(CSSPropertyID, const String& value);
void replaceFontSizeByKeywordIfPossible(RenderStyle*, CSSComputedStyleDeclaration*);
void extractFontSizeDelta();
enum CSSPropertyOverrideMode { OverrideValues, DoNotOverrideValues };
void mergeStyle(const StylePropertySet*, CSSPropertyOverrideMode);
RefPtr<MutableStylePropertySet> m_mutableStyle;
FixedPitchFontType m_fixedPitchFontType;
float m_fontSizeDelta;
friend class HTMLElementEquivalent;
friend class HTMLAttributeEquivalent;
};
} // namespace blink
#endif // SKY_ENGINE_CORE_EDITING_EDITINGSTYLE_H_
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
......@@ -43,7 +43,6 @@ class Position;
class PositionWithAffinity;
class Range;
class VisiblePosition;
class VisibleSelection;
// This file contains a set of helper functions used by the editing commands
......@@ -210,16 +209,6 @@ Element* unsplittableElementForPosition(const Position&);
bool canMergeLists(Element* firstList, Element* secondList);
// -------------------------------------------------------------------------
// VisibleSelection
// -------------------------------------------------------------------------
// Functions returning VisibleSelection
VisibleSelection selectionForParagraphIteration(const VisibleSelection&);
Position adjustedSelectionStartForStyleComputation(const VisibleSelection&);
// Miscellaneous functions on Text
inline bool isWhitespace(UChar c)
{
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册