Bug 1449010: Respect ::selection background styles for image overlays. r?mattwoodrow
Note that this is on top of
bug 509958, thus ::selection works in the test.
You can apply that or change it to ::-moz-selection and
CSSPseudoElementType::mozSelection to test locally just this patch if you want.
I don't have a strong preference about blending with white vs. just doing alpha
0.5, so I kept doing what we were doing, since Blink and WebKit also apply the
blending to the text background, and I'm not sure that's particularly desirable.
Happy to change it if you think otherwise though.
MozReview-Commit-ID: AwYtAgdlcxj
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -2179,34 +2179,61 @@ public:
mozilla::wr::IpcResourceUpdateQueue& aResources,
const StackingContextHelper& aSc,
mozilla::layers::WebRenderLayerManager* aManager,
nsDisplayListBuilder* aDisplayListBuilder) override;
NS_DISPLAY_DECL_NAME("SelectionOverlay", TYPE_SELECTION_OVERLAY)
private:
Color ComputeColor() const;
+ static Color ComputeColorFromSelectionStyle(ComputedStyle&);
+ static Color ApplyTransparencyIfNecessary(nscolor);
+
int16_t mSelectionValue;
};
Color
+nsDisplaySelectionOverlay::ApplyTransparencyIfNecessary(nscolor aColor)
+{
+ // If it has already alpha, leave it like that.
+ if (NS_GET_A(aColor) != 255) {
+ return ToDeviceColor(aColor);
+ }
+
+ // NOTE(emilio): Blink and WebKit do something slightly different here, and
+ // blend the color with white instead, both for overlays and text backgrounds.
+ auto color = Color::FromABGR(aColor);
+ color.a = 0.5;
+ return ToDeviceColor(color);
+}
+
+Color
+nsDisplaySelectionOverlay::ComputeColorFromSelectionStyle(ComputedStyle& aStyle)
+{
+ return ApplyTransparencyIfNecessary(
+ aStyle.GetVisitedDependentColor(&nsStyleBackground::mBackgroundColor));
+}
+
+Color
nsDisplaySelectionOverlay::ComputeColor() const
{
LookAndFeel::ColorID colorID;
if (mSelectionValue == nsISelectionController::SELECTION_ON) {
+ if (RefPtr<ComputedStyle> style = mFrame->ComputeSelectionStyle()) {
+ return ComputeColorFromSelectionStyle(*style);
+ }
colorID = LookAndFeel::eColorID_TextSelectBackground;
} else if (mSelectionValue == nsISelectionController::SELECTION_ATTENTION) {
colorID = LookAndFeel::eColorID_TextSelectBackgroundAttention;
} else {
colorID = LookAndFeel::eColorID_TextSelectBackgroundDisabled;
}
- Color c = Color::FromABGR(LookAndFeel::GetColor(colorID, NS_RGB(255, 255, 255)));
- c.a = .5;
- return ToDeviceColor(c);
+ return ApplyTransparencyIfNecessary(
+ LookAndFeel::GetColor(colorID, NS_RGB(255, 255, 255)));
}
void nsDisplaySelectionOverlay::Paint(nsDisplayListBuilder* aBuilder,
gfxContext* aCtx)
{
DrawTarget& aDrawTarget = *aCtx->GetDrawTarget();
ColorPattern color(ComputeColor());
@@ -2229,55 +2256,78 @@ nsDisplaySelectionOverlay::CreateWebRend
wr::LayoutRect bounds = aSc.ToRelativeLayoutRect(
LayoutDeviceRect::FromAppUnits(nsRect(ToReferenceFrame(), Frame()->GetSize()),
mFrame->PresContext()->AppUnitsPerDevPixel()));
aBuilder.PushRect(bounds, bounds, !BackfaceIsHidden(),
wr::ToColorF(ComputeColor()));
return true;
}
+static Element*
+FindElementAncestorForMozSelection(nsIContent* aContent)
+{
+ NS_ENSURE_TRUE(aContent, nullptr);
+ while (aContent && aContent->IsInNativeAnonymousSubtree()) {
+ aContent = aContent->GetBindingParent();
+ }
+ NS_ASSERTION(aContent, "aContent isn't in non-anonymous tree?");
+ while (aContent && !aContent->IsElement()) {
+ aContent = aContent->GetParent();
+ }
+ return aContent ? aContent->AsElement() : nullptr;
+}
+
+
+already_AddRefed<ComputedStyle>
+nsIFrame::ComputeSelectionStyle() const
+{
+ Element* element = FindElementAncestorForMozSelection(GetContent());
+ RefPtr<ComputedStyle> sc =
+ PresContext()->StyleSet()->ProbePseudoElementStyle(
+ element, CSSPseudoElementType::selection, Style());
+ return sc.forget();
+}
+
/********************************************************
* Refreshes each content's frame
*********************************************************/
void
nsFrame::DisplaySelectionOverlay(nsDisplayListBuilder* aBuilder,
nsDisplayList* aList,
uint16_t aContentType)
{
- if (!IsSelected() || !IsVisibleForPainting(aBuilder))
+ if (!IsSelected() || !IsVisibleForPainting(aBuilder)) {
return;
-
- nsPresContext* presContext = PresContext();
- nsIPresShell *shell = presContext->PresShell();
- if (!shell)
+ }
+
+ int16_t displaySelection = PresShell()->GetSelectionFlags();
+ if (!(displaySelection & aContentType)) {
return;
-
- int16_t displaySelection = shell->GetSelectionFlags();
- if (!(displaySelection & aContentType))
- return;
+ }
const nsFrameSelection* frameSelection = GetConstFrameSelection();
int16_t selectionValue = frameSelection->GetDisplaySelection();
- if (selectionValue <= nsISelectionController::SELECTION_HIDDEN)
+ if (selectionValue <= nsISelectionController::SELECTION_HIDDEN) {
return; // selection is hidden or off
-
- nsIContent *newContent = mContent->GetParent();
+ }
+
+ nsIContent* newContent = mContent->GetParent();
//check to see if we are anonymous content
int32_t offset = 0;
if (newContent) {
// XXXbz there has GOT to be a better way of determining this!
offset = newContent->ComputeIndexOf(mContent);
}
//look up to see what selection(s) are on this frame
- UniquePtr<SelectionDetails> details
- = frameSelection->LookUpSelection(newContent, offset, 1, false);
+ UniquePtr<SelectionDetails> details =
+ frameSelection->LookUpSelection(newContent, offset, 1, false);
if (!details)
return;
bool normal = false;
for (SelectionDetails* sd = details.get(); sd; sd = sd->mNext.get()) {
if (sd->mSelectionType == SelectionType::eNormal) {
normal = true;
}
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -868,16 +868,18 @@ public:
* The indicies must be consecutive and implementations MUST return null if
* asked for an index that is out of range.
*/
virtual ComputedStyle* GetAdditionalComputedStyle(int32_t aIndex) const = 0;
virtual void SetAdditionalComputedStyle(int32_t aIndex,
ComputedStyle* aComputedStyle) = 0;
+ already_AddRefed<ComputedStyle> ComputeSelectionStyle() const;
+
/**
* Accessor functions for geometric parent.
*/
nsContainerFrame* GetParent() const { return mParent; }
/**
* Gets the parent of a frame, using the parent of the placeholder for
* out-of-flow frames.
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -1894,24 +1894,20 @@ nsImageFrame::ShouldDisplaySelection()
{
nsPresContext* presContext = PresContext();
int16_t displaySelection = presContext->PresShell()->GetSelectionFlags();
if (!(displaySelection & nsISelectionDisplay::DISPLAY_IMAGES))
return false;//no need to check the blue border, we cannot be drawn selected
// If the image is the only selected node, don't draw the selection overlay.
// This can happen when selecting an image in contenteditable context.
- if (displaySelection == nsISelectionDisplay::DISPLAY_ALL)
- {
- const nsFrameSelection* frameSelection = GetConstFrameSelection();
- if (frameSelection)
- {
+ if (displaySelection == nsISelectionDisplay::DISPLAY_ALL) {
+ if (const nsFrameSelection* frameSelection = GetConstFrameSelection()) {
const Selection* selection = frameSelection->GetSelection(SelectionType::eNormal);
- if (selection && selection->RangeCount() == 1)
- {
+ if (selection && selection->RangeCount() == 1) {
nsINode* parent = mContent->GetParent();
int32_t thisOffset = parent->ComputeIndexOf(mContent);
nsRange* range = selection->GetRangeAt(0);
if (range->GetStartContainer() == parent &&
range->GetEndContainer() == parent &&
static_cast<int32_t>(range->StartOffset()) == thisOffset &&
static_cast<int32_t>(range->EndOffset()) == thisOffset + 1) {
return false;
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -4052,30 +4052,16 @@ nsTextPaintStyle::GetSystemFieldForegrou
nscolor
nsTextPaintStyle::GetSystemFieldBackgroundColor()
{
InitCommonColors();
return mSystemFieldBackgroundColor;
}
-static Element*
-FindElementAncestorForMozSelection(nsIContent* aContent)
-{
- NS_ENSURE_TRUE(aContent, nullptr);
- while (aContent && aContent->IsInNativeAnonymousSubtree()) {
- aContent = aContent->GetBindingParent();
- }
- NS_ASSERTION(aContent, "aContent isn't in non-anonymous tree?");
- while (aContent && !aContent->IsElement()) {
- aContent = aContent->GetParent();
- }
- return aContent ? aContent->AsElement() : nullptr;
-}
-
bool
nsTextPaintStyle::InitSelectionColorsAndShadow()
{
if (mInitSelectionColorsAndShadow)
return true;
int16_t selectionFlags;
int16_t selectionStatus = mFrame->GetSelectionStatus(&selectionFlags);
@@ -4084,35 +4070,25 @@ nsTextPaintStyle::InitSelectionColorsAnd
// Not displaying the normal selection.
// We're not caching this fact, so every call to GetSelectionColors
// will come through here. We could avoid this, but it's not really worth it.
return false;
}
mInitSelectionColorsAndShadow = true;
- nsIFrame* nonGeneratedAncestor = nsLayoutUtils::GetNonGeneratedAncestor(mFrame);
- Element* selectionElement =
- FindElementAncestorForMozSelection(nonGeneratedAncestor->GetContent());
-
- if (selectionElement &&
- selectionStatus == nsISelectionController::SELECTION_ON) {
- RefPtr<ComputedStyle> sc =
- mPresContext->StyleSet()->
- ProbePseudoElementStyle(selectionElement,
- CSSPseudoElementType::selection,
- mFrame->Style());
- // Use -moz-selection pseudo class.
- if (sc) {
+ if (selectionStatus == nsISelectionController::SELECTION_ON) {
+ // Use ::selection pseudo class.
+ if (RefPtr<ComputedStyle> style = mFrame->ComputeSelectionStyle()) {
mSelectionBGColor =
- sc->GetVisitedDependentColor(&nsStyleBackground::mBackgroundColor);
+ style->GetVisitedDependentColor(&nsStyleBackground::mBackgroundColor);
mSelectionTextColor =
- sc->GetVisitedDependentColor(&nsStyleText::mWebkitTextFillColor);
+ style->GetVisitedDependentColor(&nsStyleText::mWebkitTextFillColor);
mHasSelectionShadow = true;
- mSelectionShadow = sc->StyleText()->mTextShadow;
+ mSelectionShadow = style->StyleText()->mTextShadow;
return true;
}
}
nscolor selectionBGColor =
LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectBackground);
if (selectionStatus == nsISelectionController::SELECTION_ATTENTION) {
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -160624,16 +160624,40 @@
[
"/css/reference/ref-filled-green-100px-square.xht",
"=="
]
],
{}
]
],
+ "css/selectors/selection-image-001.html": [
+ [
+ "/css/selectors/selection-image-001.html",
+ [
+ [
+ "/css/selectors/selection-image-001-noref.html",
+ "!="
+ ]
+ ],
+ {}
+ ]
+ ],
+ "css/selectors/selection-image-002.html": [
+ [
+ "/css/selectors/selection-image-002.html",
+ [
+ [
+ "/css/selectors/selection-image-001-no-selection-noref.html",
+ "!="
+ ]
+ ],
+ {}
+ ]
+ ],
"css/selectors/selector-placeholder-shown-type-change-001.html": [
[
"/css/selectors/selector-placeholder-shown-type-change-001.html",
[
[
"/css/selectors/selector-placeholder-shown-type-change-001-ref.html",
"=="
]
@@ -262149,16 +262173,21 @@
{}
]
],
"css/selectors/of-type-selectors-ref.xhtml": [
[
{}
]
],
+ "css/selectors/resources/blue15x15.png": [
+ [
+ {}
+ ]
+ ],
"css/selectors/selector-placeholder-shown-type-change-001-ref.html": [
[
{}
]
],
"css/selectors/selector-placeholder-shown-type-change-002-ref.html": [
[
{}
@@ -385648,16 +385677,28 @@
{}
]
],
"css/mediaqueries/media-queries-003.xht": [
[
"/css/mediaqueries/media-queries-003.xht",
{}
]
+ ],
+ "css/selectors/selection-image-001-no-selection-noref.html": [
+ [
+ "/css/selectors/selection-image-001-no-selection-noref.html",
+ {}
+ ]
+ ],
+ "css/selectors/selection-image-001-noref.html": [
+ [
+ "/css/selectors/selection-image-001-noref.html",
+ {}
+ ]
]
},
"wdspec": {
"webdriver/tests/actions/key.py": [
[
"/webdriver/tests/actions/key.py",
{}
]
@@ -529060,24 +529101,44 @@
"css/selectors/of-type-selectors-ref.xhtml": [
"59f848418882c75898c422a9600c14ffab64c3d9",
"support"
],
"css/selectors/of-type-selectors.xhtml": [
"607553f41a33ce3630752cdf027c9f904833a19d",
"reftest"
],
+ "css/selectors/resources/blue15x15.png": [
+ "eb48032c07bfeb1d3b6be6e5c9c34d2fe2180767",
+ "support"
+ ],
"css/selectors/root-siblings.htm": [
"0d6e67589dab95d5362f82a99565947ebb487658",
"reftest"
],
"css/selectors/scope-without-scoping.html": [
"f70b8d60543c5a28fcf955b1780f15c03d60991a",
"reftest"
],
+ "css/selectors/selection-image-001-no-selection-noref.html": [
+ "b9a627630a8dcfaa70c74fc11ac9635aa00ac32c",
+ "visual"
+ ],
+ "css/selectors/selection-image-001-noref.html": [
+ "add1c00be4957ffef599aee52d061be7c09607bc",
+ "visual"
+ ],
+ "css/selectors/selection-image-001.html": [
+ "134b946744b45f488470dc5d1c690ee9a4855cbb",
+ "reftest"
+ ],
+ "css/selectors/selection-image-002.html": [
+ "5e2d33709b654a1f66eedcff995c8a3e9a8e01c5",
+ "reftest"
+ ],
"css/selectors/selector-placeholder-shown-type-change-001-ref.html": [
"92303d06943581738f58ff5d342ef1336539f66a",
"support"
],
"css/selectors/selector-placeholder-shown-type-change-001.html": [
"6d84d237fe4b6d7f01e6c7c6129bdc8ccb06cf90",
"reftest"
],
--- a/testing/web-platform/mozilla/meta/MANIFEST.json
+++ b/testing/web-platform/mozilla/meta/MANIFEST.json
@@ -1424,17 +1424,17 @@
"3b21a62dbc8379a202e8a0beda813c89f762ae2d",
"support"
],
"wasm/js/get_local.wast.js": [
"332a192149e2cede2d9219432fd3c6085ac69f68",
"support"
],
"wasm/js/globals.wast.js": [
- "86b3c907edd8bb3867c5a5aaaebf6066a215b154",
+ "1cb0dc48bd61564325053bf49ae7a1bdb27f5c49",
"support"
],
"wasm/js/harness/index.js": [
"c1f8ca410de85f15e1d9e57748a2fb9f607be40f",
"support"
],
"wasm/js/harness/wasm-constants.js": [
"6aba4f5fb4127cd56e1381bfb18204edde41abb2",
new file mode 100644
index 0000000000000000000000000000000000000000..89de32fdb8a4e48b1320f40f5a75352773077cee
GIT binary patch
literal 185
zc%17D@N?(olHy`uVBq!ia0vp^{2<K11SGd?VUh(>oCO|{#S9F5he4R}c>anMpde#$
zkh>GZx^prwfgF}}M_)$<hK>E)e-c@Ne1&9>AYTTCDm4a%h86~fUqGRT7Yq!g1`G_Z
z5*Qe)W-u^_7tGleXakf`@^o<w(FjgXN%(Qzfs0|4n%u+%B2Ty;@ydnvnyztk0=k6z
V)g}Gv*iewk44$rjF6*2UngFS5E#&|J
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/selectors/selection-image-001-no-selection-noref.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<title>CSS Test Reference</title>
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1449010">
+<p>
+ Some text <img src="resources/blue15x15.png"> some more.
+</p>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/selectors/selection-image-001-noref.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<title>CSS Test Reference</title>
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1449010">
+<p>
+ Some text <img src="resources/blue15x15.png"> some more.
+</p>
+<script>
+onload = () => {
+ getSelection().removeAllRanges();
+ let r = document.createRange();
+ r.selectNode(document.documentElement);
+ getSelection().addRange(r);
+}
+</script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/selectors/selection-image-001.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<title>::selection is respected on images</title>
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1449010">
+<link rel="mismatch" href="selection-image-001-noref.html">
+<style>
+img::selection {
+ background: green;
+}
+</style>
+<p>
+ Some text <img src="resources/blue15x15.png"> some more.
+</p>
+<script>
+onload = () => {
+ getSelection().removeAllRanges();
+ let r = document.createRange();
+ r.selectNode(document.documentElement);
+ getSelection().addRange(r);
+}
+</script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/selectors/selection-image-002.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<title>CSS Test: Image and text selection is painted.</title>
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1449010">
+<link rel="mismatch" href="selection-image-001-no-selection-noref.html">
+<p>
+ Some text <img src="resources/blue15x15.png"> some more.
+</p>
+<script>
+onload = () => {
+ getSelection().removeAllRanges();
+ let r = document.createRange();
+ r.selectNode(document.documentElement);
+ getSelection().addRange(r);
+}
+</script>