1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/generic/nsHTMLCanvasFrame.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,346 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +/* rendering object for the HTML <canvas> element */ 1.10 + 1.11 +#include "nsHTMLCanvasFrame.h" 1.12 + 1.13 +#include "nsGkAtoms.h" 1.14 +#include "mozilla/dom/HTMLCanvasElement.h" 1.15 +#include "nsDisplayList.h" 1.16 +#include "nsLayoutUtils.h" 1.17 +#include "Layers.h" 1.18 +#include "ActiveLayerTracker.h" 1.19 + 1.20 +#include <algorithm> 1.21 + 1.22 +using namespace mozilla; 1.23 +using namespace mozilla::dom; 1.24 +using namespace mozilla::layers; 1.25 + 1.26 +class nsDisplayCanvas : public nsDisplayItem { 1.27 +public: 1.28 + nsDisplayCanvas(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) 1.29 + : nsDisplayItem(aBuilder, aFrame) 1.30 + { 1.31 + MOZ_COUNT_CTOR(nsDisplayCanvas); 1.32 + } 1.33 +#ifdef NS_BUILD_REFCNT_LOGGING 1.34 + virtual ~nsDisplayCanvas() { 1.35 + MOZ_COUNT_DTOR(nsDisplayCanvas); 1.36 + } 1.37 +#endif 1.38 + 1.39 + NS_DISPLAY_DECL_NAME("nsDisplayCanvas", TYPE_CANVAS) 1.40 + 1.41 + virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder, 1.42 + bool* aSnap) MOZ_OVERRIDE { 1.43 + *aSnap = false; 1.44 + nsIFrame* f = Frame(); 1.45 + HTMLCanvasElement *canvas = 1.46 + HTMLCanvasElement::FromContent(f->GetContent()); 1.47 + nsRegion result; 1.48 + if (canvas->GetIsOpaque()) { 1.49 + result = GetBounds(aBuilder, aSnap); 1.50 + } 1.51 + return result; 1.52 + } 1.53 + 1.54 + virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, 1.55 + bool* aSnap) MOZ_OVERRIDE { 1.56 + *aSnap = true; 1.57 + nsHTMLCanvasFrame* f = static_cast<nsHTMLCanvasFrame*>(Frame()); 1.58 + return f->GetInnerArea() + ToReferenceFrame(); 1.59 + } 1.60 + 1.61 + virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder, 1.62 + LayerManager* aManager, 1.63 + const ContainerLayerParameters& aContainerParameters) MOZ_OVERRIDE 1.64 + { 1.65 + return static_cast<nsHTMLCanvasFrame*>(mFrame)-> 1.66 + BuildLayer(aBuilder, aManager, this, aContainerParameters); 1.67 + } 1.68 + virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder, 1.69 + LayerManager* aManager, 1.70 + const ContainerLayerParameters& aParameters) MOZ_OVERRIDE 1.71 + { 1.72 + if (HTMLCanvasElement::FromContent(mFrame->GetContent())->ShouldForceInactiveLayer(aManager)) 1.73 + return LAYER_INACTIVE; 1.74 + 1.75 + // If compositing is cheap, just do that 1.76 + if (aManager->IsCompositingCheap() || 1.77 + ActiveLayerTracker::IsContentActive(mFrame)) 1.78 + return mozilla::LAYER_ACTIVE; 1.79 + 1.80 + return LAYER_INACTIVE; 1.81 + } 1.82 +}; 1.83 + 1.84 + 1.85 +nsIFrame* 1.86 +NS_NewHTMLCanvasFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) 1.87 +{ 1.88 + return new (aPresShell) nsHTMLCanvasFrame(aContext); 1.89 +} 1.90 + 1.91 +NS_QUERYFRAME_HEAD(nsHTMLCanvasFrame) 1.92 + NS_QUERYFRAME_ENTRY(nsHTMLCanvasFrame) 1.93 +NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) 1.94 + 1.95 +NS_IMPL_FRAMEARENA_HELPERS(nsHTMLCanvasFrame) 1.96 + 1.97 +void 1.98 +nsHTMLCanvasFrame::Init(nsIContent* aContent, 1.99 + nsIFrame* aParent, 1.100 + nsIFrame* aPrevInFlow) 1.101 +{ 1.102 + nsContainerFrame::Init(aContent, aParent, aPrevInFlow); 1.103 + 1.104 + // We can fill in the canvas before the canvas frame is created, in 1.105 + // which case we never get around to marking the content as active. Therefore, 1.106 + // we mark it active here when we create the frame. 1.107 + ActiveLayerTracker::NotifyContentChange(this); 1.108 +} 1.109 + 1.110 +nsHTMLCanvasFrame::~nsHTMLCanvasFrame() 1.111 +{ 1.112 +} 1.113 + 1.114 +nsIntSize 1.115 +nsHTMLCanvasFrame::GetCanvasSize() 1.116 +{ 1.117 + nsIntSize size(0,0); 1.118 + HTMLCanvasElement *canvas = 1.119 + HTMLCanvasElement::FromContentOrNull(GetContent()); 1.120 + if (canvas) { 1.121 + size = canvas->GetSize(); 1.122 + } else { 1.123 + NS_NOTREACHED("couldn't get canvas size"); 1.124 + } 1.125 + 1.126 + return size; 1.127 +} 1.128 + 1.129 +/* virtual */ nscoord 1.130 +nsHTMLCanvasFrame::GetMinWidth(nsRenderingContext *aRenderingContext) 1.131 +{ 1.132 + // XXX The caller doesn't account for constraints of the height, 1.133 + // min-height, and max-height properties. 1.134 + nscoord result = nsPresContext::CSSPixelsToAppUnits(GetCanvasSize().width); 1.135 + DISPLAY_MIN_WIDTH(this, result); 1.136 + return result; 1.137 +} 1.138 + 1.139 +/* virtual */ nscoord 1.140 +nsHTMLCanvasFrame::GetPrefWidth(nsRenderingContext *aRenderingContext) 1.141 +{ 1.142 + // XXX The caller doesn't account for constraints of the height, 1.143 + // min-height, and max-height properties. 1.144 + nscoord result = nsPresContext::CSSPixelsToAppUnits(GetCanvasSize().width); 1.145 + DISPLAY_PREF_WIDTH(this, result); 1.146 + return result; 1.147 +} 1.148 + 1.149 +/* virtual */ nsSize 1.150 +nsHTMLCanvasFrame::GetIntrinsicRatio() 1.151 +{ 1.152 + nsIntSize size(GetCanvasSize()); 1.153 + return nsSize(nsPresContext::CSSPixelsToAppUnits(size.width), 1.154 + nsPresContext::CSSPixelsToAppUnits(size.height)); 1.155 +} 1.156 + 1.157 +/* virtual */ nsSize 1.158 +nsHTMLCanvasFrame::ComputeSize(nsRenderingContext *aRenderingContext, 1.159 + nsSize aCBSize, nscoord aAvailableWidth, 1.160 + nsSize aMargin, nsSize aBorder, nsSize aPadding, 1.161 + uint32_t aFlags) 1.162 +{ 1.163 + nsIntSize size = GetCanvasSize(); 1.164 + 1.165 + IntrinsicSize intrinsicSize; 1.166 + intrinsicSize.width.SetCoordValue(nsPresContext::CSSPixelsToAppUnits(size.width)); 1.167 + intrinsicSize.height.SetCoordValue(nsPresContext::CSSPixelsToAppUnits(size.height)); 1.168 + 1.169 + nsSize intrinsicRatio = GetIntrinsicRatio(); // won't actually be used 1.170 + 1.171 + return nsLayoutUtils::ComputeSizeWithIntrinsicDimensions( 1.172 + aRenderingContext, this, 1.173 + intrinsicSize, intrinsicRatio, aCBSize, 1.174 + aMargin, aBorder, aPadding); 1.175 +} 1.176 + 1.177 +nsresult 1.178 +nsHTMLCanvasFrame::Reflow(nsPresContext* aPresContext, 1.179 + nsHTMLReflowMetrics& aMetrics, 1.180 + const nsHTMLReflowState& aReflowState, 1.181 + nsReflowStatus& aStatus) 1.182 +{ 1.183 + DO_GLOBAL_REFLOW_COUNT("nsHTMLCanvasFrame"); 1.184 + DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus); 1.185 + NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, 1.186 + ("enter nsHTMLCanvasFrame::Reflow: availSize=%d,%d", 1.187 + aReflowState.AvailableWidth(), aReflowState.AvailableHeight())); 1.188 + 1.189 + NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow"); 1.190 + 1.191 + aStatus = NS_FRAME_COMPLETE; 1.192 + 1.193 + aMetrics.Width() = aReflowState.ComputedWidth(); 1.194 + aMetrics.Height() = aReflowState.ComputedHeight(); 1.195 + 1.196 + // stash this away so we can compute our inner area later 1.197 + mBorderPadding = aReflowState.ComputedPhysicalBorderPadding(); 1.198 + 1.199 + aMetrics.Width() += mBorderPadding.left + mBorderPadding.right; 1.200 + aMetrics.Height() += mBorderPadding.top + mBorderPadding.bottom; 1.201 + 1.202 + if (GetPrevInFlow()) { 1.203 + nscoord y = GetContinuationOffset(&aMetrics.Width()); 1.204 + aMetrics.Height() -= y + mBorderPadding.top; 1.205 + aMetrics.Height() = std::max(0, aMetrics.Height()); 1.206 + } 1.207 + 1.208 + aMetrics.SetOverflowAreasToDesiredBounds(); 1.209 + FinishAndStoreOverflow(&aMetrics); 1.210 + 1.211 + // Reflow the single anon block child. 1.212 + nsReflowStatus childStatus; 1.213 + nsSize availSize(aReflowState.ComputedWidth(), NS_UNCONSTRAINEDSIZE); 1.214 + nsIFrame* childFrame = mFrames.FirstChild(); 1.215 + NS_ASSERTION(!childFrame->GetNextSibling(), "HTML canvas should have 1 kid"); 1.216 + nsHTMLReflowMetrics childDesiredSize(aReflowState.GetWritingMode(), aMetrics.mFlags); 1.217 + nsHTMLReflowState childReflowState(aPresContext, aReflowState, childFrame, 1.218 + availSize); 1.219 + ReflowChild(childFrame, aPresContext, childDesiredSize, childReflowState, 1.220 + 0, 0, 0, childStatus, nullptr); 1.221 + FinishReflowChild(childFrame, aPresContext, childDesiredSize, 1.222 + &childReflowState, 0, 0, 0); 1.223 + 1.224 + NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, 1.225 + ("exit nsHTMLCanvasFrame::Reflow: size=%d,%d", 1.226 + aMetrics.Width(), aMetrics.Height())); 1.227 + NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics); 1.228 + 1.229 + return NS_OK; 1.230 +} 1.231 + 1.232 +// FIXME taken from nsImageFrame, but then had splittable frame stuff 1.233 +// removed. That needs to be fixed. 1.234 +nsRect 1.235 +nsHTMLCanvasFrame::GetInnerArea() const 1.236 +{ 1.237 + nsRect r; 1.238 + r.x = mBorderPadding.left; 1.239 + r.y = mBorderPadding.top; 1.240 + r.width = mRect.width - mBorderPadding.left - mBorderPadding.right; 1.241 + r.height = mRect.height - mBorderPadding.top - mBorderPadding.bottom; 1.242 + return r; 1.243 +} 1.244 + 1.245 +already_AddRefed<Layer> 1.246 +nsHTMLCanvasFrame::BuildLayer(nsDisplayListBuilder* aBuilder, 1.247 + LayerManager* aManager, 1.248 + nsDisplayItem* aItem, 1.249 + const ContainerLayerParameters& aContainerParameters) 1.250 +{ 1.251 + nsRect area = GetContentRect() - GetPosition() + aItem->ToReferenceFrame(); 1.252 + HTMLCanvasElement* element = static_cast<HTMLCanvasElement*>(GetContent()); 1.253 + nsIntSize canvasSize = GetCanvasSize(); 1.254 + 1.255 + nsPresContext* presContext = PresContext(); 1.256 + element->HandlePrintCallback(presContext->Type()); 1.257 + 1.258 + if (canvasSize.width <= 0 || canvasSize.height <= 0 || area.IsEmpty()) 1.259 + return nullptr; 1.260 + 1.261 + CanvasLayer* oldLayer = static_cast<CanvasLayer*> 1.262 + (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, aItem)); 1.263 + nsRefPtr<CanvasLayer> layer = element->GetCanvasLayer(aBuilder, oldLayer, aManager); 1.264 + if (!layer) 1.265 + return nullptr; 1.266 + 1.267 + gfxRect r = gfxRect(presContext->AppUnitsToGfxUnits(area.x), 1.268 + presContext->AppUnitsToGfxUnits(area.y), 1.269 + presContext->AppUnitsToGfxUnits(area.width), 1.270 + presContext->AppUnitsToGfxUnits(area.height)); 1.271 + 1.272 + // Transform the canvas into the right place 1.273 + gfx::Matrix transform; 1.274 + gfxPoint p = r.TopLeft() + aContainerParameters.mOffset; 1.275 + transform.Translate(p.x, p.y); 1.276 + transform.Scale(r.Width()/canvasSize.width, r.Height()/canvasSize.height); 1.277 + layer->SetBaseTransform(gfx::Matrix4x4::From2D(transform)); 1.278 + layer->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(this)); 1.279 + layer->SetVisibleRegion(nsIntRect(0, 0, canvasSize.width, canvasSize.height)); 1.280 + 1.281 + return layer.forget(); 1.282 +} 1.283 + 1.284 +void 1.285 +nsHTMLCanvasFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, 1.286 + const nsRect& aDirtyRect, 1.287 + const nsDisplayListSet& aLists) 1.288 +{ 1.289 + if (!IsVisibleForPainting(aBuilder)) 1.290 + return; 1.291 + 1.292 + DisplayBorderBackgroundOutline(aBuilder, aLists); 1.293 + 1.294 + DisplayListClipState::AutoClipContainingBlockDescendantsToContentBox 1.295 + clip(aBuilder, this, DisplayListClipState::ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT); 1.296 + 1.297 + aLists.Content()->AppendNewToTop( 1.298 + new (aBuilder) nsDisplayCanvas(aBuilder, this)); 1.299 + 1.300 + DisplaySelectionOverlay(aBuilder, aLists.Content(), 1.301 + nsISelectionDisplay::DISPLAY_IMAGES); 1.302 +} 1.303 + 1.304 +nsIAtom* 1.305 +nsHTMLCanvasFrame::GetType() const 1.306 +{ 1.307 + return nsGkAtoms::HTMLCanvasFrame; 1.308 +} 1.309 + 1.310 +// get the offset into the content area of the image where aImg starts if it is a continuation. 1.311 +// from nsImageFrame 1.312 +nscoord 1.313 +nsHTMLCanvasFrame::GetContinuationOffset(nscoord* aWidth) const 1.314 +{ 1.315 + nscoord offset = 0; 1.316 + if (aWidth) { 1.317 + *aWidth = 0; 1.318 + } 1.319 + 1.320 + if (GetPrevInFlow()) { 1.321 + for (nsIFrame* prevInFlow = GetPrevInFlow() ; prevInFlow; prevInFlow = prevInFlow->GetPrevInFlow()) { 1.322 + nsRect rect = prevInFlow->GetRect(); 1.323 + if (aWidth) { 1.324 + *aWidth = rect.width; 1.325 + } 1.326 + offset += rect.height; 1.327 + } 1.328 + offset -= mBorderPadding.top; 1.329 + offset = std::max(0, offset); 1.330 + } 1.331 + return offset; 1.332 +} 1.333 + 1.334 +#ifdef ACCESSIBILITY 1.335 +a11y::AccType 1.336 +nsHTMLCanvasFrame::AccessibleType() 1.337 +{ 1.338 + return a11y::eHTMLCanvasType; 1.339 +} 1.340 +#endif 1.341 + 1.342 +#ifdef DEBUG_FRAME_DUMP 1.343 +nsresult 1.344 +nsHTMLCanvasFrame::GetFrameName(nsAString& aResult) const 1.345 +{ 1.346 + return MakeFrameName(NS_LITERAL_STRING("HTMLCanvas"), aResult); 1.347 +} 1.348 +#endif 1.349 +