From 94d9cb25a3981832921b252175f0c3cc223dd702 Mon Sep 17 00:00:00 2001 From: Hixie Date: Thu, 16 Jul 2015 16:29:37 -0700 Subject: [PATCH] Remove editing code from the C++ side of Sky. This removes the bulk of core/editing/*. The following files remain, because they might be useful yet: EditingBoundary.h FindOptions.h htmlediting.cpp htmlediting.h PlainTextRange.cpp PlainTextRange.h PositionWithAffinity.cpp PositionWithAffinity.h RenderedPosition.cpp RenderedPosition.h TextAffinity.h TextGranularity.h TextIterator.cpp TextIterator.h VisiblePosition.cpp VisiblePosition.h VisibleSelection.cpp VisibleSelection.h VisibleUnits.cpp VisibleUnits.h In addition to remove obviously editing-related stuff like "ApplyBlockElementCommand.cpp" and "InsertLineBreakCommand.cpp", this also removes the DOM side of selection, all the caret management and painting code, composition support (IME) including the relevant events, spelling checker support, and the undo stack. Outside the core/editing/* directory, I also deleted the EditorClient, SpellCheckerClient, and EmptyClients classes. The other changes outside of editing/ are mostly just about removing mentions of the selection or carets. I tried to leave the code for _painting_ selections and composition runs, though that code is mostly disconnected now. --- sky/engine/core/core.gni | 85 +- sky/engine/core/css/SelectorChecker.cpp | 3 - sky/engine/core/dom/CharacterData.cpp | 4 - sky/engine/core/dom/Document.cpp | 22 +- sky/engine/core/dom/Document.idl | 3 - .../core/dom/DocumentMarkerController.h | 1 + sky/engine/core/dom/Element.cpp | 12 - sky/engine/core/dom/TreeScope.cpp | 21 - sky/engine/core/dom/TreeScope.h | 5 - sky/engine/core/dom/shadow/ShadowRoot.idl | 1 - sky/engine/core/editing/AppendNodeCommand.cpp | 60 - sky/engine/core/editing/AppendNodeCommand.h | 52 - .../core/editing/ApplyBlockElementCommand.cpp | 232 --- .../core/editing/ApplyBlockElementCommand.h | 60 - sky/engine/core/editing/Caret.cpp | 214 --- sky/engine/core/editing/Caret.h | 104 -- .../core/editing/CompositeEditCommand.cpp | 856 --------- .../core/editing/CompositeEditCommand.h | 154 -- .../CompositionUnderlineRangeFilterTest.cpp | 89 - sky/engine/core/editing/DOMSelection.cpp | 523 ------ sky/engine/core/editing/DOMSelection.h | 115 -- .../editing/DeleteFromTextNodeCommand.cpp | 70 - .../core/editing/DeleteFromTextNodeCommand.h | 56 - .../core/editing/DeleteSelectionCommand.cpp | 660 ------- .../core/editing/DeleteSelectionCommand.h | 96 - sky/engine/core/editing/EditAction.h | 73 - sky/engine/core/editing/EditCommand.cpp | 122 -- sky/engine/core/editing/EditCommand.h | 95 - sky/engine/core/editing/EditingBehavior.cpp | 185 -- sky/engine/core/editing/EditingBehavior.h | 96 - sky/engine/core/editing/EditingStyle.cpp | 584 ------ sky/engine/core/editing/EditingStyle.h | 136 -- sky/engine/core/editing/Editor.cpp | 793 -------- sky/engine/core/editing/Editor.h | 264 --- sky/engine/core/editing/EditorCommand.cpp | 632 ------- sky/engine/core/editing/EditorKeyBindings.cpp | 69 - sky/engine/core/editing/FrameSelection.cpp | 1624 ----------------- sky/engine/core/editing/FrameSelection.h | 293 --- sky/engine/core/editing/HTMLInterchange.cpp | 99 - sky/engine/core/editing/HTMLInterchange.h | 47 - .../core/editing/InputMethodController.cpp | 379 ---- .../core/editing/InputMethodController.h | 118 -- .../editing/InsertIntoTextNodeCommand.cpp | 63 - .../core/editing/InsertIntoTextNodeCommand.h | 55 - .../core/editing/InsertLineBreakCommand.cpp | 139 -- .../core/editing/InsertLineBreakCommand.h | 50 - .../core/editing/InsertNodeBeforeCommand.cpp | 65 - .../core/editing/InsertNodeBeforeCommand.h | 54 - .../InsertParagraphSeparatorCommand.cpp | 315 ---- .../editing/InsertParagraphSeparatorCommand.h | 62 - sky/engine/core/editing/InsertTextCommand.cpp | 221 --- sky/engine/core/editing/InsertTextCommand.h | 67 - .../core/editing/MoveSelectionCommand.cpp | 85 - .../core/editing/MoveSelectionCommand.h | 56 - sky/engine/core/editing/RemoveNodeCommand.cpp | 67 - sky/engine/core/editing/RemoveNodeCommand.h | 54 - .../RemoveNodePreservingChildrenCommand.cpp | 57 - .../RemoveNodePreservingChildrenCommand.h | 51 - .../core/editing/ReplaceSelectionCommand.cpp | 984 ---------- .../core/editing/ReplaceSelectionCommand.h | 118 -- sky/engine/core/editing/Selection.idl | 93 - sky/engine/core/editing/SelectionType.h | 35 - sky/engine/core/editing/SmartReplace.h | 40 - sky/engine/core/editing/SmartReplaceICU.cpp | 98 - .../core/editing/SpellCheckRequester.cpp | 265 --- sky/engine/core/editing/SpellCheckRequester.h | 123 -- sky/engine/core/editing/SpellChecker.cpp | 881 --------- sky/engine/core/editing/SpellChecker.h | 106 -- .../core/editing/SplitElementCommand.cpp | 109 -- sky/engine/core/editing/SplitElementCommand.h | 55 - .../core/editing/SplitTextNodeCommand.cpp | 105 -- .../core/editing/SplitTextNodeCommand.h | 57 - .../core/editing/TextCheckingHelper.cpp | 577 ------ sky/engine/core/editing/TextCheckingHelper.h | 108 -- .../core/editing/TextInsertionBaseCommand.cpp | 65 - .../core/editing/TextInsertionBaseCommand.h | 70 - sky/engine/core/editing/TypingCommand.cpp | 559 ------ sky/engine/core/editing/TypingCommand.h | 136 -- sky/engine/core/editing/UndoStack.cpp | 122 -- sky/engine/core/editing/UndoStack.h | 70 - sky/engine/core/editing/UndoStep.h | 54 - sky/engine/core/editing/VisibleSelection.cpp | 809 -------- sky/engine/core/editing/VisibleSelection.h | 189 -- sky/engine/core/editing/WritingDirection.h | 31 - sky/engine/core/editing/htmlediting.cpp | 56 - sky/engine/core/editing/htmlediting.h | 11 - sky/engine/core/events/CompositionEvent.cpp | 100 - sky/engine/core/events/CompositionEvent.h | 84 - sky/engine/core/events/CompositionEvent.idl | 42 - sky/engine/core/frame/Frame.cpp | 1 - sky/engine/core/frame/FrameView.cpp | 4 - sky/engine/core/frame/LocalDOMWindow.cpp | 6 - sky/engine/core/frame/LocalDOMWindow.h | 3 - sky/engine/core/frame/LocalFrame.cpp | 54 +- sky/engine/core/frame/LocalFrame.h | 36 - sky/engine/core/frame/Window.idl | 2 - sky/engine/core/loader/EmptyClients.cpp | 53 - sky/engine/core/loader/EmptyClients.h | 142 -- sky/engine/core/loader/FrameLoader.cpp | 6 - sky/engine/core/page/EditorClient.h | 54 - sky/engine/core/page/FocusController.cpp | 4 - sky/engine/core/page/Page.cpp | 10 - sky/engine/core/page/Page.h | 17 - sky/engine/core/page/SpellCheckerClient.h | 53 - sky/engine/core/rendering/HitTestResult.cpp | 8 +- sky/engine/core/rendering/InlineTextBox.cpp | 35 +- sky/engine/core/rendering/InlineTextBox.h | 1 + sky/engine/core/rendering/RenderBlock.cpp | 39 +- sky/engine/core/rendering/RenderBlock.h | 3 - sky/engine/core/rendering/RenderObject.cpp | 18 +- sky/engine/core/rendering/RenderTheme.cpp | 1 - .../core/rendering/RenderTreeAsText.cpp | 25 - 112 files changed, 62 insertions(+), 17233 deletions(-) delete mode 100644 sky/engine/core/editing/AppendNodeCommand.cpp delete mode 100644 sky/engine/core/editing/AppendNodeCommand.h delete mode 100644 sky/engine/core/editing/ApplyBlockElementCommand.cpp delete mode 100644 sky/engine/core/editing/ApplyBlockElementCommand.h delete mode 100644 sky/engine/core/editing/Caret.cpp delete mode 100644 sky/engine/core/editing/Caret.h delete mode 100644 sky/engine/core/editing/CompositeEditCommand.cpp delete mode 100644 sky/engine/core/editing/CompositeEditCommand.h delete mode 100644 sky/engine/core/editing/CompositionUnderlineRangeFilterTest.cpp delete mode 100644 sky/engine/core/editing/DOMSelection.cpp delete mode 100644 sky/engine/core/editing/DOMSelection.h delete mode 100644 sky/engine/core/editing/DeleteFromTextNodeCommand.cpp delete mode 100644 sky/engine/core/editing/DeleteFromTextNodeCommand.h delete mode 100644 sky/engine/core/editing/DeleteSelectionCommand.cpp delete mode 100644 sky/engine/core/editing/DeleteSelectionCommand.h delete mode 100644 sky/engine/core/editing/EditAction.h delete mode 100644 sky/engine/core/editing/EditCommand.cpp delete mode 100644 sky/engine/core/editing/EditCommand.h delete mode 100644 sky/engine/core/editing/EditingBehavior.cpp delete mode 100644 sky/engine/core/editing/EditingBehavior.h delete mode 100644 sky/engine/core/editing/EditingStyle.cpp delete mode 100644 sky/engine/core/editing/EditingStyle.h delete mode 100644 sky/engine/core/editing/Editor.cpp delete mode 100644 sky/engine/core/editing/Editor.h delete mode 100644 sky/engine/core/editing/EditorCommand.cpp delete mode 100644 sky/engine/core/editing/EditorKeyBindings.cpp delete mode 100644 sky/engine/core/editing/FrameSelection.cpp delete mode 100644 sky/engine/core/editing/FrameSelection.h delete mode 100644 sky/engine/core/editing/HTMLInterchange.cpp delete mode 100644 sky/engine/core/editing/HTMLInterchange.h delete mode 100644 sky/engine/core/editing/InputMethodController.cpp delete mode 100644 sky/engine/core/editing/InputMethodController.h delete mode 100644 sky/engine/core/editing/InsertIntoTextNodeCommand.cpp delete mode 100644 sky/engine/core/editing/InsertIntoTextNodeCommand.h delete mode 100644 sky/engine/core/editing/InsertLineBreakCommand.cpp delete mode 100644 sky/engine/core/editing/InsertLineBreakCommand.h delete mode 100644 sky/engine/core/editing/InsertNodeBeforeCommand.cpp delete mode 100644 sky/engine/core/editing/InsertNodeBeforeCommand.h delete mode 100644 sky/engine/core/editing/InsertParagraphSeparatorCommand.cpp delete mode 100644 sky/engine/core/editing/InsertParagraphSeparatorCommand.h delete mode 100644 sky/engine/core/editing/InsertTextCommand.cpp delete mode 100644 sky/engine/core/editing/InsertTextCommand.h delete mode 100644 sky/engine/core/editing/MoveSelectionCommand.cpp delete mode 100644 sky/engine/core/editing/MoveSelectionCommand.h delete mode 100644 sky/engine/core/editing/RemoveNodeCommand.cpp delete mode 100644 sky/engine/core/editing/RemoveNodeCommand.h delete mode 100644 sky/engine/core/editing/RemoveNodePreservingChildrenCommand.cpp delete mode 100644 sky/engine/core/editing/RemoveNodePreservingChildrenCommand.h delete mode 100644 sky/engine/core/editing/ReplaceSelectionCommand.cpp delete mode 100644 sky/engine/core/editing/ReplaceSelectionCommand.h delete mode 100644 sky/engine/core/editing/Selection.idl delete mode 100644 sky/engine/core/editing/SelectionType.h delete mode 100644 sky/engine/core/editing/SmartReplace.h delete mode 100644 sky/engine/core/editing/SmartReplaceICU.cpp delete mode 100644 sky/engine/core/editing/SpellCheckRequester.cpp delete mode 100644 sky/engine/core/editing/SpellCheckRequester.h delete mode 100644 sky/engine/core/editing/SpellChecker.cpp delete mode 100644 sky/engine/core/editing/SpellChecker.h delete mode 100644 sky/engine/core/editing/SplitElementCommand.cpp delete mode 100644 sky/engine/core/editing/SplitElementCommand.h delete mode 100644 sky/engine/core/editing/SplitTextNodeCommand.cpp delete mode 100644 sky/engine/core/editing/SplitTextNodeCommand.h delete mode 100644 sky/engine/core/editing/TextCheckingHelper.cpp delete mode 100644 sky/engine/core/editing/TextCheckingHelper.h delete mode 100644 sky/engine/core/editing/TextInsertionBaseCommand.cpp delete mode 100644 sky/engine/core/editing/TextInsertionBaseCommand.h delete mode 100644 sky/engine/core/editing/TypingCommand.cpp delete mode 100644 sky/engine/core/editing/TypingCommand.h delete mode 100644 sky/engine/core/editing/UndoStack.cpp delete mode 100644 sky/engine/core/editing/UndoStack.h delete mode 100644 sky/engine/core/editing/UndoStep.h delete mode 100644 sky/engine/core/editing/VisibleSelection.cpp delete mode 100644 sky/engine/core/editing/VisibleSelection.h delete mode 100644 sky/engine/core/editing/WritingDirection.h delete mode 100644 sky/engine/core/events/CompositionEvent.cpp delete mode 100644 sky/engine/core/events/CompositionEvent.h delete mode 100644 sky/engine/core/events/CompositionEvent.idl delete mode 100644 sky/engine/core/loader/EmptyClients.cpp delete mode 100644 sky/engine/core/loader/EmptyClients.h delete mode 100644 sky/engine/core/page/EditorClient.h delete mode 100644 sky/engine/core/page/SpellCheckerClient.h 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 ,