layout/generic/nsHTMLCanvasFrame.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 /* rendering object for the HTML <canvas> element */
michael@0 7
michael@0 8 #include "nsHTMLCanvasFrame.h"
michael@0 9
michael@0 10 #include "nsGkAtoms.h"
michael@0 11 #include "mozilla/dom/HTMLCanvasElement.h"
michael@0 12 #include "nsDisplayList.h"
michael@0 13 #include "nsLayoutUtils.h"
michael@0 14 #include "Layers.h"
michael@0 15 #include "ActiveLayerTracker.h"
michael@0 16
michael@0 17 #include <algorithm>
michael@0 18
michael@0 19 using namespace mozilla;
michael@0 20 using namespace mozilla::dom;
michael@0 21 using namespace mozilla::layers;
michael@0 22
michael@0 23 class nsDisplayCanvas : public nsDisplayItem {
michael@0 24 public:
michael@0 25 nsDisplayCanvas(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
michael@0 26 : nsDisplayItem(aBuilder, aFrame)
michael@0 27 {
michael@0 28 MOZ_COUNT_CTOR(nsDisplayCanvas);
michael@0 29 }
michael@0 30 #ifdef NS_BUILD_REFCNT_LOGGING
michael@0 31 virtual ~nsDisplayCanvas() {
michael@0 32 MOZ_COUNT_DTOR(nsDisplayCanvas);
michael@0 33 }
michael@0 34 #endif
michael@0 35
michael@0 36 NS_DISPLAY_DECL_NAME("nsDisplayCanvas", TYPE_CANVAS)
michael@0 37
michael@0 38 virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
michael@0 39 bool* aSnap) MOZ_OVERRIDE {
michael@0 40 *aSnap = false;
michael@0 41 nsIFrame* f = Frame();
michael@0 42 HTMLCanvasElement *canvas =
michael@0 43 HTMLCanvasElement::FromContent(f->GetContent());
michael@0 44 nsRegion result;
michael@0 45 if (canvas->GetIsOpaque()) {
michael@0 46 result = GetBounds(aBuilder, aSnap);
michael@0 47 }
michael@0 48 return result;
michael@0 49 }
michael@0 50
michael@0 51 virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
michael@0 52 bool* aSnap) MOZ_OVERRIDE {
michael@0 53 *aSnap = true;
michael@0 54 nsHTMLCanvasFrame* f = static_cast<nsHTMLCanvasFrame*>(Frame());
michael@0 55 return f->GetInnerArea() + ToReferenceFrame();
michael@0 56 }
michael@0 57
michael@0 58 virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
michael@0 59 LayerManager* aManager,
michael@0 60 const ContainerLayerParameters& aContainerParameters) MOZ_OVERRIDE
michael@0 61 {
michael@0 62 return static_cast<nsHTMLCanvasFrame*>(mFrame)->
michael@0 63 BuildLayer(aBuilder, aManager, this, aContainerParameters);
michael@0 64 }
michael@0 65 virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
michael@0 66 LayerManager* aManager,
michael@0 67 const ContainerLayerParameters& aParameters) MOZ_OVERRIDE
michael@0 68 {
michael@0 69 if (HTMLCanvasElement::FromContent(mFrame->GetContent())->ShouldForceInactiveLayer(aManager))
michael@0 70 return LAYER_INACTIVE;
michael@0 71
michael@0 72 // If compositing is cheap, just do that
michael@0 73 if (aManager->IsCompositingCheap() ||
michael@0 74 ActiveLayerTracker::IsContentActive(mFrame))
michael@0 75 return mozilla::LAYER_ACTIVE;
michael@0 76
michael@0 77 return LAYER_INACTIVE;
michael@0 78 }
michael@0 79 };
michael@0 80
michael@0 81
michael@0 82 nsIFrame*
michael@0 83 NS_NewHTMLCanvasFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
michael@0 84 {
michael@0 85 return new (aPresShell) nsHTMLCanvasFrame(aContext);
michael@0 86 }
michael@0 87
michael@0 88 NS_QUERYFRAME_HEAD(nsHTMLCanvasFrame)
michael@0 89 NS_QUERYFRAME_ENTRY(nsHTMLCanvasFrame)
michael@0 90 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
michael@0 91
michael@0 92 NS_IMPL_FRAMEARENA_HELPERS(nsHTMLCanvasFrame)
michael@0 93
michael@0 94 void
michael@0 95 nsHTMLCanvasFrame::Init(nsIContent* aContent,
michael@0 96 nsIFrame* aParent,
michael@0 97 nsIFrame* aPrevInFlow)
michael@0 98 {
michael@0 99 nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
michael@0 100
michael@0 101 // We can fill in the canvas before the canvas frame is created, in
michael@0 102 // which case we never get around to marking the content as active. Therefore,
michael@0 103 // we mark it active here when we create the frame.
michael@0 104 ActiveLayerTracker::NotifyContentChange(this);
michael@0 105 }
michael@0 106
michael@0 107 nsHTMLCanvasFrame::~nsHTMLCanvasFrame()
michael@0 108 {
michael@0 109 }
michael@0 110
michael@0 111 nsIntSize
michael@0 112 nsHTMLCanvasFrame::GetCanvasSize()
michael@0 113 {
michael@0 114 nsIntSize size(0,0);
michael@0 115 HTMLCanvasElement *canvas =
michael@0 116 HTMLCanvasElement::FromContentOrNull(GetContent());
michael@0 117 if (canvas) {
michael@0 118 size = canvas->GetSize();
michael@0 119 } else {
michael@0 120 NS_NOTREACHED("couldn't get canvas size");
michael@0 121 }
michael@0 122
michael@0 123 return size;
michael@0 124 }
michael@0 125
michael@0 126 /* virtual */ nscoord
michael@0 127 nsHTMLCanvasFrame::GetMinWidth(nsRenderingContext *aRenderingContext)
michael@0 128 {
michael@0 129 // XXX The caller doesn't account for constraints of the height,
michael@0 130 // min-height, and max-height properties.
michael@0 131 nscoord result = nsPresContext::CSSPixelsToAppUnits(GetCanvasSize().width);
michael@0 132 DISPLAY_MIN_WIDTH(this, result);
michael@0 133 return result;
michael@0 134 }
michael@0 135
michael@0 136 /* virtual */ nscoord
michael@0 137 nsHTMLCanvasFrame::GetPrefWidth(nsRenderingContext *aRenderingContext)
michael@0 138 {
michael@0 139 // XXX The caller doesn't account for constraints of the height,
michael@0 140 // min-height, and max-height properties.
michael@0 141 nscoord result = nsPresContext::CSSPixelsToAppUnits(GetCanvasSize().width);
michael@0 142 DISPLAY_PREF_WIDTH(this, result);
michael@0 143 return result;
michael@0 144 }
michael@0 145
michael@0 146 /* virtual */ nsSize
michael@0 147 nsHTMLCanvasFrame::GetIntrinsicRatio()
michael@0 148 {
michael@0 149 nsIntSize size(GetCanvasSize());
michael@0 150 return nsSize(nsPresContext::CSSPixelsToAppUnits(size.width),
michael@0 151 nsPresContext::CSSPixelsToAppUnits(size.height));
michael@0 152 }
michael@0 153
michael@0 154 /* virtual */ nsSize
michael@0 155 nsHTMLCanvasFrame::ComputeSize(nsRenderingContext *aRenderingContext,
michael@0 156 nsSize aCBSize, nscoord aAvailableWidth,
michael@0 157 nsSize aMargin, nsSize aBorder, nsSize aPadding,
michael@0 158 uint32_t aFlags)
michael@0 159 {
michael@0 160 nsIntSize size = GetCanvasSize();
michael@0 161
michael@0 162 IntrinsicSize intrinsicSize;
michael@0 163 intrinsicSize.width.SetCoordValue(nsPresContext::CSSPixelsToAppUnits(size.width));
michael@0 164 intrinsicSize.height.SetCoordValue(nsPresContext::CSSPixelsToAppUnits(size.height));
michael@0 165
michael@0 166 nsSize intrinsicRatio = GetIntrinsicRatio(); // won't actually be used
michael@0 167
michael@0 168 return nsLayoutUtils::ComputeSizeWithIntrinsicDimensions(
michael@0 169 aRenderingContext, this,
michael@0 170 intrinsicSize, intrinsicRatio, aCBSize,
michael@0 171 aMargin, aBorder, aPadding);
michael@0 172 }
michael@0 173
michael@0 174 nsresult
michael@0 175 nsHTMLCanvasFrame::Reflow(nsPresContext* aPresContext,
michael@0 176 nsHTMLReflowMetrics& aMetrics,
michael@0 177 const nsHTMLReflowState& aReflowState,
michael@0 178 nsReflowStatus& aStatus)
michael@0 179 {
michael@0 180 DO_GLOBAL_REFLOW_COUNT("nsHTMLCanvasFrame");
michael@0 181 DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
michael@0 182 NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
michael@0 183 ("enter nsHTMLCanvasFrame::Reflow: availSize=%d,%d",
michael@0 184 aReflowState.AvailableWidth(), aReflowState.AvailableHeight()));
michael@0 185
michael@0 186 NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow");
michael@0 187
michael@0 188 aStatus = NS_FRAME_COMPLETE;
michael@0 189
michael@0 190 aMetrics.Width() = aReflowState.ComputedWidth();
michael@0 191 aMetrics.Height() = aReflowState.ComputedHeight();
michael@0 192
michael@0 193 // stash this away so we can compute our inner area later
michael@0 194 mBorderPadding = aReflowState.ComputedPhysicalBorderPadding();
michael@0 195
michael@0 196 aMetrics.Width() += mBorderPadding.left + mBorderPadding.right;
michael@0 197 aMetrics.Height() += mBorderPadding.top + mBorderPadding.bottom;
michael@0 198
michael@0 199 if (GetPrevInFlow()) {
michael@0 200 nscoord y = GetContinuationOffset(&aMetrics.Width());
michael@0 201 aMetrics.Height() -= y + mBorderPadding.top;
michael@0 202 aMetrics.Height() = std::max(0, aMetrics.Height());
michael@0 203 }
michael@0 204
michael@0 205 aMetrics.SetOverflowAreasToDesiredBounds();
michael@0 206 FinishAndStoreOverflow(&aMetrics);
michael@0 207
michael@0 208 // Reflow the single anon block child.
michael@0 209 nsReflowStatus childStatus;
michael@0 210 nsSize availSize(aReflowState.ComputedWidth(), NS_UNCONSTRAINEDSIZE);
michael@0 211 nsIFrame* childFrame = mFrames.FirstChild();
michael@0 212 NS_ASSERTION(!childFrame->GetNextSibling(), "HTML canvas should have 1 kid");
michael@0 213 nsHTMLReflowMetrics childDesiredSize(aReflowState.GetWritingMode(), aMetrics.mFlags);
michael@0 214 nsHTMLReflowState childReflowState(aPresContext, aReflowState, childFrame,
michael@0 215 availSize);
michael@0 216 ReflowChild(childFrame, aPresContext, childDesiredSize, childReflowState,
michael@0 217 0, 0, 0, childStatus, nullptr);
michael@0 218 FinishReflowChild(childFrame, aPresContext, childDesiredSize,
michael@0 219 &childReflowState, 0, 0, 0);
michael@0 220
michael@0 221 NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
michael@0 222 ("exit nsHTMLCanvasFrame::Reflow: size=%d,%d",
michael@0 223 aMetrics.Width(), aMetrics.Height()));
michael@0 224 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics);
michael@0 225
michael@0 226 return NS_OK;
michael@0 227 }
michael@0 228
michael@0 229 // FIXME taken from nsImageFrame, but then had splittable frame stuff
michael@0 230 // removed. That needs to be fixed.
michael@0 231 nsRect
michael@0 232 nsHTMLCanvasFrame::GetInnerArea() const
michael@0 233 {
michael@0 234 nsRect r;
michael@0 235 r.x = mBorderPadding.left;
michael@0 236 r.y = mBorderPadding.top;
michael@0 237 r.width = mRect.width - mBorderPadding.left - mBorderPadding.right;
michael@0 238 r.height = mRect.height - mBorderPadding.top - mBorderPadding.bottom;
michael@0 239 return r;
michael@0 240 }
michael@0 241
michael@0 242 already_AddRefed<Layer>
michael@0 243 nsHTMLCanvasFrame::BuildLayer(nsDisplayListBuilder* aBuilder,
michael@0 244 LayerManager* aManager,
michael@0 245 nsDisplayItem* aItem,
michael@0 246 const ContainerLayerParameters& aContainerParameters)
michael@0 247 {
michael@0 248 nsRect area = GetContentRect() - GetPosition() + aItem->ToReferenceFrame();
michael@0 249 HTMLCanvasElement* element = static_cast<HTMLCanvasElement*>(GetContent());
michael@0 250 nsIntSize canvasSize = GetCanvasSize();
michael@0 251
michael@0 252 nsPresContext* presContext = PresContext();
michael@0 253 element->HandlePrintCallback(presContext->Type());
michael@0 254
michael@0 255 if (canvasSize.width <= 0 || canvasSize.height <= 0 || area.IsEmpty())
michael@0 256 return nullptr;
michael@0 257
michael@0 258 CanvasLayer* oldLayer = static_cast<CanvasLayer*>
michael@0 259 (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, aItem));
michael@0 260 nsRefPtr<CanvasLayer> layer = element->GetCanvasLayer(aBuilder, oldLayer, aManager);
michael@0 261 if (!layer)
michael@0 262 return nullptr;
michael@0 263
michael@0 264 gfxRect r = gfxRect(presContext->AppUnitsToGfxUnits(area.x),
michael@0 265 presContext->AppUnitsToGfxUnits(area.y),
michael@0 266 presContext->AppUnitsToGfxUnits(area.width),
michael@0 267 presContext->AppUnitsToGfxUnits(area.height));
michael@0 268
michael@0 269 // Transform the canvas into the right place
michael@0 270 gfx::Matrix transform;
michael@0 271 gfxPoint p = r.TopLeft() + aContainerParameters.mOffset;
michael@0 272 transform.Translate(p.x, p.y);
michael@0 273 transform.Scale(r.Width()/canvasSize.width, r.Height()/canvasSize.height);
michael@0 274 layer->SetBaseTransform(gfx::Matrix4x4::From2D(transform));
michael@0 275 layer->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(this));
michael@0 276 layer->SetVisibleRegion(nsIntRect(0, 0, canvasSize.width, canvasSize.height));
michael@0 277
michael@0 278 return layer.forget();
michael@0 279 }
michael@0 280
michael@0 281 void
michael@0 282 nsHTMLCanvasFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
michael@0 283 const nsRect& aDirtyRect,
michael@0 284 const nsDisplayListSet& aLists)
michael@0 285 {
michael@0 286 if (!IsVisibleForPainting(aBuilder))
michael@0 287 return;
michael@0 288
michael@0 289 DisplayBorderBackgroundOutline(aBuilder, aLists);
michael@0 290
michael@0 291 DisplayListClipState::AutoClipContainingBlockDescendantsToContentBox
michael@0 292 clip(aBuilder, this, DisplayListClipState::ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT);
michael@0 293
michael@0 294 aLists.Content()->AppendNewToTop(
michael@0 295 new (aBuilder) nsDisplayCanvas(aBuilder, this));
michael@0 296
michael@0 297 DisplaySelectionOverlay(aBuilder, aLists.Content(),
michael@0 298 nsISelectionDisplay::DISPLAY_IMAGES);
michael@0 299 }
michael@0 300
michael@0 301 nsIAtom*
michael@0 302 nsHTMLCanvasFrame::GetType() const
michael@0 303 {
michael@0 304 return nsGkAtoms::HTMLCanvasFrame;
michael@0 305 }
michael@0 306
michael@0 307 // get the offset into the content area of the image where aImg starts if it is a continuation.
michael@0 308 // from nsImageFrame
michael@0 309 nscoord
michael@0 310 nsHTMLCanvasFrame::GetContinuationOffset(nscoord* aWidth) const
michael@0 311 {
michael@0 312 nscoord offset = 0;
michael@0 313 if (aWidth) {
michael@0 314 *aWidth = 0;
michael@0 315 }
michael@0 316
michael@0 317 if (GetPrevInFlow()) {
michael@0 318 for (nsIFrame* prevInFlow = GetPrevInFlow() ; prevInFlow; prevInFlow = prevInFlow->GetPrevInFlow()) {
michael@0 319 nsRect rect = prevInFlow->GetRect();
michael@0 320 if (aWidth) {
michael@0 321 *aWidth = rect.width;
michael@0 322 }
michael@0 323 offset += rect.height;
michael@0 324 }
michael@0 325 offset -= mBorderPadding.top;
michael@0 326 offset = std::max(0, offset);
michael@0 327 }
michael@0 328 return offset;
michael@0 329 }
michael@0 330
michael@0 331 #ifdef ACCESSIBILITY
michael@0 332 a11y::AccType
michael@0 333 nsHTMLCanvasFrame::AccessibleType()
michael@0 334 {
michael@0 335 return a11y::eHTMLCanvasType;
michael@0 336 }
michael@0 337 #endif
michael@0 338
michael@0 339 #ifdef DEBUG_FRAME_DUMP
michael@0 340 nsresult
michael@0 341 nsHTMLCanvasFrame::GetFrameName(nsAString& aResult) const
michael@0 342 {
michael@0 343 return MakeFrameName(NS_LITERAL_STRING("HTMLCanvas"), aResult);
michael@0 344 }
michael@0 345 #endif
michael@0 346

mercurial