diff --git a/sky/engine/core/core.gni b/sky/engine/core/core.gni index 4133dec916650..d99a7df51fb01 100644 --- a/sky/engine/core/core.gni +++ b/sky/engine/core/core.gni @@ -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", diff --git a/sky/engine/core/css/SelectorChecker.cpp b/sky/engine/core/css/SelectorChecker.cpp index 8a157db43ea33..b41fdd2fa6d53 100644 --- a/sky/engine/core/css/SelectorChecker.cpp +++ b/sky/engine/core/css/SelectorChecker.cpp @@ -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; } diff --git a/sky/engine/core/dom/CharacterData.cpp b/sky/engine/core/dom/CharacterData.cpp index 156a1377bdfe5..ee32e92663c5c 100644 --- a/sky/engine/core/dom/CharacterData.cpp +++ b/sky/engine/core/dom/CharacterData.cpp @@ -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); } diff --git a/sky/engine/core/dom/Document.cpp b/sky/engine/core/dom/Document.cpp index 9fa0be1b1bdff..7b30dcbb4cfc0 100644 --- a/sky/engine/core/dom/Document.cpp +++ b/sky/engine/core/dom/Document.cpp @@ -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. } diff --git a/sky/engine/core/dom/Document.idl b/sky/engine/core/dom/Document.idl index 70030b718b71b..4093a86084f54 100644 --- a/sky/engine/core/dom/Document.idl +++ b/sky/engine/core/dom/Document.idl @@ -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(); diff --git a/sky/engine/core/dom/DocumentMarkerController.h b/sky/engine/core/dom/DocumentMarkerController.h index 94862959c6a02..2a396428d4c2f 100644 --- a/sky/engine/core/dom/DocumentMarkerController.h +++ b/sky/engine/core/dom/DocumentMarkerController.h @@ -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" diff --git a/sky/engine/core/dom/Element.cpp b/sky/engine/core/dom/Element.cpp index f071f191192cf..7660adad8f076 100644 --- a/sky/engine/core/dom/Element.cpp +++ b/sky/engine/core/dom/Element.cpp @@ -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 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(); } } diff --git a/sky/engine/core/dom/TreeScope.cpp b/sky/engine/core/dom/TreeScope.cpp index 256818a49c56f..b7667850839a6 100644 --- a/sky/engine/core/dom/TreeScope.cpp +++ b/sky/engine/core/dom/TreeScope.cpp @@ -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); diff --git a/sky/engine/core/dom/TreeScope.h b/sky/engine/core/dom/TreeScope.h index 9260d0307609a..672dba9240cd7 100644 --- a/sky/engine/core/dom/TreeScope.h +++ b/sky/engine/core/dom/TreeScope.h @@ -34,7 +34,6 @@ namespace blink { class ContainerNode; -class DOMSelection; class Document; class Element; class HitTestResult; @@ -68,8 +67,6 @@ class TreeScope { // 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 @@ class TreeScope { OwnPtr m_imageMapsByName; OwnPtr m_labelsByForAttribute; - mutable RefPtr m_selection; - int m_guardRefCount; }; diff --git a/sky/engine/core/dom/shadow/ShadowRoot.idl b/sky/engine/core/dom/shadow/ShadowRoot.idl index 8df518b25500a..ee040f4f52b2b 100644 --- a/sky/engine/core/dom/shadow/ShadowRoot.idl +++ b/sky/engine/core/dom/shadow/ShadowRoot.idl @@ -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, diff --git a/sky/engine/core/editing/AppendNodeCommand.cpp b/sky/engine/core/editing/AppendNodeCommand.cpp deleted file mode 100644 index 149f2b65b3810..0000000000000 --- a/sky/engine/core/editing/AppendNodeCommand.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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 parent, PassRefPtr 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 diff --git a/sky/engine/core/editing/AppendNodeCommand.h b/sky/engine/core/editing/AppendNodeCommand.h deleted file mode 100644 index 5e800a17b1dd5..0000000000000 --- a/sky/engine/core/editing/AppendNodeCommand.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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 create(PassRefPtr parent, PassRefPtr node) - { - return adoptRef(new AppendNodeCommand(parent, node)); - } - -private: - AppendNodeCommand(PassRefPtr parent, PassRefPtr); - - virtual void doApply() override; - virtual void doUnapply() override; - - RefPtr m_parent; - RefPtr m_node; -}; - -} // namespace blink - -#endif // SKY_ENGINE_CORE_EDITING_APPENDNODECOMMAND_H_ diff --git a/sky/engine/core/editing/ApplyBlockElementCommand.cpp b/sky/engine/core/editing/ApplyBlockElementCommand.cpp deleted file mode 100644 index 76e37ab04a129..0000000000000 --- a/sky/engine/core/editing/ApplyBlockElementCommand.cpp +++ /dev/null @@ -1,232 +0,0 @@ -/* - * 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 startScope = nullptr; - int startIndex = indexForVisiblePosition(startOfSelection, startScope); - RefPtr 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 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 = 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(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)); -} - -} diff --git a/sky/engine/core/editing/ApplyBlockElementCommand.h b/sky/engine/core/editing/ApplyBlockElementCommand.h deleted file mode 100644 index 69f04b021e6d5..0000000000000 --- a/sky/engine/core/editing/ApplyBlockElementCommand.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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&) = 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_ diff --git a/sky/engine/core/editing/Caret.cpp b/sky/engine/core/editing/Caret.cpp deleted file mode 100644 index 7a9ff4f3e4713..0000000000000 --- a/sky/engine/core/editing/Caret.cpp +++ /dev/null @@ -1,214 +0,0 @@ -/* - * 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::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); -} - -} diff --git a/sky/engine/core/editing/Caret.h b/sky/engine/core/editing/Caret.h deleted file mode 100644 index 7535a686869b5..0000000000000 --- a/sky/engine/core/editing/Caret.h +++ /dev/null @@ -1,104 +0,0 @@ -/* - * 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 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_ diff --git a/sky/engine/core/editing/CompositeEditCommand.cpp b/sky/engine/core/editing/CompositeEditCommand.cpp deleted file mode 100644 index a09ad2dc895a6..0000000000000 --- a/sky/engine/core/editing/CompositeEditCommand.cpp +++ /dev/null @@ -1,856 +0,0 @@ -/* - * Copyright (C) 2005, 2006, 2007, 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/CompositeEditCommand.h" - -#include "sky/engine/bindings/exception_state_placeholder.h" -#include "sky/engine/core/dom/Document.h" -#include "sky/engine/core/dom/DocumentFragment.h" -#include "sky/engine/core/dom/DocumentMarkerController.h" -#include "sky/engine/core/dom/ElementTraversal.h" -#include "sky/engine/core/dom/NodeTraversal.h" -#include "sky/engine/core/dom/Range.h" -#include "sky/engine/core/dom/Text.h" -#include "sky/engine/core/editing/AppendNodeCommand.h" -#include "sky/engine/core/editing/DeleteFromTextNodeCommand.h" -#include "sky/engine/core/editing/DeleteSelectionCommand.h" -#include "sky/engine/core/editing/Editor.h" -#include "sky/engine/core/editing/InsertIntoTextNodeCommand.h" -#include "sky/engine/core/editing/InsertLineBreakCommand.h" -#include "sky/engine/core/editing/InsertNodeBeforeCommand.h" -#include "sky/engine/core/editing/InsertParagraphSeparatorCommand.h" -#include "sky/engine/core/editing/PlainTextRange.h" -#include "sky/engine/core/editing/RemoveNodeCommand.h" -#include "sky/engine/core/editing/RemoveNodePreservingChildrenCommand.h" -#include "sky/engine/core/editing/ReplaceSelectionCommand.h" -#include "sky/engine/core/editing/SpellChecker.h" -#include "sky/engine/core/editing/SplitElementCommand.h" -#include "sky/engine/core/editing/SplitTextNodeCommand.h" -#include "sky/engine/core/editing/TextIterator.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/html/HTMLElement.h" -#include "sky/engine/core/rendering/InlineTextBox.h" -#include "sky/engine/core/rendering/RenderBlock.h" -#include "sky/engine/core/rendering/RenderText.h" - -namespace blink { - -PassRefPtr EditCommandComposition::create(Document* document, - const VisibleSelection& startingSelection, const VisibleSelection& endingSelection, EditAction editAction) -{ - return adoptRef(new EditCommandComposition(document, startingSelection, endingSelection, editAction)); -} - -EditCommandComposition::EditCommandComposition(Document* document, const VisibleSelection& startingSelection, const VisibleSelection& endingSelection, EditAction editAction) - : m_document(document) - , m_startingSelection(startingSelection) - , m_endingSelection(endingSelection) - , m_startingRootEditableElement(startingSelection.rootEditableElement()) - , m_endingRootEditableElement(endingSelection.rootEditableElement()) - , m_editAction(editAction) -{ -} - -bool EditCommandComposition::belongsTo(const LocalFrame& frame) const -{ - ASSERT(m_document); - return m_document->frame() == &frame; -} - -void EditCommandComposition::unapply() -{ - ASSERT(m_document); - RefPtr frame = m_document->frame(); - ASSERT(frame); - - // Changes to the document may have been made since the last editing operation that require a layout, as in . - // Low level operations, like RemoveNodeCommand, don't require a layout because the high level operations that use them perform one - // if one is necessary (like for the creation of VisiblePositions). - m_document->updateLayout(); - - { - size_t size = m_commands.size(); - for (size_t i = size; i; --i) - m_commands[i - 1]->doUnapply(); - } - - frame->editor().unappliedEditing(this); -} - -void EditCommandComposition::reapply() -{ - ASSERT(m_document); - RefPtr frame = m_document->frame(); - ASSERT(frame); - - // Changes to the document may have been made since the last editing operation that require a layout, as in . - // Low level operations, like RemoveNodeCommand, don't require a layout because the high level operations that use them perform one - // if one is necessary (like for the creation of VisiblePositions). - m_document->updateLayout(); - - { - size_t size = m_commands.size(); - for (size_t i = 0; i != size; ++i) - m_commands[i]->doReapply(); - } - - frame->editor().reappliedEditing(this); -} - -void EditCommandComposition::append(SimpleEditCommand* command) -{ - m_commands.append(command); -} - -void EditCommandComposition::setStartingSelection(const VisibleSelection& selection) -{ - m_startingSelection = selection; - m_startingRootEditableElement = selection.rootEditableElement(); -} - -void EditCommandComposition::setEndingSelection(const VisibleSelection& selection) -{ - m_endingSelection = selection; - m_endingRootEditableElement = selection.rootEditableElement(); -} - -CompositeEditCommand::CompositeEditCommand(Document& document) - : EditCommand(document) -{ -} - -CompositeEditCommand::~CompositeEditCommand() -{ - ASSERT(isTopLevelCommand() || !m_composition); -} - -void CompositeEditCommand::apply() -{ - if (!endingSelection().isContentRichlyEditable()) { - switch (editingAction()) { - case EditActionTyping: - case EditActionPaste: - case EditActionDrag: - case EditActionSetWritingDirection: - case EditActionCut: - case EditActionUnspecified: - break; - default: - ASSERT_NOT_REACHED(); - return; - } - } - ensureComposition(); - - // Changes to the document may have been made since the last editing operation that require a layout, as in . - // Low level operations, like RemoveNodeCommand, don't require a layout because the high level operations that use them perform one - // if one is necessary (like for the creation of VisiblePositions). - document().updateLayout(); - - LocalFrame* frame = document().frame(); - ASSERT(frame); - { - doApply(); - } - - // Only need to call appliedEditing for top-level commands, - // and TypingCommands do it on their own (see TypingCommand::typingAddedToOpenCommand). - if (!isTypingCommand()) - frame->editor().appliedEditing(this); - setShouldRetainAutocorrectionIndicator(false); -} - -EditCommandComposition* CompositeEditCommand::ensureComposition() -{ - CompositeEditCommand* command = this; - while (command && command->parent()) - command = command->parent(); - if (!command->m_composition) - command->m_composition = EditCommandComposition::create(&document(), startingSelection(), endingSelection(), editingAction()); - return command->m_composition.get(); -} - -bool CompositeEditCommand::preservesTypingStyle() const -{ - return false; -} - -bool CompositeEditCommand::isTypingCommand() const -{ - return false; -} - -void CompositeEditCommand::setShouldRetainAutocorrectionIndicator(bool) -{ -} - -// -// sugary-sweet convenience functions to help create and apply edit commands in composite commands -// -void CompositeEditCommand::applyCommandToComposite(PassRefPtr prpCommand) -{ - RefPtr command = prpCommand; - command->setParent(this); - command->doApply(); - if (command->isSimpleEditCommand()) { - command->setParent(0); - ensureComposition()->append(toSimpleEditCommand(command.get())); - } - m_commands.append(command.release()); -} - -void CompositeEditCommand::applyCommandToComposite(PassRefPtr command, const VisibleSelection& selection) -{ - command->setParent(this); - if (selection != command->endingSelection()) { - command->setStartingSelection(selection); - command->setEndingSelection(selection); - } - command->doApply(); - m_commands.append(command); -} - -void CompositeEditCommand::insertParagraphSeparator(bool useDefaultParagraphElement, bool pasteBlockqutoeIntoUnquotedArea) -{ - applyCommandToComposite(InsertParagraphSeparatorCommand::create(document(), useDefaultParagraphElement, pasteBlockqutoeIntoUnquotedArea)); -} - -bool CompositeEditCommand::isRemovableBlock(const Node* node) -{ - return false; -} - -void CompositeEditCommand::insertNodeBefore(PassRefPtr insertChild, PassRefPtr refChild, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable) -{ - applyCommandToComposite(InsertNodeBeforeCommand::create(insertChild, refChild, shouldAssumeContentIsAlwaysEditable)); -} - -void CompositeEditCommand::insertNodeAfter(PassRefPtr insertChild, PassRefPtr refChild) -{ - ASSERT(insertChild); - ASSERT(refChild); - ContainerNode* parent = refChild->parentNode(); - ASSERT(parent); - ASSERT(!parent->isShadowRoot()); - if (parent->lastChild() == refChild) - appendNode(insertChild, parent); - else { - ASSERT(refChild->nextSibling()); - insertNodeBefore(insertChild, refChild->nextSibling()); - } -} - -void CompositeEditCommand::insertNodeAt(PassRefPtr insertChild, const Position& editingPosition) -{ - ASSERT(isEditablePosition(editingPosition, ContentIsEditable, DoNotUpdateStyle)); - // For editing positions like [table, 0], insert before the table, - // likewise for replaced elements, brs, etc. - Position p = editingPosition.parentAnchoredEquivalent(); - Node* refChild = p.deprecatedNode(); - int offset = p.deprecatedEditingOffset(); - - if (canHaveChildrenForEditing(refChild)) { - Node* child = refChild->firstChild(); - for (int i = 0; child && i < offset; i++) - child = child->nextSibling(); - if (child) - insertNodeBefore(insertChild, child); - else - appendNode(insertChild, toContainerNode(refChild)); - } else if (caretMinOffset(refChild) >= offset) - insertNodeBefore(insertChild, refChild); - else if (refChild->isTextNode() && caretMaxOffset(refChild) > offset) { - splitTextNode(toText(refChild), offset); - - // Mutation events (bug 22634) from the text node insertion may have removed the refChild - if (!refChild->inDocument()) - return; - insertNodeBefore(insertChild, refChild); - } else - insertNodeAfter(insertChild, refChild); -} - -void CompositeEditCommand::appendNode(PassRefPtr node, PassRefPtr parent) -{ - ASSERT(canHaveChildrenForEditing(parent.get())); - applyCommandToComposite(AppendNodeCommand::create(parent, node)); -} - -void CompositeEditCommand::removeChildrenInRange(PassRefPtr node, unsigned from, unsigned to) -{ - Vector > children; - Node* child = NodeTraversal::childAt(*node, from); - for (unsigned i = from; child && i < to; i++, child = child->nextSibling()) - children.append(child); - - size_t size = children.size(); - for (size_t i = 0; i < size; ++i) - removeNode(children[i].release()); -} - -void CompositeEditCommand::removeNode(PassRefPtr node, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable) -{ - if (!node || !node->nonShadowBoundaryParentNode()) - return; - applyCommandToComposite(RemoveNodeCommand::create(node, shouldAssumeContentIsAlwaysEditable)); -} - -void CompositeEditCommand::removeNodePreservingChildren(PassRefPtr node, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable) -{ - applyCommandToComposite(RemoveNodePreservingChildrenCommand::create(node, shouldAssumeContentIsAlwaysEditable)); -} - -void CompositeEditCommand::removeNodeAndPruneAncestors(PassRefPtr node, Node* excludeNode) -{ - ASSERT(node.get() != excludeNode); - RefPtr parent = node->parentNode(); - removeNode(node); - prune(parent.release(), excludeNode); -} - -void CompositeEditCommand::moveRemainingSiblingsToNewParent(Node* node, Node* pastLastNodeToMove, PassRefPtr prpNewParent) -{ - NodeVector nodesToRemove; - RefPtr newParent = prpNewParent; - - for (; node && node != pastLastNodeToMove; node = node->nextSibling()) - nodesToRemove.append(node); - - for (unsigned i = 0; i < nodesToRemove.size(); i++) { - removeNode(nodesToRemove[i]); - appendNode(nodesToRemove[i], newParent); - } -} - -void CompositeEditCommand::updatePositionForNodeRemovalPreservingChildren(Position& position, Node& node) -{ - int offset = (position.anchorType() == Position::PositionIsOffsetInAnchor) ? position.offsetInContainerNode() : 0; - updatePositionForNodeRemoval(position, node); - if (offset) - position.moveToOffset(offset); -} - -void CompositeEditCommand::prune(PassRefPtr node, Node* excludeNode) -{ - if (RefPtr highestNodeToRemove = highestNodeToRemoveInPruning(node.get(), excludeNode)) - removeNode(highestNodeToRemove.release()); -} - -void CompositeEditCommand::splitTextNode(PassRefPtr node, unsigned offset) -{ - applyCommandToComposite(SplitTextNodeCommand::create(node, offset)); -} - -void CompositeEditCommand::splitElement(PassRefPtr element, PassRefPtr atChild) -{ - applyCommandToComposite(SplitElementCommand::create(element, atChild)); -} - -void CompositeEditCommand::insertTextIntoNode(PassRefPtr node, unsigned offset, const String& text) -{ - if (!text.isEmpty()) - applyCommandToComposite(InsertIntoTextNodeCommand::create(node, offset, text)); -} - -void CompositeEditCommand::deleteTextFromNode(PassRefPtr node, unsigned offset, unsigned count) -{ - applyCommandToComposite(DeleteFromTextNodeCommand::create(node, offset, count)); -} - -void CompositeEditCommand::replaceTextInNode(PassRefPtr prpNode, unsigned offset, unsigned count, const String& replacementText) -{ - RefPtr node(prpNode); - applyCommandToComposite(DeleteFromTextNodeCommand::create(node, offset, count)); - if (!replacementText.isEmpty()) - applyCommandToComposite(InsertIntoTextNodeCommand::create(node, offset, replacementText)); -} - -Position CompositeEditCommand::replaceSelectedTextInNode(const String& text) -{ - Position start = endingSelection().start(); - Position end = endingSelection().end(); - if (start.containerNode() != end.containerNode() || !start.containerNode()->isTextNode()) - return Position(); - - RefPtr textNode = start.containerText(); - replaceTextInNode(textNode, start.offsetInContainerNode(), end.offsetInContainerNode() - start.offsetInContainerNode(), text); - - return Position(textNode.release(), start.offsetInContainerNode() + text.length()); -} - -static void copyMarkerTypesAndDescriptions(const DocumentMarkerVector& markerPointers, Vector& types, Vector& descriptions) -{ - size_t arraySize = markerPointers.size(); - types.reserveCapacity(arraySize); - descriptions.reserveCapacity(arraySize); - for (size_t i = 0; i < arraySize; ++i) { - types.append(markerPointers[i]->type()); - descriptions.append(markerPointers[i]->description()); - } -} - -void CompositeEditCommand::replaceTextInNodePreservingMarkers(PassRefPtr prpNode, unsigned offset, unsigned count, const String& replacementText) -{ - RefPtr node(prpNode); - DocumentMarkerController& markerController = document().markers(); - Vector types; - Vector descriptions; - copyMarkerTypesAndDescriptions(markerController.markersInRange(Range::create(document(), node.get(), offset, node.get(), offset + count).get(), DocumentMarker::AllMarkers()), types, descriptions); - replaceTextInNode(node, offset, count, replacementText); - RefPtr newRange = Range::create(document(), node.get(), offset, node.get(), offset + replacementText.length()); - ASSERT(types.size() == descriptions.size()); - for (size_t i = 0; i < types.size(); ++i) - markerController.addMarker(newRange.get(), types[i], descriptions[i]); -} - -Position CompositeEditCommand::positionOutsideTabSpan(const Position& pos) -{ - return pos; -} - -void CompositeEditCommand::insertNodeAtTabSpanPosition(PassRefPtr node, const Position& pos) -{ - // insert node before, after, or at split of tab span - insertNodeAt(node, positionOutsideTabSpan(pos)); -} - -void CompositeEditCommand::deleteSelection(bool smartDelete, bool mergeBlocksAfterDelete, bool expandForSpecialElements, bool sanitizeMarkup) -{ - if (endingSelection().isRange()) - applyCommandToComposite(DeleteSelectionCommand::create(document(), smartDelete, mergeBlocksAfterDelete, expandForSpecialElements, sanitizeMarkup)); -} - -void CompositeEditCommand::deleteSelection(const VisibleSelection &selection, bool smartDelete, bool mergeBlocksAfterDelete, bool expandForSpecialElements, bool sanitizeMarkup) -{ - if (selection.isRange()) - applyCommandToComposite(DeleteSelectionCommand::create(selection, smartDelete, mergeBlocksAfterDelete, expandForSpecialElements, sanitizeMarkup)); -} - -static inline bool containsOnlyWhitespace(const String& text) -{ - for (unsigned i = 0; i < text.length(); ++i) { - if (!isWhitespace(text[i])) - return false; - } - - return true; -} - -bool CompositeEditCommand::shouldRebalanceLeadingWhitespaceFor(const String& text) const -{ - return containsOnlyWhitespace(text); -} - -bool CompositeEditCommand::canRebalance(const Position& position) const -{ - Node* node = position.containerNode(); - if (position.anchorType() != Position::PositionIsOffsetInAnchor || !node || !node->isTextNode()) - return false; - - Text* textNode = toText(node); - if (textNode->length() == 0) - return false; - - RenderText* renderer = textNode->renderer(); - if (renderer && !renderer->style()->collapseWhiteSpace()) - return false; - - return true; -} - -// FIXME: Doesn't go into text nodes that contribute adjacent text (siblings, cousins, etc). -void CompositeEditCommand::rebalanceWhitespaceAt(const Position& position) -{ - Node* node = position.containerNode(); - if (!canRebalance(position)) - return; - - // If the rebalance is for the single offset, and neither text[offset] nor text[offset - 1] are some form of whitespace, do nothing. - int offset = position.deprecatedEditingOffset(); - String text = toText(node)->data(); - if (!isWhitespace(text[offset])) { - offset--; - if (offset < 0 || !isWhitespace(text[offset])) - return; - } - - rebalanceWhitespaceOnTextSubstring(toText(node), position.offsetInContainerNode(), position.offsetInContainerNode()); -} - -void CompositeEditCommand::rebalanceWhitespaceOnTextSubstring(PassRefPtr prpTextNode, int startOffset, int endOffset) -{ - RefPtr textNode = prpTextNode; - - String text = textNode->data(); - ASSERT(!text.isEmpty()); - - // Set upstream and downstream to define the extent of the whitespace surrounding text[offset]. - int upstream = startOffset; - while (upstream > 0 && isWhitespace(text[upstream - 1])) - upstream--; - - int downstream = endOffset; - while ((unsigned)downstream < text.length() && isWhitespace(text[downstream])) - downstream++; - - int length = downstream - upstream; - if (!length) - return; - - VisiblePosition visibleUpstreamPos(Position(textNode, upstream)); - VisiblePosition visibleDownstreamPos(Position(textNode, downstream)); - - String string = text.substring(upstream, length); - String rebalancedString = stringWithRebalancedWhitespace(string, - // FIXME: Because of the problem mentioned at the top of this function, we must also use nbsps at the start/end of the string because - // this function doesn't get all surrounding whitespace, just the whitespace in the current text node. - isStartOfParagraph(visibleUpstreamPos) || upstream == 0, - isEndOfParagraph(visibleDownstreamPos) || (unsigned)downstream == text.length()); - - if (string != rebalancedString) - replaceTextInNodePreservingMarkers(textNode.release(), upstream, length, rebalancedString); -} - -void CompositeEditCommand::prepareWhitespaceAtPositionForSplit(Position& position) -{ - Node* node = position.deprecatedNode(); - if (!node || !node->isTextNode()) - return; - Text* textNode = toText(node); - - if (textNode->length() == 0) - return; - RenderText* renderer = textNode->renderer(); - if (renderer && !renderer->style()->collapseWhiteSpace()) - return; - - // Delete collapsed whitespace so that inserting nbsps doesn't uncollapse it. - Position upstreamPos = position.upstream(); - deleteInsignificantText(upstreamPos, position.downstream()); - position = upstreamPos.downstream(); - - VisiblePosition visiblePos(position); - VisiblePosition previousVisiblePos(visiblePos.previous()); - replaceCollapsibleWhitespaceWithNonBreakingSpaceIfNeeded(previousVisiblePos); - replaceCollapsibleWhitespaceWithNonBreakingSpaceIfNeeded(visiblePos); -} - -void CompositeEditCommand::replaceCollapsibleWhitespaceWithNonBreakingSpaceIfNeeded(const VisiblePosition& visiblePosition) -{ - if (!isCollapsibleWhitespace(visiblePosition.characterAfter())) - return; - Position pos = visiblePosition.deepEquivalent().downstream(); - if (!pos.containerNode() || !pos.containerNode()->isTextNode()) - return; - replaceTextInNodePreservingMarkers(pos.containerText(), pos.offsetInContainerNode(), 1, nonBreakingSpaceString()); -} - -void CompositeEditCommand::rebalanceWhitespace() -{ - VisibleSelection selection = endingSelection(); - if (selection.isNone()) - return; - - rebalanceWhitespaceAt(selection.start()); - if (selection.isRange()) - rebalanceWhitespaceAt(selection.end()); -} - -void CompositeEditCommand::deleteInsignificantText(PassRefPtr textNode, unsigned start, unsigned end) -{ - if (!textNode || start >= end) - return; - - document().updateLayout(); - - RenderText* textRenderer = textNode->renderer(); - if (!textRenderer) - return; - - Vector sortedTextBoxes; - size_t sortedTextBoxesPosition = 0; - - for (InlineTextBox* textBox = textRenderer->firstTextBox(); textBox; textBox = textBox->nextTextBox()) - sortedTextBoxes.append(textBox); - - // If there is mixed directionality text, the boxes can be out of order, - // (like Arabic with embedded LTR), so sort them first. - if (textRenderer->containsReversedText()) - std::sort(sortedTextBoxes.begin(), sortedTextBoxes.end(), InlineTextBox::compareByStart); - InlineTextBox* box = sortedTextBoxes.isEmpty() ? 0 : sortedTextBoxes[sortedTextBoxesPosition]; - - if (!box) { - // whole text node is empty - removeNode(textNode); - return; - } - - unsigned length = textNode->length(); - if (start >= length || end > length) - return; - - unsigned removed = 0; - InlineTextBox* prevBox = 0; - String str; - - // This loop structure works to process all gaps preceding a box, - // and also will look at the gap after the last box. - while (prevBox || box) { - unsigned gapStart = prevBox ? prevBox->start() + prevBox->len() : 0; - if (end < gapStart) - // No more chance for any intersections - break; - - unsigned gapEnd = box ? box->start() : length; - bool indicesIntersect = start <= gapEnd && end >= gapStart; - int gapLen = gapEnd - gapStart; - if (indicesIntersect && gapLen > 0) { - gapStart = std::max(gapStart, start); - if (str.isNull()) - str = textNode->data().substring(start, end - start); - // remove text in the gap - str.remove(gapStart - start - removed, gapLen); - removed += gapLen; - } - - prevBox = box; - if (box) { - if (++sortedTextBoxesPosition < sortedTextBoxes.size()) - box = sortedTextBoxes[sortedTextBoxesPosition]; - else - box = 0; - } - } - - if (!str.isNull()) { - // Replace the text between start and end with our pruned version. - if (!str.isEmpty()) - replaceTextInNode(textNode, start, end - start, str); - else { - // Assert that we are not going to delete all of the text in the node. - // If we were, that should have been done above with the call to - // removeNode and return. - ASSERT(start > 0 || end - start < textNode->length()); - deleteTextFromNode(textNode, start, end - start); - } - } -} - -void CompositeEditCommand::deleteInsignificantText(const Position& start, const Position& end) -{ - if (start.isNull() || end.isNull()) - return; - - if (comparePositions(start, end) >= 0) - return; - - Vector > nodes; - for (Node* node = start.deprecatedNode(); node; node = NodeTraversal::next(*node)) { - if (node->isTextNode()) - nodes.append(toText(node)); - if (node == end.deprecatedNode()) - break; - } - - for (size_t i = 0; i < nodes.size(); ++i) { - Text* textNode = nodes[i].get(); - int startOffset = textNode == start.deprecatedNode() ? start.deprecatedEditingOffset() : 0; - int endOffset = textNode == end.deprecatedNode() ? end.deprecatedEditingOffset() : static_cast(textNode->length()); - deleteInsignificantText(textNode, startOffset, endOffset); - } -} - -void CompositeEditCommand::deleteInsignificantTextDownstream(const Position& pos) -{ - Position end = VisiblePosition(pos, VP_DEFAULT_AFFINITY).next().deepEquivalent().downstream(); - deleteInsignificantText(pos, end); -} - -// Assumes that the position is at a placeholder and does the removal without much checking. -void CompositeEditCommand::removePlaceholderAt(const Position& p) -{ - ASSERT(lineBreakExistsAtPosition(p)); - deleteTextFromNode(toText(p.anchorNode()), p.offsetInContainerNode(), 1); -} - -void CompositeEditCommand::pushAnchorElementDown(Element* anchorNode) -{ - if (!anchorNode) - return; - - ASSERT(anchorNode->isLink()); - - setEndingSelection(VisibleSelection::selectionFromContentsOfNode(anchorNode)); - // Clones of anchorNode have been pushed down, now remove it. - if (anchorNode->inDocument()) - removeNodePreservingChildren(anchorNode); -} - -// There are bugs in deletion when it removes a fully selected table/list. -// It expands and removes the entire table/list, but will let content -// before and after the table/list collapse onto one line. -// Deleting a paragraph will leave a placeholder. Remove it (and prune -// empty or unrendered parents). - -void CompositeEditCommand::cleanupAfterDeletion(VisiblePosition destination) -{ - VisiblePosition caretAfterDelete = endingSelection().visibleStart(); - Node* destinationNode = destination.deepEquivalent().anchorNode(); - if (caretAfterDelete != destination && isStartOfParagraph(caretAfterDelete) && isEndOfParagraph(caretAfterDelete)) { - // Note: We want the rightmost candidate. - Position position = caretAfterDelete.deepEquivalent().downstream(); - Node* node = position.deprecatedNode(); - - // Bail if we'd remove an ancestor of our destination. - if (destinationNode && destinationNode->isDescendantOf(node)) - return; - - if (isBlock(node)) { - // If caret position after deletion and destination position coincides, - // node should not be removed. - if (!position.rendersInDifferentPosition(destination.deepEquivalent())) { - prune(node, destinationNode); - return; - } - removeNodeAndPruneAncestors(node, destinationNode); - } - else if (lineBreakExistsAtPosition(position)) { - // There is a preserved '\n' at caretAfterDelete. - // We can safely assume this is a text node. - Text* textNode = toText(node); - if (textNode->length() == 1) - removeNodeAndPruneAncestors(node, destinationNode); - else - deleteTextFromNode(textNode, position.deprecatedEditingOffset(), 1); - } - } -} - -void CompositeEditCommand::moveParagraph(const VisiblePosition& startOfParagraphToMove, const VisiblePosition& endOfParagraphToMove, const VisiblePosition& destination, bool preserveSelection, bool preserveStyle, Node* constrainingAncestor) -{ - ASSERT(isStartOfParagraph(startOfParagraphToMove)); - ASSERT(isEndOfParagraph(endOfParagraphToMove)); - moveParagraphs(startOfParagraphToMove, endOfParagraphToMove, destination, preserveSelection, preserveStyle, constrainingAncestor); -} - -void CompositeEditCommand::moveParagraphs(const VisiblePosition& startOfParagraphToMove, const VisiblePosition& endOfParagraphToMove, const VisiblePosition& destination, bool preserveSelection, bool preserveStyle, Node* constrainingAncestor) -{ - // FIXME(sky): Remove. - // We've probably broken editiing badly by deleting this function... -} - -// Operations use this function to avoid inserting content into an anchor when at the start or the end of -// that anchor, as in NSTextView. -// FIXME: This is only an approximation of NSTextViews insertion behavior, which varies depending on how -// the caret was made. -Position CompositeEditCommand::positionAvoidingSpecialElementBoundary(const Position& original) -{ - if (original.isNull()) - return original; - - VisiblePosition visiblePos(original); - Element* enclosingAnchor = enclosingAnchorElement(original); - Position result = original; - - if (!enclosingAnchor) - return result; - - // Don't avoid block level anchors, because that would insert content into the wrong paragraph. - if (enclosingAnchor && !isBlock(enclosingAnchor)) { - VisiblePosition firstInAnchor(firstPositionInNode(enclosingAnchor)); - VisiblePosition lastInAnchor(lastPositionInNode(enclosingAnchor)); - // If visually just after the anchor, insert *inside* the anchor unless it's the last - // VisiblePosition in the document, to match NSTextView. - if (visiblePos == lastInAnchor) { - // Make sure anchors are pushed down before avoiding them so that we don't - // also avoid structural elements like lists and blocks (5142012). - if (original.deprecatedNode() != enclosingAnchor && original.deprecatedNode()->parentNode() != enclosingAnchor) { - pushAnchorElementDown(enclosingAnchor); - enclosingAnchor = enclosingAnchorElement(original); - if (!enclosingAnchor) - return original; - } - // Don't insert outside an anchor if doing so would skip over a line break. It would - // probably be safe to move the line break so that we could still avoid the anchor here. - Position downstream(visiblePos.deepEquivalent().downstream()); - if (lineBreakExistsAtVisiblePosition(visiblePos) && downstream.deprecatedNode()->isDescendantOf(enclosingAnchor)) - return original; - - result = positionInParentAfterNode(*enclosingAnchor); - } - // If visually just before an anchor, insert *outside* the anchor unless it's the first - // VisiblePosition in a paragraph, to match NSTextView. - if (visiblePos == firstInAnchor) { - // Make sure anchors are pushed down before avoiding them so that we don't - // also avoid structural elements like lists and blocks (5142012). - if (original.deprecatedNode() != enclosingAnchor && original.deprecatedNode()->parentNode() != enclosingAnchor) { - pushAnchorElementDown(enclosingAnchor); - enclosingAnchor = enclosingAnchorElement(original); - } - if (!enclosingAnchor) - return original; - - result = positionInParentBeforeNode(*enclosingAnchor); - } - } - - if (result.isNull() || !editableRootForPosition(result)) - result = original; - - return result; -} - -// Splits the tree parent by parent until we reach the specified ancestor. We use VisiblePositions -// to determine if the split is necessary. Returns the last split node. -PassRefPtr CompositeEditCommand::splitTreeToNode(Node* start, Node* end, bool shouldSplitAncestor) -{ - ASSERT(start); - ASSERT(end); - ASSERT(start != end); - - if (shouldSplitAncestor && end->parentNode()) - end = end->parentNode(); - if (!start->isDescendantOf(end)) - return end; - - RefPtr endNode = end; - RefPtr node = nullptr; - for (node = start; node->parentNode() != endNode; node = node->parentNode()) { - Element* parentElement = node->parentElement(); - if (!parentElement) - break; - // Do not split a node when doing so introduces an empty node. - VisiblePosition positionInParent(firstPositionInNode(parentElement)); - VisiblePosition positionInNode(firstPositionInOrBeforeNode(node.get())); - if (positionInParent != positionInNode) - splitElement(parentElement, node); - } - - return node.release(); -} - -} // namespace blink diff --git a/sky/engine/core/editing/CompositeEditCommand.h b/sky/engine/core/editing/CompositeEditCommand.h deleted file mode 100644 index 37ee23694dbf5..0000000000000 --- a/sky/engine/core/editing/CompositeEditCommand.h +++ /dev/null @@ -1,154 +0,0 @@ -/* - * 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 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 m_document; - VisibleSelection m_startingSelection; - VisibleSelection m_endingSelection; - Vector > m_commands; - RefPtr m_startingRootEditableElement; - RefPtr 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, PassRefPtr parent); - void applyCommandToComposite(PassRefPtr); - void applyCommandToComposite(PassRefPtr, const VisibleSelection&); - void removeStyledElement(PassRefPtr); - 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, unsigned offset, unsigned count); - bool isRemovableBlock(const Node*); - void insertNodeAfter(PassRefPtr, PassRefPtr refChild); - void insertNodeAt(PassRefPtr, const Position&); - void insertNodeAtTabSpanPosition(PassRefPtr, const Position&); - void insertNodeBefore(PassRefPtr, PassRefPtr refChild, ShouldAssumeContentIsAlwaysEditable = DoNotAssumeContentIsAlwaysEditable); - void insertParagraphSeparator(bool useDefaultParagraphElement = false, bool pasteBlockqutoeIntoUnquotedArea = false); - void insertTextIntoNode(PassRefPtr, unsigned offset, const String& text); - void rebalanceWhitespace(); - void rebalanceWhitespaceAt(const Position&); - void rebalanceWhitespaceOnTextSubstring(PassRefPtr, int startOffset, int endOffset); - void prepareWhitespaceAtPositionForSplit(Position&); - void replaceCollapsibleWhitespaceWithNonBreakingSpaceIfNeeded(const VisiblePosition&); - bool canRebalance(const Position&) const; - bool shouldRebalanceLeadingWhitespaceFor(const String&) const; - void removeChildrenInRange(PassRefPtr, unsigned from, unsigned to); - virtual void removeNode(PassRefPtr, ShouldAssumeContentIsAlwaysEditable = DoNotAssumeContentIsAlwaysEditable); - void removeNodePreservingChildren(PassRefPtr, ShouldAssumeContentIsAlwaysEditable = DoNotAssumeContentIsAlwaysEditable); - void removeNodeAndPruneAncestors(PassRefPtr, Node* excludeNode = 0); - void moveRemainingSiblingsToNewParent(Node*, Node* pastLastNodeToMove, PassRefPtr prpNewParent); - void updatePositionForNodeRemovalPreservingChildren(Position&, Node&); - void prune(PassRefPtr, Node* excludeNode = 0); - void replaceTextInNode(PassRefPtr, unsigned offset, unsigned count, const String& replacementText); - Position replaceSelectedTextInNode(const String&); - void replaceTextInNodePreservingMarkers(PassRefPtr, unsigned offset, unsigned count, const String& replacementText); - Position positionOutsideTabSpan(const Position&); - void splitElement(PassRefPtr, PassRefPtr atChild); - void splitTextNode(PassRefPtr, unsigned offset); - - void deleteInsignificantText(PassRefPtr, 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 splitTreeToNode(Node*, Node*, bool splitAncestor = false); - - Vector > m_commands; - -private: - virtual bool isCompositeEditCommand() const override final { return true; } - - RefPtr m_composition; -}; - -DEFINE_TYPE_CASTS(CompositeEditCommand, EditCommand, command, command->isCompositeEditCommand(), command.isCompositeEditCommand()); - -} // namespace blink - -#endif // SKY_ENGINE_CORE_EDITING_COMPOSITEEDITCOMMAND_H_ diff --git a/sky/engine/core/editing/CompositionUnderlineRangeFilterTest.cpp b/sky/engine/core/editing/CompositionUnderlineRangeFilterTest.cpp deleted file mode 100644 index ab408e77eeb3a..0000000000000 --- a/sky/engine/core/editing/CompositionUnderlineRangeFilterTest.cpp +++ /dev/null @@ -1,89 +0,0 @@ -// 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 -#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* underlines) -{ - ASSERT(underlines && underlines->size() == 0U); - Vector rangeList; - testCase.split('|', rangeList); - // Intervals are named 'A', 'B', ..., 'Z', so ensure there aren't too many. - ASSERT_LE(rangeList.size(), static_cast('Z' - 'A')); - for (unsigned i = 0; i < rangeList.size(); ++i) { - String range = rangeList[i]; - Vector 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& 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('A' + code)); - } - return ret; -} - -TEST(CompositionUnderlineRangeFilterTest, Empty) -{ - Vector 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 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 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 diff --git a/sky/engine/core/editing/DOMSelection.cpp b/sky/engine/core/editing/DOMSelection.cpp deleted file mode 100644 index 62d82ed6fa266..0000000000000 --- a/sky/engine/core/editing/DOMSelection.cpp +++ /dev/null @@ -1,523 +0,0 @@ -/* - * 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::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 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 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 - // . - - 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 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 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 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 diff --git a/sky/engine/core/editing/DOMSelection.h b/sky/engine/core/editing/DOMSelection.h deleted file mode 100644 index 5f477f0abcf26..0000000000000 --- a/sky/engine/core/editing/DOMSelection.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * 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, public DartWrappable, public DOMWindowProperty { - DEFINE_WRAPPERTYPEINFO(); -public: - static PassRefPtr 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 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 m_treeScope; -}; - -} // namespace blink - -#endif // SKY_ENGINE_CORE_EDITING_DOMSELECTION_H_ diff --git a/sky/engine/core/editing/DeleteFromTextNodeCommand.cpp b/sky/engine/core/editing/DeleteFromTextNodeCommand.cpp deleted file mode 100644 index ff2e40abca246..0000000000000 --- a/sky/engine/core/editing/DeleteFromTextNodeCommand.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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 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 diff --git a/sky/engine/core/editing/DeleteFromTextNodeCommand.h b/sky/engine/core/editing/DeleteFromTextNodeCommand.h deleted file mode 100644 index 57979eb314ca3..0000000000000 --- a/sky/engine/core/editing/DeleteFromTextNodeCommand.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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 create(PassRefPtr node, unsigned offset, unsigned count) - { - return adoptRef(new DeleteFromTextNodeCommand(node, offset, count)); - } - -private: - DeleteFromTextNodeCommand(PassRefPtr, unsigned offset, unsigned count); - - virtual void doApply() override; - virtual void doUnapply() override; - - RefPtr m_node; - unsigned m_offset; - unsigned m_count; - String m_text; -}; - -} // namespace blink - -#endif // SKY_ENGINE_CORE_EDITING_DELETEFROMTEXTNODECOMMAND_H_ diff --git a/sky/engine/core/editing/DeleteSelectionCommand.cpp b/sky/engine/core/editing/DeleteSelectionCommand.cpp deleted file mode 100644 index 53300f12d0699..0000000000000 --- a/sky/engine/core/editing/DeleteSelectionCommand.cpp +++ /dev/null @@ -1,660 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -#include "sky/engine/core/editing/DeleteSelectionCommand.h" - -#include "sky/engine/core/dom/Document.h" -#include "sky/engine/core/dom/Element.h" -#include "sky/engine/core/dom/NodeTraversal.h" -#include "sky/engine/core/dom/Text.h" -#include "sky/engine/core/editing/EditingBoundary.h" -#include "sky/engine/core/editing/Editor.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/rendering/RenderText.h" - -namespace blink { - -DeleteSelectionCommand::DeleteSelectionCommand(Document& document, bool smartDelete, bool mergeBlocksAfterDelete, bool expandForSpecialElements, bool sanitizeMarkup) - : CompositeEditCommand(document) - , m_hasSelectionToDelete(false) - , m_smartDelete(smartDelete) - , m_mergeBlocksAfterDelete(mergeBlocksAfterDelete) - , m_needPlaceholder(false) - , m_expandForSpecialElements(expandForSpecialElements) - , m_pruneStartBlockIfNecessary(false) - , m_startsAtEmptyLine(false) - , m_startBlock(nullptr) - , m_endBlock(nullptr) - , m_deleteIntoBlockquoteStyle(nullptr) -{ -} - -DeleteSelectionCommand::DeleteSelectionCommand(const VisibleSelection& selection, bool smartDelete, bool mergeBlocksAfterDelete, bool expandForSpecialElements, bool sanitizeMarkup) - : CompositeEditCommand(*selection.start().document()) - , m_hasSelectionToDelete(true) - , m_smartDelete(smartDelete) - , m_mergeBlocksAfterDelete(mergeBlocksAfterDelete) - , m_needPlaceholder(false) - , m_expandForSpecialElements(expandForSpecialElements) - , m_pruneStartBlockIfNecessary(false) - , m_startsAtEmptyLine(false) - , m_selectionToDelete(selection) - , m_startBlock(nullptr) - , m_endBlock(nullptr) - , m_deleteIntoBlockquoteStyle(nullptr) -{ -} - -void DeleteSelectionCommand::initializeStartEnd(Position& start, Position& end) -{ - HTMLElement* startSpecialContainer = 0; - HTMLElement* endSpecialContainer = 0; - - start = m_selectionToDelete.start(); - end = m_selectionToDelete.end(); - - // FIXME: This is only used so that moveParagraphs can avoid the bugs in special element expansion. - if (!m_expandForSpecialElements) - return; - - while (1) { - startSpecialContainer = 0; - endSpecialContainer = 0; - - Position s = positionBeforeContainingSpecialElement(start, &startSpecialContainer); - Position e = positionAfterContainingSpecialElement(end, &endSpecialContainer); - - if (!startSpecialContainer && !endSpecialContainer) - break; - - if (VisiblePosition(start) != m_selectionToDelete.visibleStart() || VisiblePosition(end) != m_selectionToDelete.visibleEnd()) - break; - - // If we're going to expand to include the startSpecialContainer, it must be fully selected. - if (startSpecialContainer && !endSpecialContainer && comparePositions(positionInParentAfterNode(*startSpecialContainer), end) > -1) - break; - - // If we're going to expand to include the endSpecialContainer, it must be fully selected. - if (endSpecialContainer && !startSpecialContainer && comparePositions(start, positionInParentBeforeNode(*endSpecialContainer)) > -1) - break; - - if (startSpecialContainer && startSpecialContainer->isDescendantOf(endSpecialContainer)) - // Don't adjust the end yet, it is the end of a special element that contains the start - // special element (which may or may not be fully selected). - start = s; - else if (endSpecialContainer && endSpecialContainer->isDescendantOf(startSpecialContainer)) - // Don't adjust the start yet, it is the start of a special element that contains the end - // special element (which may or may not be fully selected). - end = e; - else { - start = s; - end = e; - } - } -} - -void DeleteSelectionCommand::setStartingSelectionOnSmartDelete(const Position& start, const Position& end) -{ - bool isBaseFirst = startingSelection().isBaseFirst(); - VisiblePosition newBase(isBaseFirst ? start : end); - VisiblePosition newExtent(isBaseFirst ? end : start); - setStartingSelection(VisibleSelection(newBase, newExtent, startingSelection().isDirectional())); -} - -void DeleteSelectionCommand::initializePositionData() -{ - Position start, end; - initializeStartEnd(start, end); - - ASSERT(isEditablePosition(start, ContentIsEditable, DoNotUpdateStyle)); - if (!isEditablePosition(end, ContentIsEditable, DoNotUpdateStyle)) - end = lastEditablePositionBeforePositionInRoot(end, highestEditableRoot(start)); - - m_upstreamStart = start.upstream(); - m_downstreamStart = start.downstream(); - m_upstreamEnd = end.upstream(); - m_downstreamEnd = end.downstream(); - - m_startRoot = editableRootForPosition(start); - m_endRoot = editableRootForPosition(end); - - // Usually the start and the end of the selection to delete are pulled together as a result of the deletion. - // Sometimes they aren't (like when no merge is requested), so we must choose one position to hold the caret - // and receive the placeholder after deletion. - VisiblePosition visibleEnd(m_downstreamEnd); - if (m_mergeBlocksAfterDelete && !isEndOfParagraph(visibleEnd)) - m_endingPosition = m_downstreamEnd; - else - m_endingPosition = m_downstreamStart; - - // We don't want to merge into a block if it will mean changing the quote level of content after deleting - // selections that contain a whole number paragraphs plus a line break, since it is unclear to most users - // that such a selection actually ends at the start of the next paragraph. This matches TextEdit behavior - // for indented paragraphs. - // Only apply this rule if the endingSelection is a range selection. If it is a caret, then other operations have created - // the selection we're deleting (like the process of creating a selection to delete during a backspace), and the user isn't in the situation described above. - if (numEnclosingMailBlockquotes(start) != numEnclosingMailBlockquotes(end) - && isStartOfParagraph(visibleEnd) && isStartOfParagraph(VisiblePosition(start)) - && endingSelection().isRange()) { - m_mergeBlocksAfterDelete = false; - m_pruneStartBlockIfNecessary = true; - } - - // Handle leading and trailing whitespace, as well as smart delete adjustments to the selection - m_leadingWhitespace = leadingWhitespacePosition(m_upstreamStart, m_selectionToDelete.affinity()); - m_trailingWhitespace = trailingWhitespacePosition(m_downstreamEnd, VP_DEFAULT_AFFINITY); - - if (m_smartDelete) { - - // skip smart delete if the selection to delete already starts or ends with whitespace - Position pos = VisiblePosition(m_upstreamStart, m_selectionToDelete.affinity()).deepEquivalent(); - bool skipSmartDelete = trailingWhitespacePosition(pos, VP_DEFAULT_AFFINITY, ConsiderNonCollapsibleWhitespace).isNotNull(); - if (!skipSmartDelete) - skipSmartDelete = leadingWhitespacePosition(m_downstreamEnd, VP_DEFAULT_AFFINITY, ConsiderNonCollapsibleWhitespace).isNotNull(); - - // extend selection upstream if there is whitespace there - bool hasLeadingWhitespaceBeforeAdjustment = leadingWhitespacePosition(m_upstreamStart, m_selectionToDelete.affinity(), ConsiderNonCollapsibleWhitespace).isNotNull(); - if (!skipSmartDelete && hasLeadingWhitespaceBeforeAdjustment) { - VisiblePosition visiblePos = VisiblePosition(m_upstreamStart, VP_DEFAULT_AFFINITY).previous(); - pos = visiblePos.deepEquivalent(); - // Expand out one character upstream for smart delete and recalculate - // positions based on this change. - m_upstreamStart = pos.upstream(); - m_downstreamStart = pos.downstream(); - m_leadingWhitespace = leadingWhitespacePosition(m_upstreamStart, visiblePos.affinity()); - - setStartingSelectionOnSmartDelete(m_upstreamStart, m_upstreamEnd); - } - - // trailing whitespace is only considered for smart delete if there is no leading - // whitespace, as in the case where you double-click the first word of a paragraph. - if (!skipSmartDelete && !hasLeadingWhitespaceBeforeAdjustment && trailingWhitespacePosition(m_downstreamEnd, VP_DEFAULT_AFFINITY, ConsiderNonCollapsibleWhitespace).isNotNull()) { - // Expand out one character downstream for smart delete and recalculate - // positions based on this change. - pos = VisiblePosition(m_downstreamEnd, VP_DEFAULT_AFFINITY).next().deepEquivalent(); - m_upstreamEnd = pos.upstream(); - m_downstreamEnd = pos.downstream(); - m_trailingWhitespace = trailingWhitespacePosition(m_downstreamEnd, VP_DEFAULT_AFFINITY); - - setStartingSelectionOnSmartDelete(m_downstreamStart, m_downstreamEnd); - } - } - - // We must pass call parentAnchoredEquivalent on the positions since some editing positions - // that appear inside their nodes aren't really inside them. [hr, 0] is one example. - // FIXME: parentAnchoredEquivalent should eventually be moved into enclosing element getters - // like the one below, since editing functions should obviously accept editing positions. - // FIXME: Passing false to enclosingNodeOfType tells it that it's OK to return a non-editable - // node. This was done to match existing behavior, but it seems wrong. - m_startBlock = enclosingNodeOfType(m_downstreamStart.parentAnchoredEquivalent(), &isBlock, CanCrossEditingBoundary); - m_endBlock = enclosingNodeOfType(m_upstreamEnd.parentAnchoredEquivalent(), &isBlock, CanCrossEditingBoundary); -} - -// We don't want to inherit style from an element which can't have contents. -static bool shouldNotInheritStyleFrom(const Node& node) -{ - return !node.canContainRangeEndPoint(); -} - -void DeleteSelectionCommand::saveTypingStyleState() -{ - // A common case is deleting characters that are all from the same text node. In - // that case, the style at the start of the selection before deletion will be the - // same as the style at the start of the selection after deletion (since those - // two positions will be identical). Therefore there is no need to save the - // typing style at the start of the selection, nor is there a reason to - // compute the style at the start of the selection after deletion (see the - // early return in calculateTypingStyleAfterDelete). - if (m_upstreamStart.deprecatedNode() == m_downstreamEnd.deprecatedNode() && m_upstreamStart.deprecatedNode()->isTextNode()) - return; - - if (shouldNotInheritStyleFrom(*m_selectionToDelete.start().anchorNode())) - return; - - // If we're deleting into a Mail blockquote, save the style at end() instead of start() - // We'll use this later in computeTypingStyleAfterDelete if we end up outside of a Mail blockquote - if (enclosingNodeOfType(m_selectionToDelete.start(), isMailHTMLBlockquoteElement)) - m_deleteIntoBlockquoteStyle = EditingStyle::create(m_selectionToDelete.end()); - else - m_deleteIntoBlockquoteStyle = nullptr; -} - -void DeleteSelectionCommand::removeNode(PassRefPtr node, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable) -{ - if (!node) - return; - - if (m_startRoot != m_endRoot && !(node->isDescendantOf(m_startRoot.get()) && node->isDescendantOf(m_endRoot.get()))) { - // If a node is not in both the start and end editable roots, remove it only if its inside an editable region. - if (!node->parentNode()->hasEditableStyle()) { - // Don't remove non-editable atomic nodes. - if (!node->hasChildren()) - return; - // Search this non-editable region for editable regions to empty. - RefPtr child = node->firstChild(); - while (child) { - RefPtr nextChild = child->nextSibling(); - removeNode(child.get(), shouldAssumeContentIsAlwaysEditable); - // Bail if nextChild is no longer node's child. - if (nextChild && nextChild->parentNode() != node) - return; - child = nextChild; - } - - // Don't remove editable regions that are inside non-editable ones, just clear them. - return; - } - } - - if (node->isRootEditableElement()) { - // Do not remove an element of table structure; remove its contents. - // Likewise for the root editable element. - Node* child = node->firstChild(); - while (child) { - Node* remove = child; - child = child->nextSibling(); - removeNode(remove, shouldAssumeContentIsAlwaysEditable); - } - - // Make sure empty cell has some height, if a placeholder can be inserted. - document().updateLayout(); - return; - } - - if (node == m_startBlock) { - VisiblePosition previous = VisiblePosition(firstPositionInNode(m_startBlock.get())).previous(); - if (previous.isNotNull() && !isEndOfBlock(previous)) - m_needPlaceholder = true; - } - if (node == m_endBlock) { - VisiblePosition next = VisiblePosition(lastPositionInNode(m_endBlock.get())).next(); - if (next.isNotNull() && !isStartOfBlock(next)) - m_needPlaceholder = true; - } - - // FIXME: Update the endpoints of the range being deleted. - updatePositionForNodeRemoval(m_endingPosition, *node); - updatePositionForNodeRemoval(m_leadingWhitespace, *node); - updatePositionForNodeRemoval(m_trailingWhitespace, *node); - - CompositeEditCommand::removeNode(node, shouldAssumeContentIsAlwaysEditable); -} - -static void updatePositionForTextRemoval(Text* node, int offset, int count, Position& position) -{ - if (position.anchorType() != Position::PositionIsOffsetInAnchor || position.containerNode() != node) - return; - - if (position.offsetInContainerNode() > offset + count) - position.moveToOffset(position.offsetInContainerNode() - count); - else if (position.offsetInContainerNode() > offset) - position.moveToOffset(offset); -} - -void DeleteSelectionCommand::deleteTextFromNode(PassRefPtr node, unsigned offset, unsigned count) -{ - // FIXME: Update the endpoints of the range being deleted. - updatePositionForTextRemoval(node.get(), offset, count, m_endingPosition); - updatePositionForTextRemoval(node.get(), offset, count, m_leadingWhitespace); - updatePositionForTextRemoval(node.get(), offset, count, m_trailingWhitespace); - updatePositionForTextRemoval(node.get(), offset, count, m_downstreamEnd); - - CompositeEditCommand::deleteTextFromNode(node, offset, count); -} - -void DeleteSelectionCommand::makeStylingElementsDirectChildrenOfEditableRootToPreventStyleLoss() -{ -} - -void DeleteSelectionCommand::handleGeneralDelete() -{ - if (m_upstreamStart.isNull()) - return; - - int startOffset = m_upstreamStart.deprecatedEditingOffset(); - Node* startNode = m_upstreamStart.deprecatedNode(); - ASSERT(startNode); - - makeStylingElementsDirectChildrenOfEditableRootToPreventStyleLoss(); - - // Never remove the start block unless it's a table, in which case we won't merge content in. - if (startNode == m_startBlock && !startOffset && canHaveChildrenForEditing(startNode)) { - startOffset = 0; - startNode = NodeTraversal::next(*startNode); - if (!startNode) - return; - } - - if (startOffset >= caretMaxOffset(startNode) && startNode->isTextNode()) { - Text* text = toText(startNode); - if (text->length() > (unsigned)caretMaxOffset(startNode)) - deleteTextFromNode(text, caretMaxOffset(startNode), text->length() - caretMaxOffset(startNode)); - } - - if (startOffset >= lastOffsetForEditing(startNode)) { - startNode = NodeTraversal::nextSkippingChildren(*startNode); - startOffset = 0; - } - - // Done adjusting the start. See if we're all done. - if (!startNode) - return; - - if (startNode == m_downstreamEnd.deprecatedNode()) { - if (m_downstreamEnd.deprecatedEditingOffset() - startOffset > 0) { - if (startNode->isTextNode()) { - // in a text node that needs to be trimmed - Text* text = toText(startNode); - deleteTextFromNode(text, startOffset, m_downstreamEnd.deprecatedEditingOffset() - startOffset); - } else { - removeChildrenInRange(startNode, startOffset, m_downstreamEnd.deprecatedEditingOffset()); - m_endingPosition = m_upstreamStart; - } - } - - // The selection to delete is all in one node. - if (!startNode->renderer() || (!startOffset && m_downstreamEnd.atLastEditingPositionForNode())) - removeNode(startNode); - } - else { - bool startNodeWasDescendantOfEndNode = m_upstreamStart.deprecatedNode()->isDescendantOf(m_downstreamEnd.deprecatedNode()); - // The selection to delete spans more than one node. - RefPtr node(startNode); - - if (startOffset > 0) { - if (startNode->isTextNode()) { - // in a text node that needs to be trimmed - Text* text = toText(node); - deleteTextFromNode(text, startOffset, text->length() - startOffset); - node = NodeTraversal::next(*node); - } else { - node = NodeTraversal::childAt(*startNode, startOffset); - } - } else if (startNode == m_upstreamEnd.deprecatedNode() && startNode->isTextNode()) { - Text* text = toText(m_upstreamEnd.deprecatedNode()); - deleteTextFromNode(text, 0, m_upstreamEnd.deprecatedEditingOffset()); - } - - // handle deleting all nodes that are completely selected - while (node && node != m_downstreamEnd.deprecatedNode()) { - if (comparePositions(firstPositionInOrBeforeNode(node.get()), m_downstreamEnd) >= 0) { - // NodeTraversal::nextSkippingChildren just blew past the end position, so stop deleting - node = nullptr; - } else if (!m_downstreamEnd.deprecatedNode()->isDescendantOf(node.get())) { - RefPtr nextNode = NodeTraversal::nextSkippingChildren(*node); - // if we just removed a node from the end container, update end position so the - // check above will work - updatePositionForNodeRemoval(m_downstreamEnd, *node); - removeNode(node.get()); - node = nextNode.get(); - } else { - Node& n = NodeTraversal::lastWithinOrSelf(*node); - if (m_downstreamEnd.deprecatedNode() == n && m_downstreamEnd.deprecatedEditingOffset() >= caretMaxOffset(&n)) { - removeNode(node.get()); - node = nullptr; - } else { - node = NodeTraversal::next(*node); - } - } - } - - if (m_downstreamEnd.deprecatedNode() != startNode && !m_upstreamStart.deprecatedNode()->isDescendantOf(m_downstreamEnd.deprecatedNode()) && m_downstreamEnd.inDocument() && m_downstreamEnd.deprecatedEditingOffset() >= caretMinOffset(m_downstreamEnd.deprecatedNode())) { - if (m_downstreamEnd.atLastEditingPositionForNode() && !canHaveChildrenForEditing(m_downstreamEnd.deprecatedNode())) { - // The node itself is fully selected, not just its contents. Delete it. - removeNode(m_downstreamEnd.deprecatedNode()); - } else { - if (m_downstreamEnd.deprecatedNode()->isTextNode()) { - // in a text node that needs to be trimmed - Text* text = toText(m_downstreamEnd.deprecatedNode()); - if (m_downstreamEnd.deprecatedEditingOffset() > 0) { - deleteTextFromNode(text, 0, m_downstreamEnd.deprecatedEditingOffset()); - } - // Remove children of m_downstreamEnd.deprecatedNode() that come after m_upstreamStart. - // Don't try to remove children if m_upstreamStart was inside m_downstreamEnd.deprecatedNode() - // and m_upstreamStart has been removed from the document, because then we don't - // know how many children to remove. - // FIXME: Make m_upstreamStart a position we update as we remove content, then we can - // always know which children to remove. - } else if (!(startNodeWasDescendantOfEndNode && !m_upstreamStart.inDocument())) { - int offset = 0; - if (m_upstreamStart.deprecatedNode()->isDescendantOf(m_downstreamEnd.deprecatedNode())) { - Node* n = m_upstreamStart.deprecatedNode(); - while (n && n->parentNode() != m_downstreamEnd.deprecatedNode()) - n = n->parentNode(); - if (n) - offset = n->nodeIndex() + 1; - } - removeChildrenInRange(m_downstreamEnd.deprecatedNode(), offset, m_downstreamEnd.deprecatedEditingOffset()); - m_downstreamEnd = createLegacyEditingPosition(m_downstreamEnd.deprecatedNode(), offset); - } - } - } - } -} - -void DeleteSelectionCommand::fixupWhitespace() -{ - document().updateLayout(); - // FIXME: isRenderedCharacter should be removed, and we should use VisiblePosition::characterAfter and VisiblePosition::characterBefore - if (m_leadingWhitespace.isNotNull() && !m_leadingWhitespace.isRenderedCharacter() && m_leadingWhitespace.deprecatedNode()->isTextNode()) { - Text* textNode = toText(m_leadingWhitespace.deprecatedNode()); - ASSERT(!textNode->renderer() || textNode->renderer()->style()->collapseWhiteSpace()); - replaceTextInNodePreservingMarkers(textNode, m_leadingWhitespace.deprecatedEditingOffset(), 1, nonBreakingSpaceString()); - } - if (m_trailingWhitespace.isNotNull() && !m_trailingWhitespace.isRenderedCharacter() && m_trailingWhitespace.deprecatedNode()->isTextNode()) { - Text* textNode = toText(m_trailingWhitespace.deprecatedNode()); - ASSERT(!textNode->renderer() ||textNode->renderer()->style()->collapseWhiteSpace()); - replaceTextInNodePreservingMarkers(textNode, m_trailingWhitespace.deprecatedEditingOffset(), 1, nonBreakingSpaceString()); - } -} - -// If a selection starts in one block and ends in another, we have to merge to bring content before the -// start together with content after the end. -void DeleteSelectionCommand::mergeParagraphs() -{ - if (!m_mergeBlocksAfterDelete) { - if (m_pruneStartBlockIfNecessary) { - // We aren't going to merge into the start block, so remove it if it's empty. - prune(m_startBlock); - // Removing the start block during a deletion is usually an indication that we need - // a placeholder, but not in this case. - m_needPlaceholder = false; - } - return; - } - - // It shouldn't have been asked to both try and merge content into the start block and prune it. - ASSERT(!m_pruneStartBlockIfNecessary); - - // FIXME: Deletion should adjust selection endpoints as it removes nodes so that we never get into this state (4099839). - if (!m_downstreamEnd.inDocument() || !m_upstreamStart.inDocument()) - return; - - // FIXME: The deletion algorithm shouldn't let this happen. - if (comparePositions(m_upstreamStart, m_downstreamEnd) > 0) - return; - - // There's nothing to merge. - if (m_upstreamStart == m_downstreamEnd) - return; - - VisiblePosition startOfParagraphToMove(m_downstreamEnd); - VisiblePosition mergeDestination(m_upstreamStart); - - // m_downstreamEnd's block has been emptied out by deletion. There is no content inside of it to - // move, so just remove it. - Element* endBlock = enclosingBlock(m_downstreamEnd.deprecatedNode()); - if (!endBlock || !endBlock->contains(startOfParagraphToMove.deepEquivalent().deprecatedNode()) || !startOfParagraphToMove.deepEquivalent().deprecatedNode()) { - removeNode(enclosingBlock(m_downstreamEnd.deprecatedNode())); - return; - } - - // We need to merge into m_upstreamStart's block, but it's been emptied out and collapsed by deletion. - if (!mergeDestination.deepEquivalent().deprecatedNode() || (!mergeDestination.deepEquivalent().deprecatedNode()->isDescendantOf(enclosingBlock(m_upstreamStart.containerNode())) && (!mergeDestination.deepEquivalent().anchorNode()->hasChildren() || !m_upstreamStart.containerNode()->hasChildren())) || (m_startsAtEmptyLine && mergeDestination != startOfParagraphToMove)) { - mergeDestination = VisiblePosition(m_upstreamStart); - } - - if (mergeDestination == startOfParagraphToMove) - return; - - VisiblePosition endOfParagraphToMove = endOfParagraph(startOfParagraphToMove, CanSkipOverEditingBoundary); - - if (mergeDestination == endOfParagraphToMove) - return; - - // Block images, tables and horizontal rules cannot be made inline with content at mergeDestination. If there is - // any (!isStartOfParagraph(mergeDestination)), don't merge, just move the caret to just before the selection we deleted. - // See https://bugs.webkit.org/show_bug.cgi?id=25439 - if (isRenderedAsNonInlineTableImageOrHR(startOfParagraphToMove.deepEquivalent().deprecatedNode()) && !isStartOfParagraph(mergeDestination)) { - m_endingPosition = m_upstreamStart; - return; - } - - // moveParagraphs will insert placeholders if it removes blocks that would require their use, don't let block - // removals that it does cause the insertion of *another* placeholder. - bool needPlaceholder = m_needPlaceholder; - bool paragraphToMergeIsEmpty = (startOfParagraphToMove == endOfParagraphToMove); - moveParagraph(startOfParagraphToMove, endOfParagraphToMove, mergeDestination, false, !paragraphToMergeIsEmpty); - m_needPlaceholder = needPlaceholder; - // The endingPosition was likely clobbered by the move, so recompute it (moveParagraph selects the moved paragraph). - m_endingPosition = endingSelection().start(); -} - -void DeleteSelectionCommand::calculateTypingStyleAfterDelete() -{ - document().frame()->selection().clearTypingStyle(); -} - -void DeleteSelectionCommand::clearTransientState() -{ - m_selectionToDelete = VisibleSelection(); - m_upstreamStart.clear(); - m_downstreamStart.clear(); - m_upstreamEnd.clear(); - m_downstreamEnd.clear(); - m_endingPosition.clear(); - m_leadingWhitespace.clear(); - m_trailingWhitespace.clear(); -} - -// This method removes div elements with no attributes that have only one child or no children at all. -void DeleteSelectionCommand::removeRedundantBlocks() -{ - Node* node = m_endingPosition.containerNode(); - Element* rootElement = node->rootEditableElement(); - - while (node != rootElement) { - if (isRemovableBlock(node)) { - if (node == m_endingPosition.anchorNode()) - updatePositionForNodeRemovalPreservingChildren(m_endingPosition, *node); - - CompositeEditCommand::removeNodePreservingChildren(node); - node = m_endingPosition.anchorNode(); - } else - node = node->parentNode(); - } -} - -void DeleteSelectionCommand::doApply() -{ - // If selection has not been set to a custom selection when the command was created, - // use the current ending selection. - if (!m_hasSelectionToDelete) - m_selectionToDelete = endingSelection(); - - if (!m_selectionToDelete.isNonOrphanedRange()) - return; - - // save this to later make the selection with - EAffinity affinity = m_selectionToDelete.affinity(); - - Position downstreamEnd = m_selectionToDelete.end().downstream(); - bool rootWillStayOpenWithoutPlaceholder = downstreamEnd.containerNode() == downstreamEnd.containerNode()->rootEditableElement() - || (downstreamEnd.containerNode()->isTextNode() && downstreamEnd.containerNode()->parentNode() == downstreamEnd.containerNode()->rootEditableElement()); - bool lineBreakAtEndOfSelectionToDelete = lineBreakExistsAtVisiblePosition(m_selectionToDelete.visibleEnd()); - m_needPlaceholder = !rootWillStayOpenWithoutPlaceholder - && isStartOfParagraph(m_selectionToDelete.visibleStart(), CanCrossEditingBoundary) - && isEndOfParagraph(m_selectionToDelete.visibleEnd(), CanCrossEditingBoundary) - && !lineBreakAtEndOfSelectionToDelete; - if (m_needPlaceholder) { - // Don't need a placeholder when deleting a selection that starts just before a table - // and ends inside it (we do need placeholders to hold open empty cells, but that's - // handled elsewhere). - if (Element* table = isLastPositionBeforeTable(m_selectionToDelete.visibleStart())) - if (m_selectionToDelete.end().deprecatedNode()->isDescendantOf(table)) - m_needPlaceholder = false; - } - - - // set up our state - initializePositionData(); - - bool lineBreakBeforeStart = lineBreakExistsAtVisiblePosition(VisiblePosition(m_upstreamStart).previous()); - - // Delete any text that may hinder our ability to fixup whitespace after the delete - deleteInsignificantTextDownstream(m_trailingWhitespace); - - saveTypingStyleState(); - - handleGeneralDelete(); - - fixupWhitespace(); - - mergeParagraphs(); - - if (!m_needPlaceholder && rootWillStayOpenWithoutPlaceholder) { - VisiblePosition visualEnding(m_endingPosition); - bool hasPlaceholder = lineBreakExistsAtVisiblePosition(visualEnding) - && visualEnding.next(CannotCrossEditingBoundary).isNull(); - m_needPlaceholder = hasPlaceholder && lineBreakBeforeStart && !lineBreakAtEndOfSelectionToDelete; - } - - rebalanceWhitespaceAt(m_endingPosition); - - calculateTypingStyleAfterDelete(); - - setEndingSelection(VisibleSelection(m_endingPosition, affinity, endingSelection().isDirectional())); - clearTransientState(); -} - -EditAction DeleteSelectionCommand::editingAction() const -{ - // Note that DeleteSelectionCommand is also used when the user presses the Delete key, - // but in that case there's a TypingCommand that supplies the editingAction(), so - // the Undo menu correctly shows "Undo Typing" - return EditActionCut; -} - -// Normally deletion doesn't preserve the typing style that was present before it. For example, -// type a character, Bold, then delete the character and start typing. The Bold typing style shouldn't -// stick around. Deletion should preserve a typing style that *it* sets, however. -bool DeleteSelectionCommand::preservesTypingStyle() const -{ - return false; -} - -} // namespace blink diff --git a/sky/engine/core/editing/DeleteSelectionCommand.h b/sky/engine/core/editing/DeleteSelectionCommand.h deleted file mode 100644 index 4958b0a66af9e..0000000000000 --- a/sky/engine/core/editing/DeleteSelectionCommand.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * 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 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 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, ShouldAssumeContentIsAlwaysEditable = DoNotAssumeContentIsAlwaysEditable) override; - virtual void deleteTextFromNode(PassRefPtr, 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 m_startBlock; - RefPtr m_endBlock; - RefPtr m_deleteIntoBlockquoteStyle; - RefPtr m_startRoot; - RefPtr m_endRoot; - RefPtr m_temporaryPlaceholder; -}; - -} // namespace blink - -#endif // SKY_ENGINE_CORE_EDITING_DELETESELECTIONCOMMAND_H_ diff --git a/sky/engine/core/editing/EditAction.h b/sky/engine/core/editing/EditAction.h deleted file mode 100644 index 77132c4ef2668..0000000000000 --- a/sky/engine/core/editing/EditAction.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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_ diff --git a/sky/engine/core/editing/EditCommand.cpp b/sky/engine/core/editing/EditCommand.cpp deleted file mode 100644 index e7967dbbba51f..0000000000000 --- a/sky/engine/core/editing/EditCommand.cpp +++ /dev/null @@ -1,122 +0,0 @@ -/* - * 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 diff --git a/sky/engine/core/editing/EditCommand.h b/sky/engine/core/editing/EditCommand.h deleted file mode 100644 index b46878eab9731..0000000000000 --- a/sky/engine/core/editing/EditCommand.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * 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 { -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 m_document; - VisibleSelection m_startingSelection; - VisibleSelection m_endingSelection; - RawPtr 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_ diff --git a/sky/engine/core/editing/EditingBehavior.cpp b/sky/engine/core/editing/EditingBehavior.cpp deleted file mode 100644 index e42b804b0ae93..0000000000000 --- a/sky/engine/core/editing/EditingBehavior.cpp +++ /dev/null @@ -1,185 +0,0 @@ -/* - * 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* keyDownCommandsMap = 0; - static HashMap* keyPressCommandsMap = 0; - - if (!keyDownCommandsMap) { - keyDownCommandsMap = new HashMap; - keyPressCommandsMap = new HashMap; - - 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-. - // 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-. - // 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-). - if (ch < 0x80) { - if (event.ctrlKey() && !event.altKey()) - return false; -#if OS(MACOSX) - if (event.metaKey()) - return false; -#endif - } -#endif - - return true; -} -} // namespace blink - diff --git a/sky/engine/core/editing/EditingBehavior.h b/sky/engine/core/editing/EditingBehavior.h deleted file mode 100644 index 1a6d45dbed4ba..0000000000000 --- a/sky/engine/core/editing/EditingBehavior.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * 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_ diff --git a/sky/engine/core/editing/EditingStyle.cpp b/sky/engine/core/editing/EditingStyle.cpp deleted file mode 100644 index 29618956ec571..0000000000000 --- a/sky/engine/core/editing/EditingStyle.cpp +++ /dev/null @@ -1,584 +0,0 @@ -/* - * Copyright (C) 2007, 2008, 2009 Apple Computer, Inc. - * Copyright (C) 2010, 2011 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/EditingStyle.h" - -#include "gen/sky/core/HTMLNames.h" -#include "sky/engine/bindings/exception_state_placeholder.h" -#include "sky/engine/core/css/CSSComputedStyleDeclaration.h" -#include "sky/engine/core/css/CSSPropertyMetadata.h" -#include "sky/engine/core/css/CSSValueList.h" -#include "sky/engine/core/css/CSSValuePool.h" -#include "sky/engine/core/css/FontSize.h" -#include "sky/engine/core/css/StylePropertySet.h" -#include "sky/engine/core/css/StyleRule.h" -#include "sky/engine/core/css/parser/BisonCSSParser.h" -#include "sky/engine/core/css/resolver/StyleResolver.h" -#include "sky/engine/core/dom/Document.h" -#include "sky/engine/core/dom/Element.h" -#include "sky/engine/core/dom/Node.h" -#include "sky/engine/core/dom/NodeTraversal.h" -#include "sky/engine/core/dom/Position.h" -#include "sky/engine/core/dom/QualifiedName.h" -#include "sky/engine/core/editing/Editor.h" -#include "sky/engine/core/editing/FrameSelection.h" -#include "sky/engine/core/editing/HTMLInterchange.h" -#include "sky/engine/core/editing/htmlediting.h" -#include "sky/engine/core/frame/LocalFrame.h" -#include "sky/engine/core/rendering/RenderBox.h" -#include "sky/engine/core/rendering/RenderObject.h" -#include "sky/engine/core/rendering/style/RenderStyle.h" - -namespace blink { - -static const CSSPropertyID& textDecorationPropertyForEditing() -{ - static const CSSPropertyID property = RuntimeEnabledFeatures::css3TextDecorationsEnabled() ? CSSPropertyTextDecorationLine : CSSPropertyTextDecoration; - return property; -} - -// Editing style properties must be preserved during editing operation. -// e.g. when a user inserts a new paragraph, all properties listed here must be copied to the new paragraph. -// NOTE: Use either allEditingProperties() or inheritableEditingProperties() to -// respect runtime enabling of properties. -static const CSSPropertyID staticEditingProperties[] = { - CSSPropertyBackgroundColor, - CSSPropertyColor, - CSSPropertyFontFamily, - CSSPropertyFontSize, - CSSPropertyFontStyle, - CSSPropertyFontVariant, - CSSPropertyFontWeight, - CSSPropertyLetterSpacing, - CSSPropertyLineHeight, - CSSPropertyTextAlign, - // FIXME: CSSPropertyTextDecoration needs to be removed when CSS3 Text - // Decoration feature is no longer experimental. - CSSPropertyTextDecoration, - CSSPropertyTextDecorationLine, - CSSPropertyTextIndent, - CSSPropertyWhiteSpace, - CSSPropertyWordSpacing, - CSSPropertyWebkitTextDecorationsInEffect, - CSSPropertyWebkitTextFillColor, - CSSPropertyWebkitTextStrokeColor, - CSSPropertyWebkitTextStrokeWidth, -}; - -enum EditingPropertiesType { OnlyInheritableEditingProperties, AllEditingProperties }; - -static const Vector& allEditingProperties() -{ - DEFINE_STATIC_LOCAL(Vector, properties, ()); - if (properties.isEmpty()) { - CSSPropertyMetadata::filterEnabledCSSPropertiesIntoVector(staticEditingProperties, WTF_ARRAY_LENGTH(staticEditingProperties), properties); - if (RuntimeEnabledFeatures::css3TextDecorationsEnabled()) - properties.remove(properties.find(CSSPropertyTextDecoration)); - } - return properties; -} - -static const Vector& inheritableEditingProperties() -{ - DEFINE_STATIC_LOCAL(Vector, properties, ()); - if (properties.isEmpty()) { - CSSPropertyMetadata::filterEnabledCSSPropertiesIntoVector(staticEditingProperties, WTF_ARRAY_LENGTH(staticEditingProperties), properties); - for (size_t index = 0; index < properties.size();) { - if (!CSSPropertyMetadata::isInheritedProperty(properties[index])) { - properties.remove(index); - continue; - } - ++index; - } - } - return properties; -} - -template -static PassRefPtr copyEditingProperties(StyleDeclarationType* style, EditingPropertiesType type = OnlyInheritableEditingProperties) -{ - if (type == AllEditingProperties) - return style->copyPropertiesInSet(allEditingProperties()); - return style->copyPropertiesInSet(inheritableEditingProperties()); -} - -static inline bool isEditingProperty(int id) -{ - return allEditingProperties().contains(static_cast(id)); -} - -static PassRefPtr editingStyleFromComputedStyle(PassRefPtr style, EditingPropertiesType type = OnlyInheritableEditingProperties) -{ - if (!style) - return MutableStylePropertySet::create(); - return copyEditingProperties(style.get(), type); -} - -enum LegacyFontSizeMode { AlwaysUseLegacyFontSize, UseLegacyFontSizeOnlyIfPixelValuesMatch }; - -static bool isTransparentColorValue(CSSValue*); -static bool hasTransparentBackgroundColor(CSSStyleDeclaration*); - -static PassRefPtr backgroundColorInEffect(Node*); - -class HTMLElementEquivalent { - WTF_MAKE_FAST_ALLOCATED; - DECLARE_EMPTY_VIRTUAL_DESTRUCTOR_WILL_BE_REMOVED(HTMLElementEquivalent); -public: - static PassOwnPtr create(CSSPropertyID propertyID, CSSValueID primitiveValue, const HTMLQualifiedName& tagName) - { - return adoptPtr(new HTMLElementEquivalent(propertyID, primitiveValue, tagName)); - } - - virtual bool matches(const Element* element) const { return !m_tagName || element->hasTagName(*m_tagName); } - virtual bool hasAttribute() const { return false; } - virtual bool propertyExistsInStyle(const StylePropertySet* style) const { return style->getPropertyCSSValue(m_propertyID); } - virtual bool valueIsPresentInStyle(HTMLElement*, StylePropertySet*) const; - virtual void addToStyle(Element*, EditingStyle*) const; - -protected: - HTMLElementEquivalent(CSSPropertyID); - HTMLElementEquivalent(CSSPropertyID, const HTMLQualifiedName& tagName); - HTMLElementEquivalent(CSSPropertyID, CSSValueID primitiveValue, const HTMLQualifiedName& tagName); - const CSSPropertyID m_propertyID; - const RefPtr m_primitiveValue; - const HTMLQualifiedName* m_tagName; // We can store a pointer because HTML tag names are const global. -}; - -DEFINE_EMPTY_DESTRUCTOR_WILL_BE_REMOVED(HTMLElementEquivalent); - -HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id) - : m_propertyID(id) - , m_tagName(0) -{ -} - -HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id, const HTMLQualifiedName& tagName) - : m_propertyID(id) - , m_tagName(&tagName) -{ -} - -HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id, CSSValueID primitiveValue, const HTMLQualifiedName& tagName) - : m_propertyID(id) - , m_primitiveValue(CSSPrimitiveValue::createIdentifier(primitiveValue)) - , m_tagName(&tagName) -{ - ASSERT(primitiveValue != CSSValueInvalid); -} - -bool HTMLElementEquivalent::valueIsPresentInStyle(HTMLElement* element, StylePropertySet* style) const -{ - RefPtr value = style->getPropertyCSSValue(m_propertyID); - return matches(element) && value && value->isPrimitiveValue() && toCSSPrimitiveValue(value.get())->getValueID() == m_primitiveValue->getValueID(); -} - -void HTMLElementEquivalent::addToStyle(Element*, EditingStyle* style) const -{ - style->setProperty(m_propertyID, m_primitiveValue->cssText()); -} - -class HTMLTextDecorationEquivalent final : public HTMLElementEquivalent { -public: - static PassOwnPtr create(CSSValueID primitiveValue, const HTMLQualifiedName& tagName) - { - return adoptPtr(new HTMLTextDecorationEquivalent(primitiveValue, tagName)); - } - virtual bool propertyExistsInStyle(const StylePropertySet*) const override; - virtual bool valueIsPresentInStyle(HTMLElement*, StylePropertySet*) const override; - -private: - HTMLTextDecorationEquivalent(CSSValueID primitiveValue, const HTMLQualifiedName& tagName); -}; - -HTMLTextDecorationEquivalent::HTMLTextDecorationEquivalent(CSSValueID primitiveValue, const HTMLQualifiedName& tagName) - : HTMLElementEquivalent(textDecorationPropertyForEditing(), primitiveValue, tagName) - // m_propertyID is used in HTMLElementEquivalent::addToStyle -{ -} - -bool HTMLTextDecorationEquivalent::propertyExistsInStyle(const StylePropertySet* style) const -{ - return style->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect) - || style->getPropertyCSSValue(textDecorationPropertyForEditing()); -} - -bool HTMLTextDecorationEquivalent::valueIsPresentInStyle(HTMLElement* element, StylePropertySet* style) const -{ - RefPtr styleValue = style->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect); - if (!styleValue) - styleValue = style->getPropertyCSSValue(textDecorationPropertyForEditing()); - return matches(element) && styleValue && styleValue->isValueList() && toCSSValueList(styleValue.get())->hasValue(m_primitiveValue.get()); -} - -class HTMLAttributeEquivalent : public HTMLElementEquivalent { -public: - static PassOwnPtr create(CSSPropertyID propertyID, const HTMLQualifiedName& tagName, const QualifiedName& attrName) - { - return adoptPtr(new HTMLAttributeEquivalent(propertyID, tagName, attrName)); - } - static PassOwnPtr create(CSSPropertyID propertyID, const QualifiedName& attrName) - { - return adoptPtr(new HTMLAttributeEquivalent(propertyID, attrName)); - } - - virtual bool matches(const Element* element) const override { return HTMLElementEquivalent::matches(element) && element->hasAttribute(m_attrName); } - virtual bool hasAttribute() const override { return true; } - virtual bool valueIsPresentInStyle(HTMLElement*, StylePropertySet*) const override; - virtual void addToStyle(Element*, EditingStyle*) const override; - virtual PassRefPtr attributeValueAsCSSValue(Element*) const; - inline const QualifiedName& attributeName() const { return m_attrName; } - -protected: - HTMLAttributeEquivalent(CSSPropertyID, const HTMLQualifiedName& tagName, const QualifiedName& attrName); - HTMLAttributeEquivalent(CSSPropertyID, const QualifiedName& attrName); - const QualifiedName& m_attrName; // We can store a reference because HTML attribute names are const global. -}; - -HTMLAttributeEquivalent::HTMLAttributeEquivalent(CSSPropertyID id, const HTMLQualifiedName& tagName, const QualifiedName& attrName) - : HTMLElementEquivalent(id, tagName) - , m_attrName(attrName) -{ -} - -HTMLAttributeEquivalent::HTMLAttributeEquivalent(CSSPropertyID id, const QualifiedName& attrName) - : HTMLElementEquivalent(id) - , m_attrName(attrName) -{ -} - -bool HTMLAttributeEquivalent::valueIsPresentInStyle(HTMLElement* element, StylePropertySet* style) const -{ - RefPtr value = attributeValueAsCSSValue(element); - RefPtr styleValue = style->getPropertyCSSValue(m_propertyID); - - return compareCSSValuePtr(value, styleValue); -} - -void HTMLAttributeEquivalent::addToStyle(Element* element, EditingStyle* style) const -{ - if (RefPtr value = attributeValueAsCSSValue(element)) - style->setProperty(m_propertyID, value->cssText()); -} - -PassRefPtr HTMLAttributeEquivalent::attributeValueAsCSSValue(Element* element) const -{ - ASSERT(element); - const AtomicString& value = element->getAttribute(m_attrName); - if (value.isNull()) - return nullptr; - - RefPtr dummyStyle = nullptr; - dummyStyle = MutableStylePropertySet::create(); - dummyStyle->setProperty(m_propertyID, value); - return dummyStyle->getPropertyCSSValue(m_propertyID); -} - -float EditingStyle::NoFontDelta = 0.0f; - -EditingStyle::EditingStyle() - : m_fixedPitchFontType(NonFixedPitchFont) - , m_fontSizeDelta(NoFontDelta) -{ -} - -EditingStyle::EditingStyle(ContainerNode* node, PropertiesToInclude propertiesToInclude) - : m_fixedPitchFontType(NonFixedPitchFont) - , m_fontSizeDelta(NoFontDelta) -{ - init(node, propertiesToInclude); -} - -EditingStyle::EditingStyle(const Position& position, PropertiesToInclude propertiesToInclude) - : m_fixedPitchFontType(NonFixedPitchFont) - , m_fontSizeDelta(NoFontDelta) -{ - init(position.deprecatedNode(), propertiesToInclude); -} - -EditingStyle::EditingStyle(const StylePropertySet* style) - : m_mutableStyle(style ? style->mutableCopy() : nullptr) - , m_fixedPitchFontType(NonFixedPitchFont) - , m_fontSizeDelta(NoFontDelta) -{ - extractFontSizeDelta(); -} - -EditingStyle::EditingStyle(CSSPropertyID propertyID, const String& value) - : m_mutableStyle(nullptr) - , m_fixedPitchFontType(NonFixedPitchFont) - , m_fontSizeDelta(NoFontDelta) -{ - setProperty(propertyID, value); -} - -EditingStyle::~EditingStyle() -{ -} - -void EditingStyle::init(Node* node, PropertiesToInclude propertiesToInclude) -{ - RefPtr computedStyleAtPosition = CSSComputedStyleDeclaration::create(node); - m_mutableStyle = propertiesToInclude == AllProperties && computedStyleAtPosition ? computedStyleAtPosition->copyProperties() : editingStyleFromComputedStyle(computedStyleAtPosition); - - if (propertiesToInclude == EditingPropertiesInEffect) { - if (RefPtr value = backgroundColorInEffect(node)) - m_mutableStyle->setProperty(CSSPropertyBackgroundColor, value->cssText()); - if (RefPtr value = computedStyleAtPosition->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect)) - m_mutableStyle->setProperty(CSSPropertyTextDecoration, value->cssText()); - } - - if (node && node->computedStyle()) { - RenderStyle* renderStyle = node->computedStyle(); - removeTextFillAndStrokeColorsIfNeeded(renderStyle); - replaceFontSizeByKeywordIfPossible(renderStyle, computedStyleAtPosition.get()); - } - - m_fixedPitchFontType = computedStyleAtPosition->fixedPitchFontType(); - extractFontSizeDelta(); -} - -void EditingStyle::removeTextFillAndStrokeColorsIfNeeded(RenderStyle* renderStyle) -{ - // If a node's text fill color is currentColor, then its children use - // their font-color as their text fill color (they don't - // inherit it). Likewise for stroke color. - if (renderStyle->textFillColor().isCurrentColor()) - m_mutableStyle->removeProperty(CSSPropertyWebkitTextFillColor); - if (renderStyle->textStrokeColor().isCurrentColor()) - m_mutableStyle->removeProperty(CSSPropertyWebkitTextStrokeColor); -} - -void EditingStyle::setProperty(CSSPropertyID propertyID, const String& value) -{ - if (!m_mutableStyle) - m_mutableStyle = MutableStylePropertySet::create(); - - m_mutableStyle->setProperty(propertyID, value); -} - -void EditingStyle::replaceFontSizeByKeywordIfPossible(RenderStyle* renderStyle, CSSComputedStyleDeclaration* computedStyle) -{ - ASSERT(renderStyle); - if (renderStyle->fontDescription().keywordSize()) - m_mutableStyle->setProperty(CSSPropertyFontSize, computedStyle->getFontSizeCSSValuePreferringKeyword()->cssText()); -} - -void EditingStyle::extractFontSizeDelta() -{ - if (!m_mutableStyle) - return; - - if (m_mutableStyle->getPropertyCSSValue(CSSPropertyFontSize)) { - // Explicit font size overrides any delta. - m_mutableStyle->removeProperty(CSSPropertyWebkitFontSizeDelta); - return; - } - - // Get the adjustment amount out of the style. - RefPtr value = m_mutableStyle->getPropertyCSSValue(CSSPropertyWebkitFontSizeDelta); - if (!value || !value->isPrimitiveValue()) - return; - - CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value.get()); - - // Only PX handled now. If we handle more types in the future, perhaps - // a switch statement here would be more appropriate. - if (!primitiveValue->isPx()) - return; - - m_fontSizeDelta = primitiveValue->getFloatValue(); - m_mutableStyle->removeProperty(CSSPropertyWebkitFontSizeDelta); -} - -bool EditingStyle::isEmpty() const -{ - return (!m_mutableStyle || m_mutableStyle->isEmpty()) && m_fontSizeDelta == NoFontDelta; -} - -void EditingStyle::clear() -{ - m_mutableStyle.clear(); - m_fixedPitchFontType = NonFixedPitchFont; - m_fontSizeDelta = NoFontDelta; -} - -PassRefPtr EditingStyle::copy() const -{ - RefPtr copy = EditingStyle::create(); - if (m_mutableStyle) - copy->m_mutableStyle = m_mutableStyle->mutableCopy(); - copy->m_fixedPitchFontType = m_fixedPitchFontType; - copy->m_fontSizeDelta = m_fontSizeDelta; - return copy; -} - -void EditingStyle::removeBlockProperties() -{ - if (!m_mutableStyle) - return; - - m_mutableStyle->removeBlockProperties(); -} - -static const Vector >& htmlElementEquivalents() -{ - DEFINE_STATIC_LOCAL(Vector >, HTMLElementEquivalents, ()); - return HTMLElementEquivalents; -} - -static const Vector >& htmlAttributeEquivalents() -{ - DEFINE_STATIC_LOCAL(Vector >, HTMLAttributeEquivalents, ()); - return HTMLAttributeEquivalents; -} - -bool EditingStyle::elementIsStyledSpanOrHTMLEquivalent(const Element* element) -{ - ASSERT(element); - bool elementIsSpanOrElementEquivalent = false; - const Vector >& HTMLElementEquivalents = htmlElementEquivalents(); - size_t i; - for (i = 0; i < HTMLElementEquivalents.size(); ++i) { - if (HTMLElementEquivalents[i]->matches(element)) { - elementIsSpanOrElementEquivalent = true; - break; - } - } - - AttributeCollection attributes = element->attributes(); - if (attributes.isEmpty()) - return elementIsSpanOrElementEquivalent; // span, b, etc... without any attributes - - unsigned matchedAttributes = 0; - const Vector >& HTMLAttributeEquivalents = htmlAttributeEquivalents(); - for (size_t i = 0; i < HTMLAttributeEquivalents.size(); ++i) { - if (HTMLAttributeEquivalents[i]->matches(element) && HTMLAttributeEquivalents[i]->attributeName() != HTMLNames::dirAttr) - matchedAttributes++; - } - - if (!elementIsSpanOrElementEquivalent && !matchedAttributes) - return false; // element is not a span, a html element equivalent, or font element. - - if (element->getAttribute(HTMLNames::classAttr) == AppleStyleSpanClass) - matchedAttributes++; - - if (element->hasAttribute(HTMLNames::styleAttr)) { - if (const StylePropertySet* style = element->inlineStyle()) { - unsigned propertyCount = style->propertyCount(); - for (unsigned i = 0; i < propertyCount; ++i) { - if (!isEditingProperty(style->propertyAt(i).id())) - return false; - } - } - matchedAttributes++; - } - - // font with color attribute, span with style attribute, etc... - ASSERT(matchedAttributes <= attributes.size()); - return matchedAttributes >= attributes.size(); -} - -void EditingStyle::mergeTypingStyle(Document* document) -{ - ASSERT(document); - - RefPtr typingStyle = document->frame()->selection().typingStyle(); - if (!typingStyle || typingStyle == this) - return; - - mergeStyle(typingStyle->style(), OverrideValues); -} - -static void mergeTextDecorationValues(CSSValueList* mergedValue, const CSSValueList* valueToMerge) -{ - DEFINE_STATIC_REF_WILL_BE_PERSISTENT(CSSPrimitiveValue, underline, (CSSPrimitiveValue::createIdentifier(CSSValueUnderline))); - DEFINE_STATIC_REF_WILL_BE_PERSISTENT(CSSPrimitiveValue, lineThrough, (CSSPrimitiveValue::createIdentifier(CSSValueLineThrough))); - if (valueToMerge->hasValue(underline) && !mergedValue->hasValue(underline)) - mergedValue->append(underline); - - if (valueToMerge->hasValue(lineThrough) && !mergedValue->hasValue(lineThrough)) - mergedValue->append(lineThrough); -} - -void EditingStyle::mergeStyle(const StylePropertySet* style, CSSPropertyOverrideMode mode) -{ - if (!style) - return; - - if (!m_mutableStyle) { - m_mutableStyle = style->mutableCopy(); - return; - } - - unsigned propertyCount = style->propertyCount(); - for (unsigned i = 0; i < propertyCount; ++i) { - StylePropertySet::PropertyReference property = style->propertyAt(i); - RefPtr value = m_mutableStyle->getPropertyCSSValue(property.id()); - - // text decorations never override values - if ((property.id() == textDecorationPropertyForEditing() || property.id() == CSSPropertyWebkitTextDecorationsInEffect) && property.value()->isValueList() && value) { - if (value->isValueList()) { - mergeTextDecorationValues(toCSSValueList(value.get()), toCSSValueList(property.value())); - continue; - } - value = nullptr; // text-decoration: none is equivalent to not having the property - } - - if (mode == OverrideValues || (mode == DoNotOverrideValues && !value)) - m_mutableStyle->setProperty(property.id(), property.value()->cssText()); - } -} - -bool isTransparentColorValue(CSSValue* cssValue) -{ - if (!cssValue) - return true; - if (!cssValue->isPrimitiveValue()) - return false; - CSSPrimitiveValue* value = toCSSPrimitiveValue(cssValue); - if (value->isRGBColor()) - return !alphaChannel(value->getRGBA32Value()); - return false; -} - -bool hasTransparentBackgroundColor(CSSStyleDeclaration* style) -{ - RefPtr cssValue = style->getPropertyCSSValueInternal(CSSPropertyBackgroundColor); - return isTransparentColorValue(cssValue.get()); -} - -PassRefPtr backgroundColorInEffect(Node* node) -{ - for (Node* ancestor = node; ancestor; ancestor = ancestor->parentNode()) { - RefPtr ancestorStyle = CSSComputedStyleDeclaration::create(ancestor); - if (!hasTransparentBackgroundColor(ancestorStyle.get())) - return ancestorStyle->getPropertyCSSValue(CSSPropertyBackgroundColor); - } - return nullptr; -} - -} diff --git a/sky/engine/core/editing/EditingStyle.h b/sky/engine/core/editing/EditingStyle.h deleted file mode 100644 index 7400724639d8c..0000000000000 --- a/sky/engine/core/editing/EditingStyle.h +++ /dev/null @@ -1,136 +0,0 @@ -/* - * 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 { -public: - - enum PropertiesToInclude { AllProperties, OnlyEditingInheritableProperties, EditingPropertiesInEffect }; - static float NoFontDelta; - - static PassRefPtr create() - { - return adoptRef(new EditingStyle()); - } - - static PassRefPtr create(ContainerNode* node, PropertiesToInclude propertiesToInclude = OnlyEditingInheritableProperties) - { - return adoptRef(new EditingStyle(node, propertiesToInclude)); - } - - static PassRefPtr create(const Position& position, PropertiesToInclude propertiesToInclude = OnlyEditingInheritableProperties) - { - return adoptRef(new EditingStyle(position, propertiesToInclude)); - } - - static PassRefPtr create(const StylePropertySet* style) - { - return adoptRef(new EditingStyle(style)); - } - - static PassRefPtr 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 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 m_mutableStyle; - FixedPitchFontType m_fixedPitchFontType; - float m_fontSizeDelta; - - friend class HTMLElementEquivalent; - friend class HTMLAttributeEquivalent; -}; - -} // namespace blink - -#endif // SKY_ENGINE_CORE_EDITING_EDITINGSTYLE_H_ diff --git a/sky/engine/core/editing/Editor.cpp b/sky/engine/core/editing/Editor.cpp deleted file mode 100644 index 9dee329095bdb..0000000000000 --- a/sky/engine/core/editing/Editor.cpp +++ /dev/null @@ -1,793 +0,0 @@ -/* - * Copyright (C) 2006, 2007, 2008, 2011 Apple Inc. All rights reserved. - * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) - * - * 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/Editor.h" - -#include "gen/sky/core/CSSPropertyNames.h" -#include "gen/sky/core/EventNames.h" -#include "gen/sky/core/EventTypeNames.h" -#include "sky/engine/bindings/exception_state_placeholder.h" -#include "sky/engine/core/css/CSSComputedStyleDeclaration.h" -#include "sky/engine/core/css/StylePropertySet.h" -#include "sky/engine/core/dom/DocumentFragment.h" -#include "sky/engine/core/dom/DocumentMarkerController.h" -#include "sky/engine/core/dom/NodeTraversal.h" -#include "sky/engine/core/dom/Text.h" -#include "sky/engine/core/editing/DeleteSelectionCommand.h" -#include "sky/engine/core/editing/InputMethodController.h" -#include "sky/engine/core/editing/RenderedPosition.h" -#include "sky/engine/core/editing/ReplaceSelectionCommand.h" -#include "sky/engine/core/editing/SpellChecker.h" -#include "sky/engine/core/editing/TypingCommand.h" -#include "sky/engine/core/editing/UndoStack.h" -#include "sky/engine/core/editing/VisibleUnits.h" -#include "sky/engine/core/editing/htmlediting.h" -#include "sky/engine/core/events/KeyboardEvent.h" -#include "sky/engine/core/events/TextEvent.h" -#include "sky/engine/core/frame/FrameView.h" -#include "sky/engine/core/frame/LocalFrame.h" -#include "sky/engine/core/frame/Settings.h" -#include "sky/engine/core/loader/EmptyClients.h" -#include "sky/engine/core/page/EditorClient.h" -#include "sky/engine/core/page/FocusController.h" -#include "sky/engine/core/page/Page.h" -#include "sky/engine/core/rendering/HitTestResult.h" -#include "sky/engine/platform/weborigin/KURL.h" -#include "sky/engine/wtf/unicode/CharacterNames.h" - -// This file is in tatters. It didn't survive the EventTarget removal at all well. -// TODO(ianh): It should be next to go. - -namespace blink { - -using namespace WTF::Unicode; - -Editor::RevealSelectionScope::RevealSelectionScope(Editor* editor) - : m_editor(editor) -{ - ++m_editor->m_preventRevealSelection; -} - -Editor::RevealSelectionScope::~RevealSelectionScope() -{ - ASSERT(m_editor->m_preventRevealSelection); - --m_editor->m_preventRevealSelection; - if (!m_editor->m_preventRevealSelection) - m_editor->m_frame.selection().revealSelection(ScrollAlignment::alignToEdgeIfNeeded, RevealExtent); -} - -// When an event handler has moved the selection outside of a text control -// we should use the target control's selection for this editing operation. -VisibleSelection Editor::selectionForCommand(Event* event) -{ - return m_frame.selection().selection(); -} - -// Function considers Mac editing behavior a fallback when Page or Settings is not available. -EditingBehavior Editor::behavior() const -{ - return EditingBehavior(); -} - -static EditorClient& emptyEditorClient() -{ - DEFINE_STATIC_LOCAL(EmptyEditorClient, client, ()); - return client; -} - -EditorClient& Editor::client() const -{ - if (Page* page = m_frame.page()) - return page->editorClient(); - return emptyEditorClient(); -} - -UndoStack* Editor::undoStack() const -{ - if (Page* page = m_frame.page()) - return &page->undoStack(); - return 0; -} - -bool Editor::handleTextEvent(TextEvent* event) -{ - // Default event handling for Drag and Drop will be handled by DragController - // so we leave the event for it. - if (event->isDrop()) - return false; - - if (event->isPaste()) { - if (event->pastingFragment()) - replaceSelectionWithFragment(event->pastingFragment(), false, event->shouldSmartReplace(), event->shouldMatchStyle()); - else - replaceSelectionWithText(event->data(), false, event->shouldSmartReplace()); - return true; - } - - String data = event->data(); - if (data == "\n") { - if (event->isLineBreak()) - return insertLineBreak(); - return insertParagraphSeparator(); - } - - return insertTextWithoutSendingTextEvent(data, false, event); -} - -bool Editor::canEdit() const -{ - return m_frame.selection().rootEditableElement(); -} - -bool Editor::canEditRichly() const -{ - return m_frame.selection().isContentRichlyEditable(); -} - -// WinIE uses onbeforecut and onbeforepaste to enables the cut and paste menu items. They -// also send onbeforecopy, apparently for symmetry, but it doesn't affect the menu items. -// We need to use onbeforecopy as a real menu enabler because we allow elements that are not -// normally selectable to implement copy/paste (like divs, or a document body). - -bool Editor::canDHTMLCut() -{ - return false; -} - -bool Editor::canDHTMLCopy() -{ - return false; -} - -bool Editor::canDHTMLPaste() -{ - return false; -} - -bool Editor::canCut() const -{ - return canCopy() && canDelete(); -} - -bool Editor::canCopy() const -{ - FrameSelection& selection = m_frame.selection(); - return selection.isRange() && !selection.isInPasswordField(); -} - -bool Editor::canPaste() const -{ - return canEdit(); -} - -bool Editor::canDelete() const -{ - FrameSelection& selection = m_frame.selection(); - return selection.isRange() && selection.rootEditableElement(); -} - -bool Editor::canDeleteRange(Range* range) const -{ - Node* startContainer = range->startContainer(); - Node* endContainer = range->endContainer(); - if (!startContainer || !endContainer) - return false; - - if (!startContainer->hasEditableStyle() || !endContainer->hasEditableStyle()) - return false; - - if (range->collapsed()) { - VisiblePosition start(range->startPosition(), DOWNSTREAM); - VisiblePosition previous = start.previous(); - // FIXME: We sometimes allow deletions at the start of editable roots, like when the caret is in an empty list item. - if (previous.isNull() || previous.deepEquivalent().deprecatedNode()->rootEditableElement() != startContainer->rootEditableElement()) - return false; - } - return true; -} - -bool Editor::smartInsertDeleteEnabled() const -{ - if (Settings* settings = m_frame.settings()) - return settings->smartInsertDeleteEnabled(); - return false; -} - -bool Editor::canSmartCopyOrDelete() const -{ - return smartInsertDeleteEnabled() && m_frame.selection().granularity() == WordGranularity; -} - -bool Editor::deleteWithDirection(SelectionDirection direction, TextGranularity granularity, bool isTypingAction) -{ - if (!canEdit()) - return false; - - if (m_frame.selection().isRange()) { - if (isTypingAction) { - ASSERT(m_frame.document()); - TypingCommand::deleteKeyPressed(*m_frame.document(), canSmartCopyOrDelete() ? TypingCommand::SmartDelete : 0, granularity); - revealSelectionAfterEditingOperation(); - } else { - deleteSelectionWithSmartDelete(canSmartCopyOrDelete()); - // Implicitly calls revealSelectionAfterEditingOperation(). - } - } else { - TypingCommand::Options options = 0; - if (canSmartCopyOrDelete()) - options |= TypingCommand::SmartDelete; - switch (direction) { - case DirectionForward: - case DirectionRight: - ASSERT(m_frame.document()); - TypingCommand::forwardDeleteKeyPressed(*m_frame.document(), options, granularity); - break; - case DirectionBackward: - case DirectionLeft: - ASSERT(m_frame.document()); - TypingCommand::deleteKeyPressed(*m_frame.document(), options, granularity); - break; - } - revealSelectionAfterEditingOperation(); - } - - return true; -} - -void Editor::deleteSelectionWithSmartDelete(bool smartDelete) -{ - if (m_frame.selection().isNone()) - return; - - ASSERT(m_frame.document()); - DeleteSelectionCommand::create(*m_frame.document(), smartDelete)->apply(); -} - -void Editor::pasteAsPlainText(const String& pastingText, bool smartReplace) -{ - ContainerNode* target = findEventTargetFromSelection(); - if (!target) - return; -} - -void Editor::pasteAsFragment(PassRefPtr pastingFragment, bool smartReplace, bool matchStyle) -{ - ContainerNode* target = findEventTargetFromSelection(); - if (!target) - return; -} - -bool Editor::tryDHTMLCopy() -{ - return false; -} - -bool Editor::tryDHTMLCut() -{ - return false; -} - -void Editor::replaceSelectionWithFragment(PassRefPtr fragment, bool selectReplacement, bool smartReplace, bool matchStyle) -{ - if (m_frame.selection().isNone() || !m_frame.selection().isContentEditable() || !fragment) - return; - - ReplaceSelectionCommand::CommandOptions options = ReplaceSelectionCommand::PreventNesting; - if (selectReplacement) - options |= ReplaceSelectionCommand::SelectReplacement; - if (smartReplace) - options |= ReplaceSelectionCommand::SmartReplace; - if (matchStyle) - options |= ReplaceSelectionCommand::MatchStyle; - ASSERT(m_frame.document()); - ReplaceSelectionCommand::create(*m_frame.document(), fragment, options, EditActionPaste)->apply(); - revealSelectionAfterEditingOperation(); - - if (m_frame.selection().isInPasswordField() || !spellChecker().isContinuousSpellCheckingEnabled()) - return; - spellChecker().chunkAndMarkAllMisspellingsAndBadGrammar(m_frame.selection().rootEditableElement()); -} - -void Editor::replaceSelectionWithText(const String& text, bool selectReplacement, bool smartReplace) -{ - replaceSelectionWithFragment(nullptr, selectReplacement, smartReplace, true); -} - -PassRefPtr Editor::selectedRange() -{ - return m_frame.selection().toNormalizedRange(); -} - -bool Editor::shouldDeleteRange(Range* range) const -{ - if (!range || range->collapsed()) - return false; - - return canDeleteRange(range); -} - -void Editor::notifyComponentsOnChangedSelection(const VisibleSelection& oldSelection, FrameSelection::SetSelectionOptions options) -{ - client().respondToChangedSelection(&m_frame, m_frame.selection().selectionType()); -} - -void Editor::respondToChangedContents(const VisibleSelection& endingSelection) -{ - spellChecker().updateMarkersForWordsAffectedByEditing(true); - client().respondToChangedContents(); -} - -void Editor::clearLastEditCommand() -{ - m_lastEditCommand.clear(); -} - -ContainerNode* Editor::findEventTargetFrom(const VisibleSelection& selection) const -{ - if (Element* target = selection.start().element()) - return target; - return m_frame.document(); -} - -ContainerNode* Editor::findEventTargetFromSelection() const -{ - return findEventTargetFrom(m_frame.selection().selection()); -} - -void Editor::appliedEditing(PassRefPtr cmd) -{ - m_frame.document()->updateLayout(); - - VisibleSelection newSelection(cmd->endingSelection()); - - // Don't clear the typing style with this selection change. We do those things elsewhere if necessary. - changeSelectionAfterCommand(newSelection, 0); - - if (!cmd->preservesTypingStyle()) - m_frame.selection().clearTypingStyle(); - - // Command will be equal to last edit command only in the case of typing - if (m_lastEditCommand.get() == cmd) { - ASSERT(cmd->isTypingCommand()); - } else { - // Only register a new undo command if the command passed in is - // different from the last command - m_lastEditCommand = cmd; - if (UndoStack* undoStack = this->undoStack()) - undoStack->registerUndoStep(m_lastEditCommand->ensureComposition()); - } - - respondToChangedContents(newSelection); -} - -void Editor::unappliedEditing(PassRefPtr cmd) -{ - m_frame.document()->updateLayout(); - - VisibleSelection newSelection(cmd->startingSelection()); - newSelection.validatePositionsIfNeeded(); - if (newSelection.start().document() == m_frame.document() && newSelection.end().document() == m_frame.document()) - changeSelectionAfterCommand(newSelection, FrameSelection::CloseTyping | FrameSelection::ClearTypingStyle); - - m_lastEditCommand = nullptr; - if (UndoStack* undoStack = this->undoStack()) - undoStack->registerRedoStep(cmd); - respondToChangedContents(newSelection); -} - -void Editor::reappliedEditing(PassRefPtr cmd) -{ - m_frame.document()->updateLayout(); - - VisibleSelection newSelection(cmd->endingSelection()); - changeSelectionAfterCommand(newSelection, FrameSelection::CloseTyping | FrameSelection::ClearTypingStyle); - - m_lastEditCommand = nullptr; - if (UndoStack* undoStack = this->undoStack()) - undoStack->registerUndoStep(cmd); - respondToChangedContents(newSelection); -} - -PassOwnPtr Editor::create(LocalFrame& frame) -{ - return adoptPtr(new Editor(frame)); -} - -Editor::Editor(LocalFrame& frame) - : m_frame(frame) - , m_preventRevealSelection(0) - // This is off by default, since most editors want this behavior (this matches IE but not FF). - , m_shouldStyleWithCSS(false) - , m_areMarkedTextMatchesHighlighted(false) - , m_defaultParagraphSeparator(EditorParagraphSeparatorIsDiv) - , m_overwriteModeEnabled(false) -{ -} - -Editor::~Editor() -{ -} - -void Editor::clear() -{ - m_frame.inputMethodController().clear(); - m_shouldStyleWithCSS = false; - m_defaultParagraphSeparator = EditorParagraphSeparatorIsDiv; -} - -bool Editor::insertText(const String& text, KeyboardEvent* triggeringEvent) -{ - return false; -} - -bool Editor::insertTextWithoutSendingTextEvent(const String& text, bool selectInsertedText, TextEvent* triggeringEvent) -{ - if (text.isEmpty()) - return false; - - VisibleSelection selection = selectionForCommand(triggeringEvent); - if (!selection.isContentEditable()) - return false; - - spellChecker().updateMarkersForWordsAffectedByEditing(isSpaceOrNewline(text[0])); - - // Get the selection to use for the event that triggered this insertText. - // If the event handler changed the selection, we may want to use a different selection - // that is contained in the event target. - selection = selectionForCommand(triggeringEvent); - if (selection.isContentEditable()) { - if (Node* selectionStart = selection.start().deprecatedNode()) { - RefPtr document(selectionStart->document()); - - // Insert the text - TypingCommand::Options options = 0; - if (selectInsertedText) - options |= TypingCommand::SelectInsertedText; - TypingCommand::insertText(*document.get(), text, selection, options, triggeringEvent && triggeringEvent->isComposition() ? TypingCommand::TextCompositionConfirm : TypingCommand::TextCompositionNone); - - // Reveal the current selection - if (LocalFrame* editedFrame = document->frame()) { - if (Page* page = editedFrame->page()) - page->focusController().focusedOrMainFrame()->selection().revealSelection(ScrollAlignment::alignCenterIfNeeded); - } - } - } - - return true; -} - -bool Editor::insertLineBreak() -{ - if (!canEdit()) - return false; - - VisiblePosition caret = m_frame.selection().selection().visibleStart(); - bool alignToEdge = isEndOfEditableOrNonEditableContent(caret); - ASSERT(m_frame.document()); - TypingCommand::insertLineBreak(*m_frame.document(), 0); - revealSelectionAfterEditingOperation(alignToEdge ? ScrollAlignment::alignToEdgeIfNeeded : ScrollAlignment::alignCenterIfNeeded); - - return true; -} - -bool Editor::insertParagraphSeparator() -{ - if (!canEdit()) - return false; - - if (!canEditRichly()) - return insertLineBreak(); - - VisiblePosition caret = m_frame.selection().selection().visibleStart(); - bool alignToEdge = isEndOfEditableOrNonEditableContent(caret); - ASSERT(m_frame.document()); - TypingCommand::insertParagraphSeparator(*m_frame.document(), 0); - revealSelectionAfterEditingOperation(alignToEdge ? ScrollAlignment::alignToEdgeIfNeeded : ScrollAlignment::alignCenterIfNeeded); - - return true; -} - -void Editor::cut() -{ -} - -void Editor::copy() -{ -} - -void Editor::paste() -{ -} - -void Editor::pasteAsPlainText() -{ -} - -void Editor::performDelete() -{ - if (!canDelete()) - return; - deleteSelectionWithSmartDelete(canSmartCopyOrDelete()); -} - -void Editor::copyImage(const HitTestResult& result) -{ -} - -bool Editor::canUndo() -{ - if (UndoStack* undoStack = this->undoStack()) - return undoStack->canUndo(); - return false; -} - -void Editor::undo() -{ - if (UndoStack* undoStack = this->undoStack()) - undoStack->undo(); -} - -bool Editor::canRedo() -{ - if (UndoStack* undoStack = this->undoStack()) - return undoStack->canRedo(); - return false; -} - -void Editor::redo() -{ - if (UndoStack* undoStack = this->undoStack()) - undoStack->redo(); -} - -void Editor::setBaseWritingDirection(WritingDirection direction) -{ - RefPtr style = MutableStylePropertySet::create(); - style->setProperty(CSSPropertyDirection, direction == LeftToRightWritingDirection ? "ltr" : direction == RightToLeftWritingDirection ? "rtl" : "inherit"); -} - -void Editor::revealSelectionAfterEditingOperation(const ScrollAlignment& alignment, RevealExtentOption revealExtentOption) -{ - if (m_preventRevealSelection) - return; - - m_frame.selection().revealSelection(alignment, revealExtentOption); -} - -void Editor::transpose() -{ - if (!canEdit()) - return; - - VisibleSelection selection = m_frame.selection().selection(); - if (!selection.isCaret()) - return; - - // Make a selection that goes back one character and forward two characters. - VisiblePosition caret = selection.visibleStart(); - VisiblePosition next = isEndOfParagraph(caret) ? caret : caret.next(); - VisiblePosition previous = next.previous(); - if (next == previous) - return; - previous = previous.previous(); - if (!inSameParagraph(next, previous)) - return; - RefPtr range = makeRange(previous, next); - if (!range) - return; - VisibleSelection newSelection(range.get(), DOWNSTREAM); - - // Transpose the two characters. - String text = plainText(range.get()); - if (text.length() != 2) - return; - String transposed = text.right(1) + text.left(1); - - // Select the two characters. - if (newSelection != m_frame.selection().selection()) - m_frame.selection().setSelection(newSelection); - - // Insert the transposed characters. - replaceSelectionWithText(transposed, false, false); -} - -void Editor::changeSelectionAfterCommand(const VisibleSelection& newSelection, FrameSelection::SetSelectionOptions options) -{ - // If the new selection is orphaned, then don't update the selection. - if (newSelection.start().isOrphan() || newSelection.end().isOrphan()) - return; - - // See Some shouldChangeSelectedDOMRange contain Ranges for selections that are no longer valid - bool selectionDidNotChangeDOMPosition = newSelection == m_frame.selection().selection(); - m_frame.selection().setSelection(newSelection, options); - - // Some editing operations change the selection visually without affecting its position within the DOM. - // For example when you press return in the following (the caret is marked by ^): - //
^Hello
- // WebCore inserts

*before* the current block, which correctly moves the paragraph down but which doesn't - // change the caret's DOM position (["hello", 0]). In these situations the above FrameSelection::setSelection call - // does not call EditorClient::respondToChangedSelection(), which, on the Mac, sends selection change notifications and - // starts a new kill ring sequence, but we want to do these things (matches AppKit). - if (selectionDidNotChangeDOMPosition) - client().respondToChangedSelection(&m_frame, m_frame.selection().selectionType()); -} - -IntRect Editor::firstRectForRange(Range* range) const -{ - LayoutUnit extraWidthToEndOfLine = 0; - ASSERT(range->startContainer()); - ASSERT(range->endContainer()); - - IntRect startCaretRect = RenderedPosition(VisiblePosition(range->startPosition()).deepEquivalent(), DOWNSTREAM).absoluteRect(&extraWidthToEndOfLine); - if (startCaretRect == LayoutRect()) - return IntRect(); - - IntRect endCaretRect = RenderedPosition(VisiblePosition(range->endPosition()).deepEquivalent(), UPSTREAM).absoluteRect(); - if (endCaretRect == LayoutRect()) - return IntRect(); - - if (startCaretRect.y() == endCaretRect.y()) { - // start and end are on the same line - return IntRect(std::min(startCaretRect.x(), endCaretRect.x()), - startCaretRect.y(), - abs(endCaretRect.x() - startCaretRect.x()), - std::max(startCaretRect.height(), endCaretRect.height())); - } - - // start and end aren't on the same line, so go from start to the end of its line - return IntRect(startCaretRect.x(), - startCaretRect.y(), - startCaretRect.width() + extraWidthToEndOfLine, - startCaretRect.height()); -} - -bool Editor::findString(const String& target, bool forward, bool caseFlag, bool wrapFlag, bool startInSelection) -{ - FindOptions options = (forward ? 0 : Backwards) | (caseFlag ? 0 : CaseInsensitive) | (wrapFlag ? WrapAround : 0) | (startInSelection ? StartInSelection : 0); - return findString(target, options); -} - -bool Editor::findString(const String& target, FindOptions options) -{ - VisibleSelection selection = m_frame.selection().selection(); - - RefPtr resultRange = rangeOfString(target, selection.firstRange().get(), options); - - if (!resultRange) - return false; - - m_frame.selection().setSelection(VisibleSelection(resultRange.get(), DOWNSTREAM)); - m_frame.selection().revealSelection(); - return true; -} - -PassRefPtr Editor::findStringAndScrollToVisible(const String& target, Range* previousMatch, FindOptions options) -{ - RefPtr nextMatch = rangeOfString(target, previousMatch, options); - if (!nextMatch) - return nullptr; - - // FIXME(sky): Scroll to visible. - - return nextMatch.release(); -} - -static PassRefPtr findStringBetweenPositions(const String& target, const Position& start, const Position& end, FindOptions options) -{ - Position searchStart(start); - Position searchEnd(end); - - bool forward = !(options & Backwards); - - while (true) { - Position resultStart; - Position resultEnd; - findPlainText(searchStart, searchEnd, target, options, resultStart, resultEnd); - if (resultStart == resultEnd) - return nullptr; - - RefPtr resultRange = Range::create(*resultStart.document(), resultStart, resultEnd); - if (!resultRange->collapsed()) - return resultRange.release(); - - // Found text spans over multiple TreeScopes. Since it's impossible to return such section as a Range, - // we skip this match and seek for the next occurrence. - // FIXME: Handle this case. - if (forward) - searchStart = resultStart.next(); - else - searchEnd = resultEnd.previous(); - } - - ASSERT_NOT_REACHED(); - return nullptr; -} - -PassRefPtr Editor::rangeOfString(const String& target, Range* referenceRange, FindOptions options) -{ - if (target.isEmpty()) - return nullptr; - - // Start from an edge of the reference range. Which edge is used depends on whether we're searching forward or - // backward, and whether startInSelection is set. - Position searchStart = firstPositionInNode(m_frame.document()); - Position searchEnd = lastPositionInNode(m_frame.document()); - - bool forward = !(options & Backwards); - bool startInReferenceRange = referenceRange && (options & StartInSelection); - if (referenceRange) { - if (forward) - searchStart = startInReferenceRange ? referenceRange->startPosition() : referenceRange->endPosition(); - else - searchEnd = startInReferenceRange ? referenceRange->endPosition() : referenceRange->startPosition(); - } - - RefPtr resultRange = findStringBetweenPositions(target, searchStart, searchEnd, options); - - // If we started in the reference range and the found range exactly matches the reference range, find again. - // Build a selection with the found range to remove collapsed whitespace. - // Compare ranges instead of selection objects to ignore the way that the current selection was made. - if (resultRange && startInReferenceRange && areRangesEqual(VisibleSelection(resultRange.get()).toNormalizedRange().get(), referenceRange)) { - if (forward) - searchStart = resultRange->endPosition(); - else - searchEnd = resultRange->startPosition(); - resultRange = findStringBetweenPositions(target, searchStart, searchEnd, options); - } - - if (!resultRange && options & WrapAround) { - searchStart = firstPositionInNode(m_frame.document()); - searchEnd = lastPositionInNode(m_frame.document()); - resultRange = findStringBetweenPositions(target, searchStart, searchEnd, options); - } - - return resultRange.release(); -} - -void Editor::setMarkedTextMatchesAreHighlighted(bool flag) -{ - m_areMarkedTextMatchesHighlighted = flag; -} - -void Editor::respondToChangedSelection(const VisibleSelection& oldSelection, FrameSelection::SetSelectionOptions options) -{ - spellChecker().respondToChangedSelection(oldSelection, options); - m_frame.inputMethodController().cancelCompositionIfSelectionIsInvalid(); - notifyComponentsOnChangedSelection(oldSelection, options); -} - -SpellChecker& Editor::spellChecker() const -{ - return m_frame.spellChecker(); -} - -void Editor::toggleOverwriteModeEnabled() -{ - m_overwriteModeEnabled = !m_overwriteModeEnabled; - frame().selection().setShouldShowBlockCursor(m_overwriteModeEnabled); -} - -} // namespace blink diff --git a/sky/engine/core/editing/Editor.h b/sky/engine/core/editing/Editor.h deleted file mode 100644 index 0aedd6c70c8ec..0000000000000 --- a/sky/engine/core/editing/Editor.h +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright (C) 2006, 2007, 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_EDITOR_H_ -#define SKY_ENGINE_CORE_EDITING_EDITOR_H_ - -#include "sky/engine/core/dom/DocumentMarker.h" -#include "sky/engine/core/editing/EditAction.h" -#include "sky/engine/core/editing/EditingBehavior.h" -#include "sky/engine/core/editing/FindOptions.h" -#include "sky/engine/core/editing/FrameSelection.h" -#include "sky/engine/core/editing/TextIterator.h" -#include "sky/engine/core/editing/VisibleSelection.h" -#include "sky/engine/core/editing/WritingDirection.h" -#include "sky/engine/core/frame/FrameDestructionObserver.h" -#include "sky/engine/platform/heap/Handle.h" - -namespace blink { - -class CompositeEditCommand; -class EditCommand; -class EditCommandComposition; -class EditorClient; -class EditorInternalCommand; -class LocalFrame; -class HTMLElement; -class HitTestResult; -class SharedBuffer; -class SimpleFontData; -class SpellChecker; -class StylePropertySet; -class Text; -class TextEvent; -class UndoStack; - -enum EditorCommandSource { CommandFromMenuOrKeyBinding, CommandFromDOM, CommandFromDOMWithUserInterface }; -enum EditorParagraphSeparator { EditorParagraphSeparatorIsDiv, EditorParagraphSeparatorIsP }; - -class Editor final { - WTF_MAKE_NONCOPYABLE(Editor); -public: - static PassOwnPtr create(LocalFrame&); - ~Editor(); - - EditorClient& client() const; - - LocalFrame& frame() const { return m_frame; } - - CompositeEditCommand* lastEditCommand() { return m_lastEditCommand.get(); } - - void handleKeyboardEvent(KeyboardEvent*); - bool handleTextEvent(TextEvent*); - - bool canEdit() const; - bool canEditRichly() const; - - bool canDHTMLCut(); - bool canDHTMLCopy(); - bool canDHTMLPaste(); - - bool canCut() const; - bool canCopy() const; - bool canPaste() const; - bool canDelete() const; - bool canSmartCopyOrDelete() const; - - void cut(); - void copy(); - void paste(); - void pasteAsPlainText(); - void performDelete(); - - void copyImage(const HitTestResult&); - - void transpose(); - - bool shouldDeleteRange(Range*) const; - - void respondToChangedContents(const VisibleSelection& endingSelection); - - void clearLastEditCommand(); - - bool deleteWithDirection(SelectionDirection, TextGranularity, bool isTypingAction); - void deleteSelectionWithSmartDelete(bool smartDelete); - - void appliedEditing(PassRefPtr); - void unappliedEditing(PassRefPtr); - void reappliedEditing(PassRefPtr); - - void setShouldStyleWithCSS(bool flag) { m_shouldStyleWithCSS = flag; } - bool shouldStyleWithCSS() const { return m_shouldStyleWithCSS; } - - class Command { - public: - Command(); - Command(const EditorInternalCommand*, EditorCommandSource, PassRefPtr); - - bool execute(const String& parameter = String(), Event* triggeringEvent = 0) const; - bool execute(Event* triggeringEvent) const; - - bool isSupported() const; - bool isEnabled(Event* triggeringEvent = 0) const; - - TriState state(Event* triggeringEvent = 0) const; - String value(Event* triggeringEvent = 0) const; - - bool isTextInsertion() const; - - // Returns 0 if this Command is not supported. - int idForHistogram() const; - private: - const EditorInternalCommand* m_command; - EditorCommandSource m_source; - RefPtr m_frame; - }; - Command command(const String& commandName); // Command source is CommandFromMenuOrKeyBinding. - Command command(const String& commandName, EditorCommandSource); - - // |Editor::executeCommand| is implementation of |WebFrame::executeCommand| - // rather than |Document::execCommand|. - bool executeCommand(const String&); - bool executeCommand(const String& commandName, const String& value); - - bool insertText(const String&, KeyboardEvent* triggeringEvent); - bool insertTextWithoutSendingTextEvent(const String&, bool selectInsertedText, TextEvent* triggeringEvent); - bool insertLineBreak(); - bool insertParagraphSeparator(); - - bool isOverwriteModeEnabled() const { return m_overwriteModeEnabled; } - void toggleOverwriteModeEnabled(); - - bool canUndo(); - void undo(); - bool canRedo(); - void redo(); - - void setBaseWritingDirection(WritingDirection); - - // smartInsertDeleteEnabled and selectTrailingWhitespaceEnabled are - // mutually exclusive, meaning that enabling one will disable the other. - bool smartInsertDeleteEnabled() const; - - bool preventRevealSelection() const { return m_preventRevealSelection; } - - void clear(); - - VisibleSelection selectionForCommand(Event*); - - EditingBehavior behavior() const; - - PassRefPtr selectedRange(); - - void pasteAsFragment(PassRefPtr, bool smartReplace, bool matchStyle); - void pasteAsPlainText(const String&, bool smartReplace); - - ContainerNode* findEventTargetFrom(const VisibleSelection&) const; - - bool findString(const String&, FindOptions); - // FIXME: Switch callers over to the FindOptions version and retire this one. - bool findString(const String&, bool forward, bool caseFlag, bool wrapFlag, bool startInSelection); - - PassRefPtr findStringAndScrollToVisible(const String&, Range*, FindOptions); - - const VisibleSelection& mark() const; // Mark, to be used as emacs uses it. - void setMark(const VisibleSelection&); - - void computeAndSetTypingStyle(StylePropertySet* , EditAction = EditActionUnspecified); - - IntRect firstRectForRange(Range*) const; - - void respondToChangedSelection(const VisibleSelection& oldSelection, FrameSelection::SetSelectionOptions); - - bool markedTextMatchesAreHighlighted() const; - void setMarkedTextMatchesAreHighlighted(bool); - - void replaceSelectionWithFragment(PassRefPtr, bool selectReplacement, bool smartReplace, bool matchStyle); - void replaceSelectionWithText(const String&, bool selectReplacement, bool smartReplace); - - EditorParagraphSeparator defaultParagraphSeparator() const { return m_defaultParagraphSeparator; } - void setDefaultParagraphSeparator(EditorParagraphSeparator separator) { m_defaultParagraphSeparator = separator; } - - class RevealSelectionScope { - WTF_MAKE_NONCOPYABLE(RevealSelectionScope); - public: - RevealSelectionScope(Editor*); - ~RevealSelectionScope(); - private: - Editor* m_editor; - }; - friend class RevealSelectionScope; - -private: - LocalFrame& m_frame; - RefPtr m_lastEditCommand; - int m_preventRevealSelection; - bool m_shouldStyleWithCSS; - VisibleSelection m_mark; - bool m_areMarkedTextMatchesHighlighted; - EditorParagraphSeparator m_defaultParagraphSeparator; - bool m_overwriteModeEnabled; - - explicit Editor(LocalFrame&); - - bool canDeleteRange(Range*) const; - - UndoStack* undoStack() const; - - bool tryDHTMLCopy(); - bool tryDHTMLCut(); - - void revealSelectionAfterEditingOperation(const ScrollAlignment& = ScrollAlignment::alignCenterIfNeeded, RevealExtentOption = DoNotRevealExtent); - void changeSelectionAfterCommand(const VisibleSelection& newSelection, FrameSelection::SetSelectionOptions); - void notifyComponentsOnChangedSelection(const VisibleSelection& oldSelection, FrameSelection::SetSelectionOptions); - - ContainerNode* findEventTargetFromSelection() const; - - PassRefPtr rangeOfString(const String&, Range*, FindOptions); - - SpellChecker& spellChecker() const; - - bool handleEditingKeyboardEvent(blink::KeyboardEvent*); -}; - -inline const VisibleSelection& Editor::mark() const -{ - return m_mark; -} - -inline void Editor::setMark(const VisibleSelection& selection) -{ - m_mark = selection; -} - -inline bool Editor::markedTextMatchesAreHighlighted() const -{ - return m_areMarkedTextMatchesHighlighted; -} - - -} // namespace blink - -#endif // SKY_ENGINE_CORE_EDITING_EDITOR_H_ diff --git a/sky/engine/core/editing/EditorCommand.cpp b/sky/engine/core/editing/EditorCommand.cpp deleted file mode 100644 index b20ce0c13c3cc..0000000000000 --- a/sky/engine/core/editing/EditorCommand.cpp +++ /dev/null @@ -1,632 +0,0 @@ -/* - * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. - * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) - * Copyright (C) 2009 Igalia S.L. - * - * 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/Editor.h" - -#include "gen/sky/core/CSSPropertyNames.h" -#include "gen/sky/core/CSSValueKeywords.h" -#include "sky/engine/bindings/exception_state.h" -#include "sky/engine/bindings/exception_state_placeholder.h" -#include "sky/engine/core/css/CSSValueList.h" -#include "sky/engine/core/css/StylePropertySet.h" -#include "sky/engine/core/dom/DocumentFragment.h" -#include "sky/engine/core/editing/FrameSelection.h" -#include "sky/engine/core/editing/ReplaceSelectionCommand.h" -#include "sky/engine/core/editing/SpellChecker.h" -#include "sky/engine/core/editing/TypingCommand.h" -#include "sky/engine/core/editing/htmlediting.h" -#include "sky/engine/core/events/Event.h" -#include "sky/engine/core/frame/FrameHost.h" -#include "sky/engine/core/frame/FrameView.h" -#include "sky/engine/core/frame/LocalFrame.h" -#include "sky/engine/core/frame/Settings.h" -#include "sky/engine/core/page/EditorClient.h" -#include "sky/engine/core/rendering/RenderBox.h" -#include "sky/engine/public/platform/Platform.h" -#include "sky/engine/wtf/text/AtomicString.h" - -namespace blink { - -class EditorInternalCommand { -public: - int idForUserMetrics; - bool (*execute)(LocalFrame&, Event*, EditorCommandSource, const String&); - bool (*isSupportedFromDOM)(LocalFrame*); - bool (*isEnabled)(LocalFrame&, Event*, EditorCommandSource); - TriState (*state)(LocalFrame&, Event*); - String (*value)(LocalFrame&, Event*); - bool isTextInsertion; - bool allowExecutionWhenDisabled; -}; - -typedef HashMap CommandMap; - -static const bool notTextInsertion = false; -static const bool isTextInsertion = true; - -static const bool allowExecutionWhenDisabled = true; -static const bool doNotAllowExecutionWhenDisabled = false; - -static const float kMinFractionToStepWhenPaging = 0.875f; - -static unsigned verticalScrollDistance(LocalFrame& frame) -{ - Element* focusedElement = frame.document()->focusedElement(); - if (!focusedElement) - return 0; - RenderObject* renderer = focusedElement->renderer(); - if (!renderer || !renderer->isBox()) - return 0; - RenderBox& renderBox = toRenderBox(*renderer); - RenderStyle* style = renderBox.style(); - if (!style) - return 0; - if (!focusedElement->hasEditableStyle()) - return 0; - int height = std::min(renderBox.clientHeight(), - frame.view()->height()); - return static_cast(max(height * kMinFractionToStepWhenPaging, 1)); -} - -static bool executeCopy(LocalFrame& frame, Event*, EditorCommandSource, const String&) -{ - frame.editor().copy(); - return true; -} - -static bool executeCut(LocalFrame& frame, Event*, EditorCommandSource, const String&) -{ - frame.editor().cut(); - return true; -} - -static bool executeDeleteBackward(LocalFrame& frame, Event*, EditorCommandSource, const String&) -{ - frame.editor().deleteWithDirection(DirectionBackward, CharacterGranularity, true); - return true; -} - -static bool executeDeleteForward(LocalFrame& frame, Event*, EditorCommandSource, const String&) -{ - frame.editor().deleteWithDirection(DirectionForward, CharacterGranularity, true); - return true; -} - -static bool executeDeleteWordBackward(LocalFrame& frame, Event*, EditorCommandSource, const String&) -{ - frame.editor().deleteWithDirection(DirectionBackward, WordGranularity, false); - return true; -} - -static bool executeDeleteWordForward(LocalFrame& frame, Event*, EditorCommandSource, const String&) -{ - frame.editor().deleteWithDirection(DirectionForward, WordGranularity, false); - return true; -} - -static bool executeInsertNewline(LocalFrame& frame, Event* event, EditorCommandSource, const String&) -{ - return false; -} - -static bool executeMoveDown(LocalFrame& frame, Event*, EditorCommandSource, const String&) -{ - return frame.selection().modify(FrameSelection::AlterationMove, DirectionForward, LineGranularity, UserTriggered); -} - -static bool executeMoveDownAndModifySelection(LocalFrame& frame, Event*, EditorCommandSource, const String&) -{ - frame.selection().modify(FrameSelection::AlterationExtend, DirectionForward, LineGranularity, UserTriggered); - return true; -} - -static bool executeMoveLeft(LocalFrame& frame, Event*, EditorCommandSource, const String&) -{ - return frame.selection().modify(FrameSelection::AlterationMove, DirectionLeft, CharacterGranularity, UserTriggered); -} - -static bool executeMoveLeftAndModifySelection(LocalFrame& frame, Event*, EditorCommandSource, const String&) -{ - frame.selection().modify(FrameSelection::AlterationExtend, DirectionLeft, CharacterGranularity, UserTriggered); - return true; -} - -static bool executeMovePageDown(LocalFrame& frame, Event*, EditorCommandSource, const String&) -{ - unsigned distance = verticalScrollDistance(frame); - if (!distance) - return false; - return frame.selection().modify(FrameSelection::AlterationMove, distance, FrameSelection::DirectionDown, - UserTriggered, FrameSelection::AlignCursorOnScrollAlways); -} - -static bool executeMovePageDownAndModifySelection(LocalFrame& frame, Event*, EditorCommandSource, const String&) -{ - unsigned distance = verticalScrollDistance(frame); - if (!distance) - return false; - return frame.selection().modify(FrameSelection::AlterationExtend, distance, FrameSelection::DirectionDown, - UserTriggered, FrameSelection::AlignCursorOnScrollAlways); -} - -static bool executeMovePageUp(LocalFrame& frame, Event*, EditorCommandSource, const String&) -{ - unsigned distance = verticalScrollDistance(frame); - if (!distance) - return false; - return frame.selection().modify(FrameSelection::AlterationMove, distance, FrameSelection::DirectionUp, - UserTriggered, FrameSelection::AlignCursorOnScrollAlways); -} - -static bool executeMovePageUpAndModifySelection(LocalFrame& frame, Event*, EditorCommandSource, const String&) -{ - unsigned distance = verticalScrollDistance(frame); - if (!distance) - return false; - return frame.selection().modify(FrameSelection::AlterationExtend, distance, FrameSelection::DirectionUp, - UserTriggered, FrameSelection::AlignCursorOnScrollAlways); -} - -static bool executeMoveRight(LocalFrame& frame, Event*, EditorCommandSource, const String&) -{ - return frame.selection().modify(FrameSelection::AlterationMove, DirectionRight, CharacterGranularity, UserTriggered); -} - -static bool executeMoveRightAndModifySelection(LocalFrame& frame, Event*, EditorCommandSource, const String&) -{ - frame.selection().modify(FrameSelection::AlterationExtend, DirectionRight, CharacterGranularity, UserTriggered); - return true; -} - -static bool executeMoveToBeginningOfDocument(LocalFrame& frame, Event*, EditorCommandSource, const String&) -{ - frame.selection().modify(FrameSelection::AlterationMove, DirectionBackward, DocumentBoundary, UserTriggered); - return true; -} - -static bool executeMoveToBeginningOfDocumentAndModifySelection(LocalFrame& frame, Event*, EditorCommandSource, const String&) -{ - frame.selection().modify(FrameSelection::AlterationExtend, DirectionBackward, DocumentBoundary, UserTriggered); - return true; -} - -static bool executeMoveToBeginningOfLine(LocalFrame& frame, Event*, EditorCommandSource, const String&) -{ - frame.selection().modify(FrameSelection::AlterationMove, DirectionBackward, LineBoundary, UserTriggered); - return true; -} - -static bool executeMoveToBeginningOfLineAndModifySelection(LocalFrame& frame, Event*, EditorCommandSource, const String&) -{ - frame.selection().modify(FrameSelection::AlterationExtend, DirectionBackward, LineBoundary, UserTriggered); - return true; -} - -static bool executeMoveToEndOfDocument(LocalFrame& frame, Event*, EditorCommandSource, const String&) -{ - frame.selection().modify(FrameSelection::AlterationMove, DirectionForward, DocumentBoundary, UserTriggered); - return true; -} - -static bool executeMoveToEndOfDocumentAndModifySelection(LocalFrame& frame, Event*, EditorCommandSource, const String&) -{ - frame.selection().modify(FrameSelection::AlterationExtend, DirectionForward, DocumentBoundary, UserTriggered); - return true; -} - -static bool executeMoveToEndOfLine(LocalFrame& frame, Event*, EditorCommandSource, const String&) -{ - frame.selection().modify(FrameSelection::AlterationMove, DirectionForward, LineBoundary, UserTriggered); - return true; -} - -static bool executeMoveToEndOfLineAndModifySelection(LocalFrame& frame, Event*, EditorCommandSource, const String&) -{ - frame.selection().modify(FrameSelection::AlterationExtend, DirectionForward, LineBoundary, UserTriggered); - return true; -} - -static bool executeMoveParagraphBackward(LocalFrame& frame, Event*, EditorCommandSource, const String&) -{ - frame.selection().modify(FrameSelection::AlterationMove, DirectionBackward, ParagraphGranularity, UserTriggered); - return true; -} - -static bool executeMoveParagraphBackwardAndModifySelection(LocalFrame& frame, Event*, EditorCommandSource, const String&) -{ - frame.selection().modify(FrameSelection::AlterationExtend, DirectionBackward, ParagraphGranularity, UserTriggered); - return true; -} - -static bool executeMoveParagraphForward(LocalFrame& frame, Event*, EditorCommandSource, const String&) -{ - frame.selection().modify(FrameSelection::AlterationMove, DirectionForward, ParagraphGranularity, UserTriggered); - return true; -} - -static bool executeMoveParagraphForwardAndModifySelection(LocalFrame& frame, Event*, EditorCommandSource, const String&) -{ - frame.selection().modify(FrameSelection::AlterationExtend, DirectionForward, ParagraphGranularity, UserTriggered); - return true; -} - -static bool executeMoveUp(LocalFrame& frame, Event*, EditorCommandSource, const String&) -{ - return frame.selection().modify(FrameSelection::AlterationMove, DirectionBackward, LineGranularity, UserTriggered); -} - -static bool executeMoveUpAndModifySelection(LocalFrame& frame, Event*, EditorCommandSource, const String&) -{ - frame.selection().modify(FrameSelection::AlterationExtend, DirectionBackward, LineGranularity, UserTriggered); - return true; -} - -static bool executeMoveWordLeft(LocalFrame& frame, Event*, EditorCommandSource, const String&) -{ - frame.selection().modify(FrameSelection::AlterationMove, DirectionLeft, WordGranularity, UserTriggered); - return true; -} - -static bool executeMoveWordLeftAndModifySelection(LocalFrame& frame, Event*, EditorCommandSource, const String&) -{ - frame.selection().modify(FrameSelection::AlterationExtend, DirectionLeft, WordGranularity, UserTriggered); - return true; -} - -static bool executeMoveWordRight(LocalFrame& frame, Event*, EditorCommandSource, const String&) -{ - frame.selection().modify(FrameSelection::AlterationMove, DirectionRight, WordGranularity, UserTriggered); - return true; -} - -static bool executeMoveWordRightAndModifySelection(LocalFrame& frame, Event*, EditorCommandSource, const String&) -{ - frame.selection().modify(FrameSelection::AlterationExtend, DirectionRight, WordGranularity, UserTriggered); - return true; -} - -static bool executeMoveToLeftEndOfLine(LocalFrame& frame, Event*, EditorCommandSource, const String&) -{ - frame.selection().modify(FrameSelection::AlterationMove, DirectionLeft, LineBoundary, UserTriggered); - return true; -} - -static bool executeMoveToLeftEndOfLineAndModifySelection(LocalFrame& frame, Event*, EditorCommandSource, const String&) -{ - frame.selection().modify(FrameSelection::AlterationExtend, DirectionLeft, LineBoundary, UserTriggered); - return true; -} - -static bool executeToggleOverwrite(LocalFrame& frame, Event*, EditorCommandSource, const String&) -{ - frame.editor().toggleOverwriteModeEnabled(); - return true; -} - -static bool executePaste(LocalFrame& frame, Event*, EditorCommandSource, const String&) -{ - frame.editor().paste(); - return true; -} - -static bool executePasteAndMatchStyle(LocalFrame& frame, Event*, EditorCommandSource, const String&) -{ - frame.editor().pasteAsPlainText(); - return true; -} - -static bool executeSelectAll(LocalFrame& frame, Event*, EditorCommandSource, const String&) -{ - frame.selection().selectAll(); - return true; -} - -static bool supported(LocalFrame*) -{ - return true; -} - -static bool supportedFromMenuOrKeyBinding(LocalFrame*) -{ - return false; -} - -static bool supportedCopyCut(LocalFrame* frame) -{ - if (!frame) - return false; - - Settings* settings = frame->settings(); - bool defaultValue = settings && settings->javaScriptCanAccessClipboard(); - return frame->editor().client().canCopyCut(frame, defaultValue); -} - -static bool supportedPaste(LocalFrame* frame) -{ - if (!frame) - return false; - - Settings* settings = frame->settings(); - bool defaultValue = settings && settings->javaScriptCanAccessClipboard() && settings->DOMPasteAllowed(); - return frame->editor().client().canPaste(frame, defaultValue); -} - -// Enabled functions - -static bool enabled(LocalFrame&, Event*, EditorCommandSource) -{ - return true; -} - -static bool enabledVisibleSelection(LocalFrame& frame, Event* event, EditorCommandSource) -{ - // The term "visible" here includes a caret in editable text or a range in any text. - const VisibleSelection& selection = frame.editor().selectionForCommand(event); - return (selection.isCaret() && selection.isContentEditable()) || selection.isRange(); -} - -static bool enabledCopy(LocalFrame& frame, Event*, EditorCommandSource) -{ - return frame.editor().canDHTMLCopy() || frame.editor().canCopy(); -} - -static bool enabledCut(LocalFrame& frame, Event*, EditorCommandSource) -{ - return frame.editor().canDHTMLCut() || frame.editor().canCut(); -} - -static bool enabledInEditableText(LocalFrame& frame, Event* event, EditorCommandSource) -{ - return frame.editor().selectionForCommand(event).rootEditableElement(); -} - -static bool enabledInRichlyEditableText(LocalFrame& frame, Event*, EditorCommandSource) -{ - return frame.selection().isCaretOrRange() && frame.selection().isContentRichlyEditable() && frame.selection().rootEditableElement(); -} - -static bool enabledPaste(LocalFrame& frame, Event*, EditorCommandSource) -{ - return frame.editor().canPaste(); -} - -static TriState stateNone(LocalFrame&, Event*) -{ - return FalseTriState; -} - -static String valueNull(LocalFrame&, Event*) -{ - return String(); -} - -// Map of functions - -struct CommandEntry { - const char* name; - EditorInternalCommand command; -}; - -static const CommandMap& createCommandMap() -{ - // If you add new commands, you should assign new Id to each idForUserMetrics and update MappedEditingCommands - // in chrome/trunk/src/tools/metrics/histograms/histograms.xml. - static const CommandEntry commands[] = { - { "Copy", {7, executeCopy, supportedCopyCut, enabledCopy, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabled } }, - { "Cut", {9, executeCut, supportedCopyCut, enabledCut, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabled } }, - { "DeleteBackward", {12, executeDeleteBackward, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "DeleteForward", {14, executeDeleteForward, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "DeleteWordBackward", {20, executeDeleteWordBackward, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "DeleteWordForward", {21, executeDeleteWordForward, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "InsertNewline", {37, executeInsertNewline, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, isTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveDown", {55, executeMoveDown, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveDownAndModifySelection", {56, executeMoveDownAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveLeft", {59, executeMoveLeft, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveLeftAndModifySelection", {60, executeMoveLeftAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MovePageDown", {61, executeMovePageDown, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MovePageDownAndModifySelection", {62, executeMovePageDownAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MovePageUp", {63, executeMovePageUp, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MovePageUpAndModifySelection", {64, executeMovePageUpAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveParagraphBackward", {65, executeMoveParagraphBackward, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveParagraphBackwardAndModifySelection", {66, executeMoveParagraphBackwardAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveParagraphForward", {67, executeMoveParagraphForward, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveParagraphForwardAndModifySelection", {68, executeMoveParagraphForwardAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveRight", {69, executeMoveRight, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveRightAndModifySelection", {70, executeMoveRightAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveToBeginningOfDocument", {71, executeMoveToBeginningOfDocument, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveToBeginningOfDocumentAndModifySelection", {72, executeMoveToBeginningOfDocumentAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveToBeginningOfLine", {73, executeMoveToBeginningOfLine, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveToBeginningOfLineAndModifySelection", {74, executeMoveToBeginningOfLineAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveToEndOfDocument", {79, executeMoveToEndOfDocument, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveToEndOfDocumentAndModifySelection", {80, executeMoveToEndOfDocumentAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveToEndOfLine", {81, executeMoveToEndOfLine, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveToEndOfLineAndModifySelection", {82, executeMoveToEndOfLineAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveToLeftEndOfLine", {87, executeMoveToLeftEndOfLine, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveToLeftEndOfLineAndModifySelection", {88, executeMoveToLeftEndOfLineAndModifySelection, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveUp", {91, executeMoveUp, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveUpAndModifySelection", {92, executeMoveUpAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveWordLeft", {97, executeMoveWordLeft, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveWordLeftAndModifySelection", {98, executeMoveWordLeftAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveWordRight", {99, executeMoveWordRight, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveWordRightAndModifySelection", {100, executeMoveWordRightAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "OverWrite", {102, executeToggleOverwrite, supportedFromMenuOrKeyBinding, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "Paste", {103, executePaste, supportedPaste, enabledPaste, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabled } }, - { "PasteAndMatchStyle", {104, executePasteAndMatchStyle, supportedPaste, enabledPaste, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabled } }, - { "SelectAll", {115, executeSelectAll, supported, enabled, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - }; - - CommandMap& commandMap = *new CommandMap; -#if ENABLE(ASSERT) - HashSet idSet; -#endif - for (size_t i = 0; i < WTF_ARRAY_LENGTH(commands); ++i) { - const CommandEntry& command = commands[i]; - ASSERT(!commandMap.get(command.name)); - commandMap.set(command.name, &command.command); -#if ENABLE(ASSERT) - ASSERT(!idSet.contains(command.command.idForUserMetrics)); - idSet.add(command.command.idForUserMetrics); -#endif - } - - return commandMap; -} - -static const EditorInternalCommand* internalCommand(const String& commandName) -{ - static const CommandMap& commandMap = createCommandMap(); - return commandName.isEmpty() ? 0 : commandMap.get(commandName); -} - -Editor::Command Editor::command(const String& commandName) -{ - return Command(internalCommand(commandName), CommandFromMenuOrKeyBinding, &m_frame); -} - -Editor::Command Editor::command(const String& commandName, EditorCommandSource source) -{ - return Command(internalCommand(commandName), source, &m_frame); -} - -bool Editor::executeCommand(const String& commandName) -{ - // Specially handling commands that Editor::execCommand does not directly - // support. - if (commandName == "DeleteToEndOfParagraph") { - if (!deleteWithDirection(DirectionForward, ParagraphBoundary, false)) - deleteWithDirection(DirectionForward, CharacterGranularity, false); - return true; - } - if (commandName == "DeleteBackward") - return command(AtomicString("BackwardDelete")).execute(); - if (commandName == "DeleteForward") - return command(AtomicString("ForwardDelete")).execute(); - if (commandName == "AdvanceToNextMisspelling") { - // Wee need to pass false here or else the currently selected word will never be skipped. - spellChecker().advanceToNextMisspelling(false); - return true; - } - if (commandName == "ToggleSpellPanel") { - spellChecker().showSpellingGuessPanel(); - return true; - } - return command(commandName).execute(); -} - -bool Editor::executeCommand(const String& commandName, const String& value) -{ - if (commandName == "showGuessPanel") { - spellChecker().showSpellingGuessPanel(); - return true; - } - - return command(commandName).execute(value); -} - -Editor::Command::Command() - : m_command(0) -{ -} - -Editor::Command::Command(const EditorInternalCommand* command, EditorCommandSource source, PassRefPtr frame) - : m_command(command) - , m_source(source) - , m_frame(command ? frame : nullptr) -{ - // Use separate assertions so we can tell which bad thing happened. - if (!command) - ASSERT(!m_frame); - else - ASSERT(m_frame); -} - -bool Editor::Command::execute(const String& parameter, Event* triggeringEvent) const -{ - if (!isEnabled(triggeringEvent)) { - // Let certain commands be executed when performed explicitly even if they are disabled. - if (!isSupported() || !m_frame || !m_command->allowExecutionWhenDisabled) - return false; - } - m_frame->document()->updateLayout(); - blink::Platform::current()->histogramSparse("WebCore.Editing.Commands", m_command->idForUserMetrics); - return m_command->execute(*m_frame, triggeringEvent, m_source, parameter); -} - -bool Editor::Command::execute(Event* triggeringEvent) const -{ - return execute(String(), triggeringEvent); -} - -bool Editor::Command::isSupported() const -{ - if (!m_command) - return false; - switch (m_source) { - case CommandFromMenuOrKeyBinding: - return true; - case CommandFromDOM: - case CommandFromDOMWithUserInterface: - return m_command->isSupportedFromDOM(m_frame.get()); - } - ASSERT_NOT_REACHED(); - return false; -} - -bool Editor::Command::isEnabled(Event* triggeringEvent) const -{ - if (!isSupported() || !m_frame) - return false; - return m_command->isEnabled(*m_frame, triggeringEvent, m_source); -} - -TriState Editor::Command::state(Event* triggeringEvent) const -{ - if (!isSupported() || !m_frame) - return FalseTriState; - return m_command->state(*m_frame, triggeringEvent); -} - -String Editor::Command::value(Event* triggeringEvent) const -{ - if (!isSupported() || !m_frame) - return String(); - if (m_command->value == valueNull && m_command->state != stateNone) - return m_command->state(*m_frame, triggeringEvent) == TrueTriState ? "true" : "false"; - return m_command->value(*m_frame, triggeringEvent); -} - -bool Editor::Command::isTextInsertion() const -{ - return m_command && m_command->isTextInsertion; -} - -int Editor::Command::idForHistogram() const -{ - return isSupported() ? m_command->idForUserMetrics : 0; -} - -} // namespace blink diff --git a/sky/engine/core/editing/EditorKeyBindings.cpp b/sky/engine/core/editing/EditorKeyBindings.cpp deleted file mode 100644 index 0f5bb6fc02235..0000000000000 --- a/sky/engine/core/editing/EditorKeyBindings.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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/Editor.h" - -#include "gen/sky/core/EventTypeNames.h" -#include "sky/engine/core/events/KeyboardEvent.h" -#include "sky/engine/core/frame/LocalFrame.h" -#include "sky/engine/core/page/EditorClient.h" - -namespace blink { - -bool Editor::handleEditingKeyboardEvent(KeyboardEvent* event) -{ - String commandName = behavior().interpretKeyEvent(*event); - Command command = this->command(commandName); - - if (event->type() == EventTypeNames::keydown) { - // WebKit doesn't have enough information about mode to decide how - // commands that just insert text if executed via Editor should be treated, - // so we leave it upon WebCore to either handle them immediately - // (e.g. Tab that changes focus) or let a keypress event be generated - // (e.g. Tab that inserts a Tab character, or Enter). - if (command.isTextInsertion() || commandName.isEmpty()) - return false; - return command.execute(event); - } - - if (command.execute(event)) - return true; - - if (!behavior().shouldInsertCharacter(*event) || !canEdit()) - return false; - - UChar charCode = event->charCode(); - return insertText(String(&charCode, 1), event); -} - -void Editor::handleKeyboardEvent(KeyboardEvent* evt) -{ - // Give the embedder a chance to handle the keyboard event. - if (client().handleKeyboardEvent() || handleEditingKeyboardEvent(evt)) - evt->setDefaultHandled(); -} - -} // namespace blink diff --git a/sky/engine/core/editing/FrameSelection.cpp b/sky/engine/core/editing/FrameSelection.cpp deleted file mode 100644 index 6ef8483f47f0e..0000000000000 --- a/sky/engine/core/editing/FrameSelection.cpp +++ /dev/null @@ -1,1624 +0,0 @@ -/* - * 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/FrameSelection.h" - -#include -#include "sky/engine/bindings/exception_state.h" -#include "sky/engine/core/css/StylePropertySet.h" -#include "sky/engine/core/dom/CharacterData.h" -#include "sky/engine/core/dom/Document.h" -#include "sky/engine/core/dom/Element.h" -#include "sky/engine/core/dom/ElementTraversal.h" -#include "sky/engine/core/dom/NodeTraversal.h" -#include "sky/engine/core/dom/Text.h" -#include "sky/engine/core/editing/Editor.h" -#include "sky/engine/core/editing/InputMethodController.h" -#include "sky/engine/core/editing/RenderedPosition.h" -#include "sky/engine/core/editing/SpellChecker.h" -#include "sky/engine/core/editing/TextIterator.h" -#include "sky/engine/core/editing/TypingCommand.h" -#include "sky/engine/core/editing/VisibleUnits.h" -#include "sky/engine/core/editing/htmlediting.h" -#include "sky/engine/core/events/Event.h" -#include "sky/engine/core/frame/FrameView.h" -#include "sky/engine/core/frame/LocalDOMWindow.h" -#include "sky/engine/core/frame/LocalFrame.h" -#include "sky/engine/core/frame/Settings.h" -#include "sky/engine/core/page/EditorClient.h" -#include "sky/engine/core/page/FocusController.h" -#include "sky/engine/core/page/Page.h" -#include "sky/engine/core/rendering/HitTestRequest.h" -#include "sky/engine/core/rendering/HitTestResult.h" -#include "sky/engine/core/rendering/InlineTextBox.h" -#include "sky/engine/core/rendering/RenderLayer.h" -#include "sky/engine/core/rendering/RenderText.h" -#include "sky/engine/core/rendering/RenderTheme.h" -#include "sky/engine/core/rendering/RenderView.h" -#include "sky/engine/platform/geometry/FloatQuad.h" -#include "sky/engine/platform/graphics/GraphicsContext.h" -#include "sky/engine/wtf/text/CString.h" - -#define EDIT_DEBUG 0 - -namespace blink { - -static inline LayoutUnit NoXPosForVerticalArrowNavigation() -{ - return LayoutUnit::min(); -} - -static inline bool shouldAlwaysUseDirectionalSelection(LocalFrame* frame) -{ - return !frame || frame->editor().behavior().shouldConsiderSelectionAsDirectional(); -} - -FrameSelection::FrameSelection(LocalFrame* frame) - : m_frame(frame) - , m_xPosForVerticalArrowNavigation(NoXPosForVerticalArrowNavigation()) - , m_observingVisibleSelection(false) - , m_granularity(CharacterGranularity) - , m_caretBlinkTimer(this, &FrameSelection::caretBlinkTimerFired) - , m_shouldPaintCaret(true) - , m_isCaretBlinkingSuspended(false) - , m_focused(frame && frame->page() && frame->page()->focusController().focusedFrame() == frame) - , m_shouldShowBlockCursor(false) -{ - if (shouldAlwaysUseDirectionalSelection(m_frame)) - m_selection.setIsDirectional(true); -} - -FrameSelection::~FrameSelection() -{ -#if !ENABLE(OILPAN) - // Oilpan: No need to clear out VisibleSelection observer; - // it is finalized as a part object of FrameSelection. - stopObservingVisibleSelectionChangeIfNecessary(); -#endif -} - -ContainerNode* FrameSelection::rootEditableElementOrTreeScopeRootNode() const -{ - Element* selectionRoot = m_selection.rootEditableElement(); - if (selectionRoot) - return selectionRoot; - - Node* node = m_selection.base().containerNode(); - return node ? &node->treeScope().rootNode() : 0; -} - -void FrameSelection::moveTo(const VisiblePosition &pos, EUserTriggered userTriggered, CursorAlignOnScroll align) -{ - SetSelectionOptions options = CloseTyping | ClearTypingStyle | userTriggered; - setSelection(VisibleSelection(pos.deepEquivalent(), pos.deepEquivalent(), pos.affinity(), m_selection.isDirectional()), options, align); -} - -void FrameSelection::moveTo(const VisiblePosition &base, const VisiblePosition &extent, EUserTriggered userTriggered) -{ - const bool selectionHasDirection = true; - SetSelectionOptions options = CloseTyping | ClearTypingStyle | userTriggered; - setSelection(VisibleSelection(base.deepEquivalent(), extent.deepEquivalent(), base.affinity(), selectionHasDirection), options); -} - -void FrameSelection::moveTo(const Position &pos, EAffinity affinity, EUserTriggered userTriggered) -{ - SetSelectionOptions options = CloseTyping | ClearTypingStyle | userTriggered; - setSelection(VisibleSelection(pos, affinity, m_selection.isDirectional()), options); -} - -static void adjustEndpointsAtBidiBoundary(VisiblePosition& visibleBase, VisiblePosition& visibleExtent) -{ - RenderedPosition base(visibleBase); - RenderedPosition extent(visibleExtent); - - if (base.isNull() || extent.isNull() || base.isEquivalent(extent)) - return; - - if (base.atLeftBoundaryOfBidiRun()) { - if (!extent.atRightBoundaryOfBidiRun(base.bidiLevelOnRight()) - && base.isEquivalent(extent.leftBoundaryOfBidiRun(base.bidiLevelOnRight()))) { - visibleBase = VisiblePosition(base.positionAtLeftBoundaryOfBiDiRun()); - return; - } - return; - } - - if (base.atRightBoundaryOfBidiRun()) { - if (!extent.atLeftBoundaryOfBidiRun(base.bidiLevelOnLeft()) - && base.isEquivalent(extent.rightBoundaryOfBidiRun(base.bidiLevelOnLeft()))) { - visibleBase = VisiblePosition(base.positionAtRightBoundaryOfBiDiRun()); - return; - } - return; - } - - if (extent.atLeftBoundaryOfBidiRun() && extent.isEquivalent(base.leftBoundaryOfBidiRun(extent.bidiLevelOnRight()))) { - visibleExtent = VisiblePosition(extent.positionAtLeftBoundaryOfBiDiRun()); - return; - } - - if (extent.atRightBoundaryOfBidiRun() && extent.isEquivalent(base.rightBoundaryOfBidiRun(extent.bidiLevelOnLeft()))) { - visibleExtent = VisiblePosition(extent.positionAtRightBoundaryOfBiDiRun()); - return; - } -} - -void FrameSelection::setNonDirectionalSelectionIfNeeded(const VisibleSelection& passedNewSelection, TextGranularity granularity, - EndPointsAdjustmentMode endpointsAdjustmentMode) -{ - VisibleSelection newSelection = passedNewSelection; - bool isDirectional = shouldAlwaysUseDirectionalSelection(m_frame) || newSelection.isDirectional(); - - VisiblePosition base = m_originalBase.isNotNull() ? m_originalBase : newSelection.visibleBase(); - VisiblePosition newBase = base; - VisiblePosition extent = newSelection.visibleExtent(); - VisiblePosition newExtent = extent; - if (endpointsAdjustmentMode == AdjustEndpointsAtBidiBoundary) - adjustEndpointsAtBidiBoundary(newBase, newExtent); - - if (newBase != base || newExtent != extent) { - m_originalBase = base; - newSelection.setBase(newBase); - newSelection.setExtent(newExtent); - } else if (m_originalBase.isNotNull()) { - if (m_selection.base() == newSelection.base()) - newSelection.setBase(m_originalBase); - m_originalBase.clear(); - } - - newSelection.setIsDirectional(isDirectional); // Adjusting base and extent will make newSelection always directional - if (m_selection == newSelection) - return; - - setSelection(newSelection, granularity); -} - -void FrameSelection::setSelection(const VisibleSelection& newSelection, SetSelectionOptions options, CursorAlignOnScroll align, TextGranularity granularity) -{ - bool closeTyping = options & CloseTyping; - bool shouldClearTypingStyle = options & ClearTypingStyle; - EUserTriggered userTriggered = selectionOptionsToUserTriggered(options); - - VisibleSelection s = validateSelection(newSelection); - if (shouldAlwaysUseDirectionalSelection(m_frame)) - s.setIsDirectional(true); - - if (!m_frame) { - m_selection = s; - return; - } - - // : Infinite recursion at FrameSelection::setSelection - // if document->frame() == m_frame we can get into an infinite loop - if (s.base().anchorNode()) { - Document& document = *s.base().document(); - if (document.frame() && document.frame() != m_frame && document != m_frame->document()) { - RefPtr guard = document.frame(); - document.frame()->selection().setSelection(s, options, align, granularity); - // It's possible that during the above set selection, this FrameSelection has been modified by - // selectFrameElementInParentIfFullySelected, but that the selection is no longer valid since - // the frame is about to be destroyed. If this is the case, clear our selection. - if (guard->hasOneRef() && !m_selection.isNonOrphanedCaretOrRange()) - clear(); - return; - } - } - - m_granularity = granularity; - - if (closeTyping) - TypingCommand::closeTyping(m_frame); - - if (shouldClearTypingStyle) - clearTypingStyle(); - - if (m_selection == s) { - // Even if selection was not changed, selection offsets may have been changed. - m_frame->inputMethodController().cancelCompositionIfSelectionIsInvalid(); - notifyRendererOfSelectionChange(userTriggered); - return; - } - - VisibleSelection oldSelection = m_selection; - - m_selection = s; - setCaretRectNeedsUpdate(); - - if (!s.isNone() && !(options & DoNotSetFocus)) - setFocusedNodeIfNeeded(); - - if (!(options & DoNotUpdateAppearance)) { - updateAppearance(ResetCaretBlink); - } - - // Always clear the x position used for vertical arrow navigation. - // It will be restored by the vertical arrow navigation code if necessary. - m_xPosForVerticalArrowNavigation = NoXPosForVerticalArrowNavigation(); - notifyRendererOfSelectionChange(userTriggered); - m_frame->editor().respondToChangedSelection(oldSelection, options); - if (userTriggered == UserTriggered) { - ScrollAlignment alignment = (align == AlignCursorOnScrollAlways) ? ScrollAlignment::alignTopAlways : ScrollAlignment::alignToEdgeIfNeeded; - revealSelection(alignment, RevealExtent); - } - - notifyAccessibilityForSelectionChange(); -} - -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 FrameSelection::nodeWillBeRemoved(Node& node) -{ - // There can't be a selection inside a fragment, so if a fragment's node is being removed, - // the selection in the document that created the fragment needs no adjustment. - if (isNone() || !node.inActiveDocument()) - return; - - respondToNodeModification(node, removingNodeRemovesPosition(node, m_selection.base()), removingNodeRemovesPosition(node, m_selection.extent()), - removingNodeRemovesPosition(node, m_selection.start()), removingNodeRemovesPosition(node, m_selection.end())); -} - -void FrameSelection::respondToNodeModification(Node& node, bool baseRemoved, bool extentRemoved, bool startRemoved, bool endRemoved) -{ - ASSERT(node.document().isActive()); - - bool clearRenderTreeSelection = false; - bool clearDOMTreeSelection = false; - - if (startRemoved || endRemoved) { - Position start = m_selection.start(); - Position end = m_selection.end(); - if (startRemoved) - updatePositionForNodeRemoval(start, node); - if (endRemoved) - updatePositionForNodeRemoval(end, node); - - if (start.isNotNull() && end.isNotNull()) { - if (m_selection.isBaseFirst()) - m_selection.setWithoutValidation(start, end); - else - m_selection.setWithoutValidation(end, start); - } else - clearDOMTreeSelection = true; - - clearRenderTreeSelection = true; - } else if (baseRemoved || extentRemoved) { - // The base and/or extent are about to be removed, but the start and end aren't. - // Change the base and extent to the start and end, but don't re-validate the - // selection, since doing so could move the start and end into the node - // that is about to be removed. - if (m_selection.isBaseFirst()) - m_selection.setWithoutValidation(m_selection.start(), m_selection.end()); - else - m_selection.setWithoutValidation(m_selection.end(), m_selection.start()); - } else if (RefPtr range = m_selection.firstRange()) { - TrackExceptionState exceptionState; - Range::CompareResults compareResult = range->compareNode(&node, exceptionState); - if (!exceptionState.had_exception() && (compareResult == Range::NODE_BEFORE_AND_AFTER || compareResult == Range::NODE_INSIDE)) { - // If we did nothing here, when this node's renderer was destroyed, the rect that it - // occupied would be invalidated, but, selection gaps that change as a result of - // the removal wouldn't be invalidated. - // FIXME: Don't do so much unnecessary invalidation. - clearRenderTreeSelection = true; - } - } - - if (clearRenderTreeSelection) - m_selection.start().document()->renderView()->clearSelection(); - - if (clearDOMTreeSelection) - setSelection(VisibleSelection(), DoNotSetFocus); -} - -static Position updatePositionAfterAdoptingTextReplacement(const Position& position, CharacterData* node, unsigned offset, unsigned oldLength, unsigned newLength) -{ - if (!position.anchorNode() || position.anchorNode() != node || position.anchorType() != Position::PositionIsOffsetInAnchor) - return position; - - // See: http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-Mutation - ASSERT(position.offsetInContainerNode() >= 0); - unsigned positionOffset = static_cast(position.offsetInContainerNode()); - // Replacing text can be viewed as a deletion followed by insertion. - if (positionOffset >= offset && positionOffset <= offset + oldLength) - positionOffset = offset; - - // Adjust the offset if the position is after the end of the deleted contents - // (positionOffset > offset + oldLength) to avoid having a stale offset. - if (positionOffset > offset + oldLength) - positionOffset = positionOffset - oldLength + newLength; - - ASSERT_WITH_SECURITY_IMPLICATION(positionOffset <= node->length()); - // CharacterNode in VisibleSelection must be Text node, because Comment - // and ProcessingInstruction node aren't visible. - return Position(toText(node), positionOffset); -} - -void FrameSelection::didUpdateCharacterData(CharacterData* node, unsigned offset, unsigned oldLength, unsigned newLength) -{ - // The fragment check is a performance optimization. See http://trac.webkit.org/changeset/30062. - if (isNone() || !node || !node->inDocument()) - return; - - Position base = updatePositionAfterAdoptingTextReplacement(m_selection.base(), node, offset, oldLength, newLength); - Position extent = updatePositionAfterAdoptingTextReplacement(m_selection.extent(), node, offset, oldLength, newLength); - Position start = updatePositionAfterAdoptingTextReplacement(m_selection.start(), node, offset, oldLength, newLength); - Position end = updatePositionAfterAdoptingTextReplacement(m_selection.end(), node, offset, oldLength, newLength); - updateSelectionIfNeeded(base, extent, start, end); -} - -static Position updatePostionAfterAdoptingTextNodesMerged(const Position& position, const Text& oldNode, unsigned offset) -{ - if (!position.anchorNode() || position.anchorType() != Position::PositionIsOffsetInAnchor) - return position; - - ASSERT(position.offsetInContainerNode() >= 0); - unsigned positionOffset = static_cast(position.offsetInContainerNode()); - - if (position.anchorNode() == &oldNode) - return Position(toText(oldNode.previousSibling()), positionOffset + offset); - - if (position.anchorNode() == oldNode.parentNode() && positionOffset == offset) - return Position(toText(oldNode.previousSibling()), offset); - - return position; -} - -void FrameSelection::didMergeTextNodes(const Text& oldNode, unsigned offset) -{ - if (isNone() || !oldNode.inDocument()) - return; - Position base = updatePostionAfterAdoptingTextNodesMerged(m_selection.base(), oldNode, offset); - Position extent = updatePostionAfterAdoptingTextNodesMerged(m_selection.extent(), oldNode, offset); - Position start = updatePostionAfterAdoptingTextNodesMerged(m_selection.start(), oldNode, offset); - Position end = updatePostionAfterAdoptingTextNodesMerged(m_selection.end(), oldNode, offset); - updateSelectionIfNeeded(base, extent, start, end); -} - -static Position updatePostionAfterAdoptingTextNodeSplit(const Position& position, const Text& oldNode) -{ - if (!position.anchorNode() || position.anchorNode() != &oldNode || position.anchorType() != Position::PositionIsOffsetInAnchor) - return position; - // See: http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-Mutation - ASSERT(position.offsetInContainerNode() >= 0); - unsigned positionOffset = static_cast(position.offsetInContainerNode()); - unsigned oldLength = oldNode.length(); - if (positionOffset <= oldLength) - return position; - return Position(toText(oldNode.nextSibling()), positionOffset - oldLength); -} - -void FrameSelection::didSplitTextNode(const Text& oldNode) -{ - if (isNone() || !oldNode.inDocument()) - return; - Position base = updatePostionAfterAdoptingTextNodeSplit(m_selection.base(), oldNode); - Position extent = updatePostionAfterAdoptingTextNodeSplit(m_selection.extent(), oldNode); - Position start = updatePostionAfterAdoptingTextNodeSplit(m_selection.start(), oldNode); - Position end = updatePostionAfterAdoptingTextNodeSplit(m_selection.end(), oldNode); - updateSelectionIfNeeded(base, extent, start, end); -} - -void FrameSelection::updateSelectionIfNeeded(const Position& base, const Position& extent, const Position& start, const Position& end) -{ - if (base == m_selection.base() && extent == m_selection.extent() && start == m_selection.start() && end == m_selection.end()) - return; - VisibleSelection newSelection; - if (m_selection.isBaseFirst()) - newSelection.setWithoutValidation(start, end); - else - newSelection.setWithoutValidation(end, start); - setSelection(newSelection, DoNotSetFocus); -} - -TextDirection FrameSelection::directionOfEnclosingBlock() -{ - return blink::directionOfEnclosingBlock(m_selection.extent()); -} - -TextDirection FrameSelection::directionOfSelection() -{ - InlineBox* startBox = 0; - InlineBox* endBox = 0; - int unusedOffset; - // Cache the VisiblePositions because visibleStart() and visibleEnd() - // can cause layout, which has the potential to invalidate lineboxes. - VisiblePosition startPosition = m_selection.visibleStart(); - VisiblePosition endPosition = m_selection.visibleEnd(); - if (startPosition.isNotNull()) - startPosition.getInlineBoxAndOffset(startBox, unusedOffset); - if (endPosition.isNotNull()) - endPosition.getInlineBoxAndOffset(endBox, unusedOffset); - if (startBox && endBox && startBox->direction() == endBox->direction()) - return startBox->direction(); - - return directionOfEnclosingBlock(); -} - -void FrameSelection::didChangeFocus() -{ - updateAppearance(); -} - -void FrameSelection::willBeModified(EAlteration alter, SelectionDirection direction) -{ - if (alter != AlterationExtend) - return; - - Position start = m_selection.start(); - Position end = m_selection.end(); - - bool baseIsStart = true; - - if (m_selection.isDirectional()) { - // Make base and extent match start and end so we extend the user-visible selection. - // This only matters for cases where base and extend point to different positions than - // start and end (e.g. after a double-click to select a word). - if (m_selection.isBaseFirst()) - baseIsStart = true; - else - baseIsStart = false; - } else { - switch (direction) { - case DirectionRight: - if (directionOfSelection() == LTR) - baseIsStart = true; - else - baseIsStart = false; - break; - case DirectionForward: - baseIsStart = true; - break; - case DirectionLeft: - if (directionOfSelection() == LTR) - baseIsStart = false; - else - baseIsStart = true; - break; - case DirectionBackward: - baseIsStart = false; - break; - } - } - if (baseIsStart) { - m_selection.setBase(start); - m_selection.setExtent(end); - } else { - m_selection.setBase(end); - m_selection.setExtent(start); - } -} - -VisiblePosition FrameSelection::positionForPlatform(bool isGetStart) const -{ - // FIXME: VisibleSelection should be fixed to ensure as an invariant that - // base/extent always point to the same nodes as start/end, but which points - // to which depends on the value of isBaseFirst. Then this can be changed - // to just return m_sel.extent(). - return m_selection.isBaseFirst() ? m_selection.visibleEnd() : m_selection.visibleStart(); -} - -VisiblePosition FrameSelection::startForPlatform() const -{ - return positionForPlatform(true); -} - -VisiblePosition FrameSelection::endForPlatform() const -{ - return positionForPlatform(false); -} - -VisiblePosition FrameSelection::nextWordPositionForPlatform(const VisiblePosition &originalPosition) -{ - VisiblePosition positionAfterCurrentWord = nextWordPosition(originalPosition); - - if (m_frame && m_frame->editor().behavior().shouldSkipSpaceWhenMovingRight()) { - // In order to skip spaces when moving right, we advance one - // word further and then move one word back. Given the - // semantics of previousWordPosition() this will put us at the - // beginning of the word following. - VisiblePosition positionAfterSpacingAndFollowingWord = nextWordPosition(positionAfterCurrentWord); - if (positionAfterSpacingAndFollowingWord.isNotNull() && positionAfterSpacingAndFollowingWord != positionAfterCurrentWord) - positionAfterCurrentWord = previousWordPosition(positionAfterSpacingAndFollowingWord); - - bool movingBackwardsMovedPositionToStartOfCurrentWord = positionAfterCurrentWord == previousWordPosition(nextWordPosition(originalPosition)); - if (movingBackwardsMovedPositionToStartOfCurrentWord) - positionAfterCurrentWord = positionAfterSpacingAndFollowingWord; - } - return positionAfterCurrentWord; -} - -static void adjustPositionForUserSelectAll(VisiblePosition& pos, bool isForward) -{ - if (Node* rootUserSelectAll = Position::rootUserSelectAllForNode(pos.deepEquivalent().anchorNode())) - pos = VisiblePosition(isForward ? positionAfterNode(rootUserSelectAll).downstream(CanCrossEditingBoundary) : positionBeforeNode(rootUserSelectAll).upstream(CanCrossEditingBoundary)); -} - -VisiblePosition FrameSelection::modifyExtendingRight(TextGranularity granularity) -{ - VisiblePosition pos(m_selection.extent(), m_selection.affinity()); - - // The difference between modifyExtendingRight and modifyExtendingForward is: - // modifyExtendingForward always extends forward logically. - // modifyExtendingRight behaves the same as modifyExtendingForward except for extending character or word, - // it extends forward logically if the enclosing block is LTR direction, - // but it extends backward logically if the enclosing block is RTL direction. - switch (granularity) { - case CharacterGranularity: - if (directionOfEnclosingBlock() == LTR) - pos = pos.next(CanSkipOverEditingBoundary); - else - pos = pos.previous(CanSkipOverEditingBoundary); - break; - case WordGranularity: - if (directionOfEnclosingBlock() == LTR) - pos = nextWordPositionForPlatform(pos); - else - pos = previousWordPosition(pos); - break; - case LineBoundary: - if (directionOfEnclosingBlock() == LTR) - pos = modifyExtendingForward(granularity); - else - pos = modifyExtendingBackward(granularity); - break; - case SentenceGranularity: - case LineGranularity: - case ParagraphGranularity: - case SentenceBoundary: - case ParagraphBoundary: - case DocumentBoundary: - // FIXME: implement all of the above? - pos = modifyExtendingForward(granularity); - break; - } - adjustPositionForUserSelectAll(pos, directionOfEnclosingBlock() == LTR); - return pos; -} - -VisiblePosition FrameSelection::modifyExtendingForward(TextGranularity granularity) -{ - VisiblePosition pos(m_selection.extent(), m_selection.affinity()); - switch (granularity) { - case CharacterGranularity: - pos = pos.next(CanSkipOverEditingBoundary); - break; - case WordGranularity: - pos = nextWordPositionForPlatform(pos); - break; - case SentenceGranularity: - pos = nextSentencePosition(pos); - break; - case LineGranularity: - pos = nextLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT)); - break; - case ParagraphGranularity: - pos = nextParagraphPosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT)); - break; - case SentenceBoundary: - pos = endOfSentence(endForPlatform()); - break; - case LineBoundary: - pos = logicalEndOfLine(endForPlatform()); - break; - case ParagraphBoundary: - pos = endOfParagraph(endForPlatform()); - break; - case DocumentBoundary: - pos = endForPlatform(); - if (isEditablePosition(pos.deepEquivalent())) - pos = endOfEditableContent(pos); - else - pos = endOfDocument(pos); - break; - } - adjustPositionForUserSelectAll(pos, directionOfEnclosingBlock() == LTR); - return pos; -} - -VisiblePosition FrameSelection::modifyMovingRight(TextGranularity granularity) -{ - VisiblePosition pos; - switch (granularity) { - case CharacterGranularity: - if (isRange()) { - if (directionOfSelection() == LTR) - pos = VisiblePosition(m_selection.end(), m_selection.affinity()); - else - pos = VisiblePosition(m_selection.start(), m_selection.affinity()); - } else - pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).right(true); - break; - case WordGranularity: { - bool skipsSpaceWhenMovingRight = m_frame && m_frame->editor().behavior().shouldSkipSpaceWhenMovingRight(); - pos = rightWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()), skipsSpaceWhenMovingRight); - break; - } - case SentenceGranularity: - case LineGranularity: - case ParagraphGranularity: - case SentenceBoundary: - case ParagraphBoundary: - case DocumentBoundary: - // FIXME: Implement all of the above. - pos = modifyMovingForward(granularity); - break; - case LineBoundary: - pos = rightBoundaryOfLine(startForPlatform(), directionOfEnclosingBlock()); - break; - } - return pos; -} - -VisiblePosition FrameSelection::modifyMovingForward(TextGranularity granularity) -{ - VisiblePosition pos; - // FIXME: Stay in editable content for the less common granularities. - switch (granularity) { - case CharacterGranularity: - if (isRange()) - pos = VisiblePosition(m_selection.end(), m_selection.affinity()); - else - pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).next(CanSkipOverEditingBoundary); - break; - case WordGranularity: - pos = nextWordPositionForPlatform(VisiblePosition(m_selection.extent(), m_selection.affinity())); - break; - case SentenceGranularity: - pos = nextSentencePosition(VisiblePosition(m_selection.extent(), m_selection.affinity())); - break; - case LineGranularity: { - // down-arrowing from a range selection that ends at the start of a line needs - // to leave the selection at that line start (no need to call nextLinePosition!) - pos = endForPlatform(); - if (!isRange() || !isStartOfLine(pos)) - pos = nextLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(START)); - break; - } - case ParagraphGranularity: - pos = nextParagraphPosition(endForPlatform(), lineDirectionPointForBlockDirectionNavigation(START)); - break; - case SentenceBoundary: - pos = endOfSentence(endForPlatform()); - break; - case LineBoundary: - pos = logicalEndOfLine(endForPlatform()); - break; - case ParagraphBoundary: - pos = endOfParagraph(endForPlatform()); - break; - case DocumentBoundary: - pos = endForPlatform(); - if (isEditablePosition(pos.deepEquivalent())) - pos = endOfEditableContent(pos); - else - pos = endOfDocument(pos); - break; - } - return pos; -} - -VisiblePosition FrameSelection::modifyExtendingLeft(TextGranularity granularity) -{ - VisiblePosition pos(m_selection.extent(), m_selection.affinity()); - - // The difference between modifyExtendingLeft and modifyExtendingBackward is: - // modifyExtendingBackward always extends backward logically. - // modifyExtendingLeft behaves the same as modifyExtendingBackward except for extending character or word, - // it extends backward logically if the enclosing block is LTR direction, - // but it extends forward logically if the enclosing block is RTL direction. - switch (granularity) { - case CharacterGranularity: - if (directionOfEnclosingBlock() == LTR) - pos = pos.previous(CanSkipOverEditingBoundary); - else - pos = pos.next(CanSkipOverEditingBoundary); - break; - case WordGranularity: - if (directionOfEnclosingBlock() == LTR) - pos = previousWordPosition(pos); - else - pos = nextWordPositionForPlatform(pos); - break; - case LineBoundary: - if (directionOfEnclosingBlock() == LTR) - pos = modifyExtendingBackward(granularity); - else - pos = modifyExtendingForward(granularity); - break; - case SentenceGranularity: - case LineGranularity: - case ParagraphGranularity: - case SentenceBoundary: - case ParagraphBoundary: - case DocumentBoundary: - pos = modifyExtendingBackward(granularity); - break; - } - adjustPositionForUserSelectAll(pos, !(directionOfEnclosingBlock() == LTR)); - return pos; -} - -VisiblePosition FrameSelection::modifyExtendingBackward(TextGranularity granularity) -{ - VisiblePosition pos(m_selection.extent(), m_selection.affinity()); - - // Extending a selection backward by word or character from just after a table selects - // the table. This "makes sense" from the user perspective, esp. when deleting. - // It was done here instead of in VisiblePosition because we want VPs to iterate - // over everything. - switch (granularity) { - case CharacterGranularity: - pos = pos.previous(CanSkipOverEditingBoundary); - break; - case WordGranularity: - pos = previousWordPosition(pos); - break; - case SentenceGranularity: - pos = previousSentencePosition(pos); - break; - case LineGranularity: - pos = previousLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT)); - break; - case ParagraphGranularity: - pos = previousParagraphPosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT)); - break; - case SentenceBoundary: - pos = startOfSentence(startForPlatform()); - break; - case LineBoundary: - pos = logicalStartOfLine(startForPlatform()); - break; - case ParagraphBoundary: - pos = startOfParagraph(startForPlatform()); - break; - case DocumentBoundary: - pos = startForPlatform(); - if (isEditablePosition(pos.deepEquivalent())) - pos = startOfEditableContent(pos); - else - pos = startOfDocument(pos); - break; - } - adjustPositionForUserSelectAll(pos, !(directionOfEnclosingBlock() == LTR)); - return pos; -} - -VisiblePosition FrameSelection::modifyMovingLeft(TextGranularity granularity) -{ - VisiblePosition pos; - switch (granularity) { - case CharacterGranularity: - if (isRange()) - if (directionOfSelection() == LTR) - pos = VisiblePosition(m_selection.start(), m_selection.affinity()); - else - pos = VisiblePosition(m_selection.end(), m_selection.affinity()); - else - pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).left(true); - break; - case WordGranularity: { - bool skipsSpaceWhenMovingRight = m_frame && m_frame->editor().behavior().shouldSkipSpaceWhenMovingRight(); - pos = leftWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()), skipsSpaceWhenMovingRight); - break; - } - case SentenceGranularity: - case LineGranularity: - case ParagraphGranularity: - case SentenceBoundary: - case ParagraphBoundary: - case DocumentBoundary: - // FIXME: Implement all of the above. - pos = modifyMovingBackward(granularity); - break; - case LineBoundary: - pos = leftBoundaryOfLine(startForPlatform(), directionOfEnclosingBlock()); - break; - } - return pos; -} - -VisiblePosition FrameSelection::modifyMovingBackward(TextGranularity granularity) -{ - VisiblePosition pos; - switch (granularity) { - case CharacterGranularity: - if (isRange()) - pos = VisiblePosition(m_selection.start(), m_selection.affinity()); - else - pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).previous(CanSkipOverEditingBoundary); - break; - case WordGranularity: - pos = previousWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity())); - break; - case SentenceGranularity: - pos = previousSentencePosition(VisiblePosition(m_selection.extent(), m_selection.affinity())); - break; - case LineGranularity: - pos = previousLinePosition(startForPlatform(), lineDirectionPointForBlockDirectionNavigation(START)); - break; - case ParagraphGranularity: - pos = previousParagraphPosition(startForPlatform(), lineDirectionPointForBlockDirectionNavigation(START)); - break; - case SentenceBoundary: - pos = startOfSentence(startForPlatform()); - break; - case LineBoundary: - pos = logicalStartOfLine(startForPlatform()); - break; - case ParagraphBoundary: - pos = startOfParagraph(startForPlatform()); - break; - case DocumentBoundary: - pos = startForPlatform(); - if (isEditablePosition(pos.deepEquivalent())) - pos = startOfEditableContent(pos); - else - pos = startOfDocument(pos); - break; - } - return pos; -} - -static bool isBoundary(TextGranularity granularity) -{ - return granularity == LineBoundary || granularity == ParagraphBoundary || granularity == DocumentBoundary; -} - -bool FrameSelection::modify(EAlteration alter, SelectionDirection direction, TextGranularity granularity, EUserTriggered userTriggered) -{ - if (userTriggered == UserTriggered) { - OwnPtr trialFrameSelection = FrameSelection::create(); - trialFrameSelection->setSelection(m_selection); - trialFrameSelection->modify(alter, direction, granularity, NotUserTriggered); - - if (trialFrameSelection->selection().isRange() && m_selection.isCaret() && !dispatchSelectStart()) - return false; - } - - willBeModified(alter, direction); - - VisiblePosition originalStartPosition = m_selection.visibleStart(); - VisiblePosition position; - switch (direction) { - case DirectionRight: - if (alter == AlterationMove) - position = modifyMovingRight(granularity); - else - position = modifyExtendingRight(granularity); - break; - case DirectionForward: - if (alter == AlterationExtend) - position = modifyExtendingForward(granularity); - else - position = modifyMovingForward(granularity); - break; - case DirectionLeft: - if (alter == AlterationMove) - position = modifyMovingLeft(granularity); - else - position = modifyExtendingLeft(granularity); - break; - case DirectionBackward: - if (alter == AlterationExtend) - position = modifyExtendingBackward(granularity); - else - position = modifyMovingBackward(granularity); - break; - } - - if (position.isNull()) - return false; - - // Some of the above operations set an xPosForVerticalArrowNavigation. - // Setting a selection will clear it, so save it to possibly restore later. - // Note: the START position type is arbitrary because it is unused, it would be - // the requested position type if there were no xPosForVerticalArrowNavigation set. - LayoutUnit x = lineDirectionPointForBlockDirectionNavigation(START); - m_selection.setIsDirectional(shouldAlwaysUseDirectionalSelection(m_frame) || alter == AlterationExtend); - - switch (alter) { - case AlterationMove: - moveTo(position, userTriggered); - break; - case AlterationExtend: - - if (!m_selection.isCaret() - && (granularity == WordGranularity || granularity == ParagraphGranularity || granularity == LineGranularity) - && m_frame && !m_frame->editor().behavior().shouldExtendSelectionByWordOrLineAcrossCaret()) { - // Don't let the selection go across the base position directly. Needed to match mac - // behavior when, for instance, word-selecting backwards starting with the caret in - // the middle of a word and then word-selecting forward, leaving the caret in the - // same place where it was, instead of directly selecting to the end of the word. - VisibleSelection newSelection = m_selection; - newSelection.setExtent(position); - if (m_selection.isBaseFirst() != newSelection.isBaseFirst()) - position = m_selection.visibleBase(); - } - - // 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. - if (!m_frame || !m_frame->editor().behavior().shouldAlwaysGrowSelectionWhenExtendingToBoundary() || m_selection.isCaret() || !isBoundary(granularity)) - setExtent(position, userTriggered); - else { - TextDirection textDirection = directionOfEnclosingBlock(); - if (direction == DirectionForward || (textDirection == LTR && direction == DirectionRight) || (textDirection == RTL && direction == DirectionLeft)) - setEnd(position, userTriggered); - else - setStart(position, userTriggered); - } - break; - } - - if (granularity == LineGranularity || granularity == ParagraphGranularity) - m_xPosForVerticalArrowNavigation = x; - - if (userTriggered == UserTriggered) - m_granularity = CharacterGranularity; - - setCaretRectNeedsUpdate(); - - return true; -} - -// FIXME: Maybe baseline would be better? -static bool absoluteCaretY(const VisiblePosition &c, int &y) -{ - IntRect rect = c.absoluteCaretBounds(); - if (rect.isEmpty()) - return false; - y = rect.y() + rect.height() / 2; - return true; -} - -bool FrameSelection::modify(EAlteration alter, unsigned verticalDistance, VerticalDirection direction, EUserTriggered userTriggered, CursorAlignOnScroll align) -{ - if (!verticalDistance) - return false; - - if (userTriggered == UserTriggered) { - OwnPtr trialFrameSelection = FrameSelection::create(); - trialFrameSelection->setSelection(m_selection); - trialFrameSelection->modify(alter, verticalDistance, direction, NotUserTriggered); - } - - willBeModified(alter, direction == DirectionUp ? DirectionBackward : DirectionForward); - - VisiblePosition pos; - LayoutUnit xPos = 0; - switch (alter) { - case AlterationMove: - pos = VisiblePosition(direction == DirectionUp ? m_selection.start() : m_selection.end(), m_selection.affinity()); - xPos = lineDirectionPointForBlockDirectionNavigation(direction == DirectionUp ? START : END); - m_selection.setAffinity(direction == DirectionUp ? UPSTREAM : DOWNSTREAM); - break; - case AlterationExtend: - pos = VisiblePosition(m_selection.extent(), m_selection.affinity()); - xPos = lineDirectionPointForBlockDirectionNavigation(EXTENT); - m_selection.setAffinity(DOWNSTREAM); - break; - } - - int startY; - if (!absoluteCaretY(pos, startY)) - return false; - if (direction == DirectionUp) - startY = -startY; - int lastY = startY; - - VisiblePosition result; - VisiblePosition next; - for (VisiblePosition p = pos; ; p = next) { - if (direction == DirectionUp) - next = previousLinePosition(p, xPos); - else - next = nextLinePosition(p, xPos); - - if (next.isNull() || next == p) - break; - int nextY; - if (!absoluteCaretY(next, nextY)) - break; - if (direction == DirectionUp) - nextY = -nextY; - if (nextY - startY > static_cast(verticalDistance)) - break; - if (nextY >= lastY) { - lastY = nextY; - result = next; - } - } - - if (result.isNull()) - return false; - - switch (alter) { - case AlterationMove: - moveTo(result, userTriggered, align); - break; - case AlterationExtend: - setExtent(result, userTriggered); - break; - } - - if (userTriggered == UserTriggered) - m_granularity = CharacterGranularity; - - m_selection.setIsDirectional(shouldAlwaysUseDirectionalSelection(m_frame) || alter == AlterationExtend); - - return true; -} - -LayoutUnit FrameSelection::lineDirectionPointForBlockDirectionNavigation(EPositionType type) -{ - LayoutUnit x = 0; - - if (isNone()) - return x; - - Position pos; - switch (type) { - case START: - pos = m_selection.start(); - break; - case END: - pos = m_selection.end(); - break; - case BASE: - pos = m_selection.base(); - break; - case EXTENT: - pos = m_selection.extent(); - break; - } - - LocalFrame* frame = pos.document()->frame(); - if (!frame) - return x; - - if (m_xPosForVerticalArrowNavigation == NoXPosForVerticalArrowNavigation()) { - VisiblePosition visiblePosition(pos, m_selection.affinity()); - // VisiblePosition creation can fail here if a node containing the selection becomes visibility:hidden - // after the selection is created and before this function is called. - x = visiblePosition.isNotNull() ? visiblePosition.lineDirectionPointForBlockDirectionNavigation() : 0; - m_xPosForVerticalArrowNavigation = x; - } else - x = m_xPosForVerticalArrowNavigation; - - return x; -} - -void FrameSelection::clear() -{ - m_granularity = CharacterGranularity; - setSelection(VisibleSelection()); -} - -void FrameSelection::prepareForDestruction() -{ - m_granularity = CharacterGranularity; - - m_caretBlinkTimer.stop(); - - RenderView* view = m_frame->contentRenderer(); - if (view) - view->clearSelection(); - - m_frame = 0; - - setSelection(VisibleSelection(), CloseTyping | ClearTypingStyle | DoNotUpdateAppearance); -} - -void FrameSelection::setStart(const VisiblePosition &pos, EUserTriggered trigger) -{ - if (m_selection.isBaseFirst()) - setBase(pos, trigger); - else - setExtent(pos, trigger); -} - -void FrameSelection::setEnd(const VisiblePosition &pos, EUserTriggered trigger) -{ - if (m_selection.isBaseFirst()) - setExtent(pos, trigger); - else - setBase(pos, trigger); -} - -void FrameSelection::setBase(const VisiblePosition &pos, EUserTriggered userTriggered) -{ - const bool selectionHasDirection = true; - setSelection(VisibleSelection(pos.deepEquivalent(), m_selection.extent(), pos.affinity(), selectionHasDirection), CloseTyping | ClearTypingStyle | userTriggered); -} - -void FrameSelection::setExtent(const VisiblePosition &pos, EUserTriggered userTriggered) -{ - const bool selectionHasDirection = true; - setSelection(VisibleSelection(m_selection.base(), pos.deepEquivalent(), pos.affinity(), selectionHasDirection), CloseTyping | ClearTypingStyle | userTriggered); -} - -RenderBlock* FrameSelection::caretRenderer() const -{ - return CaretBase::caretRenderer(m_selection.start().deprecatedNode()); -} - -static bool isNonOrphanedCaret(const VisibleSelection& selection) -{ - return selection.isCaret() && !selection.start().isOrphan() && !selection.end().isOrphan(); -} - -IntRect FrameSelection::absoluteCaretBounds() -{ - m_frame->document()->updateLayout(); - if (!isNonOrphanedCaret(m_selection)) { - clearCaretRect(); - } else { - updateCaretRect(m_frame->document(), VisiblePosition(m_selection.start(), m_selection.affinity())); - } - return absoluteBoundsForLocalRect(m_selection.start().deprecatedNode(), localCaretRectWithoutUpdate()); -} - -void FrameSelection::paintCaret(GraphicsContext* context, const LayoutPoint& paintOffset, const LayoutRect& clipRect) -{ - if (m_selection.isCaret() && m_shouldPaintCaret) { - updateCaretRect(m_frame->document(), PositionWithAffinity(m_selection.start(), m_selection.affinity())); - CaretBase::paintCaret(m_selection.start().deprecatedNode(), context, paintOffset, clipRect); - } -} - -bool FrameSelection::contains(const LayoutPoint& point) -{ - Document* document = m_frame->document(); - - // Treat a collapsed selection like no selection. - if (!isRange()) - return false; - if (!document->renderView()) - return false; - - HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active); - HitTestResult result(point); - document->renderView()->hitTest(request, result); - Node* innerNode = result.innerNode(); - if (!innerNode || !innerNode->renderer()) - return false; - - VisiblePosition visiblePos(innerNode->renderer()->positionForPoint(result.localPoint())); - if (visiblePos.isNull()) - return false; - - if (m_selection.visibleStart().isNull() || m_selection.visibleEnd().isNull()) - return false; - - Position start(m_selection.visibleStart().deepEquivalent()); - Position end(m_selection.visibleEnd().deepEquivalent()); - Position p(visiblePos.deepEquivalent()); - - return comparePositions(start, p) <= 0 && comparePositions(p, end) <= 0; -} - -void FrameSelection::selectAll() -{ - Document* document = m_frame->document(); - - RefPtr root = nullptr; - Node* selectStartTarget = 0; - if (isContentEditable()) { - root = highestEditableRoot(m_selection.start()); - if (Node* shadowRoot = m_selection.nonBoundaryShadowTreeRootNode()) - selectStartTarget = shadowRoot->shadowHost(); - else - selectStartTarget = root.get(); - } else { - root = m_selection.nonBoundaryShadowTreeRootNode(); - if (root) { - selectStartTarget = root->shadowHost(); - } else { - root = document; - selectStartTarget = document; - } - } - if (!root) - return; - - VisibleSelection newSelection(VisibleSelection::selectionFromContentsOfNode(root.get())); - setSelection(newSelection); - notifyRendererOfSelectionChange(UserTriggered); -} - -bool FrameSelection::setSelectedRange(Range* range, EAffinity affinity, DirectoinalOption directional, SetSelectionOptions options) -{ - if (!range || !range->startContainer() || !range->endContainer()) - return false; - ASSERT(range->startContainer()->document() == range->endContainer()->document()); - - // Non-collapsed ranges are not allowed to start at the end of a line that is wrapped, - // they start at the beginning of the next line instead - m_logicalRange = nullptr; - stopObservingVisibleSelectionChangeIfNecessary(); - - VisibleSelection newSelection(range, affinity, directional == Directional); - setSelection(newSelection, options); - - m_logicalRange = range->cloneRange(); - startObservingVisibleSelectionChange(); - - return true; -} - -PassRefPtr FrameSelection::firstRange() const -{ - if (m_logicalRange) - return m_logicalRange->cloneRange(); - return m_selection.firstRange(); -} - -bool FrameSelection::isInPasswordField() const -{ - return false; -} - -void FrameSelection::notifyAccessibilityForSelectionChange() -{ -} - -void FrameSelection::focusedOrActiveStateChanged() -{ - bool activeAndFocused = isFocusedAndActive(); - - RefPtr document = m_frame->document(); - document->updateRenderTreeIfNeeded(); - - // Caret appears in the active frame. - if (activeAndFocused) - setSelectionFromNone(); - else - m_frame->spellChecker().spellCheckAfterBlur(); - setCaretVisibility(activeAndFocused ? Visible : Hidden); - - // We may have lost active status even though the focusElement hasn't changed - // give the element a chance to recalc style if its affected by focus. - if (Element* element = document->focusedElement()) - element->focusStateChanged(); -} - -void FrameSelection::pageActivationChanged() -{ - focusedOrActiveStateChanged(); -} - -void FrameSelection::setFocused(bool flag) -{ - if (m_focused == flag) - return; - m_focused = flag; - - focusedOrActiveStateChanged(); -} - -bool FrameSelection::isFocusedAndActive() const -{ - return m_focused && m_frame->page() && m_frame->page()->focusController().isActive(); -} - -inline static bool shouldStopBlinkingDueToTypingCommand(LocalFrame* frame) -{ - return frame->editor().lastEditCommand() && frame->editor().lastEditCommand()->shouldStopCaretBlinking(); -} - -void FrameSelection::updateAppearance(ResetCaretBlinkOption option) -{ - // Paint a block cursor instead of a caret in overtype mode unless the caret is at the end of a line (in this case - // the FrameSelection will paint a blinking caret as usual). - bool paintBlockCursor = m_shouldShowBlockCursor && m_selection.isCaret() && !isLogicalEndOfLine(m_selection.visibleEnd()); - - bool shouldBlink = !paintBlockCursor && shouldBlinkCaret(); - - bool willNeedCaretRectUpdate = false; - - // If the caret moved, stop the blink timer so we can restart with a - // black caret in the new location. - if (option == ResetCaretBlink || !shouldBlink || shouldStopBlinkingDueToTypingCommand(m_frame)) { - m_caretBlinkTimer.stop(); - - m_shouldPaintCaret = false; - willNeedCaretRectUpdate = true; - } - - // Start blinking with a black caret. Be sure not to restart if we're - // already blinking in the right location. - if (shouldBlink && !m_caretBlinkTimer.isActive()) { - if (double blinkInterval = RenderTheme::theme().caretBlinkInterval()) - m_caretBlinkTimer.startRepeating(blinkInterval, FROM_HERE); - - m_shouldPaintCaret = true; - willNeedCaretRectUpdate = true; - } - - if (willNeedCaretRectUpdate) - setCaretRectNeedsUpdate(); - - RenderView* view = m_frame->contentRenderer(); - if (!view) - return; - - // Construct a new VisibleSolution, since m_selection is not necessarily valid, and the following steps - // assume a valid selection. See and . - - VisibleSelection selection; - VisiblePosition endVisiblePosition = paintBlockCursor ? modifyExtendingForward(CharacterGranularity) : m_selection.visibleEnd(); - selection = VisibleSelection(m_selection.visibleStart(), endVisiblePosition); - - if (!selection.isRange()) { - view->clearSelection(); - return; - } - - m_frame->document()->updateLayout(); - - // Use the rightmost candidate for the start of the selection, and the leftmost candidate for the end of the selection. - // Example: foo bar. Imagine that a line wrap occurs after 'foo', and that 'bar' is selected. If we pass [foo, 3] - // as the start of the selection, the selection painting code will think that content on the line containing 'foo' is selected - // and will fill the gap before 'bar'. - Position startPos = selection.start(); - Position candidate = startPos.downstream(); - if (candidate.isCandidate()) - startPos = candidate; - Position endPos = selection.end(); - candidate = endPos.upstream(); - if (candidate.isCandidate()) - endPos = candidate; - - // We can get into a state where the selection endpoints map to the same VisiblePosition when a selection is deleted - // because we don't yet notify the FrameSelection of text removal. - if (startPos.isNotNull() && endPos.isNotNull() && selection.visibleStart() != selection.visibleEnd()) { - RenderObject* startRenderer = startPos.deprecatedNode()->renderer(); - RenderObject* endRenderer = endPos.deprecatedNode()->renderer(); - if (startRenderer->view() == view && endRenderer->view() == view) - view->setSelection(startRenderer, startPos.deprecatedEditingOffset(), endRenderer, endPos.deprecatedEditingOffset()); - } -} - -void FrameSelection::setCaretVisibility(CaretVisibility visibility) -{ - if (caretVisibility() == visibility) - return; - - CaretBase::setCaretVisibility(visibility); - - updateAppearance(); -} - -bool FrameSelection::shouldBlinkCaret() const -{ - if (!caretIsVisible() || !isCaret()) - return false; - - Element* root = rootEditableElement(); - if (!root) - return false; - - Element* focusedElement = root->document().focusedElement(); - if (!focusedElement) - return false; - - return focusedElement->containsIncludingShadowDOM(m_selection.start().anchorNode()); -} - -void FrameSelection::caretBlinkTimerFired(Timer*) -{ - ASSERT(caretIsVisible()); - ASSERT(isCaret()); - if (isCaretBlinkingSuspended() && m_shouldPaintCaret) - return; - m_shouldPaintCaret = !m_shouldPaintCaret; - setCaretRectNeedsUpdate(); -} - -void FrameSelection::notifyRendererOfSelectionChange(EUserTriggered userTriggered) -{ -} - -// Helper function that tells whether a particular node is an element that has an entire -// LocalFrame and FrameView, a ,