Bug 1644366 - Restore behavior of selection into view to keep scrolling ancestors on focus. r=smaug

... yet keep respecting the preventScroll flag.

Differential Revision: https://phabricator.services.mozilla.com/D78900
This commit is contained in:
Emilio Cobos Álvarez 2020-06-09 16:15:04 +00:00
Родитель 4af6525e90
Коммит bbf8acf349
4 изменённых файлов: 71 добавлений и 25 удалений

Просмотреть файл

@ -24,6 +24,7 @@
#include "nsCaret.h"
#include "nsIBaseWindow.h"
#include "nsIAppWindow.h"
#include "nsTextControlFrame.h"
#include "nsViewManager.h"
#include "nsFrameSelection.h"
#include "mozilla/dom/Document.h"
@ -2675,6 +2676,15 @@ void nsFocusManager::ScrollIntoView(PresShell* aPresShell, nsIContent* aContent,
aContent, ScrollAxis(kScrollMinimum, WhenToScroll::IfNotVisible),
ScrollAxis(kScrollMinimum, WhenToScroll::IfNotVisible),
ScrollFlags::ScrollOverflowHidden);
// Scroll the input / textarea selection into view, unless focused with the
// mouse, see bug 572649.
if (aFlags & FLAG_BYMOUSE) {
return;
}
// ScrollContentIntoView flushes layout, so no need to flush again here.
if (nsTextControlFrame* tf = do_QueryFrame(aContent->GetPrimaryFrame())) {
tf->ScrollSelectionIntoViewAsync(nsTextControlFrame::ScrollAncestors::Yes);
}
}
void nsFocusManager::RaiseWindow(nsPIDOMWindowOuter* aWindow,

Просмотреть файл

@ -57,6 +57,7 @@ nsIFrame* NS_NewTextControlFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
NS_IMPL_FRAMEARENA_HELPERS(nsTextControlFrame)
NS_QUERYFRAME_HEAD(nsTextControlFrame)
NS_QUERYFRAME_ENTRY(nsTextControlFrame)
NS_QUERYFRAME_ENTRY(nsIFormControlFrame)
NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
NS_QUERYFRAME_ENTRY(nsITextControlFrame)
@ -714,26 +715,7 @@ void nsTextControlFrame::SetFocus(bool aOn, bool aRepaint) {
return;
}
// Scroll the current selection into view
Selection* caretSelection = caret->GetSelection();
const bool isFocusedRightNow = ourSel == caretSelection;
if (!isFocusedRightNow) {
// Don't scroll the current selection if we've been focused using the mouse.
uint32_t lastFocusMethod = 0;
Document* doc = GetContent()->GetComposedDoc();
if (doc) {
nsIFocusManager* fm = nsFocusManager::GetFocusManager();
if (fm) {
fm->GetLastFocusMethod(doc->GetWindow(), &lastFocusMethod);
}
}
if (!(lastFocusMethod & nsIFocusManager::FLAG_BYMOUSE)) {
// NOTE(emilio): This is asynchronous, so it can't cause havoc.
ScrollSelectionIntoViewAsync();
}
}
// tell the caret to use our selection
// Tell the caret to use our selection
caret->SetSelection(ourSel);
// mutual-exclusion: the selection is either controlled by the
@ -843,18 +825,23 @@ nsresult nsTextControlFrame::SetSelectionInternal(
return NS_OK;
}
void nsTextControlFrame::ScrollSelectionIntoViewAsync() {
void nsTextControlFrame::ScrollSelectionIntoViewAsync(
ScrollAncestors aScrollAncestors) {
auto* textControlElement = TextControlElement::FromNode(GetContent());
MOZ_ASSERT(textControlElement);
nsISelectionController* selCon = textControlElement->GetSelectionController();
if (!selCon) {
return;
}
int16_t flags = aScrollAncestors == ScrollAncestors::Yes
? 0
: nsISelectionController::SCROLL_FIRST_ANCESTOR_ONLY;
// Scroll the selection into view (see bug 231389).
selCon->ScrollSelectionIntoView(
nsISelectionController::SELECTION_NORMAL,
nsISelectionController::SELECTION_FOCUS_REGION,
nsISelectionController::SCROLL_FIRST_ANCESTOR_ONLY);
nsISelectionController::SELECTION_FOCUS_REGION, flags);
}
nsresult nsTextControlFrame::SelectAllOrCollapseToEndOfText(bool aSelect) {

Просмотреть файл

@ -188,6 +188,11 @@ class nsTextControlFrame : public nsContainerFrame,
NS_DECL_QUERYFRAME
// Whether we should scroll only the current selection into view in the inner
// scroller, or also ancestors as needed.
enum class ScrollAncestors { No, Yes };
void ScrollSelectionIntoViewAsync(ScrollAncestors = ScrollAncestors::No);
protected:
/**
* Launch the reflow on the child frames - see nsTextControlFrame::Reflow()
@ -290,8 +295,6 @@ class nsTextControlFrame : public nsContainerFrame,
mozilla::WritingMode aWM,
float aFontSizeInflation) const;
void ScrollSelectionIntoViewAsync();
private:
// helper methods
MOZ_CAN_RUN_SCRIPT nsresult SetSelectionInternal(

Просмотреть файл

@ -0,0 +1,46 @@
<!doctype html>
<title>programatic focus() scrolls selection into view including ancestors</title>
<link rel="author" href="https://mozilla.org" title="Mozilla">
<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez">
<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1644366">
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<div style="overflow: auto; height: 100px">
<textarea style="overflow: hidden; height: 200px">
Some text
That is surely more
Than 100px tall
For sure.
For sure.
For sure.
For sure.
For sure.
For sure.
For sure.
For sure.
For sure.
For sure.
For sure.
For sure.
For sure.
For sure.
For sure.
For sure.
For sure.
For sure.
</textarea>
</div>
<script>
promise_test(async function(t) {
await new Promise(resolve => window.addEventListener("load", resolve));
let textarea = document.querySelector("textarea");
textarea.setSelectionRange(textarea.value.length, textarea.value.length);
textarea.focus();
await new Promise(resolve => {
requestAnimationFrame(() => requestAnimationFrame(resolve));
});
assert_not_equals(textarea.parentNode.scrollTop, 0, "Should've scrolled ancestor to show the selection");
});
</script>