зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1620289 - Part 1 Clone Selection Ranges For Printing r=jwatt
- Clones selection ranges to the static document for printing - Static docs can remove selections without referring to original doc Differential Revision: https://phabricator.services.mozilla.com/D71110
This commit is contained in:
Родитель
ea62971514
Коммит
232bc61244
|
@ -142,7 +142,7 @@ static const char* gPrintRangeStr[] = {
|
|||
// This processes the selection on aOrigDoc and creates an inverted selection on
|
||||
// aDoc, which it then deletes. If the start or end of the inverted selection
|
||||
// ranges occur in text nodes then an ellipsis is added.
|
||||
static nsresult DeleteUnselectedNodes(Document* aOrigDoc, Document* aDoc);
|
||||
static nsresult DeleteNonSelectedNodes(Document& aDoc);
|
||||
|
||||
#ifdef EXTENDED_DEBUG_PRINTING
|
||||
// Forward Declarations
|
||||
|
@ -2053,8 +2053,7 @@ nsresult nsPrintJob::ReflowPrintObject(const UniquePtr<nsPrintObject>& aPO) {
|
|||
int16_t printRangeType = nsIPrintSettings::kRangeAllPages;
|
||||
printData->mPrintSettings->GetPrintRange(&printRangeType);
|
||||
if (printRangeType == nsIPrintSettings::kRangeSelection) {
|
||||
DeleteUnselectedNodes(aPO->mDocument->GetOriginalDocument(),
|
||||
aPO->mDocument);
|
||||
DeleteNonSelectedNodes(*aPO->mDocument);
|
||||
}
|
||||
|
||||
bool doReturn = false;
|
||||
|
@ -2202,61 +2201,39 @@ bool nsPrintJob::PrintDocContent(const UniquePtr<nsPrintObject>& aPO,
|
|||
return false;
|
||||
}
|
||||
|
||||
static nsINode* GetCorrespondingNodeInDocument(const nsINode* aNode,
|
||||
Document* aDoc) {
|
||||
MOZ_ASSERT(aNode);
|
||||
MOZ_ASSERT(aDoc);
|
||||
|
||||
// Selections in anonymous subtrees aren't supported.
|
||||
if (aNode->IsInAnonymousSubtree()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsTArray<int32_t> indexArray;
|
||||
const nsINode* child = aNode;
|
||||
while (const nsINode* parent = child->GetParentNode()) {
|
||||
int32_t index = parent->ComputeIndexOf(child);
|
||||
MOZ_ASSERT(index >= 0);
|
||||
indexArray.AppendElement(index);
|
||||
child = parent;
|
||||
}
|
||||
MOZ_ASSERT(child->IsDocument());
|
||||
|
||||
nsINode* correspondingNode = aDoc;
|
||||
for (int32_t i = indexArray.Length() - 1; i >= 0; --i) {
|
||||
correspondingNode = correspondingNode->GetChildAt_Deprecated(indexArray[i]);
|
||||
NS_ENSURE_TRUE(correspondingNode, nullptr);
|
||||
}
|
||||
|
||||
return correspondingNode;
|
||||
}
|
||||
|
||||
static NS_NAMED_LITERAL_STRING(kEllipsis, u"\x2026");
|
||||
|
||||
MOZ_CAN_RUN_SCRIPT_BOUNDARY static nsresult DeleteUnselectedNodes(
|
||||
Document* aOrigDoc, Document* aDoc) {
|
||||
PresShell* origPresShell = aOrigDoc->GetPresShell();
|
||||
PresShell* presShell = aDoc->GetPresShell();
|
||||
NS_ENSURE_STATE(origPresShell && presShell);
|
||||
/**
|
||||
* Builds the complement set of ranges and adds those to the selection.
|
||||
* Deletes all of the nodes contained in the complement set of ranges
|
||||
* leaving behind only nodes that were originally selected.
|
||||
* Adds ellipses to a selected node's text if text is truncated by a range.
|
||||
* This is used to implement the "Print Selection Only" user interface option.
|
||||
*/
|
||||
MOZ_CAN_RUN_SCRIPT_BOUNDARY static nsresult DeleteNonSelectedNodes(
|
||||
Document& aDoc) {
|
||||
MOZ_ASSERT(aDoc.IsStaticDocument());
|
||||
const auto* printRanges = static_cast<nsTArray<RefPtr<nsRange>>*>(
|
||||
aDoc.GetProperty(nsGkAtoms::printselectionranges));
|
||||
if (!printRanges) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
RefPtr<Selection> origSelection =
|
||||
origPresShell->GetCurrentSelection(SelectionType::eNormal);
|
||||
PresShell* presShell = aDoc.GetPresShell();
|
||||
NS_ENSURE_STATE(presShell);
|
||||
RefPtr<Selection> selection =
|
||||
presShell->GetCurrentSelection(SelectionType::eNormal);
|
||||
NS_ENSURE_STATE(origSelection && selection);
|
||||
NS_ENSURE_STATE(selection);
|
||||
|
||||
nsINode* bodyNode = aDoc->GetBodyElement();
|
||||
MOZ_ASSERT(!selection->RangeCount());
|
||||
nsINode* bodyNode = aDoc.GetBodyElement();
|
||||
nsINode* startNode = bodyNode;
|
||||
uint32_t startOffset = 0;
|
||||
uint32_t ellipsisOffset = 0;
|
||||
|
||||
int32_t rangeCount = origSelection->RangeCount();
|
||||
for (int32_t i = 0; i < rangeCount; ++i) {
|
||||
nsRange* origRange = origSelection->GetRangeAt(i);
|
||||
|
||||
for (nsRange* origRange : *printRanges) {
|
||||
// New end is start of original range.
|
||||
nsINode* endNode =
|
||||
GetCorrespondingNodeInDocument(origRange->GetStartContainer(), aDoc);
|
||||
nsINode* endNode = origRange->GetStartContainer();
|
||||
|
||||
// If we're no longer in the same text node reset the ellipsis offset.
|
||||
if (endNode != startNode) {
|
||||
|
@ -2267,13 +2244,12 @@ MOZ_CAN_RUN_SCRIPT_BOUNDARY static nsresult DeleteUnselectedNodes(
|
|||
// Create the range that we want to remove. Note that if startNode or
|
||||
// endNode are null nsRange::Create() will fail and we won't remove
|
||||
// that section.
|
||||
RefPtr<nsRange> range = nsRange::Create(startNode, startOffset, endNode,
|
||||
endOffset, IgnoreErrors());
|
||||
RefPtr<nsRange> unselectedRange = nsRange::Create(
|
||||
startNode, startOffset, endNode, endOffset, IgnoreErrors());
|
||||
|
||||
if (range && !range->Collapsed()) {
|
||||
selection->AddRangeAndSelectFramesAndNotifyListeners(*range,
|
||||
if (unselectedRange && !unselectedRange->Collapsed()) {
|
||||
selection->AddRangeAndSelectFramesAndNotifyListeners(*unselectedRange,
|
||||
IgnoreErrors());
|
||||
|
||||
// Unless we've already added an ellipsis at the start, if we ended mid
|
||||
// text node then add ellipsis.
|
||||
Text* text = endNode->GetAsText();
|
||||
|
@ -2284,8 +2260,7 @@ MOZ_CAN_RUN_SCRIPT_BOUNDARY static nsresult DeleteUnselectedNodes(
|
|||
}
|
||||
|
||||
// Next new start is end of original range.
|
||||
startNode =
|
||||
GetCorrespondingNodeInDocument(origRange->GetEndContainer(), aDoc);
|
||||
startNode = origRange->GetEndContainer();
|
||||
|
||||
// If we're no longer in the same text node reset the ellipsis offset.
|
||||
if (startNode != endNode) {
|
||||
|
@ -2310,7 +2285,6 @@ MOZ_CAN_RUN_SCRIPT_BOUNDARY static nsresult DeleteUnselectedNodes(
|
|||
selection->AddRangeAndSelectFramesAndNotifyListeners(*lastRange,
|
||||
IgnoreErrors());
|
||||
}
|
||||
|
||||
selection->DeleteFromDocument(IgnoreErrors());
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -52,11 +52,115 @@ nsPrintObject::~nsPrintObject() {
|
|||
nsDocShell::Cast(mDocShell)->Destroy();
|
||||
bc->Detach();
|
||||
}
|
||||
if (mDocument) {
|
||||
mDocument->RemoveProperty(nsGkAtoms::printselectionranges);
|
||||
}
|
||||
mDocShell = nullptr;
|
||||
mTreeOwner = nullptr; // mTreeOwner must be released after mDocShell;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Retrieves the node in a static-clone document that corresponds to aOrigNOde,
|
||||
* which is a node in the original document from which aStaticClone was cloned.
|
||||
*/
|
||||
static nsINode* GetCorrespondingNodeInDocument(const nsINode* aOrigNode,
|
||||
Document& aStaticClone) {
|
||||
MOZ_ASSERT(aOrigNode);
|
||||
|
||||
// Selections in anonymous subtrees aren't supported.
|
||||
if (aOrigNode->IsInAnonymousSubtree() || aOrigNode->IsInShadowTree()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsTArray<int32_t> indexArray;
|
||||
const nsINode* child = aOrigNode;
|
||||
while (const nsINode* parent = child->GetParentNode()) {
|
||||
int32_t index = parent->ComputeIndexOf(child);
|
||||
MOZ_ASSERT(index >= 0);
|
||||
indexArray.AppendElement(index);
|
||||
child = parent;
|
||||
}
|
||||
MOZ_ASSERT(child->IsDocument());
|
||||
|
||||
nsINode* correspondingNode = &aStaticClone;
|
||||
for (int32_t i : Reversed(indexArray)) {
|
||||
correspondingNode = correspondingNode->GetChildAt_Deprecated(i);
|
||||
NS_ENSURE_TRUE(correspondingNode, nullptr);
|
||||
}
|
||||
|
||||
return correspondingNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Caches the selection ranges from the source document onto the static clone in
|
||||
* case the "Print Selection Only" functionality is invoked.
|
||||
*
|
||||
* Note that we cannot use the selection obtained from
|
||||
* Document::GetOriginalDocument() since that selection may have mutated after
|
||||
* the print was invoked.
|
||||
*
|
||||
* Note also that because nsRange objects point into a specific document's
|
||||
* nodes, we cannot reuse an array of nsRange objects across multiple static
|
||||
* clone documents. For that reason we cache a new array of ranges on each
|
||||
* static clone that we create.
|
||||
*/
|
||||
static void CachePrintSelectionRanges(const Document& aSourceDoc,
|
||||
Document& aStaticClone) {
|
||||
MOZ_ASSERT(aStaticClone.IsStaticDocument());
|
||||
MOZ_ASSERT(!aStaticClone.GetProperty(nsGkAtoms::printselectionranges));
|
||||
|
||||
const Selection* origSelection = nullptr;
|
||||
const nsTArray<RefPtr<nsRange>>* origRanges = nullptr;
|
||||
bool sourceDocIsStatic = aSourceDoc.IsStaticDocument();
|
||||
|
||||
if (sourceDocIsStatic) {
|
||||
origRanges = static_cast<nsTArray<RefPtr<nsRange>>*>(
|
||||
aSourceDoc.GetProperty(nsGkAtoms::printselectionranges));
|
||||
} else if (PresShell* shell = aSourceDoc.GetPresShell()) {
|
||||
origSelection = shell->GetCurrentSelection(SelectionType::eNormal);
|
||||
}
|
||||
|
||||
if (!origSelection && !origRanges) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t rangeCount =
|
||||
sourceDocIsStatic ? origRanges->Length() : origSelection->RangeCount();
|
||||
auto* printRanges = new nsTArray<RefPtr<nsRange>>(rangeCount);
|
||||
|
||||
for (size_t i = 0; i < rangeCount; ++i) {
|
||||
nsRange* range = sourceDocIsStatic ? origRanges->ElementAt(i).get()
|
||||
: origSelection->GetRangeAt(i);
|
||||
nsINode* startContainer = range->GetStartContainer();
|
||||
nsINode* endContainer = range->GetEndContainer();
|
||||
|
||||
if (!startContainer || !endContainer) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nsINode* startNode =
|
||||
GetCorrespondingNodeInDocument(startContainer, aStaticClone);
|
||||
nsINode* endNode =
|
||||
GetCorrespondingNodeInDocument(endContainer, aStaticClone);
|
||||
|
||||
if (!startNode || !endNode) {
|
||||
continue;
|
||||
}
|
||||
|
||||
RefPtr<nsRange> clonedRange =
|
||||
nsRange::Create(startNode, range->StartOffset(), endNode,
|
||||
range->EndOffset(), IgnoreErrors());
|
||||
if (clonedRange && !clonedRange->Collapsed()) {
|
||||
printRanges->AppendElement(std::move(clonedRange));
|
||||
}
|
||||
}
|
||||
|
||||
aStaticClone.SetProperty(nsGkAtoms::printselectionranges, printRanges,
|
||||
nsINode::DeleteProperty<nsTArray<RefPtr<nsRange>>>);
|
||||
}
|
||||
|
||||
nsresult nsPrintObject::InitAsRootObject(nsIDocShell* aDocShell, Document* aDoc,
|
||||
bool aForPrintPreview) {
|
||||
NS_ENSURE_STATE(aDocShell);
|
||||
|
@ -97,6 +201,7 @@ nsresult nsPrintObject::InitAsRootObject(nsIDocShell* aDocShell, Document* aDoc,
|
|||
|
||||
mDocument = aDoc->CreateStaticClone(mDocShell);
|
||||
NS_ENSURE_STATE(mDocument);
|
||||
CachePrintSelectionRanges(*aDoc, *mDocument);
|
||||
|
||||
nsCOMPtr<nsIContentViewer> viewer;
|
||||
mDocShell->GetContentViewer(getter_AddRefs(viewer));
|
||||
|
|
|
@ -996,6 +996,7 @@ STATIC_ATOMS = [
|
|||
Atom("previewDiv", "preview-div"),
|
||||
Atom("primary", "primary"),
|
||||
Atom("print", "print"),
|
||||
Atom("printselectionranges", "printselectionranges"),
|
||||
Atom("priority", "priority"),
|
||||
Atom("processingInstruction", "processing-instruction"),
|
||||
Atom("profile", "profile"),
|
||||
|
|
Загрузка…
Ссылка в новой задаче