зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1663475 - Paint caret geometry from the containing block. r=mats
This matches other browsers and shouldn't be too terrible, though I'm not quite a fan of it. Differential Revision: https://phabricator.services.mozilla.com/D89483
This commit is contained in:
Родитель
253ba9aba0
Коммит
9d6f081a11
|
@ -19,6 +19,7 @@
|
|||
#include "nsIFrame.h"
|
||||
#include "nsIScrollableFrame.h"
|
||||
#include "nsIContent.h"
|
||||
#include "nsIFrameInlines.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
#include "nsPresContext.h"
|
||||
#include "nsBlockFrame.h"
|
||||
|
@ -448,7 +449,27 @@ void nsCaret::CheckSelectionLanguageChange() {
|
|||
}
|
||||
}
|
||||
|
||||
nsIFrame* nsCaret::GetPaintGeometry(nsRect* aRect) {
|
||||
// This ensures that the caret is not affected by clips on inlines and so forth.
|
||||
[[nodiscard]] static nsIFrame* MapToContainingBlock(nsIFrame* aFrame,
|
||||
nsRect* aCaretRect,
|
||||
nsRect* aHookRect) {
|
||||
if (aFrame->IsBlockOutside() || aFrame->IsBlockFrameOrSubclass()) {
|
||||
return aFrame;
|
||||
}
|
||||
nsIFrame* containingBlock = aFrame->GetContainingBlock();
|
||||
if (!containingBlock) {
|
||||
return aFrame;
|
||||
}
|
||||
|
||||
*aCaretRect = nsLayoutUtils::TransformFrameRectToAncestor(aFrame, *aCaretRect,
|
||||
containingBlock);
|
||||
*aHookRect = nsLayoutUtils::TransformFrameRectToAncestor(aFrame, *aHookRect,
|
||||
containingBlock);
|
||||
return containingBlock;
|
||||
}
|
||||
|
||||
nsIFrame* nsCaret::GetPaintGeometry(nsRect* aCaretRect, nsRect* aHookRect,
|
||||
nscolor* aCaretColor) {
|
||||
// Return null if we should not be visible.
|
||||
if (!IsVisible() || !mIsBlinkOn) {
|
||||
return nullptr;
|
||||
|
@ -491,43 +512,44 @@ nsIFrame* nsCaret::GetPaintGeometry(nsRect* aRect) {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
if (aCaretColor) {
|
||||
*aCaretColor = frame->GetCaretColorAt(frameOffset);
|
||||
}
|
||||
|
||||
ComputeCaretRects(frame, frameOffset, aCaretRect, aHookRect);
|
||||
return MapToContainingBlock(frame, aCaretRect, aHookRect);
|
||||
}
|
||||
|
||||
nsIFrame* nsCaret::GetPaintGeometry(nsRect* aRect) {
|
||||
nsRect caretRect;
|
||||
nsRect hookRect;
|
||||
ComputeCaretRects(frame, frameOffset, &caretRect, &hookRect);
|
||||
|
||||
nsIFrame* frame = GetPaintGeometry(&caretRect, &hookRect);
|
||||
aRect->UnionRect(caretRect, hookRect);
|
||||
return frame;
|
||||
}
|
||||
|
||||
nsIFrame* nsCaret::GetFrame(int32_t* aContentOffset) {
|
||||
return GetFrameAndOffset(GetSelection(), mOverrideContent, mOverrideOffset,
|
||||
aContentOffset);
|
||||
}
|
||||
|
||||
void nsCaret::PaintCaret(DrawTarget& aDrawTarget, nsIFrame* aForFrame,
|
||||
const nsPoint& aOffset) {
|
||||
int32_t contentOffset;
|
||||
nsIFrame* frame = GetFrame(&contentOffset);
|
||||
nsRect caretRect;
|
||||
nsRect hookRect;
|
||||
nscolor color;
|
||||
nsIFrame* frame = GetPaintGeometry(&caretRect, &hookRect, &color);
|
||||
MOZ_ASSERT(frame == aForFrame, "We're referring different frame");
|
||||
|
||||
if (!frame) {
|
||||
return;
|
||||
}
|
||||
NS_ASSERTION(frame == aForFrame, "We're referring different frame");
|
||||
|
||||
int32_t appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
|
||||
|
||||
nsRect caretRect;
|
||||
nsRect hookRect;
|
||||
ComputeCaretRects(frame, contentOffset, &caretRect, &hookRect);
|
||||
|
||||
Rect devPxCaretRect = NSRectToSnappedRect(caretRect + aOffset,
|
||||
appUnitsPerDevPixel, aDrawTarget);
|
||||
Rect devPxHookRect =
|
||||
NSRectToSnappedRect(hookRect + aOffset, appUnitsPerDevPixel, aDrawTarget);
|
||||
ColorPattern color(ToDeviceColor(frame->GetCaretColorAt(contentOffset)));
|
||||
|
||||
aDrawTarget.FillRect(devPxCaretRect, color);
|
||||
ColorPattern pattern(ToDeviceColor(color));
|
||||
aDrawTarget.FillRect(devPxCaretRect, pattern);
|
||||
if (!hookRect.IsEmpty()) {
|
||||
aDrawTarget.FillRect(devPxHookRect, color);
|
||||
aDrawTarget.FillRect(devPxHookRect, pattern);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -143,6 +143,12 @@ class nsCaret final : public nsISelectionListener {
|
|||
* off).
|
||||
*/
|
||||
nsIFrame* GetPaintGeometry(nsRect* aRect);
|
||||
/**
|
||||
* Same as the overload above, but returns the caret and hook rects
|
||||
* separately, and also computes the color if requested.
|
||||
*/
|
||||
nsIFrame* GetPaintGeometry(nsRect* aCaretRect, nsRect* aHookRect,
|
||||
nscolor* aCaretColor = nullptr);
|
||||
/**
|
||||
* A simple wrapper around GetGeometry. Does not take any caret state into
|
||||
* account other than the current selection.
|
||||
|
@ -194,10 +200,6 @@ class nsCaret final : public nsISelectionListener {
|
|||
|
||||
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
|
||||
|
||||
nsIFrame* GetFrame(int32_t* aContentOffset);
|
||||
void ComputeCaretRects(nsIFrame* aFrame, int32_t aFrameOffset,
|
||||
nsRect* aCaretRect, nsRect* aHookRect);
|
||||
|
||||
protected:
|
||||
static void CaretBlinkCallback(nsITimer* aTimer, void* aClosure);
|
||||
|
||||
|
@ -212,6 +214,8 @@ class nsCaret final : public nsISelectionListener {
|
|||
};
|
||||
static Metrics ComputeMetrics(nsIFrame* aFrame, int32_t aOffset,
|
||||
nscoord aCaretHeight);
|
||||
void ComputeCaretRects(nsIFrame* aFrame, int32_t aFrameOffset,
|
||||
nsRect* aCaretRect, nsRect* aHookRect);
|
||||
|
||||
// Returns true if we should not draw the caret because of XUL menu popups.
|
||||
// The caret should be hidden if:
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
<!doctype html>
|
||||
<html class="reftest-wait">
|
||||
<title>Caret is correctly painted over inline with clip</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<style>
|
||||
div {
|
||||
font: 16px/1 monospace;
|
||||
outline: 2px solid blue;
|
||||
caret-color: black;
|
||||
}
|
||||
span {
|
||||
color: transparent;
|
||||
}
|
||||
</style>
|
||||
<div contenteditable spellcheck="no">
|
||||
<span>ab</span>c<span>de</span>
|
||||
</div>
|
||||
<script>
|
||||
SimpleTest.waitForFocus(function() {
|
||||
document.querySelector('[contenteditable]').focus();
|
||||
requestAnimationFrame(function() {
|
||||
// Position the caret between "a" and "b".
|
||||
synthesizeKey("KEY_ArrowRight");
|
||||
document.documentElement.removeAttribute("class");
|
||||
});
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,32 @@
|
|||
<!doctype html>
|
||||
<html class="reftest-wait">
|
||||
<title>Caret is correctly painted over inline with clip</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<style>
|
||||
div {
|
||||
font: 16px/1 monospace;
|
||||
caret-color: black;
|
||||
}
|
||||
div:focus {
|
||||
outline: 2px solid blue;
|
||||
}
|
||||
span {
|
||||
/* This should only leave the "c" letter visible, but the caret should
|
||||
still be visible when between "a" and "b" */
|
||||
clip-path: inset(0 2ch);
|
||||
}
|
||||
</style>
|
||||
<div contenteditable spellcheck="no">
|
||||
<span>abcde</span>
|
||||
</div>
|
||||
<script>
|
||||
SimpleTest.waitForFocus(function() {
|
||||
document.querySelector('[contenteditable]').focus();
|
||||
requestAnimationFrame(function() {
|
||||
// Position the caret between "a" and "b".
|
||||
synthesizeKey("KEY_ArrowRight");
|
||||
document.documentElement.removeAttribute("class");
|
||||
});
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,29 @@
|
|||
<!doctype html>
|
||||
<html class="reftest-wait">
|
||||
<title>Caret is correctly painted over inline with clip</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<style>
|
||||
span[contenteditable] {
|
||||
font: 16px/1 monospace;
|
||||
caret-color: black;
|
||||
display: inline-block;
|
||||
outline: 2px solid blue;
|
||||
}
|
||||
span > span {
|
||||
color: transparent;
|
||||
}
|
||||
</style>
|
||||
<span contenteditable spellcheck="no">
|
||||
<span>ab</span>c<span>de</span>
|
||||
</span>
|
||||
<script>
|
||||
SimpleTest.waitForFocus(function() {
|
||||
document.querySelector('[contenteditable]').focus();
|
||||
requestAnimationFrame(function() {
|
||||
// Position the caret between "a" and "b".
|
||||
synthesizeKey("KEY_ArrowRight");
|
||||
document.documentElement.removeAttribute("class");
|
||||
});
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,33 @@
|
|||
<!doctype html>
|
||||
<html class="reftest-wait">
|
||||
<title>Caret is correctly painted over inline with clip</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<style>
|
||||
span[contenteditable] {
|
||||
font: 16px/1 monospace;
|
||||
caret-color: black;
|
||||
display: inline-block;
|
||||
}
|
||||
span[contenteditable]:focus {
|
||||
outline: 2px solid blue;
|
||||
}
|
||||
span > span {
|
||||
/* This should only leave the "c" letter visible, but the caret should
|
||||
still be visible when between "a" and "b" */
|
||||
clip-path: inset(0 2ch);
|
||||
}
|
||||
</style>
|
||||
<span contenteditable spellcheck="no">
|
||||
<span>abcde</span>
|
||||
</span>
|
||||
<script>
|
||||
SimpleTest.waitForFocus(function() {
|
||||
document.querySelector('[contenteditable]').focus();
|
||||
requestAnimationFrame(function() {
|
||||
// Position the caret between "a" and "b".
|
||||
synthesizeKey("KEY_ArrowRight");
|
||||
document.documentElement.removeAttribute("class");
|
||||
});
|
||||
});
|
||||
</script>
|
|
@ -375,6 +375,10 @@ support-files =
|
|||
bug1637476-2-ref.html
|
||||
bug1637476-3.html
|
||||
bug1637476-3-ref.html
|
||||
bug1663475-1.html
|
||||
bug1663475-1-ref.html
|
||||
bug1663475-2.html
|
||||
bug1663475-2-ref.html
|
||||
image_rgrg-256x256.png
|
||||
input-invalid-ref.html
|
||||
input-maxlength-invalid-change.html
|
||||
|
|
|
@ -274,6 +274,8 @@ var tests = [
|
|||
[ 'bug1637476-1.html' , 'bug1637476-1-ref.html' ] ,
|
||||
[ 'bug1637476-2.html' , 'bug1637476-2-ref.html' ] ,
|
||||
[ 'bug1637476-3.html' , 'bug1637476-3-ref.html' ] ,
|
||||
[ 'bug1663475-1.html' , 'bug1663475-1-ref.html' ] ,
|
||||
[ 'bug1663475-2.html' , 'bug1663475-2-ref.html' ] ,
|
||||
function() {SpecialPowers.pushPrefEnv({'clear': [['layout.accessiblecaret.enabled_on_touch']]}, nextTest);} ,
|
||||
function() {SpecialPowers.pushPrefEnv({'set': [['accessibility.browsewithcaret', true]]}, nextTest);} ,
|
||||
[ 'bug1529492-1.html' , 'bug1529492-1-ref.html' ] ,
|
||||
|
|
|
@ -4935,20 +4935,18 @@ bool nsDisplayCaret::CreateWebRenderCommands(
|
|||
mozilla::layers::RenderRootStateManager* aManager,
|
||||
nsDisplayListBuilder* aDisplayListBuilder) {
|
||||
using namespace mozilla::layers;
|
||||
int32_t contentOffset;
|
||||
nsIFrame* frame = mCaret->GetFrame(&contentOffset);
|
||||
nsRect caretRect;
|
||||
nsRect hookRect;
|
||||
nscolor caretColor;
|
||||
nsIFrame* frame =
|
||||
mCaret->GetPaintGeometry(&caretRect, &hookRect, &caretColor);
|
||||
MOZ_ASSERT(frame == mFrame, "We're referring different frame");
|
||||
if (!frame) {
|
||||
return true;
|
||||
}
|
||||
NS_ASSERTION(frame == mFrame, "We're referring different frame");
|
||||
|
||||
int32_t appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
|
||||
|
||||
nsRect caretRect;
|
||||
nsRect hookRect;
|
||||
mCaret->ComputeCaretRects(frame, contentOffset, &caretRect, &hookRect);
|
||||
|
||||
gfx::DeviceColor color = ToDeviceColor(frame->GetCaretColorAt(contentOffset));
|
||||
gfx::DeviceColor color = ToDeviceColor(caretColor);
|
||||
LayoutDeviceRect devCaretRect = LayoutDeviceRect::FromAppUnits(
|
||||
caretRect + ToReferenceFrame(), appUnitsPerDevPixel);
|
||||
LayoutDeviceRect devHookRect = LayoutDeviceRect::FromAppUnits(
|
||||
|
|
Загрузка…
Ссылка в новой задаче