Bug 454059 - Generate hyperlinks in PDF output for HTML link elements. r=mstange,mattwoodrow

Differential Revision: https://phabricator.services.mozilla.com/D114208
This commit is contained in:
Jonathan Kew 2021-05-11 17:00:30 +00:00
Родитель 63f05b6d5a
Коммит d4614f8d3f
3 изменённых файлов: 75 добавлений и 0 удалений

Просмотреть файл

@ -3991,6 +3991,20 @@ void nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
return;
}
// If we're generating a display list for printing, include Link items for
// frames that correspond to HTML link elements so that we can have active
// links in saved PDF output. Note that the state of "within a link" is
// set on the display-list builder, such that all descendants of the link
// element will generate display-list links.
// TODO: we should be able to optimize this so as to avoid creating links
// for the same destination that entirely overlap each other, which adds
// nothing useful to the final PDF.
Maybe<nsDisplayListBuilder::Linkifier> linkifier;
if (aBuilder->IsForPrinting()) {
linkifier.emplace(aBuilder, aChild);
linkifier->MaybeAppendLink(aBuilder, aChild, aLists.Content());
}
nsIFrame* child = aChild;
auto* placeholder = child->IsPlaceholderFrame()
? static_cast<nsPlaceholderFrame*>(child)

Просмотреть файл

@ -554,6 +554,47 @@ nsRect nsDisplayListBuilder::OutOfFlowDisplayData::ComputeVisibleRectForFrame(
return visible;
}
nsDisplayListBuilder::Linkifier::Linkifier(nsDisplayListBuilder* aBuilder,
nsIFrame* aFrame) {
// Links don't nest, so if the builder already has a destination, no need to
// check for a link element here.
if (!aBuilder->mLinkSpec.IsEmpty()) {
return;
}
// Find the element that we need to check for link-ness, bailing out if
// we can't find one.
Element* elem = Element::FromNodeOrNull(aFrame->GetContent());
if (!elem) {
return;
}
// Check if we have actually found a link and it has a usable spec.
nsCOMPtr<nsIURI> linkURI;
if (!elem->IsLink(getter_AddRefs(linkURI))) {
return;
}
if (NS_FAILED(linkURI->GetSpec(aBuilder->mLinkSpec)) ||
aBuilder->mLinkSpec.IsEmpty()) {
return;
}
// Record that we need to reset the builder's state on destruction.
mBuilderToReset = aBuilder;
}
void nsDisplayListBuilder::Linkifier::MaybeAppendLink(
nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList) {
// Note that we may generate a link here even if the constructor bailed out
// without updating aBuilder->LinkSpec(), because it may have been set by
// an ancestor that was associated with a link element.
if (!aBuilder->mLinkSpec.IsEmpty()) {
auto* link = MakeDisplayItem<nsDisplayLink>(
aBuilder, aFrame, aBuilder->mLinkSpec.get(), aFrame->GetRect());
aList->AppendToTop(link);
}
}
nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
nsDisplayListBuilderMode aMode,
bool aBuildCaret,

Просмотреть файл

@ -1824,6 +1824,25 @@ class nsDisplayListBuilder {
void AddScrollFrameToNotify(nsIScrollableFrame* aScrollFrame);
void NotifyAndClearScrollFrames();
// Helper class to find what link spec (if any) to associate with a frame,
// recording it in the builder, and generate the corresponding DisplayItem.
class Linkifier {
public:
Linkifier(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame);
~Linkifier() {
if (mBuilderToReset) {
mBuilderToReset->mLinkSpec.Truncate(0);
}
}
void MaybeAppendLink(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
nsDisplayList* aList);
private:
nsDisplayListBuilder* mBuilderToReset = nullptr;
};
private:
bool MarkOutOfFlowFrameForDisplay(nsIFrame* aDirtyFrame, nsIFrame* aFrame,
const nsRect& aVisibleRect,
@ -2002,6 +2021,7 @@ class nsDisplayListBuilder {
// filter. Otherwise nullptr.
const ActiveScrolledRoot* mFilterASR;
std::unordered_set<nsIScrollableFrame*> mScrollFramesToNotify;
nsCString mLinkSpec; // Destination of link currently being emitted, if any.
bool mContainsBlendMode;
bool mIsBuildingScrollbar;
bool mCurrentScrollbarWillHaveLayer;