mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-22 18:32:00 +00:00
Bug 1881096 - Make nsPrintJob handles shadow-crossing selection r=emilio
Differential Revision: https://phabricator.services.mozilla.com/D212929
This commit is contained in:
parent
0fd347f4a2
commit
3389d06b50
@ -15,6 +15,7 @@
|
||||
#include "mozilla/dom/Document.h"
|
||||
#include "mozilla/dom/StaticRange.h"
|
||||
#include "mozilla/dom/Selection.h"
|
||||
#include "mozilla/dom/CrossShadowBoundaryRange.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsGkAtoms.h"
|
||||
@ -573,6 +574,24 @@ JSObject* AbstractRange::WrapObject(JSContext* aCx,
|
||||
MOZ_CRASH("Must be overridden");
|
||||
}
|
||||
|
||||
bool AbstractRange::AreNormalRangeAndCrossShadowBoundaryRangeCollapsed() const {
|
||||
if (!Collapsed()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We know normal range is collapsed at this point
|
||||
if (IsStaticRange()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (const CrossShadowBoundaryRange* crossShadowBoundaryRange =
|
||||
AsDynamicRange()->GetCrossShadowBoundaryRange()) {
|
||||
return crossShadowBoundaryRange->Collapsed();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AbstractRange::ClearForReuse() {
|
||||
mOwner = nullptr;
|
||||
mStart = RangeBoundary();
|
||||
|
@ -125,6 +125,8 @@ class AbstractRange : public nsISupports,
|
||||
StartOffset() == EndOffset());
|
||||
}
|
||||
|
||||
bool AreNormalRangeAndCrossShadowBoundaryRangeCollapsed() const;
|
||||
|
||||
nsINode* GetParentObject() const;
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
@ -13458,8 +13458,8 @@ static void CachePrintSelectionRanges(const Document& aSourceDoc,
|
||||
const nsRange* range = sourceDocIsStatic ? origRanges->ElementAt(i).get()
|
||||
: origSelection->GetRangeAt(i);
|
||||
MOZ_ASSERT(range);
|
||||
nsINode* startContainer = range->GetStartContainer();
|
||||
nsINode* endContainer = range->GetEndContainer();
|
||||
nsINode* startContainer = range->GetMayCrossShadowBoundaryStartContainer();
|
||||
nsINode* endContainer = range->GetMayCrossShadowBoundaryEndContainer();
|
||||
|
||||
if (!startContainer || !endContainer) {
|
||||
continue;
|
||||
@ -13474,10 +13474,11 @@ static void CachePrintSelectionRanges(const Document& aSourceDoc,
|
||||
continue;
|
||||
}
|
||||
|
||||
RefPtr<nsRange> clonedRange =
|
||||
nsRange::Create(startNode, range->StartOffset(), endNode,
|
||||
range->EndOffset(), IgnoreErrors());
|
||||
if (clonedRange && !clonedRange->Collapsed()) {
|
||||
RefPtr<nsRange> clonedRange = nsRange::Create(
|
||||
startNode, range->MayCrossShadowBoundaryStartOffset(), endNode,
|
||||
range->MayCrossShadowBoundaryEndOffset(), IgnoreErrors());
|
||||
if (clonedRange &&
|
||||
!clonedRange->AreNormalRangeAndCrossShadowBoundaryRangeCollapsed()) {
|
||||
printRanges->AppendElement(std::move(clonedRange));
|
||||
}
|
||||
}
|
||||
|
@ -730,8 +730,12 @@ void nsRange::ContentRemoved(nsIContent* aChild, nsIContent* aPreviousSibling) {
|
||||
bool newStartIsSet = newStart.IsSet();
|
||||
bool newEndIsSet = newEnd.IsSet();
|
||||
if (newStartIsSet || newEndIsSet) {
|
||||
DoSetRange(newStartIsSet ? newStart : mStart.AsRaw(),
|
||||
newEndIsSet ? newEnd : mEnd.AsRaw(), mRoot);
|
||||
DoSetRange(
|
||||
newStartIsSet ? newStart : mStart.AsRaw(),
|
||||
newEndIsSet ? newEnd : mEnd.AsRaw(), mRoot, false,
|
||||
// CrossShadowBoundaryRange mutates content
|
||||
// removal fot itself, so no need for nsRange to do anything with it.
|
||||
RangeBehaviour::KeepDefaultRangeAndCrossShadowBoundaryRanges);
|
||||
} else {
|
||||
nsRange::AssertIfMismatchRootAndRangeBoundaries(mStart, mEnd, mRoot);
|
||||
}
|
||||
@ -1463,7 +1467,8 @@ class MOZ_STACK_CLASS RangeSubtreeIterator {
|
||||
RangeSubtreeIterator() : mIterState(eDone) {}
|
||||
~RangeSubtreeIterator() = default;
|
||||
|
||||
nsresult Init(nsRange* aRange);
|
||||
nsresult Init(nsRange* aRange, AllowRangeCrossShadowBoundary =
|
||||
AllowRangeCrossShadowBoundary::No);
|
||||
already_AddRefed<nsINode> GetCurrentNode();
|
||||
void First();
|
||||
void Last();
|
||||
@ -1473,9 +1478,10 @@ class MOZ_STACK_CLASS RangeSubtreeIterator {
|
||||
bool IsDone() { return mIterState == eDone; }
|
||||
};
|
||||
|
||||
nsresult RangeSubtreeIterator::Init(nsRange* aRange) {
|
||||
nsresult RangeSubtreeIterator::Init(
|
||||
nsRange* aRange, AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary) {
|
||||
mIterState = eDone;
|
||||
if (aRange->Collapsed()) {
|
||||
if (aRange->AreNormalRangeAndCrossShadowBoundaryRangeCollapsed()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -1487,14 +1493,14 @@ nsresult RangeSubtreeIterator::Init(nsRange* aRange) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsINode* node = aRange->GetStartContainer();
|
||||
nsINode* node = aRange->GetMayCrossShadowBoundaryStartContainer();
|
||||
if (NS_WARN_IF(!node)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (node->IsCharacterData() ||
|
||||
(node->IsElement() &&
|
||||
node->AsElement()->GetChildCount() == aRange->StartOffset())) {
|
||||
(node->IsElement() && node->AsElement()->GetChildCount() ==
|
||||
aRange->MayCrossShadowBoundaryStartOffset())) {
|
||||
mStart = node;
|
||||
}
|
||||
|
||||
@ -1502,13 +1508,13 @@ nsresult RangeSubtreeIterator::Init(nsRange* aRange) {
|
||||
// a CharacterData pointer. If it is CharacterData store
|
||||
// a pointer to the node.
|
||||
|
||||
node = aRange->GetEndContainer();
|
||||
node = aRange->GetMayCrossShadowBoundaryEndContainer();
|
||||
if (NS_WARN_IF(!node)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (node->IsCharacterData() ||
|
||||
(node->IsElement() && aRange->EndOffset() == 0)) {
|
||||
(node->IsElement() && aRange->MayCrossShadowBoundaryEndOffset() == 0)) {
|
||||
mEnd = node;
|
||||
}
|
||||
|
||||
@ -1524,7 +1530,10 @@ nsresult RangeSubtreeIterator::Init(nsRange* aRange) {
|
||||
|
||||
mSubtreeIter.emplace();
|
||||
|
||||
nsresult res = mSubtreeIter->Init(aRange);
|
||||
nsresult res =
|
||||
aAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes
|
||||
? mSubtreeIter->InitWithAllowCrossShadowBoundary(aRange)
|
||||
: mSubtreeIter->Init(aRange);
|
||||
if (NS_FAILED(res)) return res;
|
||||
|
||||
if (mSubtreeIter->IsDone()) {
|
||||
@ -1757,14 +1766,18 @@ void nsRange::CutContents(DocumentFragment** aFragment, ErrorResult& aRv) {
|
||||
*aFragment = nullptr;
|
||||
}
|
||||
|
||||
if (!CanAccess(*mStart.Container()) || !CanAccess(*mEnd.Container())) {
|
||||
if (!CanAccess(*GetMayCrossShadowBoundaryStartContainer()) ||
|
||||
!CanAccess(*GetMayCrossShadowBoundaryEndContainer())) {
|
||||
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<Document> doc = mStart.Container()->OwnerDoc();
|
||||
|
||||
nsCOMPtr<nsINode> commonAncestor = GetCommonAncestorContainer(aRv);
|
||||
nsCOMPtr<nsINode> commonAncestor = GetCommonAncestorContainer(
|
||||
aRv, StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()
|
||||
? AllowRangeCrossShadowBoundary::Yes
|
||||
: AllowRangeCrossShadowBoundary::No);
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
@ -1783,13 +1796,16 @@ void nsRange::CutContents(DocumentFragment** aFragment, ErrorResult& aRv) {
|
||||
// Save the range end points locally to avoid interference
|
||||
// of Range gravity during our edits!
|
||||
|
||||
nsCOMPtr<nsINode> startContainer = mStart.Container();
|
||||
nsCOMPtr<nsINode> startContainer = GetMayCrossShadowBoundaryStartContainer();
|
||||
// `GetCommonAncestorContainer()` above ensures the range is positioned, hence
|
||||
// there have to be valid offsets.
|
||||
uint32_t startOffset =
|
||||
*mStart.Offset(RangeBoundary::OffsetFilter::kValidOffsets);
|
||||
nsCOMPtr<nsINode> endContainer = mEnd.Container();
|
||||
uint32_t endOffset = *mEnd.Offset(RangeBoundary::OffsetFilter::kValidOffsets);
|
||||
|
||||
const uint32_t startOffset = *MayCrossShadowBoundaryStartRef().Offset(
|
||||
RangeBoundary::OffsetFilter::kValidOffsets);
|
||||
|
||||
nsCOMPtr<nsINode> endContainer = GetMayCrossShadowBoundaryEndContainer();
|
||||
const uint32_t endOffset = *MayCrossShadowBoundaryEndRef().Offset(
|
||||
RangeBoundary::OffsetFilter::kValidOffsets);
|
||||
|
||||
if (retval) {
|
||||
// For extractContents(), abort early if there's a doctype (bug 719533).
|
||||
@ -1820,7 +1836,10 @@ void nsRange::CutContents(DocumentFragment** aFragment, ErrorResult& aRv) {
|
||||
|
||||
RangeSubtreeIterator iter;
|
||||
|
||||
aRv = iter.Init(this);
|
||||
aRv = iter.Init(this,
|
||||
StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()
|
||||
? AllowRangeCrossShadowBoundary::Yes
|
||||
: AllowRangeCrossShadowBoundary::No);
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
|
@ -223,12 +223,15 @@ class nsRange final : public mozilla::dom::AbstractRange,
|
||||
void DeleteContents(ErrorResult& aRv);
|
||||
already_AddRefed<mozilla::dom::DocumentFragment> ExtractContents(
|
||||
ErrorResult& aErr);
|
||||
nsINode* GetCommonAncestorContainer(ErrorResult& aRv) const {
|
||||
nsINode* GetCommonAncestorContainer(
|
||||
ErrorResult& aRv,
|
||||
AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary =
|
||||
AllowRangeCrossShadowBoundary::No) const {
|
||||
if (!mIsPositioned) {
|
||||
aRv.Throw(NS_ERROR_NOT_INITIALIZED);
|
||||
return nullptr;
|
||||
}
|
||||
return GetClosestCommonInclusiveAncestor();
|
||||
return GetClosestCommonInclusiveAncestor(aAllowCrossShadowBoundary);
|
||||
}
|
||||
void InsertNode(nsINode& aNode, ErrorResult& aErr);
|
||||
bool IntersectsNode(nsINode& aNode, ErrorResult& aRv);
|
||||
|
@ -1553,14 +1553,16 @@ struct MOZ_STACK_CLASS SelectionRangeState {
|
||||
void SelectionRangeState::SelectComplementOf(
|
||||
Span<const RefPtr<nsRange>> aRanges) {
|
||||
for (const auto& range : aRanges) {
|
||||
auto start = Position{range->GetStartContainer(), range->StartOffset()};
|
||||
auto end = Position{range->GetEndContainer(), range->EndOffset()};
|
||||
auto start = Position{range->GetMayCrossShadowBoundaryStartContainer(),
|
||||
range->MayCrossShadowBoundaryStartOffset()};
|
||||
auto end = Position{range->GetMayCrossShadowBoundaryEndContainer(),
|
||||
range->MayCrossShadowBoundaryEndOffset()};
|
||||
SelectNodesExcept(start, end);
|
||||
}
|
||||
}
|
||||
|
||||
void SelectionRangeState::SelectRange(nsRange* aRange) {
|
||||
if (aRange && !aRange->Collapsed()) {
|
||||
if (aRange && !aRange->AreNormalRangeAndCrossShadowBoundaryRangeCollapsed()) {
|
||||
mSelection->AddRangeAndSelectFramesAndNotifyListeners(*aRange,
|
||||
IgnoreErrors());
|
||||
}
|
||||
@ -1569,11 +1571,16 @@ void SelectionRangeState::SelectRange(nsRange* aRange) {
|
||||
void SelectionRangeState::SelectNodesExcept(const Position& aStart,
|
||||
const Position& aEnd) {
|
||||
SelectNodesExceptInSubtree(aStart, aEnd);
|
||||
if (auto* shadow = ShadowRoot::FromNode(aStart.mNode->SubtreeRoot())) {
|
||||
auto* host = shadow->Host();
|
||||
SelectNodesExcept(Position{host, 0}, Position{host, host->GetChildCount()});
|
||||
} else {
|
||||
MOZ_ASSERT(aStart.mNode->IsInUncomposedDoc());
|
||||
if (!StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()) {
|
||||
if (auto* shadow = ShadowRoot::FromNode(aStart.mNode->SubtreeRoot())) {
|
||||
auto* host = shadow->Host();
|
||||
// Can't just select other nodes except the host, because other nodes that
|
||||
// are not in this particular shadow tree could also be selected
|
||||
SelectNodesExcept(Position{host, 0},
|
||||
Position{host, host->GetChildCount()});
|
||||
} else {
|
||||
MOZ_ASSERT(aStart.mNode->IsInUncomposedDoc());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1581,7 +1588,11 @@ void SelectionRangeState::SelectNodesExceptInSubtree(const Position& aStart,
|
||||
const Position& aEnd) {
|
||||
static constexpr auto kEllipsis = u"\x2026"_ns;
|
||||
|
||||
nsINode* root = aStart.mNode->SubtreeRoot();
|
||||
// Finish https://bugzilla.mozilla.org/show_bug.cgi?id=1903871 once the pref
|
||||
// is shipped, so that we only need one position.
|
||||
nsINode* root = StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()
|
||||
? aStart.mNode->OwnerDoc()
|
||||
: aStart.mNode->SubtreeRoot();
|
||||
auto& start =
|
||||
mPositions.WithEntryHandle(root, [&](auto&& entry) -> Position& {
|
||||
return entry.OrInsertWith([&] { return Position{root, 0}; });
|
||||
|
Loading…
x
Reference in New Issue
Block a user