Issue #2388 - Part 1: Mark Selection as "called by JS" when caused by a JS call to the selection API

This commit is contained in:
Moonchild 2024-01-16 19:28:24 +01:00 committed by roytam1
commit a83e4b6a34
6 changed files with 239 additions and 34 deletions

View file

@ -269,6 +269,7 @@ nsRange::nsRange(nsINode* aNode)
, mStartOffsetWasIncremented(false)
, mEndOffsetWasIncremented(false)
, mEnableGravitationOnElementRemoval(true)
, mCalledByJS(false)
#ifdef DEBUG
, mAssertNextInsertOrAppendIndex(-1)
, mAssertNextInsertOrAppendNode(nullptr)
@ -997,7 +998,7 @@ nsRange::DoSetRange(nsINode* aStartN, uint32_t aStartOffset,
// Notify any selection listeners. This has to occur last because otherwise the world
// could be observed by a selection listener while the range was in an invalid state.
if (mSelection) {
mSelection->NotifySelectionListeners();
mSelection->NotifySelectionListeners(mCalledByJS);
}
}
@ -1224,6 +1225,13 @@ nsRange::IsValidBoundary(nsINode* aNode)
return root;
}
void
nsRange::SetStartJS(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr)
{
AutoCalledByJSSetter markAsCalledByJS(*this);
SetStart(aNode, aOffset, aErr);
}
void
nsRange::SetStart(nsINode& aNode, uint32_t aOffset, ErrorResult& aRv)
{
@ -1279,6 +1287,13 @@ nsRange::SetStart(nsINode* aParent, uint32_t aOffset)
return NS_OK;
}
void
nsRange::SetStartBeforeJS(nsINode& aNode, ErrorResult& aErr)
{
AutoCalledByJSSetter markAsCalledByJS(*this);
SetStartBefore(aNode, aErr);
}
void
nsRange::SetStartBefore(nsINode& aNode, ErrorResult& aRv)
{
@ -1310,6 +1325,13 @@ nsRange::SetStartBefore(nsIDOMNode* aSibling)
return rv.StealNSResult();
}
void
nsRange::SetStartAfterJS(nsINode& aNode, ErrorResult& aErr)
{
AutoCalledByJSSetter markAsCalledByJS(*this);
SetStartAfter(aNode, aErr);
}
void
nsRange::SetStartAfter(nsINode& aNode, ErrorResult& aRv)
{
@ -1341,6 +1363,13 @@ nsRange::SetStartAfter(nsIDOMNode* aSibling)
return rv.StealNSResult();
}
void
nsRange::SetEndJS(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr)
{
AutoCalledByJSSetter markAsCalledByJS(*this);
SetEnd(aNode, aOffset, aErr);
}
void
nsRange::SetEnd(nsINode& aNode, uint32_t aOffset, ErrorResult& aRv)
{
@ -1455,6 +1484,13 @@ nsRange::SetStartAndEnd(nsINode* aStartParent, uint32_t aStartOffset,
return NS_OK;
}
void
nsRange::SetEndBeforeJS(nsINode& aNode, ErrorResult& aErr)
{
AutoCalledByJSSetter markAsCalledByJS(*this);
SetEndBefore(aNode, aErr);
}
void
nsRange::SetEndBefore(nsINode& aNode, ErrorResult& aRv)
{
@ -1486,6 +1522,13 @@ nsRange::SetEndBefore(nsIDOMNode* aSibling)
return rv.StealNSResult();
}
void
nsRange::SetEndAfterJS(nsINode& aNode, ErrorResult& aErr)
{
AutoCalledByJSSetter markAsCalledByJS(*this);
SetEndAfter(aNode, aErr);
}
void
nsRange::SetEndAfter(nsINode& aNode, ErrorResult& aRv)
{
@ -1532,6 +1575,13 @@ nsRange::Collapse(bool aToStart)
return NS_OK;
}
void
nsRange::CollapseJS(bool aToStart)
{
AutoCalledByJSSetter markAsCalledByJS(*this);
Unused << Collapse(aToStart);
}
NS_IMETHODIMP
nsRange::SelectNode(nsIDOMNode* aN)
{
@ -1543,6 +1593,13 @@ nsRange::SelectNode(nsIDOMNode* aN)
return rv.StealNSResult();
}
void
nsRange::SelectNodeJS(nsINode& aNode, ErrorResult& aErr)
{
AutoCalledByJSSetter markAsCalledByJS(*this);
SelectNode(aNode, aErr);
}
void
nsRange::SelectNode(nsINode& aNode, ErrorResult& aRv)
{
@ -1582,6 +1639,13 @@ nsRange::SelectNodeContents(nsIDOMNode* aN)
return rv.StealNSResult();
}
void
nsRange::SelectNodeContentsJS(nsINode& aNode, ErrorResult& aErr)
{
AutoCalledByJSSetter markAsCalledByJS(*this);
SelectNodeContents(aNode, aErr);
}
void
nsRange::SelectNodeContents(nsINode& aNode, ErrorResult& aRv)
{

View file

@ -20,6 +20,7 @@
#include "nsStubMutationObserver.h"
#include "nsWrapperCache.h"
#include "mozilla/Attributes.h"
#include "mozilla/GuardObjects.h"
namespace mozilla {
class ErrorResult;
@ -256,14 +257,20 @@ public:
void InsertNode(nsINode& aNode, ErrorResult& aErr);
bool IntersectsNode(nsINode& aNode, ErrorResult& aRv);
bool IsPointInRange(nsINode& aParent, uint32_t aOffset, ErrorResult& aErr);
void SelectNode(nsINode& aNode, ErrorResult& aErr);
void SelectNodeContents(nsINode& aNode, ErrorResult& aErr);
void SetEnd(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr);
void SetEndAfter(nsINode& aNode, ErrorResult& aErr);
void SetEndBefore(nsINode& aNode, ErrorResult& aErr);
void SetStart(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr);
void SetStartAfter(nsINode& aNode, ErrorResult& aErr);
void SetStartBefore(nsINode& aNode, ErrorResult& aErr);
// *JS() methods are mapped to Range.*() of DOM.
// They may move focus only when the range represents normal selection.
// These methods shouldn't be used from internal.
void CollapseJS(bool aToStart);
void SelectNodeJS(nsINode& aNode, ErrorResult& aErr);
void SelectNodeContentsJS(nsINode& aNode, ErrorResult& aErr);
void SetEndJS(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr);
void SetEndAfterJS(nsINode& aNode, ErrorResult& aErr);
void SetEndBeforeJS(nsINode& aNode, ErrorResult& aErr);
void SetStartJS(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr);
void SetStartAfterJS(nsINode& aNode, ErrorResult& aErr);
void SetStartBeforeJS(nsINode& aNode, ErrorResult& aErr);
void SurroundContents(nsINode& aNode, ErrorResult& aErr);
already_AddRefed<DOMRect> GetBoundingClientRect(bool aClampToEdge = true,
bool aFlushLayout = true);
@ -272,6 +279,17 @@ public:
void GetClientRectsAndTexts(
mozilla::dom::ClientRectsAndTexts& aResult,
ErrorResult& aErr);
// Following methods should be used for internal use instead of *JS().
void SelectNode(nsINode& aNode, ErrorResult& aErr);
void SelectNodeContents(nsINode& aNode, ErrorResult& aErr);
void SetEnd(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr);
void SetEndAfter(nsINode& aNode, ErrorResult& aErr);
void SetEndBefore(nsINode& aNode, ErrorResult& aErr);
void SetStart(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr);
void SetStartAfter(nsINode& aNode, ErrorResult& aErr);
void SetStartBefore(nsINode& aNode, ErrorResult& aErr);
static void GetInnerTextNoFlush(mozilla::dom::DOMString& aValue,
mozilla::ErrorResult& aError,
nsIContent* aStartParent,
@ -392,6 +410,31 @@ protected:
size_t aRangeStart,
size_t aRangeEnd);
// Assume that this is guaranteed that this is held by the caller when
// this is used. (Note that we cannot use AutoRestore for mCalledByJS
// due to a bit field.)
class MOZ_RAII AutoCalledByJSSetter final
{
private:
nsRange& mRange;
bool mOldValue;
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
public:
explicit AutoCalledByJSSetter(nsRange& aRange
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: mRange(aRange)
, mOldValue(aRange.mCalledByJS)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
mRange.mCalledByJS = true;
}
~AutoCalledByJSSetter()
{
mRange.mCalledByJS = mOldValue;
}
};
struct MOZ_STACK_CLASS AutoInvalidateSelection
{
explicit AutoInvalidateSelection(nsRange* aRange) : mRange(aRange)
@ -428,6 +471,7 @@ protected:
bool mStartOffsetWasIncremented : 1;
bool mEndOffsetWasIncremented : 1;
bool mEnableGravitationOnElementRemoval : 1;
bool mCalledByJS : 1;
#ifdef DEBUG
int32_t mAssertNextInsertOrAppendIndex;
nsINode* mAssertNextInsertOrAppendNode;

View file

@ -26,22 +26,23 @@ interface Range {
[Throws]
readonly attribute Node commonAncestorContainer;
[Throws]
[Throws, BinaryName="setStartJS"]
void setStart(Node refNode, unsigned long offset);
[Throws]
[Throws, BinaryName="setEndJS"]
void setEnd(Node refNode, unsigned long offset);
[Throws]
[Throws, BinaryName="setStartBeforeJS"]
void setStartBefore(Node refNode);
[Throws]
[Throws, BinaryName="setStartAfterJS"]
void setStartAfter(Node refNode);
[Throws]
[Throws, BinaryName="setEndBeforeJS"]
void setEndBefore(Node refNode);
[Throws]
[Throws, BinaryName="setEndAfterJS"]
void setEndAfter(Node refNode);
[BinaryName="collapseJS"]
void collapse(optional boolean toStart = false);
[Throws]
[Throws, BinaryName="selectNodeJS"]
void selectNode(Node refNode);
[Throws]
[Throws, BinaryName="selectNodeContentsJS"]
void selectNodeContents(Node refNode);
const unsigned short START_TO_START = 0;

View file

@ -17,17 +17,17 @@ interface Selection {
readonly attribute unsigned long focusOffset;
readonly attribute boolean isCollapsed;
[Throws]
[Throws, BinaryName="collapseJS"]
void collapse(Node node, unsigned long offset);
[Throws]
[Throws, BinaryName="collapseToStartJS"]
void collapseToStart();
[Throws]
[Throws, BinaryName="collapseToEndJS"]
void collapseToEnd();
[Throws]
[Throws, BinaryName="extendJS"]
void extend(Node node, unsigned long offset);
[Throws]
[Throws, BinaryName="selectAllChildrenJS"]
void selectAllChildren(Node node);
[Throws]
void deleteFromDocument();
@ -36,7 +36,7 @@ interface Selection {
readonly attribute DOMString type;
[Throws]
Range getRangeAt(unsigned long index);
[Throws]
[Throws, BinaryName="addRangeJS"]
void addRange(Range range);
[Throws]
void removeRange(Range range);
@ -46,7 +46,7 @@ interface Selection {
[Throws]
boolean containsNode(Node node, boolean allowPartialContainment);
[Throws]
[Throws, BinaryName="setBaseAndExtentJS"]
void setBaseAndExtent(Node anchorNode,
unsigned long anchorOffset,
Node focusNode,