Bug 1518339 - Make user-select: auto behave like user-select: text for editing roots. r=mats

This is the closest to the spec behavior, I think, and less likely to
cause interop issues, but if you prefer me to stop the 'inheritance' chain at
contenteditable elements or what not I can also do that.

Differential Revision: https://phabricator.services.mozilla.com/D15963

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Emilio Cobos Álvarez 2019-01-08 20:21:12 +00:00
Родитель 12ae3deec9
Коммит 2caa18fcc1
8 изменённых файлов: 119 добавлений и 0 удалений

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

@ -849,6 +849,7 @@ class nsGenericHTMLElement : public nsGenericHTMLElementBase {
// Used by A, AREA, LINK, and STYLE.
already_AddRefed<nsIURI> GetHrefURIForAnchors() const;
public:
/**
* Returns whether this element is an editable root. There are two types of
* editable roots:

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

@ -0,0 +1,16 @@
<!doctype html>
<html class="reftest-wait">
<title>Test reference</title>
<script src="/tests/SimpleTest/EventUtils.js"></script>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<div contenteditable="true">
Can you edit <b>me</b>?
</div>
<script>
SimpleTest.waitForFocus(function() {
const editable = document.querySelector('div[contenteditable="true"]');
editable.focus();
synthesizeMouseAtCenter(editable.querySelector("b"), {});
setTimeout(() => document.documentElement.removeAttribute("class"), 0);
});
</script>

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

@ -0,0 +1,23 @@
<!doctype html>
<html class="reftest-wait">
<title>contenteditable is selectable by default, even with a user-select: none ancestor</title>
<script src="/tests/SimpleTest/EventUtils.js"></script>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<style>
:root {
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
}
</style>
<div contenteditable="true">
Can you edit <b>me</b>?
</div>
<script>
SimpleTest.waitForFocus(function() {
const editable = document.querySelector('div[contenteditable="true"]');
editable.focus();
synthesizeMouseAtCenter(editable.querySelector("b"), {});
setTimeout(() => document.documentElement.removeAttribute("class"), 0);
});
</script>

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

@ -0,0 +1,25 @@
<!doctype html>
<html class="reftest-wait">
<title>Test reference</title>
<script src="/tests/SimpleTest/EventUtils.js"></script>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<style>
div {
-webkit-user-select: all;
-moz-user-select: all;
user-select: all;
}
</style>
<div contenteditable="true">
<div>
You should be able to select <b>all</b> of me.
</div>
</div>
<script>
SimpleTest.waitForFocus(function() {
const editable = document.querySelector('div[contenteditable="true"]');
editable.focus();
synthesizeMouseAtCenter(editable.querySelector("b"), {});
setTimeout(() => document.documentElement.removeAttribute("class"), 0);
});
</script>

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

@ -0,0 +1,23 @@
<!doctype html>
<html class="reftest-wait">
<title>user-select can be overriden on a contenteditable element</title>
<script src="/tests/SimpleTest/EventUtils.js"></script>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<style>
div {
-webkit-user-select: all;
-moz-user-select: all;
user-select: all;
}
</style>
<div contenteditable="true">
You should be able to select <b>all</b> of me.
</div>
<script>
SimpleTest.waitForFocus(function() {
const editable = document.querySelector('div[contenteditable="true"]');
editable.focus();
synthesizeMouseAtCenter(editable.querySelector("b"), {});
setTimeout(() => document.documentElement.removeAttribute("class"), 0);
});
</script>

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

@ -364,6 +364,10 @@ support-files =
bug1506547-6.html
bug1506547-4-ref.html
bug1506547-5-ref.html
bug1518339-1.html
bug1518339-1-ref.html
bug1518339-2.html
bug1518339-2-ref.html
[test_remote_frame.html]
[test_resize_flush.html]

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

@ -213,6 +213,8 @@ var tests = [
[ 'bug1506547-6.html' , 'bug1506547-5-ref.html' ] ,
[ 'bug1510942-1.html' , 'bug1510942-1-ref.html' ] ,
[ 'bug1510942-2.html' , 'bug1510942-2-ref.html' ] ,
[ 'bug1518339-1.html' , 'bug1518339-1-ref.html' ] ,
[ 'bug1518339-2.html' , 'bug1518339-2-ref.html' ] ,
function() {SpecialPowers.pushPrefEnv({'clear': [['layout.accessiblecaret.enabled']]}, nextTest);} ,
];

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

@ -3931,16 +3931,41 @@ nsresult nsFrame::GetDataForTableSelection(
return NS_OK;
}
static bool IsEditingHost(const nsIFrame* aFrame) {
auto* element = nsGenericHTMLElement::FromNodeOrNull(aFrame->GetContent());
return element && element->IsEditableRoot();
}
static StyleUserSelect UsedUserSelect(const nsIFrame* aFrame) {
if (aFrame->HasAnyStateBits(NS_FRAME_GENERATED_CONTENT)) {
return StyleUserSelect::None;
}
// Per https://drafts.csswg.org/css-ui-4/#content-selection:
//
// The computed value is the specified value, except:
//
// 1 - on editable elements where the computed value is always 'contain'
// regardless of the specified value.
// 2 - when the specified value is auto, which computes to one of the other
// values [...]
//
// See https://github.com/w3c/csswg-drafts/issues/3344 to see why we do this
// at used-value time instead of at computed-value time.
//
// Also, we check for auto first to allow explicitly overriding the value for
// the editing host.
auto style = aFrame->StyleUIReset()->mUserSelect;
if (style != StyleUserSelect::Auto) {
return style;
}
if (IsEditingHost(aFrame)) {
// We don't implement 'contain' itself, but we make 'text' behave as
// 'contain' for contenteditable elements anyway so this is ok.
return StyleUserSelect::Text;
}
auto* parent = nsLayoutUtils::GetParentOrPlaceholderFor(aFrame);
return parent ? UsedUserSelect(parent) : StyleUserSelect::Text;
}