Bug 1318312 part.2 Mark Selection as "called by JS" when every Selection API which may cause changing selection is called by JS r=smaug

Selection needs to be able to distinguish if every selection change is caused by JS (i.e., via Selection API) or the others.

This patch maps some methods of Range and Selection to *JS().  Each of them marks its instance as "used by JS" and calls corresponding method.

With this change, Selection::NotifySelectionListeners() can move focus only when it's caused by Selection API.

MozReview-Commit-ID: 1GoLHiIJ10Y

--HG--
extra : rebase_source : 02d497f9e55b3325a2e01c3041cadb90881dccb8
This commit is contained in:
Masayuki Nakano 2017-03-10 16:55:12 +09:00
parent 0c84f34bac
commit a959abd5c5
6 changed files with 240 additions and 34 deletions

View File

@ -255,6 +255,7 @@ nsRange::nsRange(nsINode* aNode)
, mStartOffsetWasIncremented(false)
, mEndOffsetWasIncremented(false)
, mEnableGravitationOnElementRemoval(true)
, mCalledByJS(false)
#ifdef DEBUG
, mAssertNextInsertOrAppendIndex(-1)
, mAssertNextInsertOrAppendNode(nullptr)
@ -967,7 +968,7 @@ nsRange::DoSetRange(nsINode* aStartN, int32_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);
}
}
@ -1185,6 +1186,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)
{
@ -1238,6 +1246,13 @@ nsRange::SetStart(nsINode* aParent, int32_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)
{
@ -1264,6 +1279,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)
{
@ -1290,6 +1312,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)
{
@ -1342,6 +1371,13 @@ nsRange::SetEnd(nsINode* aParent, int32_t aOffset)
return NS_OK;
}
void
nsRange::SetEndBeforeJS(nsINode& aNode, ErrorResult& aErr)
{
AutoCalledByJSSetter markAsCalledByJS(*this);
SetEndBefore(aNode, aErr);
}
void
nsRange::SetEndBefore(nsINode& aNode, ErrorResult& aRv)
{
@ -1368,6 +1404,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)
{
@ -1409,6 +1452,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)
{
@ -1420,6 +1470,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)
{
@ -1457,6 +1514,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

@ -21,6 +21,7 @@
#include "nsStubMutationObserver.h"
#include "nsWrapperCache.h"
#include "mozilla/Attributes.h"
#include "mozilla/GuardObjects.h"
namespace mozilla {
class ErrorResult;
@ -200,14 +201,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);
@ -216,6 +223,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,
@ -323,6 +341,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)
@ -359,6 +402,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();
@ -35,7 +35,7 @@ interface Selection {
readonly attribute unsigned long rangeCount;
[Throws]
Range getRangeAt(unsigned long index);
[Throws]
[Throws, BinaryName="addRangeJS"]
void addRange(Range range);
[Throws]
void removeRange(Range range);
@ -45,7 +45,7 @@ interface Selection {
[Throws]
boolean containsNode(Node node, boolean allowPartialContainment);
[Throws]
[Throws, BinaryName="setBaseAndExtentJS"]
void setBaseAndExtent(Node anchorNode,
unsigned long anchorOffset,
Node focusNode,

View File

@ -167,13 +167,20 @@ public:
uint32_t FocusOffset();
bool IsCollapsed() const;
void Collapse(nsINode& aNode, uint32_t aOffset, mozilla::ErrorResult& aRv);
void CollapseToStart(mozilla::ErrorResult& aRv);
void CollapseToEnd(mozilla::ErrorResult& aRv);
void Extend(nsINode& aNode, uint32_t aOffset, mozilla::ErrorResult& aRv);
// *JS() methods are mapped to Selection.*().
// They may move focus only when the range represents normal selection.
// These methods shouldn't be used by non-JS callers.
void CollapseJS(nsINode& aNode, uint32_t aOffset,
mozilla::ErrorResult& aRv);
void CollapseToStartJS(mozilla::ErrorResult& aRv);
void CollapseToEndJS(mozilla::ErrorResult& aRv);
void ExtendJS(nsINode& aNode, uint32_t aOffset,
mozilla::ErrorResult& aRv);
void SelectAllChildrenJS(nsINode& aNode, mozilla::ErrorResult& aRv);
void SelectAllChildren(nsINode& aNode, mozilla::ErrorResult& aRv);
void DeleteFromDocument(mozilla::ErrorResult& aRv);
uint32_t RangeCount() const
@ -181,7 +188,7 @@ public:
return mRanges.Length();
}
nsRange* GetRangeAt(uint32_t aIndex, mozilla::ErrorResult& aRv);
void AddRange(nsRange& aRange, mozilla::ErrorResult& aRv);
void AddRangeJS(nsRange& aRange, mozilla::ErrorResult& aRv);
void RemoveRange(nsRange& aRange, mozilla::ErrorResult& aRv);
void RemoveAllRanges(mozilla::ErrorResult& aRv);
@ -201,9 +208,9 @@ public:
void Modify(const nsAString& aAlter, const nsAString& aDirection,
const nsAString& aGranularity, mozilla::ErrorResult& aRv);
void SetBaseAndExtent(nsINode& aAnchorNode, uint32_t aAnchorOffset,
nsINode& aFocusNode, uint32_t aFocusOffset,
mozilla::ErrorResult& aRv);
void SetBaseAndExtentJS(nsINode& aAnchorNode, uint32_t aAnchorOffset,
nsINode& aFocusNode, uint32_t aFocusOffset,
mozilla::ErrorResult& aRv);
bool GetInterlinePosition(mozilla::ErrorResult& aRv);
void SetInterlinePosition(bool aValue, mozilla::ErrorResult& aRv);
@ -237,6 +244,17 @@ public:
int16_t aVPercent, int16_t aHPercent,
mozilla::ErrorResult& aRv);
// Non-JS callers should use the following methods.
void Collapse(nsINode& aNode, uint32_t aOffset, mozilla::ErrorResult& aRv);
void CollapseToStart(mozilla::ErrorResult& aRv);
void CollapseToEnd(mozilla::ErrorResult& aRv);
void Extend(nsINode& aNode, uint32_t aOffset, mozilla::ErrorResult& aRv);
void AddRange(nsRange& aRange, mozilla::ErrorResult& aRv);
void SelectAllChildren(nsINode& aNode, mozilla::ErrorResult& aRv);
void SetBaseAndExtent(nsINode& aAnchorNode, uint32_t aAnchorOffset,
nsINode& aFocusNode, uint32_t aFocusOffset,
mozilla::ErrorResult& aRv);
void AddSelectionChangeBlocker();
void RemoveSelectionChangeBlocker();
bool IsBlockingSelectionChangeEvents() const;
@ -259,7 +277,8 @@ public:
mSelectionType = aSelectionType;
}
nsresult NotifySelectionListeners();
nsresult NotifySelectionListeners(bool aCalledByJS);
nsresult NotifySelectionListeners();
friend struct AutoUserInitiated;
struct MOZ_RAII AutoUserInitiated
@ -365,6 +384,12 @@ private:
*/
bool mUserInitiated;
/**
* When the selection change is caused by a call of Selection API,
* mCalledByJS is true. Otherwise, false.
*/
bool mCalledByJS;
// Non-zero if we don't want any changes we make to the selection to be
// visible to content. If non-zero, content won't be notified about changes.
uint32_t mSelectionChangeBlockerCount;

View File

@ -11,6 +11,7 @@
#include "mozilla/dom/Selection.h"
#include "mozilla/Attributes.h"
#include "mozilla/AutoRestore.h"
#include "mozilla/EventStates.h"
#include "nsCOMPtr.h"
@ -3479,6 +3480,7 @@ Selection::Selection()
, mDirection(eDirNext)
, mSelectionType(SelectionType::eNormal)
, mUserInitiated(false)
, mCalledByJS(false)
, mSelectionChangeBlockerCount(0)
{
}
@ -3489,6 +3491,7 @@ Selection::Selection(nsFrameSelection* aList)
, mDirection(eDirNext)
, mSelectionType(SelectionType::eNormal)
, mUserInitiated(false)
, mCalledByJS(false)
, mSelectionChangeBlockerCount(0)
{
}
@ -4961,6 +4964,14 @@ Selection::AddRange(nsIDOMRange* aDOMRange)
return result.StealNSResult();
}
void
Selection::AddRangeJS(nsRange& aRange, ErrorResult& aRv)
{
AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
mCalledByJS = true;
AddRange(aRange, aRv);
}
void
Selection::AddRange(nsRange& aRange, ErrorResult& aRv)
{
@ -5133,6 +5144,14 @@ Selection::CollapseNative(nsINode* aParentNode, int32_t aOffset)
return Collapse(aParentNode, aOffset);
}
void
Selection::CollapseJS(nsINode& aNode, uint32_t aOffset, ErrorResult& aRv)
{
AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
mCalledByJS = true;
Collapse(aNode, aOffset, aRv);
}
nsresult
Selection::Collapse(nsINode* aParentNode, int32_t aOffset)
{
@ -5237,6 +5256,14 @@ Selection::CollapseToStart()
return result.StealNSResult();
}
void
Selection::CollapseToStartJS(ErrorResult& aRv)
{
AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
mCalledByJS = true;
CollapseToStart(aRv);
}
void
Selection::CollapseToStart(ErrorResult& aRv)
{
@ -5278,6 +5305,14 @@ Selection::CollapseToEnd()
return result.StealNSResult();
}
void
Selection::CollapseToEndJS(ErrorResult& aRv)
{
AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
mCalledByJS = true;
CollapseToEnd(aRv);
}
void
Selection::CollapseToEnd(ErrorResult& aRv)
{
@ -5480,6 +5515,14 @@ Selection::ExtendNative(nsINode* aParentNode, int32_t aOffset)
return Extend(aParentNode, aOffset);
}
void
Selection::ExtendJS(nsINode& aNode, uint32_t aOffset, ErrorResult& aRv)
{
AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
mCalledByJS = true;
Extend(aNode, aOffset, aRv);
}
nsresult
Selection::Extend(nsINode* aParentNode, int32_t aOffset)
{
@ -5791,6 +5834,14 @@ Selection::SelectAllChildren(nsIDOMNode* aParentNode)
return result.StealNSResult();
}
void
Selection::SelectAllChildrenJS(nsINode& aNode, ErrorResult& aRv)
{
AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
mCalledByJS = true;
SelectAllChildren(aNode, aRv);
}
void
Selection::SelectAllChildren(nsINode& aNode, ErrorResult& aRv)
{
@ -6243,6 +6294,14 @@ Selection::RemoveSelectionListener(nsISelectionListener* aListenerToRemove,
}
}
nsresult
Selection::NotifySelectionListeners(bool aCalledByJS)
{
AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
mCalledByJS = aCalledByJS;
return NotifySelectionListeners();
}
nsresult
Selection::NotifySelectionListeners()
{
@ -6453,6 +6512,19 @@ Selection::Modify(const nsAString& aAlter, const nsAString& aDirection,
}
}
void
Selection::SetBaseAndExtentJS(nsINode& aAnchorNode,
uint32_t aAnchorOffset,
nsINode& aFocusNode,
uint32_t aFocusOffset,
ErrorResult& aRv)
{
AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
mCalledByJS = true;
SetBaseAndExtent(aAnchorNode, aAnchorOffset,
aFocusNode, aFocusOffset, aRv);
}
void
Selection::SetBaseAndExtent(nsINode& aAnchorNode, uint32_t aAnchorOffset,
nsINode& aFocusNode, uint32_t aFocusOffset,