mirror of
https://github.com/ManchildProductions/UXP-Fixed.git
synced 2026-06-25 22:59:04 +00:00
cbb61ab832
Part 1: Remove current table item, as it's never set. Part 2: Get rid of generic table painting code, and handle each class separately. Part 4: Hoist outline skipping into col(group) frame code. Part 5: Skip box-shadow for table column and column groups. Part 6: Store column and column group backgrounds separately, and then append them before the rest of the table contents. Part 7: Pass rects in display list coordinates to AppendBackgroundItemsToTop. Part 8: Create column and column group background display items as part of the cell's BuildDisplayList. Part 9: Used cached values instead of calling nsDisplayListBuilder::ToReferenceFrame when possible, since it can be expensive when the requested frame isn't the builder's current frame. Part 10: Make sure we build display items for table parts where only the normal position is visible, since we may need to create background items for ancestors at that position. Part 11: Create an AutoBuildingDisplayList when we create background items for table columns and column groups, so that we initialize the invalidation state correctly.
437 lines
15 KiB
C++
437 lines
15 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
/* rendering object for the HTML <canvas> element */
|
|
|
|
#include "nsHTMLCanvasFrame.h"
|
|
|
|
#include "nsGkAtoms.h"
|
|
#include "mozilla/Assertions.h"
|
|
#include "mozilla/dom/HTMLCanvasElement.h"
|
|
#include "nsDisplayList.h"
|
|
#include "nsLayoutUtils.h"
|
|
#include "nsStyleUtil.h"
|
|
#include "ImageLayers.h"
|
|
#include "Layers.h"
|
|
#include "ActiveLayerTracker.h"
|
|
|
|
#include <algorithm>
|
|
|
|
using namespace mozilla;
|
|
using namespace mozilla::dom;
|
|
using namespace mozilla::layers;
|
|
using namespace mozilla::gfx;
|
|
|
|
/* Helper for our nsIFrame::GetIntrinsicSize() impl. Takes the result of
|
|
* "GetCanvasSize()" as a parameter, which may help avoid redundant
|
|
* indirect calls to GetCanvasSize().
|
|
*
|
|
* @param aCanvasSizeInPx The canvas's size in CSS pixels, as returned
|
|
* by GetCanvasSize().
|
|
* @return The canvas's intrinsic size, as an IntrinsicSize object.
|
|
*/
|
|
static IntrinsicSize
|
|
IntrinsicSizeFromCanvasSize(const nsIntSize& aCanvasSizeInPx)
|
|
{
|
|
IntrinsicSize intrinsicSize;
|
|
intrinsicSize.width.SetCoordValue(
|
|
nsPresContext::CSSPixelsToAppUnits(aCanvasSizeInPx.width));
|
|
intrinsicSize.height.SetCoordValue(
|
|
nsPresContext::CSSPixelsToAppUnits(aCanvasSizeInPx.height));
|
|
|
|
return intrinsicSize;
|
|
}
|
|
|
|
/* Helper for our nsIFrame::GetIntrinsicRatio() impl. Takes the result of
|
|
* "GetCanvasSize()" as a parameter, which may help avoid redundant
|
|
* indirect calls to GetCanvasSize().
|
|
*
|
|
* @param aCanvasSizeInPx The canvas's size in CSS pixels, as returned
|
|
* by GetCanvasSize().
|
|
* @return The canvas's intrinsic ratio, as a nsSize.
|
|
*/
|
|
static nsSize
|
|
IntrinsicRatioFromCanvasSize(const nsIntSize& aCanvasSizeInPx)
|
|
{
|
|
return nsSize(nsPresContext::CSSPixelsToAppUnits(aCanvasSizeInPx.width),
|
|
nsPresContext::CSSPixelsToAppUnits(aCanvasSizeInPx.height));
|
|
}
|
|
|
|
class nsDisplayCanvas : public nsDisplayItem {
|
|
public:
|
|
nsDisplayCanvas(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
|
|
: nsDisplayItem(aBuilder, aFrame)
|
|
{
|
|
MOZ_COUNT_CTOR(nsDisplayCanvas);
|
|
}
|
|
#ifdef NS_BUILD_REFCNT_LOGGING
|
|
virtual ~nsDisplayCanvas() {
|
|
MOZ_COUNT_DTOR(nsDisplayCanvas);
|
|
}
|
|
#endif
|
|
|
|
NS_DISPLAY_DECL_NAME("nsDisplayCanvas", TYPE_CANVAS)
|
|
|
|
virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
|
|
bool* aSnap) override {
|
|
*aSnap = false;
|
|
nsHTMLCanvasFrame* f = static_cast<nsHTMLCanvasFrame*>(Frame());
|
|
HTMLCanvasElement* canvas =
|
|
HTMLCanvasElement::FromContent(f->GetContent());
|
|
nsRegion result;
|
|
if (canvas->GetIsOpaque()) {
|
|
// OK, the entire region painted by the canvas is opaque. But what is
|
|
// that region? It's the canvas's "dest rect" (controlled by the
|
|
// object-fit/object-position CSS properties), clipped to the container's
|
|
// content box (which is what GetBounds() returns). So, we grab those
|
|
// rects and intersect them.
|
|
nsRect constraintRect = GetBounds(aBuilder, aSnap);
|
|
|
|
// Need intrinsic size & ratio, for ComputeObjectDestRect:
|
|
nsIntSize canvasSize = f->GetCanvasSize();
|
|
IntrinsicSize intrinsicSize = IntrinsicSizeFromCanvasSize(canvasSize);
|
|
nsSize intrinsicRatio = IntrinsicRatioFromCanvasSize(canvasSize);
|
|
|
|
const nsRect destRect =
|
|
nsLayoutUtils::ComputeObjectDestRect(constraintRect,
|
|
intrinsicSize, intrinsicRatio,
|
|
f->StylePosition());
|
|
return nsRegion(destRect.Intersect(constraintRect));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
|
|
bool* aSnap) override {
|
|
*aSnap = true;
|
|
nsHTMLCanvasFrame* f = static_cast<nsHTMLCanvasFrame*>(Frame());
|
|
return f->GetInnerArea() + ToReferenceFrame();
|
|
}
|
|
|
|
virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
|
|
LayerManager* aManager,
|
|
const ContainerLayerParameters& aContainerParameters) override
|
|
{
|
|
return static_cast<nsHTMLCanvasFrame*>(mFrame)->
|
|
BuildLayer(aBuilder, aManager, this, aContainerParameters);
|
|
}
|
|
virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
|
|
LayerManager* aManager,
|
|
const ContainerLayerParameters& aParameters) override
|
|
{
|
|
if (HTMLCanvasElement::FromContent(mFrame->GetContent())->ShouldForceInactiveLayer(aManager))
|
|
return LAYER_INACTIVE;
|
|
|
|
// If compositing is cheap, just do that
|
|
if (aManager->IsCompositingCheap() ||
|
|
ActiveLayerTracker::IsContentActive(mFrame))
|
|
return mozilla::LAYER_ACTIVE;
|
|
|
|
return LAYER_INACTIVE;
|
|
}
|
|
};
|
|
|
|
|
|
nsIFrame*
|
|
NS_NewHTMLCanvasFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
|
|
{
|
|
return new (aPresShell) nsHTMLCanvasFrame(aContext);
|
|
}
|
|
|
|
NS_QUERYFRAME_HEAD(nsHTMLCanvasFrame)
|
|
NS_QUERYFRAME_ENTRY(nsHTMLCanvasFrame)
|
|
NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
|
|
|
|
NS_IMPL_FRAMEARENA_HELPERS(nsHTMLCanvasFrame)
|
|
|
|
void
|
|
nsHTMLCanvasFrame::Init(nsIContent* aContent,
|
|
nsContainerFrame* aParent,
|
|
nsIFrame* aPrevInFlow)
|
|
{
|
|
nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
|
|
|
|
// We can fill in the canvas before the canvas frame is created, in
|
|
// which case we never get around to marking the content as active. Therefore,
|
|
// we mark it active here when we create the frame.
|
|
ActiveLayerTracker::NotifyContentChange(this);
|
|
}
|
|
|
|
nsHTMLCanvasFrame::~nsHTMLCanvasFrame()
|
|
{
|
|
}
|
|
|
|
nsIntSize
|
|
nsHTMLCanvasFrame::GetCanvasSize()
|
|
{
|
|
nsIntSize size(0,0);
|
|
HTMLCanvasElement *canvas =
|
|
HTMLCanvasElement::FromContentOrNull(GetContent());
|
|
if (canvas) {
|
|
size = canvas->GetSize();
|
|
MOZ_ASSERT(size.width >= 0 && size.height >= 0,
|
|
"we should've required <canvas> width/height attrs to be "
|
|
"unsigned (non-negative) values");
|
|
} else {
|
|
NS_NOTREACHED("couldn't get canvas size");
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
/* virtual */ nscoord
|
|
nsHTMLCanvasFrame::GetMinISize(nsRenderingContext *aRenderingContext)
|
|
{
|
|
// XXX The caller doesn't account for constraints of the height,
|
|
// min-height, and max-height properties.
|
|
bool vertical = GetWritingMode().IsVertical();
|
|
nscoord result = nsPresContext::CSSPixelsToAppUnits(
|
|
vertical ? GetCanvasSize().height : GetCanvasSize().width);
|
|
DISPLAY_MIN_WIDTH(this, result);
|
|
return result;
|
|
}
|
|
|
|
/* virtual */ nscoord
|
|
nsHTMLCanvasFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
|
|
{
|
|
// XXX The caller doesn't account for constraints of the height,
|
|
// min-height, and max-height properties.
|
|
bool vertical = GetWritingMode().IsVertical();
|
|
nscoord result = nsPresContext::CSSPixelsToAppUnits(
|
|
vertical ? GetCanvasSize().height : GetCanvasSize().width);
|
|
DISPLAY_PREF_WIDTH(this, result);
|
|
return result;
|
|
}
|
|
|
|
/* virtual */ IntrinsicSize
|
|
nsHTMLCanvasFrame::GetIntrinsicSize()
|
|
{
|
|
return IntrinsicSizeFromCanvasSize(GetCanvasSize());
|
|
}
|
|
|
|
/* virtual */ nsSize
|
|
nsHTMLCanvasFrame::GetIntrinsicRatio()
|
|
{
|
|
return IntrinsicRatioFromCanvasSize(GetCanvasSize());
|
|
}
|
|
|
|
/* virtual */
|
|
LogicalSize
|
|
nsHTMLCanvasFrame::ComputeSize(nsRenderingContext *aRenderingContext,
|
|
WritingMode aWM,
|
|
const LogicalSize& aCBSize,
|
|
nscoord aAvailableISize,
|
|
const LogicalSize& aMargin,
|
|
const LogicalSize& aBorder,
|
|
const LogicalSize& aPadding,
|
|
ComputeSizeFlags aFlags)
|
|
{
|
|
nsIntSize size = GetCanvasSize();
|
|
|
|
IntrinsicSize intrinsicSize;
|
|
intrinsicSize.width.SetCoordValue(nsPresContext::CSSPixelsToAppUnits(size.width));
|
|
intrinsicSize.height.SetCoordValue(nsPresContext::CSSPixelsToAppUnits(size.height));
|
|
|
|
nsSize intrinsicRatio = GetIntrinsicRatio(); // won't actually be used
|
|
|
|
return ComputeSizeWithIntrinsicDimensions(aRenderingContext, aWM,
|
|
intrinsicSize, intrinsicRatio,
|
|
aCBSize, aMargin, aBorder, aPadding,
|
|
aFlags);
|
|
}
|
|
|
|
void
|
|
nsHTMLCanvasFrame::Reflow(nsPresContext* aPresContext,
|
|
ReflowOutput& aMetrics,
|
|
const ReflowInput& aReflowInput,
|
|
nsReflowStatus& aStatus)
|
|
{
|
|
MarkInReflow();
|
|
DO_GLOBAL_REFLOW_COUNT("nsHTMLCanvasFrame");
|
|
DISPLAY_REFLOW(aPresContext, this, aReflowInput, aMetrics, aStatus);
|
|
NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
|
|
("enter nsHTMLCanvasFrame::Reflow: availSize=%d,%d",
|
|
aReflowInput.AvailableWidth(), aReflowInput.AvailableHeight()));
|
|
|
|
NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow");
|
|
|
|
aStatus = NS_FRAME_COMPLETE;
|
|
|
|
WritingMode wm = aReflowInput.GetWritingMode();
|
|
LogicalSize finalSize(wm,
|
|
aReflowInput.ComputedISize(),
|
|
aReflowInput.ComputedBSize());
|
|
|
|
// stash this away so we can compute our inner area later
|
|
mBorderPadding = aReflowInput.ComputedLogicalBorderPadding();
|
|
|
|
finalSize.ISize(wm) += mBorderPadding.IStartEnd(wm);
|
|
finalSize.BSize(wm) += mBorderPadding.BStartEnd(wm);
|
|
|
|
if (GetPrevInFlow()) {
|
|
nscoord y = GetContinuationOffset(&finalSize.ISize(wm));
|
|
finalSize.BSize(wm) -= y + mBorderPadding.BStart(wm);
|
|
finalSize.BSize(wm) = std::max(0, finalSize.BSize(wm));
|
|
}
|
|
|
|
aMetrics.SetSize(wm, finalSize);
|
|
aMetrics.SetOverflowAreasToDesiredBounds();
|
|
FinishAndStoreOverflow(&aMetrics);
|
|
|
|
// Reflow the single anon block child.
|
|
nsReflowStatus childStatus;
|
|
nsIFrame* childFrame = mFrames.FirstChild();
|
|
WritingMode childWM = childFrame->GetWritingMode();
|
|
LogicalSize availSize = aReflowInput.ComputedSize(childWM);
|
|
availSize.BSize(childWM) = NS_UNCONSTRAINEDSIZE;
|
|
NS_ASSERTION(!childFrame->GetNextSibling(), "HTML canvas should have 1 kid");
|
|
ReflowOutput childDesiredSize(aReflowInput.GetWritingMode(), aMetrics.mFlags);
|
|
ReflowInput childReflowInput(aPresContext, aReflowInput, childFrame,
|
|
availSize);
|
|
ReflowChild(childFrame, aPresContext, childDesiredSize, childReflowInput,
|
|
0, 0, 0, childStatus, nullptr);
|
|
FinishReflowChild(childFrame, aPresContext, childDesiredSize,
|
|
&childReflowInput, 0, 0, 0);
|
|
|
|
NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
|
|
("exit nsHTMLCanvasFrame::Reflow: size=%d,%d",
|
|
aMetrics.ISize(wm), aMetrics.BSize(wm)));
|
|
NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aMetrics);
|
|
}
|
|
|
|
// FIXME taken from nsImageFrame, but then had splittable frame stuff
|
|
// removed. That needs to be fixed.
|
|
// XXXdholbert As in nsImageFrame, this function's clients should probably
|
|
// just be calling GetContentRectRelativeToSelf().
|
|
nsRect
|
|
nsHTMLCanvasFrame::GetInnerArea() const
|
|
{
|
|
nsMargin bp = mBorderPadding.GetPhysicalMargin(GetWritingMode());
|
|
nsRect r;
|
|
r.x = bp.left;
|
|
r.y = bp.top;
|
|
r.width = mRect.width - bp.left - bp.right;
|
|
r.height = mRect.height - bp.top - bp.bottom;
|
|
return r;
|
|
}
|
|
|
|
already_AddRefed<Layer>
|
|
nsHTMLCanvasFrame::BuildLayer(nsDisplayListBuilder* aBuilder,
|
|
LayerManager* aManager,
|
|
nsDisplayItem* aItem,
|
|
const ContainerLayerParameters& aContainerParameters)
|
|
{
|
|
nsRect area = GetContentRectRelativeToSelf() + aItem->ToReferenceFrame();
|
|
HTMLCanvasElement* element = static_cast<HTMLCanvasElement*>(GetContent());
|
|
nsIntSize canvasSizeInPx = GetCanvasSize();
|
|
|
|
nsPresContext* presContext = PresContext();
|
|
element->HandlePrintCallback(presContext->Type());
|
|
|
|
if (canvasSizeInPx.width <= 0 || canvasSizeInPx.height <= 0 || area.IsEmpty())
|
|
return nullptr;
|
|
|
|
CanvasLayer* oldLayer = static_cast<CanvasLayer*>
|
|
(aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, aItem));
|
|
RefPtr<Layer> layer = element->GetCanvasLayer(aBuilder, oldLayer, aManager);
|
|
if (!layer)
|
|
return nullptr;
|
|
|
|
IntrinsicSize intrinsicSize = IntrinsicSizeFromCanvasSize(canvasSizeInPx);
|
|
nsSize intrinsicRatio = IntrinsicRatioFromCanvasSize(canvasSizeInPx);
|
|
|
|
nsRect dest =
|
|
nsLayoutUtils::ComputeObjectDestRect(area, intrinsicSize, intrinsicRatio,
|
|
StylePosition());
|
|
|
|
gfxRect destGFXRect = presContext->AppUnitsToGfxUnits(dest);
|
|
|
|
// Transform the canvas into the right place
|
|
gfxPoint p = destGFXRect.TopLeft() + aContainerParameters.mOffset;
|
|
Matrix transform = Matrix::Translation(p.x, p.y);
|
|
transform.PreScale(destGFXRect.Width() / canvasSizeInPx.width,
|
|
destGFXRect.Height() / canvasSizeInPx.height);
|
|
layer->SetBaseTransform(gfx::Matrix4x4::From2D(transform));
|
|
if (layer->GetType() == layers::Layer::TYPE_CANVAS) {
|
|
RefPtr<CanvasLayer> canvasLayer = static_cast<CanvasLayer*>(layer.get());
|
|
canvasLayer->SetSamplingFilter(nsLayoutUtils::GetSamplingFilterForFrame(this));
|
|
} else if (layer->GetType() == layers::Layer::TYPE_IMAGE) {
|
|
RefPtr<ImageLayer> imageLayer = static_cast<ImageLayer*>(layer.get());
|
|
imageLayer->SetSamplingFilter(nsLayoutUtils::GetSamplingFilterForFrame(this));
|
|
}
|
|
|
|
return layer.forget();
|
|
}
|
|
|
|
void
|
|
nsHTMLCanvasFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
|
const nsDisplayListSet& aLists)
|
|
{
|
|
if (!IsVisibleForPainting(aBuilder))
|
|
return;
|
|
|
|
DisplayBorderBackgroundOutline(aBuilder, aLists);
|
|
|
|
uint32_t clipFlags =
|
|
nsStyleUtil::ObjectPropsMightCauseOverflow(StylePosition()) ?
|
|
0 : DisplayListClipState::ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT;
|
|
|
|
DisplayListClipState::AutoClipContainingBlockDescendantsToContentBox
|
|
clip(aBuilder, this, clipFlags);
|
|
|
|
aLists.Content()->AppendNewToTop(
|
|
new (aBuilder) nsDisplayCanvas(aBuilder, this));
|
|
|
|
DisplaySelectionOverlay(aBuilder, aLists.Content(),
|
|
nsISelectionDisplay::DISPLAY_IMAGES);
|
|
}
|
|
|
|
nsIAtom*
|
|
nsHTMLCanvasFrame::GetType() const
|
|
{
|
|
return nsGkAtoms::HTMLCanvasFrame;
|
|
}
|
|
|
|
// get the offset into the content area of the image where aImg starts if it is a continuation.
|
|
// from nsImageFrame
|
|
nscoord
|
|
nsHTMLCanvasFrame::GetContinuationOffset(nscoord* aWidth) const
|
|
{
|
|
nscoord offset = 0;
|
|
if (aWidth) {
|
|
*aWidth = 0;
|
|
}
|
|
|
|
if (GetPrevInFlow()) {
|
|
for (nsIFrame* prevInFlow = GetPrevInFlow() ; prevInFlow; prevInFlow = prevInFlow->GetPrevInFlow()) {
|
|
nsRect rect = prevInFlow->GetRect();
|
|
if (aWidth) {
|
|
*aWidth = rect.width;
|
|
}
|
|
offset += rect.height;
|
|
}
|
|
offset -= mBorderPadding.GetPhysicalMargin(GetWritingMode()).top;
|
|
offset = std::max(0, offset);
|
|
}
|
|
return offset;
|
|
}
|
|
|
|
#ifdef ACCESSIBILITY
|
|
a11y::AccType
|
|
nsHTMLCanvasFrame::AccessibleType()
|
|
{
|
|
return a11y::eHTMLCanvasType;
|
|
}
|
|
#endif
|
|
|
|
#ifdef DEBUG_FRAME_DUMP
|
|
nsresult
|
|
nsHTMLCanvasFrame::GetFrameName(nsAString& aResult) const
|
|
{
|
|
return MakeFrameName(NS_LITERAL_STRING("HTMLCanvas"), aResult);
|
|
}
|
|
#endif
|
|
|