Do not set invalid selection on mouse down

This patch changes |SelectionController| not to set invalid selection by
mouse button down. When processing mouse button down, Blink dispatches
"selectstart" event then updates selection.

Before this patch, Blink attempts to set invalid selection which invalidate by
"selectstart" event handler.

BUG=407791
TEST=LayoutTests/editing/selection/selectstart-event-crash.html

Review URL: https://codereview.chromium.org/1288883002

git-svn-id: svn://svn.chromium.org/blink/trunk@200521 bbb929c8-8fbe-4397-9dbb-9b2b20218538
diff --git a/third_party/WebKit/LayoutTests/editing/selection/selectstart-event-crash.html b/third_party/WebKit/LayoutTests/editing/selection/selectstart-event-crash.html
new file mode 100644
index 0000000..c247f07
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/editing/selection/selectstart-event-crash.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<div id="sample"><table id="one"><caption></caption></table></div>
+<div id="log"></div>
+<script>
+var one = document.querySelector('#one');
+document.addEventListener('selectstart', function() {
+    one.remove();
+});
+test(function() {
+    if (!window.eventSender)
+        return;
+    eventSender.mouseMoveTo(one.offsetLeft, one.offsetTop);
+    eventSender.mouseDown();
+    var selection = getSelection();
+    assert_equals(selection.rangeCount, 0);
+});
+</script>
diff --git a/third_party/WebKit/Source/core/editing/SelectionController.cpp b/third_party/WebKit/Source/core/editing/SelectionController.cpp
index 4a33683f..9ba2fe7 100644
--- a/third_party/WebKit/Source/core/editing/SelectionController.cpp
+++ b/third_party/WebKit/Source/core/editing/SelectionController.cpp
@@ -238,6 +238,10 @@
     if (m_selectionState == SelectionState::HaveNotStartedSelection && !dispatchSelectStart(target))
         return;
 
+    // TODO(yosin) We should check |mousePressNode|, |targetPosition|, and
+    // |newSelection| are valid for |m_frame->document()|.
+    // |dispatchSelectStart()| can change them by "selectstart" event handler.
+
     if (m_selectionState != SelectionState::ExtendedSelection) {
         // Always extend selection here because it's caused by a mouse drag
         m_selectionState = SelectionState::ExtendedSelection;
@@ -285,6 +289,9 @@
     if (!dispatchSelectStart(targetNode))
         return false;
 
+    if (!selection.isValidFor(*m_frame->document()))
+        return false;
+
     if (selection.isRange()) {
         m_selectionState = SelectionState::ExtendedSelection;
     } else {
diff --git a/third_party/WebKit/Source/core/editing/VisibleSelection.cpp b/third_party/WebKit/Source/core/editing/VisibleSelection.cpp
index 8d0e83d..52a649e 100644
--- a/third_party/WebKit/Source/core/editing/VisibleSelection.cpp
+++ b/third_party/WebKit/Source/core/editing/VisibleSelection.cpp
@@ -738,6 +738,18 @@
     adjustStartAndEndInComposedTree();
 }
 
+bool VisibleSelection::isValidFor(const Document& document) const
+{
+    if (isNone())
+        return true;
+
+    return m_base.document() == &document
+        && !m_base.isOrphan() && !m_extent.isOrphan()
+        && !m_start.isOrphan() && !m_end.isOrphan()
+        && !m_baseInComposedTree.isOrphan() && !m_extentInComposedTree.isOrphan()
+        && !m_startInComposedTree.isOrphan() && !m_endInComposedTree.isOrphan();
+}
+
 // FIXME: This function breaks the invariant of this class.
 // But because we use VisibleSelection to store values in editing commands for use when
 // undoing the command, we need to be able to create a selection that while currently
diff --git a/third_party/WebKit/Source/core/editing/VisibleSelection.h b/third_party/WebKit/Source/core/editing/VisibleSelection.h
index 4609d9e..3a8dfef 100644
--- a/third_party/WebKit/Source/core/editing/VisibleSelection.h
+++ b/third_party/WebKit/Source/core/editing/VisibleSelection.h
@@ -173,6 +173,7 @@
     VisiblePosition visiblePositionRespectingEditingBoundary(const LayoutPoint& localPoint, Node* targetNode) const;
     PositionWithAffinity positionRespectingEditingBoundary(const LayoutPoint& localPoint, Node* targetNode) const;
 
+    bool isValidFor(const Document&) const;
     void setWithoutValidation(const Position&, const Position&);
     void setWithoutValidation(const PositionInComposedTree&, const PositionInComposedTree&);