| Index: third_party/WebKit/Source/core/editing/FrameSelection.cpp | 
| diff --git a/third_party/WebKit/Source/core/editing/FrameSelection.cpp b/third_party/WebKit/Source/core/editing/FrameSelection.cpp | 
| index 5956a8b6601e1715e2faed739d94f128389642fc..6b3befe3fbbba703bf73f5f1f76fef32e29def64 100644 | 
| --- a/third_party/WebKit/Source/core/editing/FrameSelection.cpp | 
| +++ b/third_party/WebKit/Source/core/editing/FrameSelection.cpp | 
| @@ -101,14 +101,24 @@ FrameSelection::FrameSelection(LocalFrame* frame) | 
| , m_frameCaret(new FrameCaret(frame)) | 
| { | 
| DCHECK(frame); | 
| -    if (shouldAlwaysUseDirectionalSelection(m_frame)) | 
| -        m_selectionEditor->setIsDirectional(true); | 
| } | 
|  | 
| FrameSelection::~FrameSelection() | 
| { | 
| } | 
|  | 
| +const Document& FrameSelection::document() const | 
| +{ | 
| +    DCHECK(m_document); | 
| +    return *m_document; | 
| +} | 
| + | 
| +Document& FrameSelection::document() | 
| +{ | 
| +    DCHECK(m_document); | 
| +    return *m_document; | 
| +} | 
| + | 
| template <> | 
| VisiblePosition FrameSelection::originalBase<EditingStrategy>() const | 
| { | 
| @@ -139,7 +149,7 @@ const VisibleSelectionInFlatTree& FrameSelection::visibleSelection<EditingInFlat | 
| Element* FrameSelection::rootEditableElementOrDocumentElement() const | 
| { | 
| Element* selectionRoot = selection().rootEditableElement(); | 
| -    return selectionRoot ? selectionRoot : m_frame->document()->documentElement(); | 
| +    return selectionRoot ? selectionRoot : document().documentElement(); | 
| } | 
|  | 
| ContainerNode* FrameSelection::rootEditableElementOrTreeScopeRootNode() const | 
| @@ -274,6 +284,9 @@ void FrameSelection::setNonDirectionalSelectionIfNeeded(const VisibleSelectionIn | 
| template <typename Strategy> | 
| void FrameSelection::setSelectionAlgorithm(const VisibleSelectionTemplate<Strategy>& newSelection, SetSelectionOptions options, CursorAlignOnScroll align, TextGranularity granularity) | 
| { | 
| +    DCHECK(isAvailable()); | 
| +    DCHECK(newSelection.isValidFor(document())); | 
| +    const Document& currentDocument = document(); | 
| if (m_granularityStrategy && (options & FrameSelection::DoNotClearStrategy) == 0) | 
| m_granularityStrategy->Clear(); | 
| bool closeTyping = options & CloseTyping; | 
| @@ -284,26 +297,6 @@ void FrameSelection::setSelectionAlgorithm(const VisibleSelectionTemplate<Strate | 
| if (shouldAlwaysUseDirectionalSelection(m_frame)) | 
| s.setIsDirectional(true); | 
|  | 
| -    // <http://bugs.webkit.org/show_bug.cgi?id=23464>: 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(); | 
| -        // TODO(hajimehoshi): validateSelection already checks if the selection | 
| -        // is valid, thus we don't need this 'if' clause any more. | 
| -        if (document.frame() && document.frame() != m_frame && document != m_frame->document()) { | 
| -            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 (!document.frame()->host() && !selection().isNonOrphanedCaretOrRange()) | 
| -                clear(); | 
| -            return; | 
| -        } | 
| -    } | 
| - | 
| m_granularity = granularity; | 
|  | 
| // TODO(yosin): We should move to call |TypingCommand::closeTyping()| to | 
| @@ -331,8 +324,17 @@ void FrameSelection::setSelectionAlgorithm(const VisibleSelectionTemplate<Strate | 
| else | 
| m_frameCaret->clear(); | 
|  | 
| -    if (!s.isNone() && !(options & DoNotSetFocus)) | 
| +    if (!s.isNone() && !(options & DoNotSetFocus)) { | 
| setFocusedNodeIfNeeded(); | 
| +        // |setFocusedNodeIfNeeded()| dispatches sync events "FocusOut" and | 
| +        // "FocusIn", |m_frame| may associate to another document. | 
| +        if (!isAvailable() || document() != currentDocument) { | 
| +            // Once we get test case to reach here, we should change this | 
| +            // if-statement to |DCHECK()|. | 
| +            NOTREACHED(); | 
| +            return; | 
| +        } | 
| +    } | 
|  | 
| if (!(options & DoNotUpdateAppearance)) { | 
| // Hits in compositing/overflow/do-not-paint-outline-into-composited-scrolling-contents.html | 
| @@ -346,6 +348,12 @@ void FrameSelection::setSelectionAlgorithm(const VisibleSelectionTemplate<Strate | 
| m_xPosForVerticalArrowNavigation = NoXPosForVerticalArrowNavigation(); | 
| // This may dispatch a synchronous focus-related events. | 
| selectFrameElementInParentIfFullySelected(); | 
| +    if (!isAvailable() || document() != currentDocument) { | 
| +        // editing/selection/selectallchildren-crash.html and | 
| +        // editing/selection/longpress-selection-in-iframe-removed-crash.html | 
| +        // reach here. | 
| +        return; | 
| +    } | 
| notifyLayoutObjectOfSelectionChange(userTriggered); | 
| // If the selections are same in the DOM tree but not in the flat tree, | 
| // don't fire events. For example, if the selection crosses shadow tree | 
| @@ -480,7 +488,7 @@ void FrameSelection::respondToNodeModification(Node& node, bool baseRemoved, boo | 
|  | 
| // TODO(yosin): We should move to call |TypingCommand::closeTyping()| to | 
| // |Editor| class. | 
| -    if (!m_frame->document()->isRunningExecCommand()) | 
| +    if (!document().isRunningExecCommand()) | 
| TypingCommand::closeTyping(m_frame); | 
| } | 
|  | 
| @@ -584,7 +592,7 @@ void FrameSelection::updateSelectionIfNeeded(const Position& base, const Positio | 
| return; | 
| // TODO(yosin): We should move to call |TypingCommand::closeTyping()| to | 
| // |Editor| class. | 
| -    if (!m_frame->document()->isRunningExecCommand()) | 
| +    if (!document().isRunningExecCommand()) | 
| TypingCommand::closeTyping(m_frame); | 
| VisibleSelection newSelection; | 
| if (selection().isBaseFirst()) | 
| @@ -663,8 +671,18 @@ void FrameSelection::clear() | 
| setSelection(VisibleSelection()); | 
| } | 
|  | 
| -void FrameSelection::prepareForDestruction() | 
| +void FrameSelection::documentAttached(Document* document) | 
| +{ | 
| +    DCHECK(document); | 
| +    DCHECK(!m_document) << "FrameSelection is already attached to " << m_document; | 
| +    m_document = document; | 
| +    m_selectionEditor->documentAttached(document); | 
| +} | 
| + | 
| +void FrameSelection::documentDetached(const Document& document) | 
| { | 
| +    DCHECK_EQ(m_document, document); | 
| +    m_document = nullptr; | 
| m_originalBase = VisiblePosition(); | 
| m_originalBaseInFlatTree = VisiblePositionInFlatTree(); | 
| m_granularity = CharacterGranularity; | 
| @@ -673,14 +691,14 @@ void FrameSelection::prepareForDestruction() | 
| if (!view.isNull()) | 
| view.clearSelection(); | 
|  | 
| -    setSelection(VisibleSelection(), CloseTyping | ClearTypingStyle | DoNotUpdateAppearance); | 
| -    m_selectionEditor->dispose(); | 
| -    m_frameCaret->prepareForDestruction(); | 
| +    clearTypingStyle(); | 
| +    m_selectionEditor->documentDetached(document); | 
| +    m_frameCaret->documentDetached(); | 
| } | 
|  | 
| LayoutBlock* FrameSelection::caretLayoutObject() const | 
| { | 
| -    DCHECK(selection().isValidFor(*m_frame->document())); | 
| +    DCHECK(selection().isValidFor(document())); | 
| if (!isCaret()) | 
| return nullptr; | 
| return CaretBase::caretLayoutObject(selection().start().anchorNode()); | 
| @@ -709,8 +727,7 @@ void FrameSelection::paintCaret(GraphicsContext& context, const LayoutPoint& pai | 
|  | 
| bool FrameSelection::contains(const LayoutPoint& point) | 
| { | 
| -    Document* document = m_frame->document(); | 
| -    if (document->layoutViewItem().isNull()) | 
| +    if (document().layoutViewItem().isNull()) | 
| return false; | 
|  | 
| // Treat a collapsed selection like no selection. | 
| @@ -720,7 +737,7 @@ bool FrameSelection::contains(const LayoutPoint& point) | 
|  | 
| HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active); | 
| HitTestResult result(request, point); | 
| -    document->layoutViewItem().hitTest(result); | 
| +    document().layoutViewItem().hitTest(result); | 
| Node* innerNode = result.innerNode(); | 
| if (!innerNode || !innerNode->layoutObject()) | 
| return false; | 
| @@ -803,10 +820,8 @@ static Node* nonBoundaryShadowTreeRootNode(const Position& position) | 
|  | 
| void FrameSelection::selectAll() | 
| { | 
| -    Document* document = m_frame->document(); | 
| - | 
| -    if (isHTMLSelectElement(document->focusedElement())) { | 
| -        HTMLSelectElement* selectElement = toHTMLSelectElement(document->focusedElement()); | 
| +    if (isHTMLSelectElement(document().focusedElement())) { | 
| +        HTMLSelectElement* selectElement = toHTMLSelectElement(document().focusedElement()); | 
| if (selectElement->canSelectAll()) { | 
| selectElement->selectAll(); | 
| return; | 
| @@ -826,18 +841,19 @@ void FrameSelection::selectAll() | 
| if (root) { | 
| selectStartTarget = root->shadowHost(); | 
| } else { | 
| -            root = document->documentElement(); | 
| -            selectStartTarget = document->body(); | 
| +            root = document().documentElement(); | 
| +            selectStartTarget = document().body(); | 
| } | 
| } | 
| if (!root || editingIgnoresContent(root)) | 
| return; | 
|  | 
| if (selectStartTarget) { | 
| +        const Document& expectedDocument = document(); | 
| if (selectStartTarget->dispatchEvent(Event::createCancelableBubble(EventTypeNames::selectstart)) != DispatchEventResult::NotCanceled) | 
| return; | 
| // |root| may be detached due to selectstart event. | 
| -        if (!root->inShadowIncludingDocument() || root->document() != document) | 
| +        if (!root->inShadowIncludingDocument() || expectedDocument != root->document()) | 
| return; | 
| } | 
|  | 
| @@ -884,7 +900,7 @@ bool FrameSelection::isInPasswordField() const | 
| void FrameSelection::notifyAccessibilityForSelectionChange() | 
| { | 
| if (selection().start().isNotNull() && selection().end().isNotNull()) { | 
| -        if (AXObjectCache* cache = m_frame->document()->existingAXObjectCache()) | 
| +        if (AXObjectCache* cache = document().existingAXObjectCache()) | 
| cache->selectionChanged(selection().start().computeContainerNode()); | 
| } | 
| } | 
| @@ -905,20 +921,19 @@ void FrameSelection::notifyEventHandlerForSelectionChange() | 
| void FrameSelection::focusedOrActiveStateChanged() | 
| { | 
| bool activeAndFocused = isFocusedAndActive(); | 
| -    Document* document = m_frame->document(); | 
|  | 
| // Trigger style invalidation from the focused element. Even though | 
| // the focused element hasn't changed, the evaluation of focus pseudo | 
| // selectors are dependent on whether the frame is focused and active. | 
| -    if (Element* element = document->focusedElement()) | 
| +    if (Element* element = document().focusedElement()) | 
| element->focusStateChanged(); | 
|  | 
| -    document->updateStyleAndLayoutTree(); | 
| +    document().updateStyleAndLayoutTree(); | 
|  | 
| // Because LayoutObject::selectionBackgroundColor() and | 
| // LayoutObject::selectionForegroundColor() check if the frame is active, | 
| // we have to update places those colors were painted. | 
| -    LayoutViewItem view = document->layoutViewItem(); | 
| +    LayoutViewItem view = document().layoutViewItem(); | 
| if (!view.isNull()) | 
| view.invalidatePaintForSelection(); | 
|  | 
| @@ -933,7 +948,7 @@ void FrameSelection::focusedOrActiveStateChanged() | 
| m_frame->eventHandler().capsLockStateMayHaveChanged(); | 
|  | 
| // Secure keyboard entry is set by the active frame. | 
| -    if (document->useSecureKeyboardEntryWhenActive()) | 
| +    if (document().useSecureKeyboardEntryWhenActive()) | 
| setUseSecureKeyboardEntry(activeAndFocused); | 
| } | 
|  | 
| @@ -944,8 +959,8 @@ void FrameSelection::pageActivationChanged() | 
|  | 
| void FrameSelection::updateSecureKeyboardEntryIfActive() | 
| { | 
| -    if (m_frame->document() && isFocusedAndActive()) | 
| -        setUseSecureKeyboardEntry(m_frame->document()->useSecureKeyboardEntryWhenActive()); | 
| +    if (isFocusedAndActive()) | 
| +        setUseSecureKeyboardEntry(document().useSecureKeyboardEntryWhenActive()); | 
| } | 
|  | 
| void FrameSelection::setUseSecureKeyboardEntry(bool enable) | 
| @@ -1023,7 +1038,7 @@ void FrameSelection::setFocusedNodeIfNeeded() | 
|  | 
| if (Element* target = rootEditableElement()) { | 
| // Walk up the DOM tree to search for a node to focus. | 
| -        m_frame->document()->updateStyleAndLayoutTreeIgnorePendingStylesheets(); | 
| +        document().updateStyleAndLayoutTreeIgnorePendingStylesheets(); | 
| while (target) { | 
| // We don't want to set focus on a subframe when selecting in a parent frame, | 
| // so add the !isFrameElement check here. There's probably a better way to make this | 
| @@ -1034,7 +1049,7 @@ void FrameSelection::setFocusedNodeIfNeeded() | 
| } | 
| target = target->parentOrShadowHostElement(); | 
| } | 
| -        m_frame->document()->clearFocusedElement(); | 
| +        document().clearFocusedElement(); | 
| } | 
|  | 
| if (caretBrowsing) | 
| @@ -1119,7 +1134,7 @@ static HTMLFormElement* scanForForm(Node* start) | 
| HTMLFormElement* FrameSelection::currentForm() const | 
| { | 
| // Start looking either at the active (first responder) node, or where the selection is. | 
| -    Node* start = m_frame->document()->focusedElement(); | 
| +    Node* start = document().focusedElement(); | 
| if (!start) | 
| start = this->start().anchorNode(); | 
| if (!start) | 
| @@ -1238,6 +1253,7 @@ void FrameSelection::showTreeForThis() const | 
|  | 
| DEFINE_TRACE(FrameSelection) | 
| { | 
| +    visitor->trace(m_document); | 
| visitor->trace(m_frame); | 
| visitor->trace(m_pendingSelection); | 
| visitor->trace(m_selectionEditor); | 
|  |