mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-10 11:55:49 +00:00
Backed out changeset 1575904619b5 (bug 1506547) for mochitest failures on test_reftests_with_caret.html.
This commit is contained in:
parent
b74c31e4d9
commit
31f0c21cca
@ -547,8 +547,7 @@ HyperTextAccessible::FindOffset(uint32_t aOffset, nsDirection aDirection,
|
||||
nsPeekOffsetStruct pos(aAmount, aDirection, innerContentOffset,
|
||||
nsPoint(0, 0), kIsJumpLinesOk, kIsScrollViewAStop,
|
||||
kIsKeyboardSelect, kIsVisualBidi,
|
||||
false, nsPeekOffsetStruct::ForceEditableRegion::No,
|
||||
aWordMovementType);
|
||||
false, aWordMovementType);
|
||||
nsresult rv = frameAtOffset->PeekOffset(&pos);
|
||||
|
||||
// PeekOffset fails on last/first lines of the text in certain cases.
|
||||
|
@ -48,6 +48,10 @@ let whitelist = [
|
||||
{sourceName: /(?:res|gre-resources)\/forms\.css$/i,
|
||||
errorMessage: /Error in parsing value for \u2018-moz-appearance\u2019/iu,
|
||||
isFromDevTools: false},
|
||||
// -moz-user-select: -moz-text is only enabled to user-agent stylesheets.
|
||||
{sourceName: /contenteditable.css$/i,
|
||||
errorMessage: /Error in parsing value for \u2018-moz-user-select\u2019/iu,
|
||||
isFromDevTools: false},
|
||||
// These variables are declared somewhere else, and error when we load the
|
||||
// files directly. They're all marked intermittent because their appearance
|
||||
// in the error console seems to not be consistent.
|
||||
|
@ -1358,6 +1358,7 @@ exports.CSS_PROPERTIES = {
|
||||
"supports": [],
|
||||
"values": [
|
||||
"-moz-none",
|
||||
"-moz-text",
|
||||
"all",
|
||||
"auto",
|
||||
"inherit",
|
||||
@ -2749,6 +2750,7 @@ exports.CSS_PROPERTIES = {
|
||||
"supports": [],
|
||||
"values": [
|
||||
"-moz-none",
|
||||
"-moz-text",
|
||||
"all",
|
||||
"auto",
|
||||
"inherit",
|
||||
|
@ -1639,6 +1639,21 @@ Element::GetElementsWithGrid(nsTArray<RefPtr<Element>>& aElements)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the count of descendants (inclusive of aContent) in
|
||||
* the uncomposed document that are explicitly set as editable.
|
||||
*/
|
||||
static uint32_t
|
||||
EditableInclusiveDescendantCount(nsIContent* aContent)
|
||||
{
|
||||
auto htmlElem = nsGenericHTMLElement::FromNode(aContent);
|
||||
if (htmlElem) {
|
||||
return htmlElem->EditableInclusiveDescendantCount();
|
||||
}
|
||||
|
||||
return aContent->EditableDescendantCount();
|
||||
}
|
||||
|
||||
nsresult
|
||||
Element::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||
nsIContent* aBindingParent)
|
||||
@ -1787,6 +1802,8 @@ Element::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||
SetDirOnBind(this, aParent);
|
||||
}
|
||||
|
||||
uint32_t editableDescendantCount = 0;
|
||||
|
||||
UpdateEditableState(false);
|
||||
|
||||
// If we had a pre-existing XBL binding, we might have anonymous children that
|
||||
@ -1809,6 +1826,30 @@ Element::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||
child = child->GetNextSibling()) {
|
||||
rv = child->BindToTree(aDocument, this, aBindingParent);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
editableDescendantCount += EditableInclusiveDescendantCount(child);
|
||||
}
|
||||
|
||||
if (aDocument) {
|
||||
// Update our editable descendant count because we don't keep track of it
|
||||
// for content that is not in the uncomposed document.
|
||||
MOZ_ASSERT(EditableDescendantCount() == 0);
|
||||
ChangeEditableDescendantCount(editableDescendantCount);
|
||||
|
||||
if (!hadParent) {
|
||||
uint32_t editableDescendantChange = EditableInclusiveDescendantCount(this);
|
||||
if (editableDescendantChange != 0) {
|
||||
// If we are binding a subtree root to the document, we need to update
|
||||
// the editable descendant count of all the ancestors.
|
||||
// But we don't cross Shadow DOM boundary.
|
||||
// (The expected behavior with Shadow DOM is unclear)
|
||||
nsIContent* parent = GetParent();
|
||||
while (parent && parent->IsElement()) {
|
||||
parent->ChangeEditableDescendantCount(editableDescendantChange);
|
||||
parent = parent->GetParent();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsNodeUtils::ParentChainChanged(this);
|
||||
@ -1950,6 +1991,19 @@ Element::UnbindFromTree(bool aDeep, bool aNullParent)
|
||||
}
|
||||
|
||||
if (aNullParent) {
|
||||
if (GetParent() && GetParent()->IsInUncomposedDoc()) {
|
||||
// Update the editable descendant count in the ancestors before we
|
||||
// lose the reference to the parent.
|
||||
int32_t editableDescendantChange = -1 * EditableInclusiveDescendantCount(this);
|
||||
if (editableDescendantChange != 0) {
|
||||
nsIContent* parent = GetParent();
|
||||
while (parent) {
|
||||
parent->ChangeEditableDescendantCount(editableDescendantChange);
|
||||
parent = parent->GetParent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (IsRootOfNativeAnonymousSubtree()) {
|
||||
nsNodeUtils::NativeAnonymousChildListChange(this, true);
|
||||
}
|
||||
@ -2004,6 +2058,10 @@ Element::UnbindFromTree(bool aDeep, bool aNullParent)
|
||||
}
|
||||
}
|
||||
|
||||
// Editable descendant count only counts descendants that
|
||||
// are in the uncomposed document.
|
||||
ResetEditableDescendantCount();
|
||||
|
||||
if (aNullParent || !mParent->IsInShadowTree()) {
|
||||
UnsetFlags(NODE_IS_IN_SHADOW_TREE);
|
||||
|
||||
|
@ -488,22 +488,6 @@ Selection::GetInterlinePosition(ErrorResult& aRv)
|
||||
return mFrameSelection->GetHint() == CARET_ASSOCIATE_AFTER;
|
||||
}
|
||||
|
||||
bool
|
||||
Selection::IsEditorSelection() const
|
||||
{
|
||||
nsINode* focusNode = GetFocusNode();
|
||||
if (!focusNode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (focusNode->IsEditable()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto* element = Element::FromNode(focusNode);
|
||||
return element && element->State().HasState(NS_EVENT_STATE_MOZ_READWRITE);
|
||||
}
|
||||
|
||||
Nullable<int16_t>
|
||||
Selection::GetCaretBidiLevel(mozilla::ErrorResult& aRv) const
|
||||
{
|
||||
@ -783,7 +767,7 @@ NS_IMPL_MAIN_THREAD_ONLY_CYCLE_COLLECTING_ADDREF(Selection)
|
||||
NS_IMPL_MAIN_THREAD_ONLY_CYCLE_COLLECTING_RELEASE(Selection)
|
||||
|
||||
const RangeBoundary&
|
||||
Selection::AnchorRef() const
|
||||
Selection::AnchorRef()
|
||||
{
|
||||
if (!mAnchorFocusRange) {
|
||||
static RangeBoundary sEmpty;
|
||||
@ -798,7 +782,7 @@ Selection::AnchorRef() const
|
||||
}
|
||||
|
||||
const RangeBoundary&
|
||||
Selection::FocusRef() const
|
||||
Selection::FocusRef()
|
||||
{
|
||||
if (!mAnchorFocusRange) {
|
||||
static RangeBoundary sEmpty;
|
||||
@ -1573,7 +1557,6 @@ Selection::GetPrimaryOrCaretFrameForNodeOffset(nsIContent* aContent,
|
||||
return nsCaret::GetCaretFrameForNodeOffset(mFrameSelection,
|
||||
aContent, aOffset, hint,
|
||||
caretBidiLevel, aReturnFrame,
|
||||
/* aReturnUnadjustedFrame = */ nullptr,
|
||||
aOffsetUsed);
|
||||
}
|
||||
|
||||
|
@ -206,11 +206,7 @@ public:
|
||||
return mAnchorFocusRange;
|
||||
}
|
||||
|
||||
nsDirection GetDirection() const
|
||||
{
|
||||
return mDirection;
|
||||
}
|
||||
|
||||
nsDirection GetDirection(){return mDirection;}
|
||||
void SetDirection(nsDirection aDir){mDirection = aDir;}
|
||||
nsresult SetAnchorFocusToRange(nsRange *aRange);
|
||||
void ReplaceAnchorFocusRange(nsRange *aRange);
|
||||
@ -240,22 +236,22 @@ public:
|
||||
JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
// WebIDL methods
|
||||
nsINode* GetAnchorNode() const
|
||||
nsINode* GetAnchorNode()
|
||||
{
|
||||
const RangeBoundary& anchor = AnchorRef();
|
||||
return anchor.IsSet() ? anchor.Container() : nullptr;
|
||||
}
|
||||
uint32_t AnchorOffset() const
|
||||
uint32_t AnchorOffset()
|
||||
{
|
||||
const RangeBoundary& anchor = AnchorRef();
|
||||
return anchor.IsSet() ? anchor.Offset() : 0;
|
||||
}
|
||||
nsINode* GetFocusNode() const
|
||||
nsINode* GetFocusNode()
|
||||
{
|
||||
const RangeBoundary& focus = FocusRef();
|
||||
return focus.IsSet() ? focus.Container() : nullptr;
|
||||
}
|
||||
uint32_t FocusOffset() const
|
||||
uint32_t FocusOffset()
|
||||
{
|
||||
const RangeBoundary& focus = FocusRef();
|
||||
return focus.IsSet() ? focus.Offset() : 0;
|
||||
@ -272,8 +268,8 @@ public:
|
||||
return focus.IsSet() ? focus.GetChildAtOffset() : nullptr;
|
||||
}
|
||||
|
||||
const RangeBoundary& AnchorRef() const;
|
||||
const RangeBoundary& FocusRef() const;
|
||||
const RangeBoundary& AnchorRef();
|
||||
const RangeBoundary& FocusRef();
|
||||
|
||||
/*
|
||||
* IsCollapsed -- is the whole selection just one point, or unset?
|
||||
@ -479,9 +475,6 @@ public:
|
||||
void RemoveSelectionChangeBlocker();
|
||||
bool IsBlockingSelectionChangeEvents() const;
|
||||
|
||||
// Whether this selection is focused in an editable element.
|
||||
bool IsEditorSelection() const;
|
||||
|
||||
/**
|
||||
* Set the painting style for the range. The range must be a range in
|
||||
* the selection. The textRangeStyle will be used by text frame
|
||||
|
@ -119,7 +119,8 @@ using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
nsINode::nsSlots::nsSlots()
|
||||
: mWeakReference(nullptr)
|
||||
: mWeakReference(nullptr),
|
||||
mEditableDescendantCount(0)
|
||||
{
|
||||
}
|
||||
|
||||
@ -1179,6 +1180,38 @@ nsINode::GetOwnerGlobal() const
|
||||
return OwnerDoc()->GetScriptHandlingObject(dummy);
|
||||
}
|
||||
|
||||
void
|
||||
nsINode::ChangeEditableDescendantCount(int32_t aDelta)
|
||||
{
|
||||
if (aDelta == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsSlots* s = Slots();
|
||||
MOZ_ASSERT(aDelta > 0 ||
|
||||
s->mEditableDescendantCount >= (uint32_t) (-1 * aDelta));
|
||||
s->mEditableDescendantCount += aDelta;
|
||||
}
|
||||
|
||||
void
|
||||
nsINode::ResetEditableDescendantCount()
|
||||
{
|
||||
nsSlots* s = GetExistingSlots();
|
||||
if (s) {
|
||||
s->mEditableDescendantCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t
|
||||
nsINode::EditableDescendantCount()
|
||||
{
|
||||
nsSlots* s = GetExistingSlots();
|
||||
if (s) {
|
||||
return s->mEditableDescendantCount;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool
|
||||
nsINode::UnoptimizableCCNode() const
|
||||
{
|
||||
|
@ -1141,6 +1141,12 @@ public:
|
||||
* allocation bucket size, at the cost of some complexity.
|
||||
*/
|
||||
mozilla::UniquePtr<mozilla::LinkedList<nsRange>> mCommonAncestorRanges;
|
||||
|
||||
/**
|
||||
* Number of descendant nodes in the uncomposed document that have been
|
||||
* explicitly set as editable.
|
||||
*/
|
||||
uint32_t mEditableDescendantCount;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1176,11 +1182,28 @@ public:
|
||||
nsWrapperCache::UnsetFlags(aFlagsToUnset);
|
||||
}
|
||||
|
||||
void ChangeEditableDescendantCount(int32_t aDelta);
|
||||
|
||||
/**
|
||||
* Returns the count of descendant nodes in the uncomposed
|
||||
* document that are explicitly set as editable.
|
||||
*/
|
||||
uint32_t EditableDescendantCount();
|
||||
|
||||
/**
|
||||
* Sets the editable descendant count to 0. The editable
|
||||
* descendant count only counts explicitly editable nodes
|
||||
* that are in the uncomposed document so this method
|
||||
* should be called when nodes are are removed from it.
|
||||
*/
|
||||
void ResetEditableDescendantCount();
|
||||
|
||||
void SetEditableFlag(bool aEditable)
|
||||
{
|
||||
if (aEditable) {
|
||||
SetFlags(NODE_IS_EDITABLE);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
UnsetFlags(NODE_IS_EDITABLE);
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=816298
|
||||
<p id="test2">This<span style="-moz-user-select: none;"><span style="-moz-user-select: text"> text should</span> NOT</span> be copied.</p>
|
||||
<p id="test3">This text should<span style="-moz-user-select: -moz-none;"> NOT</span> be copied.</p>
|
||||
<p id="test4">This<span style="-moz-user-select: -moz-none;"><span style="-moz-user-select: text"> text should</span> NOT</span> be copied.</p>
|
||||
<p id="test5">This<span style="-moz-user-select: all"> text should</span> be copied.</p>
|
||||
<p id="test5">This<span style="-moz-user-select: all"> text<span style="-moz-user-select: none"> should</span></span> be copied.</p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
@ -107,7 +107,7 @@ var clipboardHTML = [
|
||||
'<p id=\"test2\">This<span style=\"-moz-user-select: text\"> text should</span> be copied.</p>',
|
||||
'<p id=\"test3\">This text should be copied.</p>',
|
||||
'<p id=\"test4\">This<span style=\"-moz-user-select: text\"> text should</span> be copied.</p>',
|
||||
'<p id=\"test5\">This<span style=\"-moz-user-select: all\"> text should</span> be copied.</p>',
|
||||
'<p id=\"test5\">This<span style=\"-moz-user-select: all\"> text<span style=\"-moz-user-select: none\"> should</span></span> be copied.</p>',
|
||||
];
|
||||
|
||||
// expected results for clipboard text/unicode
|
||||
@ -127,7 +127,7 @@ var innerHTMLStrings = [
|
||||
'This<span style=\"-moz-user-select: none;\"><span style=\"-moz-user-select: text\"> text should</span> NOT</span> be copied.',
|
||||
'This text should<span style=\"-moz-user-select: -moz-none;\"> NOT</span> be copied.',
|
||||
'This<span style=\"-moz-user-select: -moz-none;\"><span style=\"-moz-user-select: text\"> text should</span> NOT</span> be copied.',
|
||||
'This<span style=\"-moz-user-select: all\"> text should</span> be copied.',
|
||||
'This<span style=\"-moz-user-select: all\"> text<span style=\"-moz-user-select: none\"> should</span></span> be copied.',
|
||||
];
|
||||
|
||||
// expected results for pasting into a TEXTAREA
|
||||
|
@ -407,6 +407,14 @@ nsGenericHTMLElement::IntrinsicState() const
|
||||
return state;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
nsGenericHTMLElement::EditableInclusiveDescendantCount()
|
||||
{
|
||||
bool isEditable = IsInComposedDoc() && HasFlag(NODE_IS_EDITABLE) &&
|
||||
GetContentEditableValue() == eTrue;
|
||||
return EditableDescendantCount() + isEditable;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsGenericHTMLElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||
nsIContent* aBindingParent)
|
||||
@ -2671,7 +2679,7 @@ MakeContentDescendantsEditable(nsIContent *aContent, nsIDocument *aDocument)
|
||||
return;
|
||||
}
|
||||
|
||||
Element* element = aContent->AsElement();
|
||||
Element *element = aContent->AsElement();
|
||||
|
||||
element->UpdateEditableState(true);
|
||||
|
||||
@ -2695,9 +2703,18 @@ nsGenericHTMLElement::ChangeEditableState(int32_t aChange)
|
||||
}
|
||||
|
||||
if (aChange != 0) {
|
||||
if (nsCOMPtr<nsIHTMLDocument> htmlDocument = do_QueryInterface(document)) {
|
||||
nsCOMPtr<nsIHTMLDocument> htmlDocument =
|
||||
do_QueryInterface(document);
|
||||
if (htmlDocument) {
|
||||
htmlDocument->ChangeContentEditableCount(this, aChange);
|
||||
}
|
||||
|
||||
nsIContent* parent = GetParent();
|
||||
// Don't update across Shadow DOM boundary.
|
||||
while (parent && parent->IsElement()) {
|
||||
parent->ChangeEditableDescendantCount(aChange);
|
||||
parent = parent->GetParent();
|
||||
}
|
||||
}
|
||||
|
||||
if (document->HasFlag(NODE_IS_EDITABLE)) {
|
||||
|
@ -4681,16 +4681,10 @@ EditorBase::InitializeSelection(EventTarget* aFocusEventTarget)
|
||||
if (NS_WARN_IF(!caret)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
caret->SetIgnoreUserModify(false);
|
||||
caret->SetSelection(SelectionRefPtr());
|
||||
selectionController->SetCaretReadOnly(IsReadonly());
|
||||
selectionController->SetCaretEnabled(true);
|
||||
// NOTE(emilio): It's important for this call to be after
|
||||
// SetCaretEnabled(true), since that would override mIgnoreUserModify to true.
|
||||
//
|
||||
// Also, make sure to always ignore it for designMode, since that effectively
|
||||
// overrides everything and we allow to edit stuff with
|
||||
// contenteditable="false" subtrees in such a document.
|
||||
caret->SetIgnoreUserModify(targetNode->OwnerDoc()->HasFlag(NODE_IS_EDITABLE));
|
||||
|
||||
// Init selection
|
||||
selectionController->SetDisplaySelection(
|
||||
@ -4779,10 +4773,6 @@ EditorBase::FinalizeSelection()
|
||||
nsCOMPtr<nsIPresShell> presShell = GetPresShell();
|
||||
NS_ENSURE_TRUE(presShell, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
if (RefPtr<nsCaret> caret = presShell->GetCaret()) {
|
||||
caret->SetIgnoreUserModify(true);
|
||||
}
|
||||
|
||||
selectionController->SetCaretEnabled(false);
|
||||
|
||||
nsFocusManager* fm = nsFocusManager::GetFocusManager();
|
||||
|
@ -48,7 +48,7 @@ function test() {
|
||||
synthesizeKey("KEY_Enter", {shiftKey: true});
|
||||
synthesizeKey("KEY_Backspace");
|
||||
synthesizeKey("KEY_Backspace");
|
||||
}]];
|
||||
}, "A ; B ; C "]];
|
||||
[
|
||||
"insertorderedlist",
|
||||
"insertunorderedlist",
|
||||
|
@ -99,14 +99,12 @@ AdjustCaretFrameForLineEnd(nsIFrame** aFrame, int32_t* aOffset)
|
||||
for (nsIFrame* f = line->mFirstChild; count > 0; --count, f = f->GetNextSibling())
|
||||
{
|
||||
nsIFrame* r = CheckForTrailingTextFrameRecursive(f, *aFrame);
|
||||
if (r == *aFrame) {
|
||||
if (r == *aFrame)
|
||||
return;
|
||||
}
|
||||
if (r) {
|
||||
// We found our frame, but we may not be able to properly paint the caret if
|
||||
// -moz-user-modify differs from our actual frame.
|
||||
MOZ_ASSERT(r->IsTextFrame(), "Expected text frame");
|
||||
if (r)
|
||||
{
|
||||
*aFrame = r;
|
||||
NS_ASSERTION(r->IsTextFrame(), "Expected text frame");
|
||||
*aOffset = (static_cast<nsTextFrame*>(r))->GetContentEnd();
|
||||
return;
|
||||
}
|
||||
@ -382,13 +380,8 @@ nsCaret::GetGeometryForFrame(nsIFrame* aFrame,
|
||||
nsIFrame*
|
||||
nsCaret::GetFrameAndOffset(Selection* aSelection,
|
||||
nsINode* aOverrideNode, int32_t aOverrideOffset,
|
||||
int32_t* aFrameOffset,
|
||||
nsIFrame** aUnadjustedFrame)
|
||||
int32_t* aFrameOffset)
|
||||
{
|
||||
if (aUnadjustedFrame) {
|
||||
*aUnadjustedFrame = nullptr;
|
||||
}
|
||||
|
||||
nsINode* focusNode;
|
||||
int32_t focusOffset;
|
||||
|
||||
@ -412,11 +405,11 @@ nsCaret::GetFrameAndOffset(Selection* aSelection,
|
||||
nsIFrame* frame;
|
||||
nsresult rv = nsCaret::GetCaretFrameForNodeOffset(
|
||||
frameSelection, contentNode, focusOffset,
|
||||
frameSelection->GetHint(), bidiLevel, &frame, aUnadjustedFrame,
|
||||
aFrameOffset);
|
||||
frameSelection->GetHint(), bidiLevel, &frame, aFrameOffset);
|
||||
if (NS_FAILED(rv) || !frame) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
@ -500,26 +493,16 @@ nsCaret::GetPaintGeometry(nsRect* aRect)
|
||||
CheckSelectionLanguageChange();
|
||||
|
||||
int32_t frameOffset;
|
||||
nsIFrame* unadjustedFrame = nullptr;
|
||||
nsIFrame* frame = GetFrameAndOffset(GetSelection(),
|
||||
mOverrideContent, mOverrideOffset, &frameOffset, &unadjustedFrame);
|
||||
MOZ_ASSERT(!!frame == !!unadjustedFrame);
|
||||
mOverrideContent, mOverrideOffset, &frameOffset);
|
||||
if (!frame) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Now we have a frame, check whether it's appropriate to show the caret here.
|
||||
// Note we need to check the unadjusted frame, otherwise consider the
|
||||
// following case:
|
||||
//
|
||||
// <div contenteditable><span contenteditable=false>Text </span><br>
|
||||
//
|
||||
// Where the selection is targeting the <br>. We want to display the caret,
|
||||
// since the <br> we're focused at is editable, but we do want to paint it at
|
||||
// the adjusted frame offset, so that we can see the collapsed whitespace.
|
||||
const nsStyleUI* ui = unadjustedFrame->StyleUI();
|
||||
// now we have a frame, check whether it's appropriate to show the caret here
|
||||
const nsStyleUI* ui = frame->StyleUI();
|
||||
if ((!mIgnoreUserModify && ui->mUserModify == StyleUserModify::ReadOnly) ||
|
||||
unadjustedFrame->IsContentDisabled()) {
|
||||
frame->IsContentDisabled()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -662,7 +645,6 @@ nsCaret::GetCaretFrameForNodeOffset(nsFrameSelection* aFrameSelection,
|
||||
CaretAssociationHint aFrameHint,
|
||||
nsBidiLevel aBidiLevel,
|
||||
nsIFrame** aReturnFrame,
|
||||
nsIFrame** aReturnUnadjustedFrame,
|
||||
int32_t* aReturnOffset)
|
||||
{
|
||||
if (!aFrameSelection)
|
||||
@ -683,10 +665,6 @@ nsCaret::GetCaretFrameForNodeOffset(nsFrameSelection* aFrameSelection,
|
||||
if (!theFrame)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
if (aReturnUnadjustedFrame) {
|
||||
*aReturnUnadjustedFrame = theFrame;
|
||||
}
|
||||
|
||||
// if theFrame is after a text frame that's logically at the end of the line
|
||||
// (e.g. if theFrame is a <br> frame), then put the caret at the end of
|
||||
// that text frame instead. This way, the caret will be positioned as if
|
||||
@ -699,7 +677,8 @@ nsCaret::GetCaretFrameForNodeOffset(nsFrameSelection* aFrameSelection,
|
||||
// ------------------
|
||||
// NS_STYLE_DIRECTION_LTR : LTR or Default
|
||||
// NS_STYLE_DIRECTION_RTL
|
||||
if (theFrame->PresContext()->BidiEnabled()) {
|
||||
if (theFrame->PresContext()->BidiEnabled())
|
||||
{
|
||||
// If there has been a reflow, take the caret Bidi level to be the level of the current frame
|
||||
if (aBidiLevel & BIDI_LEVEL_UNDEFINED) {
|
||||
aBidiLevel = theFrame->GetEmbeddingLevel();
|
||||
|
@ -182,7 +182,6 @@ class nsCaret final : public nsISelectionListener
|
||||
CaretAssociationHint aFrameHint,
|
||||
uint8_t aBidiLevel,
|
||||
nsIFrame** aReturnFrame,
|
||||
nsIFrame** aReturnUnadjustedFrame,
|
||||
int32_t* aReturnOffset);
|
||||
static nsRect GetGeometryForFrame(nsIFrame* aFrame,
|
||||
int32_t aFrameOffset,
|
||||
@ -192,14 +191,11 @@ class nsCaret final : public nsISelectionListener
|
||||
// of aSelection. If aOverrideNode and aOverride are provided, use them
|
||||
// instead.
|
||||
// @param aFrameOffset return the frame offset if non-null.
|
||||
// @param aUnadjustedFrame return the original frame that the selection is
|
||||
// targeting, without any adjustment for painting.
|
||||
// @return the frame of the focus node.
|
||||
static nsIFrame* GetFrameAndOffset(mozilla::dom::Selection* aSelection,
|
||||
nsINode* aOverrideNode,
|
||||
int32_t aOverrideOffset,
|
||||
int32_t* aFrameOffset,
|
||||
nsIFrame** aUnadjustedFrame = nullptr);
|
||||
int32_t* aFrameOffset);
|
||||
|
||||
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
|
||||
|
||||
|
@ -4778,7 +4778,7 @@ nsLayoutUtils::GetNonGeneratedAncestor(nsIFrame* aFrame)
|
||||
}
|
||||
|
||||
nsIFrame*
|
||||
nsLayoutUtils::GetParentOrPlaceholderFor(const nsIFrame* aFrame)
|
||||
nsLayoutUtils::GetParentOrPlaceholderFor(nsIFrame* aFrame)
|
||||
{
|
||||
if ((aFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW)
|
||||
&& !aFrame->GetPrevInFlow()) {
|
||||
|
@ -1389,7 +1389,7 @@ public:
|
||||
* If aFrame is an out of flow frame, return its placeholder, otherwise
|
||||
* return its parent.
|
||||
*/
|
||||
static nsIFrame* GetParentOrPlaceholderFor(const nsIFrame* aFrame);
|
||||
static nsIFrame* GetParentOrPlaceholderFor(nsIFrame* aFrame);
|
||||
|
||||
/**
|
||||
* If aFrame is an out of flow frame, return its placeholder, otherwise
|
||||
|
@ -13,7 +13,7 @@
|
||||
var sel = getSelection();
|
||||
sel.collapse(div, 0);
|
||||
// Press Right four times to set the caret right before "baz"
|
||||
for (var i = 0; i < 4; ++i) {
|
||||
for (var i = 0; i < 5; ++i) {
|
||||
synthesizeKey("KEY_ArrowRight");
|
||||
}
|
||||
document.documentElement.removeAttribute("class");
|
||||
|
@ -13,7 +13,7 @@
|
||||
var sel = getSelection();
|
||||
sel.collapse(div, 0);
|
||||
// Press Right four times to set the caret right before "bar"
|
||||
for (var i = 0; i < 4; ++i) {
|
||||
for (var i = 0; i < 6; ++i) {
|
||||
synthesizeKey("KEY_ArrowRight");
|
||||
}
|
||||
document.documentElement.removeAttribute("class");
|
||||
|
@ -1,17 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<html class="reftest-wait">
|
||||
<title>Can drag-select non-editable content inside editable content</title>
|
||||
<script src="selection-utils.js"></script>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<div contenteditable spellcheck="false"
|
||||
style="outline: none">foo<span contenteditable=false>bar</span>baz</div>
|
||||
<script>
|
||||
SimpleTest.waitForFocus(function() {
|
||||
const span = document.querySelector("span");
|
||||
const rect = span.getBoundingClientRect();
|
||||
dragSelectPoints(span, 0, rect.height / 2, rect.width, rect.height / 2);
|
||||
setTimeout(() => document.documentElement.removeAttribute("class"));
|
||||
});
|
||||
function test() {
|
||||
focus();
|
||||
synthesizeMouseAtCenter(document.querySelector("span"), {});
|
||||
}
|
||||
function focused() {
|
||||
document.documentElement.removeAttribute("class");
|
||||
}
|
||||
</script>
|
||||
<body onload="setTimeout(test, 0)">
|
||||
<div contenteditable spellcheck="false" onfocus="focused()"
|
||||
style="outline: none">foo<span contenteditable=false>bar</span>baz</div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,29 +0,0 @@
|
||||
<!doctype html>
|
||||
<html class="reftest-wait">
|
||||
<title>Moving the caret in an editor jumps over non-editable nodes.</title>
|
||||
<script src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<style>
|
||||
* { outline: none }
|
||||
|
||||
div {
|
||||
border: 1px solid red;
|
||||
margin: 5px;
|
||||
padding: 2px;
|
||||
}
|
||||
</style>
|
||||
<div contenteditable="true">
|
||||
I am div number one
|
||||
<div contenteditable="false">X X X</div>
|
||||
However I am editable
|
||||
</div>
|
||||
<script>
|
||||
SimpleTest.waitForFocus(function() {
|
||||
const editable = document.querySelector('div[contenteditable="true"]');
|
||||
const noneditable = document.querySelector('div[contenteditable="false"]');
|
||||
editable.focus();
|
||||
synthesizeKey("KEY_ArrowDown");
|
||||
setTimeout(() => document.documentElement.removeAttribute("class"), 0);
|
||||
});
|
||||
</script>
|
||||
</html>
|
@ -1,30 +0,0 @@
|
||||
<!doctype html>
|
||||
<html class="reftest-wait">
|
||||
<title>Moving the caret in an editor jumps over non-editable nodes.</title>
|
||||
<script src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<style>
|
||||
* { outline: none }
|
||||
|
||||
div {
|
||||
border: 1px solid red;
|
||||
margin: 5px;
|
||||
padding: 2px;
|
||||
}
|
||||
</style>
|
||||
<div contenteditable="true">
|
||||
I am div number one
|
||||
<div contenteditable="false">X X X</div>
|
||||
However I am editable
|
||||
</div>
|
||||
<script>
|
||||
SimpleTest.waitForFocus(function() {
|
||||
const editable = document.querySelector('div[contenteditable="true"]');
|
||||
editable.focus();
|
||||
// 5 words in the first line, plus the non-editable node.
|
||||
for (let i = 0; i < "I am div number one".length + 2; ++i)
|
||||
synthesizeKey("KEY_ArrowRight");
|
||||
setTimeout(() => document.documentElement.removeAttribute("class"), 0);
|
||||
});
|
||||
</script>
|
||||
</html>
|
@ -1,32 +0,0 @@
|
||||
<!doctype html>
|
||||
<html class="reftest-wait">
|
||||
<title>Moving the caret in an editor jumps over non-editable nodes.</title>
|
||||
<script src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<style>
|
||||
* { outline: none }
|
||||
|
||||
div {
|
||||
border: 1px solid red;
|
||||
margin: 5px;
|
||||
padding: 2px;
|
||||
}
|
||||
</style>
|
||||
<div contenteditable="true">
|
||||
I am div number one
|
||||
<div contenteditable="false">X X X</div>
|
||||
However I am editable
|
||||
</div>
|
||||
<script>
|
||||
SimpleTest.waitForFocus(function() {
|
||||
const editable = document.querySelector('div[contenteditable="true"]');
|
||||
const noneditable = document.querySelector('div[contenteditable="false"]');
|
||||
editable.focus();
|
||||
synthesizeKey("KEY_End");
|
||||
synthesizeKey("KEY_ArrowDown");
|
||||
for (let i = 0; i < 4; ++i)
|
||||
synthesizeKey("KEY_ArrowLeft", { ctrlKey: true });
|
||||
setTimeout(() => document.documentElement.removeAttribute("class"), 0);
|
||||
});
|
||||
</script>
|
||||
</html>
|
@ -1,21 +0,0 @@
|
||||
<!doctype html>
|
||||
<title>Caret on editable line with non-editable content and whitespace.</title>
|
||||
<script src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<style>
|
||||
* { outline: none }
|
||||
|
||||
div {
|
||||
border: 1px solid red;
|
||||
margin: 5px;
|
||||
padding: 2px;
|
||||
}
|
||||
</style>
|
||||
<div contenteditable="true"><span>xyz </span><br>editable</div>
|
||||
<script>
|
||||
SimpleTest.waitForFocus(function() {
|
||||
const editable = document.querySelector('div[contenteditable="true"]');
|
||||
editable.focus();
|
||||
synthesizeMouse(editable, 100, 10, {});
|
||||
});
|
||||
</script>
|
@ -1,21 +0,0 @@
|
||||
<!doctype html>
|
||||
<title>Caret on editable line with non-editable content and whitespace.</title>
|
||||
<script src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<style>
|
||||
* { outline: none }
|
||||
|
||||
div {
|
||||
border: 1px solid red;
|
||||
margin: 5px;
|
||||
padding: 2px;
|
||||
}
|
||||
</style>
|
||||
<div contenteditable="true"><span contenteditable="false">xyz </span><br>editable</div>
|
||||
<script>
|
||||
SimpleTest.waitForFocus(function() {
|
||||
const editable = document.querySelector('div[contenteditable="true"]');
|
||||
editable.focus();
|
||||
synthesizeMouse(editable, 100, 10, {});
|
||||
});
|
||||
</script>
|
@ -1,26 +0,0 @@
|
||||
<!doctype html>
|
||||
<html class="reftest-wait">
|
||||
<title>Caret on editable line with non-editable content and whitespace.</title>
|
||||
<script src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<style>
|
||||
* { outline: none }
|
||||
|
||||
div {
|
||||
border: 1px solid red;
|
||||
margin: 5px;
|
||||
padding: 2px;
|
||||
}
|
||||
</style>
|
||||
<div contenteditable="true"><span>xyz </span><br>editable</div>
|
||||
<script>
|
||||
SimpleTest.waitForFocus(function() {
|
||||
const editable = document.querySelector('div[contenteditable="true"]');
|
||||
editable.focus();
|
||||
synthesizeMouse(editable, 100, 10, {});
|
||||
setTimeout(() => {
|
||||
sendString("xxx");
|
||||
setTimeout(() => document.documentElement.className = "");
|
||||
});
|
||||
});
|
||||
</script>
|
@ -1,27 +0,0 @@
|
||||
<!doctype html>
|
||||
<html class="reftest-wait">
|
||||
<title>Caret on editable line with non-editable content and whitespace.</title>
|
||||
<script src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<style>
|
||||
* { outline: none }
|
||||
|
||||
div {
|
||||
border: 1px solid red;
|
||||
margin: 5px;
|
||||
padding: 2px;
|
||||
}
|
||||
</style>
|
||||
<div contenteditable="true"><span contenteditable="false">xyz </span><br>editable</div>
|
||||
<script>
|
||||
SimpleTest.waitForFocus(function() {
|
||||
const editable = document.querySelector('div[contenteditable="true"]');
|
||||
editable.focus();
|
||||
synthesizeMouse(editable, 100, 10, {});
|
||||
setTimeout(() => {
|
||||
sendString("xxx");
|
||||
setTimeout(() => document.documentElement.className = "");
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</html>
|
@ -1,27 +0,0 @@
|
||||
<!doctype html>
|
||||
<html class="reftest-wait">
|
||||
<title>Caret on editable line with non-editable content and whitespace.</title>
|
||||
<script src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<style>
|
||||
* { outline: none }
|
||||
|
||||
div {
|
||||
border: 1px solid red;
|
||||
margin: 5px;
|
||||
padding: 2px;
|
||||
}
|
||||
</style>
|
||||
<div contenteditable="true"><span contenteditable="false">xyz </span><br>editable</div>
|
||||
<script>
|
||||
SimpleTest.waitForFocus(function() {
|
||||
const editable = document.querySelector('div[contenteditable="true"]');
|
||||
editable.focus();
|
||||
synthesizeKey("KEY_ArrowDown");
|
||||
synthesizeKey("KEY_ArrowLeft");
|
||||
setTimeout(() => {
|
||||
sendString("xxx");
|
||||
setTimeout(() => document.documentElement.className = "");
|
||||
});
|
||||
});
|
||||
</script>
|
@ -352,15 +352,6 @@ support-files =
|
||||
textarea-minlength-valid-before-change.html
|
||||
textarea-minlength-valid-change.html
|
||||
textarea-valid-ref.html
|
||||
bug1506547-1.html
|
||||
bug1506547-2.html
|
||||
bug1506547-3.html
|
||||
bug1506547-4.html
|
||||
bug1506547-5.html
|
||||
bug1506547-6.html
|
||||
bug1506547-4-ref.html
|
||||
bug1506547-5-ref.html
|
||||
|
||||
[test_remote_frame.html]
|
||||
[test_resize_flush.html]
|
||||
support-files = resize_flush_iframe.html
|
||||
|
@ -206,11 +206,6 @@ var tests = [
|
||||
// [ 'bug1423331-4.html' , 'bug1423331-2-ref.html' ] ,
|
||||
[ 'bug1484094-1.html' , 'bug1484094-1-ref.html' ] ,
|
||||
[ 'bug1484094-2.html' , 'bug1484094-2-ref.html' ] ,
|
||||
[ 'bug1506547-1.html' , 'bug1506547-2.html' ] ,
|
||||
[ 'bug1506547-2.html' , 'bug1506547-3.html' ] ,
|
||||
[ 'bug1506547-4.html' , 'bug1506547-4-ref.html' ] ,
|
||||
[ 'bug1506547-5.html' , 'bug1506547-5-ref.html' ] ,
|
||||
[ 'bug1506547-6.html' , 'bug1506547-5-ref.html' ] ,
|
||||
function() {SpecialPowers.pushPrefEnv({'clear': [['layout.accessiblecaret.enabled']]}, nextTest);} ,
|
||||
];
|
||||
|
||||
|
@ -4129,30 +4129,80 @@ nsFrame::GetDataForTableSelection(const nsFrameSelection* aFrameSelection,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static StyleUserSelect
|
||||
UsedUserSelect(const nsIFrame* aFrame)
|
||||
{
|
||||
if (aFrame->HasAnyStateBits(NS_FRAME_GENERATED_CONTENT)) {
|
||||
return StyleUserSelect::None;
|
||||
}
|
||||
|
||||
auto style = aFrame->StyleUIReset()->mUserSelect;
|
||||
if (style != StyleUserSelect::Auto) {
|
||||
return style;
|
||||
}
|
||||
|
||||
auto* parent = nsLayoutUtils::GetParentOrPlaceholderFor(aFrame);
|
||||
return parent ? UsedUserSelect(parent) : StyleUserSelect::Text;
|
||||
}
|
||||
|
||||
bool
|
||||
nsIFrame::IsSelectable(StyleUserSelect* aSelectStyle) const
|
||||
{
|
||||
auto style = UsedUserSelect(this);
|
||||
if (aSelectStyle) {
|
||||
*aSelectStyle = style;
|
||||
// it's ok if aSelectStyle is null
|
||||
|
||||
// Like 'visibility', we must check all the parents: if a parent
|
||||
// is not selectable, none of its children is selectable.
|
||||
//
|
||||
// The -moz-all value acts similarly: if a frame has 'user-select:-moz-all',
|
||||
// all its children are selectable, even those with 'user-select:none'.
|
||||
//
|
||||
// As a result, if 'none' and '-moz-all' are not present in the frame hierarchy,
|
||||
// aSelectStyle returns the first style that is not AUTO. If these values
|
||||
// are present in the frame hierarchy, aSelectStyle returns the style of the
|
||||
// topmost parent that has either 'none' or '-moz-all'.
|
||||
//
|
||||
// The -moz-text value acts as a way to override an ancestor's all/-moz-all value.
|
||||
//
|
||||
// For instance, if the frame hierarchy is:
|
||||
// AUTO -> _MOZ_ALL -> NONE -> TEXT, the returned value is ALL
|
||||
// AUTO -> _MOZ_ALL -> NONE -> _MOZ_TEXT, the returned value is TEXT.
|
||||
// TEXT -> NONE -> AUTO -> _MOZ_ALL, the returned value is TEXT
|
||||
// _MOZ_ALL -> TEXT -> AUTO -> AUTO, the returned value is ALL
|
||||
// _MOZ_ALL -> _MOZ_TEXT -> AUTO -> AUTO, the returned value is TEXT.
|
||||
// AUTO -> CELL -> TEXT -> AUTO, the returned value is TEXT
|
||||
//
|
||||
StyleUserSelect selectStyle = StyleUserSelect::Auto;
|
||||
nsIFrame* frame = const_cast<nsIFrame*>(this);
|
||||
bool containsEditable = false;
|
||||
|
||||
while (frame) {
|
||||
const nsStyleUIReset* userinterface = frame->StyleUIReset();
|
||||
switch (userinterface->mUserSelect) {
|
||||
case StyleUserSelect::All: {
|
||||
// override the previous values
|
||||
if (selectStyle != StyleUserSelect::MozText) {
|
||||
selectStyle = userinterface->mUserSelect;
|
||||
}
|
||||
nsIContent* frameContent = frame->GetContent();
|
||||
containsEditable = frameContent &&
|
||||
frameContent->EditableDescendantCount() > 0;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// otherwise return the first value which is not 'auto'
|
||||
if (selectStyle == StyleUserSelect::Auto) {
|
||||
selectStyle = userinterface->mUserSelect;
|
||||
}
|
||||
break;
|
||||
}
|
||||
frame = nsLayoutUtils::GetParentOrPlaceholderFor(frame);
|
||||
}
|
||||
return style != StyleUserSelect::None;
|
||||
|
||||
// convert internal values to standard values
|
||||
if (selectStyle == StyleUserSelect::Auto ||
|
||||
selectStyle == StyleUserSelect::MozText) {
|
||||
selectStyle = StyleUserSelect::Text;
|
||||
}
|
||||
|
||||
// If user tries to select all of a non-editable content,
|
||||
// prevent selection if it contains editable content.
|
||||
bool allowSelection = true;
|
||||
if (selectStyle == StyleUserSelect::All) {
|
||||
allowSelection = !containsEditable;
|
||||
}
|
||||
|
||||
// return stuff
|
||||
if (aSelectStyle) {
|
||||
*aSelectStyle = selectStyle;
|
||||
}
|
||||
|
||||
return !(mState & NS_FRAME_GENERATED_CONTENT) &&
|
||||
allowSelection &&
|
||||
selectStyle != StyleUserSelect::None;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -4926,21 +4976,15 @@ static FrameTarget GetSelectionClosestFrameForLine(
|
||||
WritingMode wm = aLine->mWritingMode;
|
||||
LogicalPoint pt(wm, aPoint, aLine->mContainerSize);
|
||||
bool canSkipBr = false;
|
||||
bool lastFrameWasEditable = false;
|
||||
for (int32_t n = aLine->GetChildCount(); n;
|
||||
--n, frame = frame->GetNextSibling()) {
|
||||
// Skip brFrames. Can only skip if the line contains at least
|
||||
// one selectable and non-empty frame before. Also, avoid skipping brs if
|
||||
// the previous thing had a different editableness than us, since then we
|
||||
// may end up not being able to select after it if the br is the last thing
|
||||
// on the line.
|
||||
// one selectable and non-empty frame before
|
||||
if (!SelfIsSelectable(frame, aFlags) || frame->IsEmpty() ||
|
||||
(canSkipBr && frame->IsBrFrame() &&
|
||||
lastFrameWasEditable == frame->GetContent()->IsEditable())) {
|
||||
(canSkipBr && frame->IsBrFrame())) {
|
||||
continue;
|
||||
}
|
||||
canSkipBr = true;
|
||||
lastFrameWasEditable = frame->GetContent() && frame->GetContent()->IsEditable();
|
||||
LogicalRect frameRect = LogicalRect(wm, frame->GetRect(),
|
||||
aLine->mContainerSize);
|
||||
if (pt.I(wm) >= frameRect.IStart(wm)) {
|
||||
@ -5131,14 +5175,18 @@ OffsetsForSingleFrame(nsIFrame* aFrame, const nsPoint& aPoint)
|
||||
|
||||
static nsIFrame* AdjustFrameForSelectionStyles(nsIFrame* aFrame) {
|
||||
nsIFrame* adjustedFrame = aFrame;
|
||||
for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent()) {
|
||||
for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent())
|
||||
{
|
||||
// These are the conditions that make all children not able to handle
|
||||
// a cursor.
|
||||
StyleUserSelect userSelect = frame->StyleUIReset()->mUserSelect;
|
||||
if (userSelect != StyleUserSelect::Auto && userSelect != StyleUserSelect::All) {
|
||||
if (userSelect == StyleUserSelect::MozText) {
|
||||
// If we see a -moz-text element, we shouldn't look further up the parent
|
||||
// chain!
|
||||
break;
|
||||
}
|
||||
if (userSelect == StyleUserSelect::All || frame->IsGeneratedContentFrame()) {
|
||||
if (userSelect == StyleUserSelect::All ||
|
||||
frame->IsGeneratedContentFrame()) {
|
||||
adjustedFrame = frame;
|
||||
}
|
||||
}
|
||||
@ -5151,7 +5199,8 @@ nsIFrame::ContentOffsets nsIFrame::GetContentOffsetsFromPoint(const nsPoint& aPo
|
||||
nsIFrame *adjustedFrame;
|
||||
if (aFlags & IGNORE_SELECTION_STYLE) {
|
||||
adjustedFrame = this;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
// This section of code deals with special selection styles. Note that
|
||||
// -moz-all exists, even though it doesn't need to be explicitly handled.
|
||||
//
|
||||
@ -5162,7 +5211,8 @@ nsIFrame::ContentOffsets nsIFrame::GetContentOffsetsFromPoint(const nsPoint& aPo
|
||||
|
||||
// -moz-user-select: all needs special handling, because clicking on it
|
||||
// should lead to the whole frame being selected
|
||||
if (adjustedFrame->StyleUIReset()->mUserSelect == StyleUserSelect::All) {
|
||||
if (adjustedFrame && adjustedFrame->StyleUIReset()->mUserSelect ==
|
||||
StyleUserSelect::All) {
|
||||
nsPoint adjustedPoint = aPoint + this->GetOffsetTo(adjustedFrame);
|
||||
return OffsetsForSingleFrame(adjustedFrame, adjustedPoint);
|
||||
}
|
||||
@ -8082,7 +8132,8 @@ nsFrame::GetNextPrevLineFromeBlockFrame(nsPresContext* aPresContext,
|
||||
nsPeekOffsetStruct *aPos,
|
||||
nsIFrame *aBlockFrame,
|
||||
int32_t aLineStart,
|
||||
int8_t aOutSideLimit)
|
||||
int8_t aOutSideLimit
|
||||
)
|
||||
{
|
||||
//magic numbers aLineStart will be -1 for end of block 0 will be start of block
|
||||
if (!aBlockFrame || !aPos)
|
||||
@ -8190,19 +8241,6 @@ nsFrame::GetNextPrevLineFromeBlockFrame(nsPresContext* aPresContext,
|
||||
if (NS_FAILED(result))
|
||||
return result;
|
||||
|
||||
auto FoundValidFrame = [aPos](const ContentOffsets& aOffsets, const nsIFrame* aFrame) {
|
||||
if (!aOffsets.content) {
|
||||
return false;
|
||||
}
|
||||
if (!aFrame->IsSelectable(nullptr)) {
|
||||
return false;
|
||||
}
|
||||
if (aPos->mForceEditableRegion && !aOffsets.content->IsEditable()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
nsIFrame *storeOldResultFrame = resultFrame;
|
||||
while ( !found ){
|
||||
nsPoint point;
|
||||
@ -8256,7 +8294,8 @@ nsFrame::GetNextPrevLineFromeBlockFrame(nsPresContext* aPresContext,
|
||||
}
|
||||
}
|
||||
|
||||
if (!resultFrame->HasView()) {
|
||||
if (!resultFrame->HasView())
|
||||
{
|
||||
nsView* view;
|
||||
nsPoint offset;
|
||||
resultFrame->GetOffsetFromView(offset, &view);
|
||||
@ -8265,9 +8304,12 @@ nsFrame::GetNextPrevLineFromeBlockFrame(nsPresContext* aPresContext,
|
||||
aPos->mResultContent = offsets.content;
|
||||
aPos->mContentOffset = offsets.offset;
|
||||
aPos->mAttach = offsets.associate;
|
||||
if (FoundValidFrame(offsets, resultFrame)) {
|
||||
found = true;
|
||||
break;
|
||||
if (offsets.content)
|
||||
{
|
||||
if (resultFrame->IsSelectable(nullptr)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -8304,13 +8346,16 @@ nsFrame::GetNextPrevLineFromeBlockFrame(nsPresContext* aPresContext,
|
||||
aPos->mResultContent = offsets.content;
|
||||
aPos->mContentOffset = offsets.offset;
|
||||
aPos->mAttach = offsets.associate;
|
||||
if (FoundValidFrame(offsets, resultFrame)) {
|
||||
found = true;
|
||||
if (resultFrame == farStoppingFrame)
|
||||
aPos->mAttach = CARET_ASSOCIATE_BEFORE;
|
||||
else
|
||||
aPos->mAttach = CARET_ASSOCIATE_AFTER;
|
||||
break;
|
||||
if (offsets.content)
|
||||
{
|
||||
if (resultFrame->IsSelectable(nullptr)) {
|
||||
found = true;
|
||||
if (resultFrame == farStoppingFrame)
|
||||
aPos->mAttach = CARET_ASSOCIATE_BEFORE;
|
||||
else
|
||||
aPos->mAttach = CARET_ASSOCIATE_AFTER;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (aPos->mDirection == eDirPrevious && (resultFrame == nearStoppingFrame))
|
||||
break;
|
||||
@ -8534,7 +8579,6 @@ nsIFrame::PeekOffset(nsPeekOffsetStruct* aPos)
|
||||
result =
|
||||
current->GetFrameFromDirection(aPos->mDirection, aPos->mVisual,
|
||||
aPos->mJumpLines, aPos->mScrollViewStop,
|
||||
aPos->mForceEditableRegion,
|
||||
¤t, &offset, &jumpedLine,
|
||||
&movedOverNonSelectable);
|
||||
if (NS_FAILED(result))
|
||||
@ -8546,7 +8590,9 @@ nsIFrame::PeekOffset(nsPeekOffsetStruct* aPos)
|
||||
eatingNonRenderableWS = true;
|
||||
|
||||
// Remember if we moved over non-selectable text when finding another frame.
|
||||
movedOverNonSelectableText |= movedOverNonSelectable;
|
||||
if (movedOverNonSelectable) {
|
||||
movedOverNonSelectableText = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Found frame, but because we moved over non selectable text we want the offset
|
||||
@ -8636,7 +8682,6 @@ nsIFrame::PeekOffset(nsPeekOffsetStruct* aPos)
|
||||
result =
|
||||
current->GetFrameFromDirection(aPos->mDirection, aPos->mVisual,
|
||||
aPos->mJumpLines, aPos->mScrollViewStop,
|
||||
aPos->mForceEditableRegion,
|
||||
&nextFrame, &nextFrameOffset, &jumpedLine,
|
||||
&movedOverNonSelectableText);
|
||||
// We can't jump lines if we're looking for whitespace following
|
||||
@ -8994,7 +9039,6 @@ nsFrame::GetLineNumber(nsIFrame *aFrame, bool aLockScroll, nsIFrame** aContainin
|
||||
nsresult
|
||||
nsIFrame::GetFrameFromDirection(nsDirection aDirection, bool aVisual,
|
||||
bool aJumpLines, bool aScrollViewStop,
|
||||
bool aForceEditableRegion,
|
||||
nsIFrame** aOutFrame, int32_t* aOutOffset,
|
||||
bool* aOutJumpedLine, bool* aOutMovedOverNonSelectableText)
|
||||
{
|
||||
@ -9099,31 +9143,24 @@ nsIFrame::GetFrameFromDirection(nsDirection aDirection, bool aVisual,
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
auto IsSelectable = [aForceEditableRegion](const nsIFrame* aFrame) {
|
||||
if (!aFrame->IsSelectable(nullptr)) {
|
||||
return false;
|
||||
// Skip brFrames, but only if they are not the only frame in the line
|
||||
if (atLineEdge && aDirection == eDirPrevious &&
|
||||
traversedFrame->IsBrFrame()) {
|
||||
int32_t lineFrameCount;
|
||||
nsIFrame *currentBlockFrame, *currentFirstFrame;
|
||||
nsRect usedRect;
|
||||
int32_t currentLine = nsFrame::GetLineNumber(traversedFrame, aScrollViewStop, ¤tBlockFrame);
|
||||
nsAutoLineIterator iter = currentBlockFrame->GetLineIterator();
|
||||
result = iter->GetLine(currentLine, ¤tFirstFrame, &lineFrameCount, usedRect);
|
||||
if (NS_FAILED(result)) {
|
||||
return result;
|
||||
}
|
||||
return !aForceEditableRegion || aFrame->GetContent()->IsEditable();
|
||||
};
|
||||
|
||||
// Skip brFrames, but only we can select something before hitting the end of
|
||||
// the line or a non-selectable region.
|
||||
if (atLineEdge && aDirection == eDirPrevious && traversedFrame->IsBrFrame()) {
|
||||
bool canSkipBr = false;
|
||||
for (nsIFrame* current = traversedFrame->GetPrevSibling();
|
||||
current;
|
||||
current = current->GetPrevSibling()) {
|
||||
if (IsSelectable(current)) {
|
||||
canSkipBr = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (canSkipBr) {
|
||||
if (lineFrameCount > 1) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
selectable = IsSelectable(traversedFrame);
|
||||
selectable = traversedFrame->IsSelectable(nullptr);
|
||||
if (!selectable) {
|
||||
*aOutMovedOverNonSelectableText = true;
|
||||
}
|
||||
|
@ -119,7 +119,6 @@ nsPeekOffsetStruct::nsPeekOffsetStruct(nsSelectionAmount aAmount,
|
||||
bool aIsKeyboardSelect,
|
||||
bool aVisual,
|
||||
bool aExtend,
|
||||
ForceEditableRegion aForceEditableRegion,
|
||||
EWordMovementType aWordMovementType)
|
||||
: mAmount(aAmount)
|
||||
, mDirection(aDirection)
|
||||
@ -131,7 +130,6 @@ nsPeekOffsetStruct::nsPeekOffsetStruct(nsSelectionAmount aAmount,
|
||||
, mIsKeyboardSelect(aIsKeyboardSelect)
|
||||
, mVisual(aVisual)
|
||||
, mExtend(aExtend)
|
||||
, mForceEditableRegion(aForceEditableRegion == ForceEditableRegion::Yes)
|
||||
, mResultContent()
|
||||
, mResultFrame(nullptr)
|
||||
, mContentOffset(0)
|
||||
@ -708,8 +706,12 @@ nsFrameSelection::MoveCaret(nsDirection aDirection,
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
|
||||
int32_t scrollFlags = Selection::SCROLL_FOR_CARET_MOVE;
|
||||
const bool isEditorSelection = sel->IsEditorSelection();
|
||||
if (isEditorSelection) {
|
||||
nsINode* focusNode = sel->GetFocusNode();
|
||||
if (focusNode &&
|
||||
(focusNode->IsEditable() ||
|
||||
(focusNode->IsElement() &&
|
||||
focusNode->AsElement()->State().
|
||||
HasState(NS_EVENT_STATE_MOZ_READWRITE)))) {
|
||||
// If caret moves in editor, it should cause scrolling even if it's in
|
||||
// overflow: hidden;.
|
||||
scrollFlags |= Selection::SCROLL_OVERFLOW_HIDDEN;
|
||||
@ -777,14 +779,11 @@ nsFrameSelection::MoveCaret(nsDirection aDirection,
|
||||
if (NS_FAILED(result) || !frame)
|
||||
return NS_FAILED(result) ? result : NS_ERROR_FAILURE;
|
||||
|
||||
const auto forceEditableRegion = isEditorSelection
|
||||
? nsPeekOffsetStruct::ForceEditableRegion::Yes
|
||||
: nsPeekOffsetStruct::ForceEditableRegion::No;
|
||||
//set data using mLimiter to stop on scroll views. If we have a limiter then we stop peeking
|
||||
//when we hit scrollable views. If no limiter then just let it go ahead
|
||||
nsPeekOffsetStruct pos(aAmount, eDirPrevious, offsetused, desiredPos,
|
||||
true, mLimiter != nullptr, true, visualMovement,
|
||||
aContinueSelection, forceEditableRegion);
|
||||
aContinueSelection);
|
||||
|
||||
nsBidiDirection paraDir = nsBidiPresUtils::ParagraphDirection(frame);
|
||||
|
||||
@ -946,7 +945,7 @@ nsFrameSelection::GetPrevNextBidiLevels(nsIContent* aNode,
|
||||
int32_t offset;
|
||||
bool jumpedLine, movedOverNonSelectableText;
|
||||
nsresult rv = currentFrame->GetFrameFromDirection(direction, false,
|
||||
aJumpLines, true, false,
|
||||
aJumpLines, true,
|
||||
&newFrame, &offset, &jumpedLine,
|
||||
&movedOverNonSelectableText);
|
||||
if (NS_FAILED(rv))
|
||||
|
@ -75,12 +75,6 @@ class nsIPresShell;
|
||||
*/
|
||||
struct MOZ_STACK_CLASS nsPeekOffsetStruct
|
||||
{
|
||||
enum class ForceEditableRegion
|
||||
{
|
||||
No,
|
||||
Yes,
|
||||
};
|
||||
|
||||
nsPeekOffsetStruct(nsSelectionAmount aAmount,
|
||||
nsDirection aDirection,
|
||||
int32_t aStartOffset,
|
||||
@ -90,7 +84,6 @@ struct MOZ_STACK_CLASS nsPeekOffsetStruct
|
||||
bool aIsKeyboardSelect,
|
||||
bool aVisual,
|
||||
bool aExtend,
|
||||
ForceEditableRegion = ForceEditableRegion::No,
|
||||
mozilla::EWordMovementType aWordMovementType = mozilla::eDefaultBehavior);
|
||||
|
||||
// Note: Most arguments (input and output) are only used with certain values
|
||||
@ -149,10 +142,6 @@ struct MOZ_STACK_CLASS nsPeekOffsetStruct
|
||||
// mExtend: Whether the selection is being extended or moved.
|
||||
bool mExtend;
|
||||
|
||||
// mForceEditableRegion: If true, the offset has to end up in an editable
|
||||
// node, otherwise we'll keep searching.
|
||||
const bool mForceEditableRegion;
|
||||
|
||||
/*** Output arguments ***/
|
||||
|
||||
// mResultContent: Content reached as a result of the peek.
|
||||
@ -160,7 +149,7 @@ struct MOZ_STACK_CLASS nsPeekOffsetStruct
|
||||
|
||||
// mResultFrame: Frame reached as a result of the peek.
|
||||
// Used with: eSelectCharacter, eSelectWord.
|
||||
nsIFrame* mResultFrame;
|
||||
nsIFrame *mResultFrame;
|
||||
|
||||
// mContentOffset: Offset into content reached as a result of the peek.
|
||||
int32_t mContentOffset;
|
||||
|
@ -3288,7 +3288,6 @@ public:
|
||||
*/
|
||||
nsresult GetFrameFromDirection(nsDirection aDirection, bool aVisual,
|
||||
bool aJumpLines, bool aScrollViewStop,
|
||||
bool aForceEditableRegion,
|
||||
nsIFrame** aOutFrame, int32_t* aOutOffset,
|
||||
bool* aOutJumpedLine, bool* aOutMovedOverNonSelectableText);
|
||||
|
||||
|
@ -9,6 +9,15 @@
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
*|*:-moz-read-write :-moz-read-only {
|
||||
-moz-user-select: all;
|
||||
}
|
||||
|
||||
*|*:-moz-read-only > :-moz-read-write {
|
||||
/* override the above -moz-user-select: all rule. */
|
||||
-moz-user-select: -moz-text;
|
||||
}
|
||||
|
||||
input:-moz-read-write > .anonymous-div:-moz-read-only,
|
||||
textarea:-moz-read-write > .anonymous-div:-moz-read-only {
|
||||
-moz-user-select: text;
|
||||
@ -91,7 +100,7 @@ input[contenteditable="true"][type="hidden"] {
|
||||
}
|
||||
|
||||
label:-moz-read-write {
|
||||
-moz-user-select: all;
|
||||
-moz-user-select: all;
|
||||
}
|
||||
|
||||
*|*::-moz-display-comboboxcontrol-frame {
|
||||
|
@ -39,7 +39,6 @@ ${helpers.predefined_type(
|
||||
gecko_ffi_name="mUserSelect",
|
||||
alias="-webkit-user-select",
|
||||
animation_value_type="discrete",
|
||||
needs_context=False,
|
||||
spec="https://drafts.csswg.org/css-ui-4/#propdef-user-select",
|
||||
)}
|
||||
|
||||
|
@ -141,6 +141,11 @@ impl Parse for ScrollbarColor {
|
||||
}
|
||||
}
|
||||
|
||||
fn in_ua_sheet(context: &ParserContext) -> bool {
|
||||
use crate::stylesheets::Origin;
|
||||
context.stylesheet_origin == Origin::UserAgent
|
||||
}
|
||||
|
||||
/// The specified value for the `user-select` property.
|
||||
///
|
||||
/// https://drafts.csswg.org/css-ui-4/#propdef-user-select
|
||||
@ -163,6 +168,15 @@ pub enum UserSelect {
|
||||
Text,
|
||||
#[parse(aliases = "-moz-none")]
|
||||
None,
|
||||
/// Force selection of all children.
|
||||
/// Force selection of all children, unless an ancestor has `none` set.
|
||||
All,
|
||||
/// Like `text`, except that it won't get overridden by ancestors having
|
||||
/// `all`.
|
||||
///
|
||||
/// FIXME(emilio): This only has one use in contenteditable.css, can we find
|
||||
/// a better way to do this?
|
||||
///
|
||||
/// See bug 1181130.
|
||||
#[parse(condition = "in_ua_sheet")]
|
||||
MozText,
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user