Bug 1665550 - part 3: Make nsTextControlFrame set the source node and selection of drag session to new ones when it's reframed r=smaug

When `nsTextControlFrame` is reframed, `TextEditor`, anonymous `<div>`, its
`Text` and the independent `Selection`s are deleted temporarily and recreated
them.

If users are dragging text in `<input>` or `<textarea>`, the drag session's
source node is set to the anonymous text node in the element and the selection
is set to the independent selection.  So, if the element is reframed during a
drag, the source node is disconnected from the document and `EndDragSession`
failed to dispatch `eDragEnd` event.

Therefore, this patch makes `nsTextControlFrame` replaces the source node and
selection when it's recreated and only when the drag session's original source
node was in the text control element.  For checking which text control had the
anonymous text node, this patch makes `nsTextControlFrame` replaces source
node with the `<input>` or `<textarea>` element when the frame is destroyed.

Differential Revision: https://phabricator.services.mozilla.com/D119487
This commit is contained in:
Masayuki Nakano
2021-07-14 01:20:19 +00:00
parent 84d5aacaed
commit 78f53525c3
6 changed files with 219 additions and 2 deletions

View File

@@ -34,6 +34,7 @@
#include "nsFocusManager.h"
#include "mozilla/PresShell.h"
#include "mozilla/PresState.h"
#include "mozilla/TextEditor.h"
#include "nsAttrValueInlines.h"
#include "mozilla/dom/Selection.h"
#include "nsContentUtils.h"
@@ -149,6 +150,23 @@ void nsTextControlFrame::DestroyFrom(nsIFrame* aDestructRoot,
mMutationObserver = nullptr;
}
// If there is a drag session, user may be dragging selection in removing
// text node in the text control. If so, we should set source node to the
// text control because another text node may be recreated soon if the text
// control is just reframed.
if (nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession()) {
if (dragSession->IsDraggingTextInTextControl() && mRootNode &&
mRootNode->GetFirstChild()) {
nsCOMPtr<nsINode> sourceNode;
if (NS_SUCCEEDED(
dragSession->GetSourceNode(getter_AddRefs(sourceNode))) &&
mRootNode->Contains(sourceNode)) {
MOZ_ASSERT(sourceNode->IsText());
dragSession->UpdateSource(textControlElement, nullptr);
}
}
}
// If we're a subclass like nsNumberControlFrame, then it owns the root of the
// anonymous subtree where mRootNode is.
aPostDestroyData.AddAnonymousContent(mRootNode.forget());
@@ -447,6 +465,20 @@ bool nsTextControlFrame::ShouldInitializeEagerly() const {
}
}
// If text in the editor is being dragged, we need the editor to create
// new source node for the drag session (TextEditor creates the text node
// in the anonymous <div> element.
if (nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession()) {
if (dragSession->IsDraggingTextInTextControl()) {
nsCOMPtr<nsINode> sourceNode;
if (NS_SUCCEEDED(
dragSession->GetSourceNode(getter_AddRefs(sourceNode))) &&
sourceNode == textControlElement) {
return true;
}
}
}
return false;
}
@@ -1292,6 +1324,33 @@ nsTextControlFrame::EditorInitializer::Run() {
return NS_ERROR_FAILURE;
}
// If there is a drag session which is for dragging text in a text control
// and its source node is the text control element, we're being reframed.
// In this case we should restore the source node of the drag session to
// new text node because it's required for dispatching `dragend` event.
if (nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession()) {
if (dragSession->IsDraggingTextInTextControl()) {
nsCOMPtr<nsINode> sourceNode;
if (NS_SUCCEEDED(
dragSession->GetSourceNode(getter_AddRefs(sourceNode))) &&
mFrame->GetContent() == sourceNode) {
if (TextControlElement* textControlElement =
TextControlElement::FromNode(mFrame->GetContent())) {
if (TextEditor* textEditor =
textControlElement->GetTextEditorWithoutCreation()) {
if (Element* anonymousDivElement = textEditor->GetRoot()) {
if (anonymousDivElement && anonymousDivElement->GetFirstChild()) {
MOZ_ASSERT(anonymousDivElement->GetFirstChild()->IsText());
dragSession->UpdateSource(anonymousDivElement->GetFirstChild(),
textEditor->GetSelection());
}
}
}
}
}
}
}
mFrame->FinishedInitializer();
return NS_OK;
}