Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / page / DOMSelection.cpp
index fbba561..04a5a90 100644 (file)
 #include "core/editing/FrameSelection.h"
 #include "core/editing/TextIterator.h"
 #include "core/editing/htmlediting.h"
-#include "core/frame/Frame.h"
+#include "core/frame/LocalFrame.h"
 #include "wtf/text/WTFString.h"
 
 namespace WebCore {
 
-static Node* selectionShadowAncestor(Frame* frame)
+static Node* selectionShadowAncestor(LocalFrame* frame)
 {
     Node* node = frame->selection().selection().base().anchorNode();
     if (!node)
@@ -60,7 +60,7 @@ static Node* selectionShadowAncestor(Frame* frame)
 }
 
 DOMSelection::DOMSelection(const TreeScope* treeScope)
-    : DOMWindowProperty(treeScope->rootNode()->document().frame())
+    : DOMWindowProperty(treeScope->rootNode().document().frame())
     , m_treeScope(treeScope)
 {
     ScriptWrappable::init(this);
@@ -68,7 +68,7 @@ DOMSelection::DOMSelection(const TreeScope* treeScope)
 
 void DOMSelection::clearTreeScope()
 {
-    m_treeScope = 0;
+    m_treeScope = nullptr;
 }
 
 const VisibleSelection& DOMSelection::visibleSelection() const
@@ -194,13 +194,14 @@ int DOMSelection::rangeCount() const
     return m_frame->selection().isNone() ? 0 : 1;
 }
 
-void DOMSelection::collapse(Node* node, int offset, ExceptionState& es)
+void DOMSelection::collapse(Node* node, int offset, ExceptionState& exceptionState)
 {
+    ASSERT(node);
     if (!m_frame)
         return;
 
     if (offset < 0) {
-        es.throwDOMException(IndexSizeError, ExceptionMessages::failedToExecute("collapse", "Selection", String::number(offset) + " is not a valid offset."));
+        exceptionState.throwDOMException(IndexSizeError, String::number(offset) + " is not a valid offset.");
         return;
     }
 
@@ -211,7 +212,12 @@ void DOMSelection::collapse(Node* node, int offset, ExceptionState& es)
     m_frame->selection().moveTo(VisiblePosition(createLegacyEditingPosition(node, offset), DOWNSTREAM));
 }
 
-void DOMSelection::collapseToEnd(ExceptionState& es)
+void DOMSelection::collapse(Node* node, ExceptionState& exceptionState)
+{
+    collapse(node, 0, exceptionState);
+}
+
+void DOMSelection::collapseToEnd(ExceptionState& exceptionState)
 {
     if (!m_frame)
         return;
@@ -219,14 +225,14 @@ void DOMSelection::collapseToEnd(ExceptionState& es)
     const VisibleSelection& selection = m_frame->selection().selection();
 
     if (selection.isNone()) {
-        es.throwDOMException(InvalidStateError, ExceptionMessages::failedToExecute("collapseToEnd", "Selection", "there is no selection."));
+        exceptionState.throwDOMException(InvalidStateError, "there is no selection.");
         return;
     }
 
     m_frame->selection().moveTo(VisiblePosition(selection.end(), DOWNSTREAM));
 }
 
-void DOMSelection::collapseToStart(ExceptionState& es)
+void DOMSelection::collapseToStart(ExceptionState& exceptionState)
 {
     if (!m_frame)
         return;
@@ -234,7 +240,7 @@ void DOMSelection::collapseToStart(ExceptionState& es)
     const VisibleSelection& selection = m_frame->selection().selection();
 
     if (selection.isNone()) {
-        es.throwDOMException(InvalidStateError, ExceptionMessages::failedToExecute("collapseToStart", "Selection", "there is no selection."));
+        exceptionState.throwDOMException(InvalidStateError, "there is no selection.");
         return;
     }
 
@@ -248,18 +254,18 @@ void DOMSelection::empty()
     m_frame->selection().clear();
 }
 
-void DOMSelection::setBaseAndExtent(Node* baseNode, int baseOffset, Node* extentNode, int extentOffset, ExceptionState& es)
+void DOMSelection::setBaseAndExtent(Node* baseNode, int baseOffset, Node* extentNode, int extentOffset, ExceptionState& exceptionState)
 {
     if (!m_frame)
         return;
 
     if (baseOffset < 0) {
-        es.throwDOMException(IndexSizeError, ExceptionMessages::failedToExecute("setBaseAndExtent", "Selection", String::number(baseOffset) + " is not a valid base offset."));
+        exceptionState.throwDOMException(IndexSizeError, String::number(baseOffset) + " is not a valid base offset.");
         return;
     }
 
     if (extentOffset < 0) {
-        es.throwDOMException(IndexSizeError, ExceptionMessages::failedToExecute("setBaseAndExtent", "Selection", String::number(extentOffset) + " is not a valid extent offset."));
+        exceptionState.throwDOMException(IndexSizeError, String::number(extentOffset) + " is not a valid extent offset.");
         return;
     }
 
@@ -273,22 +279,6 @@ void DOMSelection::setBaseAndExtent(Node* baseNode, int baseOffset, Node* extent
     m_frame->selection().moveTo(visibleBase, visibleExtent);
 }
 
-void DOMSelection::setPosition(Node* node, int offset, ExceptionState& es)
-{
-    if (!m_frame)
-        return;
-    if (offset < 0) {
-        es.throwDOMException(IndexSizeError, ExceptionMessages::failedToExecute("setPosition", "Selection", String::number(offset) + " is not a valid offset."));
-        return;
-    }
-
-    if (!isValidForPosition(node))
-        return;
-
-    // FIXME: Eliminate legacy editing positions
-    m_frame->selection().moveTo(VisiblePosition(createLegacyEditingPosition(node, offset), DOWNSTREAM));
-}
-
 void DOMSelection::modify(const String& alterString, const String& directionString, const String& granularityString)
 {
     if (!m_frame)
@@ -339,22 +329,22 @@ void DOMSelection::modify(const String& alterString, const String& directionStri
     m_frame->selection().modify(alter, direction, granularity);
 }
 
-void DOMSelection::extend(Node* node, int offset, ExceptionState& es)
+void DOMSelection::extend(Node* node, int offset, ExceptionState& exceptionState)
 {
     if (!m_frame)
         return;
 
     if (!node) {
-        es.throwDOMException(TypeMismatchError, ExceptionMessages::failedToExecute("extend", "Selection", "The node provided is invalid."));
+        exceptionState.throwDOMException(TypeMismatchError, ExceptionMessages::argumentNullOrIncorrectType(1, "Node"));
         return;
     }
 
     if (offset < 0) {
-        es.throwDOMException(IndexSizeError, ExceptionMessages::failedToExecute("extend", "Selection", String::number(offset) + " is not a valid offset."));
+        exceptionState.throwDOMException(IndexSizeError, String::number(offset) + " is not a valid offset.");
         return;
     }
-    if (offset > (node->offsetInCharacters() ? caretMaxOffset(node) : (int)node->childNodeCount())) {
-        es.throwDOMException(IndexSizeError, ExceptionMessages::failedToExecute("extend", "Selection", String::number(offset) + " is larger than the given node's length."));
+    if (offset > (node->offsetInCharacters() ? caretMaxOffset(node) : (int)node->countChildren())) {
+        exceptionState.throwDOMException(IndexSizeError, String::number(offset) + " is larger than the given node's length.");
         return;
     }
 
@@ -365,27 +355,27 @@ void DOMSelection::extend(Node* node, int offset, ExceptionState& es)
     m_frame->selection().setExtent(VisiblePosition(createLegacyEditingPosition(node, offset), DOWNSTREAM));
 }
 
-PassRefPtr<Range> DOMSelection::getRangeAt(int index, ExceptionState& es)
+PassRefPtrWillBeRawPtr<Range> DOMSelection::getRangeAt(int index, ExceptionState& exceptionState)
 {
     if (!m_frame)
-        return 0;
+        return nullptr;
 
     if (index < 0 || index >= rangeCount()) {
-        es.throwDOMException(IndexSizeError, ExceptionMessages::failedToExecute("getRangeAt", "Selection", String::number(index) + " is not a valid index."));
-        return 0;
+        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)) {
-        ContainerNode* container = shadowAncestor->parentNodeGuaranteedHostFree();
+        ASSERT(!shadowAncestor->isShadowRoot());
+        ContainerNode* container = shadowAncestor->parentOrShadowHostNode();
         int offset = shadowAncestor->nodeIndex();
         return Range::create(shadowAncestor->document(), container, offset, container, offset);
     }
 
-    const VisibleSelection& selection = m_frame->selection().selection();
-    return selection.firstRange();
+    return m_frame->selection().firstRange();
 }
 
 void DOMSelection::removeAllRanges()
@@ -395,45 +385,56 @@ void DOMSelection::removeAllRanges()
     m_frame->selection().clear();
 }
 
-void DOMSelection::addRange(Range* r)
+void DOMSelection::addRange(Range* newRange)
 {
     if (!m_frame)
         return;
-    if (!r)
+
+    // FIXME: Should we throw DOMException for error cases below?
+    if (!newRange) {
+        addConsoleError("The given range is null.");
         return;
+    }
+
+    if (!newRange->startContainer()) {
+        addConsoleError("The given range has no container. Perhaps 'detach()' has been invoked on it?");
+        return;
+    }
 
     FrameSelection& selection = m_frame->selection();
 
     if (selection.isNone()) {
-        selection.setSelection(VisibleSelection(r));
+        selection.setSelectedRange(newRange, VP_DEFAULT_AFFINITY);
         return;
     }
 
-    RefPtr<Range> range = selection.selection().toNormalizedRange();
-    if (r->compareBoundaryPoints(Range::START_TO_START, range.get(), IGNORE_EXCEPTION) == -1) {
-        // We don't support discontiguous selection. We don't do anything if r and range don't intersect.
-        if (r->compareBoundaryPoints(Range::START_TO_END, range.get(), IGNORE_EXCEPTION) > -1) {
-            if (r->compareBoundaryPoints(Range::END_TO_END, range.get(), IGNORE_EXCEPTION) == -1) {
-                // The original range and r intersect.
-                selection.setSelection(VisibleSelection(r->startPosition(), range->endPosition(), DOWNSTREAM));
-            } else {
-                // r contains the original range.
-                selection.setSelection(VisibleSelection(r));
-            }
-        }
-    } else {
-        // We don't support discontiguous selection. We don't do anything if r and range don't intersect.
-        TrackExceptionState es;
-        if (r->compareBoundaryPoints(Range::END_TO_START, range.get(), es) < 1 && !es.hadException()) {
-            if (r->compareBoundaryPoints(Range::END_TO_END, range.get(), IGNORE_EXCEPTION) == -1) {
-                // The original range contains r.
-                selection.setSelection(VisibleSelection(range.get()));
-            } else {
-                // The original range and r intersect.
-                selection.setSelection(VisibleSelection(range->startPosition(), r->endPosition(), DOWNSTREAM));
-            }
-        }
+    RefPtrWillBeRawPtr<Range> originalRange = selection.firstRange();
+
+    if (originalRange->startContainer()->document() != newRange->startContainer()->document()) {
+        addConsoleError("The given range does not belong to the current selection's document.");
+        return;
     }
+    if (originalRange->startContainer()->treeScope() != newRange->startContainer()->treeScope()) {
+        addConsoleError("The given range and the current selection belong to two different document fragments.");
+        return;
+    }
+
+    if (originalRange->compareBoundaryPoints(Range::START_TO_END, newRange, ASSERT_NO_EXCEPTION) < 0
+        || newRange->compareBoundaryPoints(Range::START_TO_END, originalRange.get(), ASSERT_NO_EXCEPTION) < 0) {
+        addConsoleError("Discontiguous selection is not supported.");
+        return;
+    }
+
+    // FIXME: "Merge the ranges if they intersect" is Blink-specific behavior; other browsers supporting discontiguous
+    // selection (obviously) keep each Range added and return it in getRangeAt(). But it's unclear if we can really
+    // do the same, since we don't support discontiguous selection. Further discussions at
+    // <https://code.google.com/p/chromium/issues/detail?id=353069>.
+
+    Range* start = originalRange->compareBoundaryPoints(Range::START_TO_START, newRange, ASSERT_NO_EXCEPTION) < 0 ? originalRange.get() : newRange;
+    Range* end = originalRange->compareBoundaryPoints(Range::END_TO_END, newRange, ASSERT_NO_EXCEPTION) < 0 ? newRange : originalRange.get();
+    RefPtrWillBeRawPtr<Range> merged = Range::create(originalRange->startContainer()->document(), start->startContainer(), start->startOffset(), end->endContainer(), end->endOffset());
+    EAffinity affinity = selection.selection().affinity();
+    selection.setSelectedRange(merged.get(), affinity);
 }
 
 void DOMSelection::deleteFromDocument()
@@ -446,16 +447,13 @@ void DOMSelection::deleteFromDocument()
     if (selection.isNone())
         return;
 
-    if (isCollapsed())
-        selection.modify(FrameSelection::AlterationExtend, DirectionBackward, CharacterGranularity);
-
-    RefPtr<Range> selectedRange = selection.selection().toNormalizedRange();
+    RefPtrWillBeRawPtr<Range> selectedRange = selection.selection().toNormalizedRange();
     if (!selectedRange)
         return;
 
     selectedRange->deleteContents(ASSERT_NO_EXCEPTION);
 
-    setBaseAndExtent(selectedRange->startContainer(ASSERT_NO_EXCEPTION), selectedRange->startOffset(), selectedRange->startContainer(), selectedRange->startOffset(), ASSERT_NO_EXCEPTION);
+    setBaseAndExtent(selectedRange->startContainer(), selectedRange->startOffset(), selectedRange->startContainer(), selectedRange->startOffset(), ASSERT_NO_EXCEPTION);
 }
 
 bool DOMSelection::containsNode(const Node* n, bool allowPartial) const
@@ -469,35 +467,36 @@ bool DOMSelection::containsNode(const Node* n, bool allowPartial) const
         return false;
 
     unsigned nodeIndex = n->nodeIndex();
-    RefPtr<Range> selectedRange = selection.selection().toNormalizedRange();
+    RefPtrWillBeRawPtr<Range> selectedRange = selection.selection().toNormalizedRange();
 
     ContainerNode* parentNode = n->parentNode();
     if (!parentNode)
         return false;
 
-    TrackExceptionState es;
-    bool nodeFullySelected = Range::compareBoundaryPoints(parentNode, nodeIndex, selectedRange->startContainer(), selectedRange->startOffset(), es) >= 0 && !es.hadException()
-        && Range::compareBoundaryPoints(parentNode, nodeIndex + 1, selectedRange->endContainer(), selectedRange->endOffset(), es) <= 0 && !es.hadException();
-    ASSERT(!es.hadException());
+    TrackExceptionState exceptionState;
+    bool nodeFullySelected = Range::compareBoundaryPoints(parentNode, nodeIndex, selectedRange->startContainer(), selectedRange->startOffset(), exceptionState) >= 0 && !exceptionState.hadException()
+        && Range::compareBoundaryPoints(parentNode, nodeIndex + 1, selectedRange->endContainer(), selectedRange->endOffset(), exceptionState) <= 0 && !exceptionState.hadException();
+    if (exceptionState.hadException())
+        return false;
     if (nodeFullySelected)
         return true;
 
-    bool nodeFullyUnselected = (Range::compareBoundaryPoints(parentNode, nodeIndex, selectedRange->endContainer(), selectedRange->endOffset(), es) > 0 && !es.hadException())
-        || (Range::compareBoundaryPoints(parentNode, nodeIndex + 1, selectedRange->startContainer(), selectedRange->startOffset(), es) < 0 && !es.hadException());
-    ASSERT(!es.hadException());
+    bool nodeFullyUnselected = (Range::compareBoundaryPoints(parentNode, nodeIndex, selectedRange->endContainer(), selectedRange->endOffset(), exceptionState) > 0 && !exceptionState.hadException())
+        || (Range::compareBoundaryPoints(parentNode, nodeIndex + 1, selectedRange->startContainer(), selectedRange->startOffset(), exceptionState) < 0 && !exceptionState.hadException());
+    ASSERT(!exceptionState.hadException());
     if (nodeFullyUnselected)
         return false;
 
     return allowPartial || n->isTextNode();
 }
 
-void DOMSelection::selectAllChildren(Node* n, ExceptionState& es)
+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->childNodeCount(), es);
+    setBaseAndExtent(n, 0, n, n->countChildren(), exceptionState);
 }
 
 String DOMSelection::toString()
@@ -522,7 +521,8 @@ Node* DOMSelection::shadowAdjustedNode(const Position& position) const
     if (containerNode == adjustedNode)
         return containerNode;
 
-    return adjustedNode->parentNodeGuaranteedHostFree();
+    ASSERT(!adjustedNode->isShadowRoot());
+    return adjustedNode->parentOrShadowHostNode();
 }
 
 int DOMSelection::shadowAdjustedOffset(const Position& position) const
@@ -550,4 +550,10 @@ bool DOMSelection::isValidForPosition(Node* node) const
     return node->document() == m_frame->document();
 }
 
+void DOMSelection::addConsoleError(const String& message)
+{
+    if (m_treeScope)
+        m_treeScope->document().addConsoleMessage(JSMessageSource, ErrorMessageLevel, message);
+}
+
 } // namespace WebCore