Thu, 22 Jan 2015 13:21:57 +0100
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 that goes directly inside the document's scrollbars */ |
michael@0 | 7 | |
michael@0 | 8 | #include "nsCanvasFrame.h" |
michael@0 | 9 | #include "nsContainerFrame.h" |
michael@0 | 10 | #include "nsCSSRendering.h" |
michael@0 | 11 | #include "nsPresContext.h" |
michael@0 | 12 | #include "nsStyleContext.h" |
michael@0 | 13 | #include "nsRenderingContext.h" |
michael@0 | 14 | #include "nsGkAtoms.h" |
michael@0 | 15 | #include "nsIPresShell.h" |
michael@0 | 16 | #include "nsDisplayList.h" |
michael@0 | 17 | #include "nsCSSFrameConstructor.h" |
michael@0 | 18 | #include "nsFrameManager.h" |
michael@0 | 19 | #include "gfxPlatform.h" |
michael@0 | 20 | |
michael@0 | 21 | // for focus |
michael@0 | 22 | #include "nsIScrollableFrame.h" |
michael@0 | 23 | #ifdef DEBUG_CANVAS_FOCUS |
michael@0 | 24 | #include "nsIDocShell.h" |
michael@0 | 25 | #endif |
michael@0 | 26 | |
michael@0 | 27 | //#define DEBUG_CANVAS_FOCUS |
michael@0 | 28 | |
michael@0 | 29 | using namespace mozilla; |
michael@0 | 30 | using namespace mozilla::layout; |
michael@0 | 31 | using namespace mozilla::gfx; |
michael@0 | 32 | |
michael@0 | 33 | nsIFrame* |
michael@0 | 34 | NS_NewCanvasFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) |
michael@0 | 35 | { |
michael@0 | 36 | return new (aPresShell) nsCanvasFrame(aContext); |
michael@0 | 37 | } |
michael@0 | 38 | |
michael@0 | 39 | NS_IMPL_FRAMEARENA_HELPERS(nsCanvasFrame) |
michael@0 | 40 | |
michael@0 | 41 | NS_QUERYFRAME_HEAD(nsCanvasFrame) |
michael@0 | 42 | NS_QUERYFRAME_ENTRY(nsCanvasFrame) |
michael@0 | 43 | NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) |
michael@0 | 44 | |
michael@0 | 45 | void |
michael@0 | 46 | nsCanvasFrame::DestroyFrom(nsIFrame* aDestructRoot) |
michael@0 | 47 | { |
michael@0 | 48 | nsIScrollableFrame* sf = |
michael@0 | 49 | PresContext()->GetPresShell()->GetRootScrollFrameAsScrollable(); |
michael@0 | 50 | if (sf) { |
michael@0 | 51 | sf->RemoveScrollPositionListener(this); |
michael@0 | 52 | } |
michael@0 | 53 | |
michael@0 | 54 | nsContainerFrame::DestroyFrom(aDestructRoot); |
michael@0 | 55 | } |
michael@0 | 56 | |
michael@0 | 57 | void |
michael@0 | 58 | nsCanvasFrame::ScrollPositionWillChange(nscoord aX, nscoord aY) |
michael@0 | 59 | { |
michael@0 | 60 | if (mDoPaintFocus) { |
michael@0 | 61 | mDoPaintFocus = false; |
michael@0 | 62 | PresContext()->FrameManager()->GetRootFrame()->InvalidateFrameSubtree(); |
michael@0 | 63 | } |
michael@0 | 64 | } |
michael@0 | 65 | |
michael@0 | 66 | NS_IMETHODIMP |
michael@0 | 67 | nsCanvasFrame::SetHasFocus(bool aHasFocus) |
michael@0 | 68 | { |
michael@0 | 69 | if (mDoPaintFocus != aHasFocus) { |
michael@0 | 70 | mDoPaintFocus = aHasFocus; |
michael@0 | 71 | PresContext()->FrameManager()->GetRootFrame()->InvalidateFrameSubtree(); |
michael@0 | 72 | |
michael@0 | 73 | if (!mAddedScrollPositionListener) { |
michael@0 | 74 | nsIScrollableFrame* sf = |
michael@0 | 75 | PresContext()->GetPresShell()->GetRootScrollFrameAsScrollable(); |
michael@0 | 76 | if (sf) { |
michael@0 | 77 | sf->AddScrollPositionListener(this); |
michael@0 | 78 | mAddedScrollPositionListener = true; |
michael@0 | 79 | } |
michael@0 | 80 | } |
michael@0 | 81 | } |
michael@0 | 82 | return NS_OK; |
michael@0 | 83 | } |
michael@0 | 84 | |
michael@0 | 85 | nsresult |
michael@0 | 86 | nsCanvasFrame::SetInitialChildList(ChildListID aListID, |
michael@0 | 87 | nsFrameList& aChildList) |
michael@0 | 88 | { |
michael@0 | 89 | NS_ASSERTION(aListID != kPrincipalList || |
michael@0 | 90 | aChildList.IsEmpty() || aChildList.OnlyChild(), |
michael@0 | 91 | "Primary child list can have at most one frame in it"); |
michael@0 | 92 | return nsContainerFrame::SetInitialChildList(aListID, aChildList); |
michael@0 | 93 | } |
michael@0 | 94 | |
michael@0 | 95 | nsresult |
michael@0 | 96 | nsCanvasFrame::AppendFrames(ChildListID aListID, |
michael@0 | 97 | nsFrameList& aFrameList) |
michael@0 | 98 | { |
michael@0 | 99 | NS_ASSERTION(aListID == kPrincipalList || |
michael@0 | 100 | aListID == kAbsoluteList, "unexpected child list ID"); |
michael@0 | 101 | NS_PRECONDITION(aListID != kAbsoluteList || |
michael@0 | 102 | mFrames.IsEmpty(), "already have a child frame"); |
michael@0 | 103 | if (aListID != kPrincipalList) { |
michael@0 | 104 | // We only support the Principal and Absolute child lists. |
michael@0 | 105 | return NS_ERROR_INVALID_ARG; |
michael@0 | 106 | } |
michael@0 | 107 | |
michael@0 | 108 | if (!mFrames.IsEmpty()) { |
michael@0 | 109 | // We only allow a single principal child frame. |
michael@0 | 110 | return NS_ERROR_INVALID_ARG; |
michael@0 | 111 | } |
michael@0 | 112 | |
michael@0 | 113 | // Insert the new frames |
michael@0 | 114 | NS_ASSERTION(aFrameList.FirstChild() == aFrameList.LastChild(), |
michael@0 | 115 | "Only one principal child frame allowed"); |
michael@0 | 116 | #ifdef DEBUG |
michael@0 | 117 | nsFrame::VerifyDirtyBitSet(aFrameList); |
michael@0 | 118 | #endif |
michael@0 | 119 | mFrames.AppendFrames(nullptr, aFrameList); |
michael@0 | 120 | |
michael@0 | 121 | PresContext()->PresShell()-> |
michael@0 | 122 | FrameNeedsReflow(this, nsIPresShell::eTreeChange, |
michael@0 | 123 | NS_FRAME_HAS_DIRTY_CHILDREN); |
michael@0 | 124 | |
michael@0 | 125 | return NS_OK; |
michael@0 | 126 | } |
michael@0 | 127 | |
michael@0 | 128 | nsresult |
michael@0 | 129 | nsCanvasFrame::InsertFrames(ChildListID aListID, |
michael@0 | 130 | nsIFrame* aPrevFrame, |
michael@0 | 131 | nsFrameList& aFrameList) |
michael@0 | 132 | { |
michael@0 | 133 | // Because we only support a single child frame inserting is the same |
michael@0 | 134 | // as appending |
michael@0 | 135 | NS_PRECONDITION(!aPrevFrame, "unexpected previous sibling frame"); |
michael@0 | 136 | if (aPrevFrame) |
michael@0 | 137 | return NS_ERROR_UNEXPECTED; |
michael@0 | 138 | |
michael@0 | 139 | return AppendFrames(aListID, aFrameList); |
michael@0 | 140 | } |
michael@0 | 141 | |
michael@0 | 142 | nsresult |
michael@0 | 143 | nsCanvasFrame::RemoveFrame(ChildListID aListID, |
michael@0 | 144 | nsIFrame* aOldFrame) |
michael@0 | 145 | { |
michael@0 | 146 | NS_ASSERTION(aListID == kPrincipalList || |
michael@0 | 147 | aListID == kAbsoluteList, "unexpected child list ID"); |
michael@0 | 148 | if (aListID != kPrincipalList && aListID != kAbsoluteList) { |
michael@0 | 149 | // We only support the Principal and Absolute child lists. |
michael@0 | 150 | return NS_ERROR_INVALID_ARG; |
michael@0 | 151 | } |
michael@0 | 152 | |
michael@0 | 153 | if (aOldFrame != mFrames.FirstChild()) |
michael@0 | 154 | return NS_ERROR_FAILURE; |
michael@0 | 155 | |
michael@0 | 156 | // Remove the frame and destroy it |
michael@0 | 157 | mFrames.DestroyFrame(aOldFrame); |
michael@0 | 158 | |
michael@0 | 159 | PresContext()->PresShell()-> |
michael@0 | 160 | FrameNeedsReflow(this, nsIPresShell::eTreeChange, |
michael@0 | 161 | NS_FRAME_HAS_DIRTY_CHILDREN); |
michael@0 | 162 | return NS_OK; |
michael@0 | 163 | } |
michael@0 | 164 | |
michael@0 | 165 | nsRect nsCanvasFrame::CanvasArea() const |
michael@0 | 166 | { |
michael@0 | 167 | // Not clear which overflow rect we want here, but it probably doesn't |
michael@0 | 168 | // matter. |
michael@0 | 169 | nsRect result(GetVisualOverflowRect()); |
michael@0 | 170 | |
michael@0 | 171 | nsIScrollableFrame *scrollableFrame = do_QueryFrame(GetParent()); |
michael@0 | 172 | if (scrollableFrame) { |
michael@0 | 173 | nsRect portRect = scrollableFrame->GetScrollPortRect(); |
michael@0 | 174 | result.UnionRect(result, nsRect(nsPoint(0, 0), portRect.Size())); |
michael@0 | 175 | } |
michael@0 | 176 | return result; |
michael@0 | 177 | } |
michael@0 | 178 | |
michael@0 | 179 | void |
michael@0 | 180 | nsDisplayCanvasBackgroundColor::Paint(nsDisplayListBuilder* aBuilder, |
michael@0 | 181 | nsRenderingContext* aCtx) |
michael@0 | 182 | { |
michael@0 | 183 | nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame); |
michael@0 | 184 | nsPoint offset = ToReferenceFrame(); |
michael@0 | 185 | nsRect bgClipRect = frame->CanvasArea() + offset; |
michael@0 | 186 | if (NS_GET_A(mColor) > 0) { |
michael@0 | 187 | aCtx->SetColor(mColor); |
michael@0 | 188 | aCtx->FillRect(bgClipRect); |
michael@0 | 189 | } |
michael@0 | 190 | } |
michael@0 | 191 | |
michael@0 | 192 | static void BlitSurface(gfxContext* aDest, const gfxRect& aRect, gfxASurface* aSource) |
michael@0 | 193 | { |
michael@0 | 194 | aDest->Translate(gfxPoint(aRect.x, aRect.y)); |
michael@0 | 195 | aDest->SetSource(aSource); |
michael@0 | 196 | aDest->NewPath(); |
michael@0 | 197 | aDest->Rectangle(gfxRect(0, 0, aRect.width, aRect.height)); |
michael@0 | 198 | aDest->Fill(); |
michael@0 | 199 | aDest->Translate(-gfxPoint(aRect.x, aRect.y)); |
michael@0 | 200 | } |
michael@0 | 201 | |
michael@0 | 202 | static void BlitSurface(DrawTarget* aDest, const gfxRect& aRect, DrawTarget* aSource) |
michael@0 | 203 | { |
michael@0 | 204 | RefPtr<SourceSurface> source = aSource->Snapshot(); |
michael@0 | 205 | aDest->DrawSurface(source, |
michael@0 | 206 | Rect(aRect.x, aRect.y, aRect.width, aRect.height), |
michael@0 | 207 | Rect(0, 0, aRect.width, aRect.height)); |
michael@0 | 208 | } |
michael@0 | 209 | |
michael@0 | 210 | void |
michael@0 | 211 | nsDisplayCanvasBackgroundImage::Paint(nsDisplayListBuilder* aBuilder, |
michael@0 | 212 | nsRenderingContext* aCtx) |
michael@0 | 213 | { |
michael@0 | 214 | nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame); |
michael@0 | 215 | nsPoint offset = ToReferenceFrame(); |
michael@0 | 216 | nsRect bgClipRect = frame->CanvasArea() + offset; |
michael@0 | 217 | |
michael@0 | 218 | nsRefPtr<nsRenderingContext> context; |
michael@0 | 219 | nsRefPtr<gfxContext> dest = aCtx->ThebesContext(); |
michael@0 | 220 | nsRefPtr<gfxASurface> surf; |
michael@0 | 221 | RefPtr<DrawTarget> dt; |
michael@0 | 222 | nsRefPtr<gfxContext> ctx; |
michael@0 | 223 | gfxRect destRect; |
michael@0 | 224 | #ifndef MOZ_GFX_OPTIMIZE_MOBILE |
michael@0 | 225 | if (IsSingleFixedPositionImage(aBuilder, bgClipRect, &destRect) && |
michael@0 | 226 | aBuilder->IsPaintingToWindow() && !aBuilder->IsCompositingCheap() && |
michael@0 | 227 | !dest->CurrentMatrix().HasNonIntegerTranslation()) { |
michael@0 | 228 | // Snap image rectangle to nearest pixel boundaries. This is the right way |
michael@0 | 229 | // to snap for this context, because we checked HasNonIntegerTranslation above. |
michael@0 | 230 | destRect.Round(); |
michael@0 | 231 | if (dest->IsCairo()) { |
michael@0 | 232 | surf = static_cast<gfxASurface*>(Frame()->Properties().Get(nsIFrame::CachedBackgroundImage())); |
michael@0 | 233 | nsRefPtr<gfxASurface> destSurf = dest->CurrentSurface(); |
michael@0 | 234 | if (surf && surf->GetType() == destSurf->GetType()) { |
michael@0 | 235 | BlitSurface(dest, destRect, surf); |
michael@0 | 236 | return; |
michael@0 | 237 | } |
michael@0 | 238 | surf = destSurf->CreateSimilarSurface( |
michael@0 | 239 | gfxContentType::COLOR_ALPHA, |
michael@0 | 240 | gfxIntSize(ceil(destRect.width), ceil(destRect.height))); |
michael@0 | 241 | } else { |
michael@0 | 242 | dt = static_cast<DrawTarget*>(Frame()->Properties().Get(nsIFrame::CachedBackgroundImageDT())); |
michael@0 | 243 | DrawTarget* destDT = dest->GetDrawTarget(); |
michael@0 | 244 | if (dt) { |
michael@0 | 245 | BlitSurface(destDT, destRect, dt); |
michael@0 | 246 | return; |
michael@0 | 247 | } |
michael@0 | 248 | dt = destDT->CreateSimilarDrawTarget(IntSize(ceil(destRect.width), ceil(destRect.height)), SurfaceFormat::B8G8R8A8); |
michael@0 | 249 | } |
michael@0 | 250 | if (surf || dt) { |
michael@0 | 251 | if (surf) { |
michael@0 | 252 | ctx = new gfxContext(surf); |
michael@0 | 253 | } else { |
michael@0 | 254 | ctx = new gfxContext(dt); |
michael@0 | 255 | } |
michael@0 | 256 | ctx->Translate(-gfxPoint(destRect.x, destRect.y)); |
michael@0 | 257 | context = new nsRenderingContext(); |
michael@0 | 258 | context->Init(aCtx->DeviceContext(), ctx); |
michael@0 | 259 | } |
michael@0 | 260 | } |
michael@0 | 261 | #endif |
michael@0 | 262 | |
michael@0 | 263 | PaintInternal(aBuilder, |
michael@0 | 264 | (surf || dt) ? context.get() : aCtx, |
michael@0 | 265 | (surf || dt) ? bgClipRect: mVisibleRect, |
michael@0 | 266 | &bgClipRect); |
michael@0 | 267 | |
michael@0 | 268 | if (surf) { |
michael@0 | 269 | BlitSurface(dest, destRect, surf); |
michael@0 | 270 | frame->Properties().Set(nsIFrame::CachedBackgroundImage(), |
michael@0 | 271 | surf.forget().take()); |
michael@0 | 272 | } |
michael@0 | 273 | if (dt) { |
michael@0 | 274 | BlitSurface(dest->GetDrawTarget(), destRect, dt); |
michael@0 | 275 | frame->Properties().Set(nsIFrame::CachedBackgroundImageDT(), dt.forget().drop()); |
michael@0 | 276 | } |
michael@0 | 277 | } |
michael@0 | 278 | |
michael@0 | 279 | void |
michael@0 | 280 | nsDisplayCanvasThemedBackground::Paint(nsDisplayListBuilder* aBuilder, |
michael@0 | 281 | nsRenderingContext* aCtx) |
michael@0 | 282 | { |
michael@0 | 283 | nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame); |
michael@0 | 284 | nsPoint offset = ToReferenceFrame(); |
michael@0 | 285 | nsRect bgClipRect = frame->CanvasArea() + offset; |
michael@0 | 286 | |
michael@0 | 287 | PaintInternal(aBuilder, aCtx, mVisibleRect, &bgClipRect); |
michael@0 | 288 | } |
michael@0 | 289 | |
michael@0 | 290 | /** |
michael@0 | 291 | * A display item to paint the focus ring for the document. |
michael@0 | 292 | * |
michael@0 | 293 | * The only reason this can't use nsDisplayGeneric is overriding GetBounds. |
michael@0 | 294 | */ |
michael@0 | 295 | class nsDisplayCanvasFocus : public nsDisplayItem { |
michael@0 | 296 | public: |
michael@0 | 297 | nsDisplayCanvasFocus(nsDisplayListBuilder* aBuilder, nsCanvasFrame *aFrame) |
michael@0 | 298 | : nsDisplayItem(aBuilder, aFrame) |
michael@0 | 299 | { |
michael@0 | 300 | MOZ_COUNT_CTOR(nsDisplayCanvasFocus); |
michael@0 | 301 | } |
michael@0 | 302 | virtual ~nsDisplayCanvasFocus() { |
michael@0 | 303 | MOZ_COUNT_DTOR(nsDisplayCanvasFocus); |
michael@0 | 304 | } |
michael@0 | 305 | |
michael@0 | 306 | virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, |
michael@0 | 307 | bool* aSnap) MOZ_OVERRIDE |
michael@0 | 308 | { |
michael@0 | 309 | *aSnap = false; |
michael@0 | 310 | // This is an overestimate, but that's not a problem. |
michael@0 | 311 | nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame); |
michael@0 | 312 | return frame->CanvasArea() + ToReferenceFrame(); |
michael@0 | 313 | } |
michael@0 | 314 | |
michael@0 | 315 | virtual void Paint(nsDisplayListBuilder* aBuilder, |
michael@0 | 316 | nsRenderingContext* aCtx) MOZ_OVERRIDE |
michael@0 | 317 | { |
michael@0 | 318 | nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame); |
michael@0 | 319 | frame->PaintFocus(*aCtx, ToReferenceFrame()); |
michael@0 | 320 | } |
michael@0 | 321 | |
michael@0 | 322 | NS_DISPLAY_DECL_NAME("CanvasFocus", TYPE_CANVAS_FOCUS) |
michael@0 | 323 | }; |
michael@0 | 324 | |
michael@0 | 325 | void |
michael@0 | 326 | nsCanvasFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, |
michael@0 | 327 | const nsRect& aDirtyRect, |
michael@0 | 328 | const nsDisplayListSet& aLists) |
michael@0 | 329 | { |
michael@0 | 330 | if (GetPrevInFlow()) { |
michael@0 | 331 | DisplayOverflowContainers(aBuilder, aDirtyRect, aLists); |
michael@0 | 332 | } |
michael@0 | 333 | |
michael@0 | 334 | // Force a background to be shown. We may have a background propagated to us, |
michael@0 | 335 | // in which case StyleBackground wouldn't have the right background |
michael@0 | 336 | // and the code in nsFrame::DisplayBorderBackgroundOutline might not give us |
michael@0 | 337 | // a background. |
michael@0 | 338 | // We don't have any border or outline, and our background draws over |
michael@0 | 339 | // the overflow area, so just add nsDisplayCanvasBackground instead of |
michael@0 | 340 | // calling DisplayBorderBackgroundOutline. |
michael@0 | 341 | if (IsVisibleForPainting(aBuilder)) { |
michael@0 | 342 | nsStyleContext* bgSC; |
michael@0 | 343 | const nsStyleBackground* bg = nullptr; |
michael@0 | 344 | bool isThemed = IsThemed(); |
michael@0 | 345 | if (!isThemed && nsCSSRendering::FindBackground(this, &bgSC)) { |
michael@0 | 346 | bg = bgSC->StyleBackground(); |
michael@0 | 347 | } |
michael@0 | 348 | aLists.BorderBackground()->AppendNewToTop( |
michael@0 | 349 | new (aBuilder) nsDisplayCanvasBackgroundColor(aBuilder, this)); |
michael@0 | 350 | |
michael@0 | 351 | if (isThemed) { |
michael@0 | 352 | aLists.BorderBackground()->AppendNewToTop( |
michael@0 | 353 | new (aBuilder) nsDisplayCanvasThemedBackground(aBuilder, this)); |
michael@0 | 354 | return; |
michael@0 | 355 | } |
michael@0 | 356 | |
michael@0 | 357 | if (!bg) { |
michael@0 | 358 | return; |
michael@0 | 359 | } |
michael@0 | 360 | |
michael@0 | 361 | // Create separate items for each background layer. |
michael@0 | 362 | NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, bg) { |
michael@0 | 363 | if (bg->mLayers[i].mImage.IsEmpty()) { |
michael@0 | 364 | continue; |
michael@0 | 365 | } |
michael@0 | 366 | aLists.BorderBackground()->AppendNewToTop( |
michael@0 | 367 | new (aBuilder) nsDisplayCanvasBackgroundImage(aBuilder, this, i, bg)); |
michael@0 | 368 | } |
michael@0 | 369 | } |
michael@0 | 370 | |
michael@0 | 371 | nsIFrame* kid; |
michael@0 | 372 | for (kid = GetFirstPrincipalChild(); kid; kid = kid->GetNextSibling()) { |
michael@0 | 373 | // Put our child into its own pseudo-stack. |
michael@0 | 374 | BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists); |
michael@0 | 375 | } |
michael@0 | 376 | |
michael@0 | 377 | #ifdef DEBUG_CANVAS_FOCUS |
michael@0 | 378 | nsCOMPtr<nsIContent> focusContent; |
michael@0 | 379 | aPresContext->EventStateManager()-> |
michael@0 | 380 | GetFocusedContent(getter_AddRefs(focusContent)); |
michael@0 | 381 | |
michael@0 | 382 | bool hasFocus = false; |
michael@0 | 383 | nsCOMPtr<nsISupports> container; |
michael@0 | 384 | aPresContext->GetContainer(getter_AddRefs(container)); |
michael@0 | 385 | nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(container)); |
michael@0 | 386 | if (docShell) { |
michael@0 | 387 | docShell->GetHasFocus(&hasFocus); |
michael@0 | 388 | printf("%p - nsCanvasFrame::Paint R:%d,%d,%d,%d DR: %d,%d,%d,%d\n", this, |
michael@0 | 389 | mRect.x, mRect.y, mRect.width, mRect.height, |
michael@0 | 390 | aDirtyRect.x, aDirtyRect.y, aDirtyRect.width, aDirtyRect.height); |
michael@0 | 391 | } |
michael@0 | 392 | printf("%p - Focus: %s c: %p DoPaint:%s\n", docShell.get(), hasFocus?"Y":"N", |
michael@0 | 393 | focusContent.get(), mDoPaintFocus?"Y":"N"); |
michael@0 | 394 | #endif |
michael@0 | 395 | |
michael@0 | 396 | if (!mDoPaintFocus) |
michael@0 | 397 | return; |
michael@0 | 398 | // Only paint the focus if we're visible |
michael@0 | 399 | if (!StyleVisibility()->IsVisible()) |
michael@0 | 400 | return; |
michael@0 | 401 | |
michael@0 | 402 | aLists.Outlines()->AppendNewToTop(new (aBuilder) |
michael@0 | 403 | nsDisplayCanvasFocus(aBuilder, this)); |
michael@0 | 404 | } |
michael@0 | 405 | |
michael@0 | 406 | void |
michael@0 | 407 | nsCanvasFrame::PaintFocus(nsRenderingContext& aRenderingContext, nsPoint aPt) |
michael@0 | 408 | { |
michael@0 | 409 | nsRect focusRect(aPt, GetSize()); |
michael@0 | 410 | |
michael@0 | 411 | nsIScrollableFrame *scrollableFrame = do_QueryFrame(GetParent()); |
michael@0 | 412 | if (scrollableFrame) { |
michael@0 | 413 | nsRect portRect = scrollableFrame->GetScrollPortRect(); |
michael@0 | 414 | focusRect.width = portRect.width; |
michael@0 | 415 | focusRect.height = portRect.height; |
michael@0 | 416 | focusRect.MoveBy(scrollableFrame->GetScrollPosition()); |
michael@0 | 417 | } |
michael@0 | 418 | |
michael@0 | 419 | // XXX use the root frame foreground color, but should we find BODY frame |
michael@0 | 420 | // for HTML documents? |
michael@0 | 421 | nsIFrame* root = mFrames.FirstChild(); |
michael@0 | 422 | const nsStyleColor* color = root ? root->StyleColor() : StyleColor(); |
michael@0 | 423 | if (!color) { |
michael@0 | 424 | NS_ERROR("current color cannot be found"); |
michael@0 | 425 | return; |
michael@0 | 426 | } |
michael@0 | 427 | |
michael@0 | 428 | nsCSSRendering::PaintFocus(PresContext(), aRenderingContext, |
michael@0 | 429 | focusRect, color->mColor); |
michael@0 | 430 | } |
michael@0 | 431 | |
michael@0 | 432 | /* virtual */ nscoord |
michael@0 | 433 | nsCanvasFrame::GetMinWidth(nsRenderingContext *aRenderingContext) |
michael@0 | 434 | { |
michael@0 | 435 | nscoord result; |
michael@0 | 436 | DISPLAY_MIN_WIDTH(this, result); |
michael@0 | 437 | if (mFrames.IsEmpty()) |
michael@0 | 438 | result = 0; |
michael@0 | 439 | else |
michael@0 | 440 | result = mFrames.FirstChild()->GetMinWidth(aRenderingContext); |
michael@0 | 441 | return result; |
michael@0 | 442 | } |
michael@0 | 443 | |
michael@0 | 444 | /* virtual */ nscoord |
michael@0 | 445 | nsCanvasFrame::GetPrefWidth(nsRenderingContext *aRenderingContext) |
michael@0 | 446 | { |
michael@0 | 447 | nscoord result; |
michael@0 | 448 | DISPLAY_PREF_WIDTH(this, result); |
michael@0 | 449 | if (mFrames.IsEmpty()) |
michael@0 | 450 | result = 0; |
michael@0 | 451 | else |
michael@0 | 452 | result = mFrames.FirstChild()->GetPrefWidth(aRenderingContext); |
michael@0 | 453 | return result; |
michael@0 | 454 | } |
michael@0 | 455 | |
michael@0 | 456 | nsresult |
michael@0 | 457 | nsCanvasFrame::Reflow(nsPresContext* aPresContext, |
michael@0 | 458 | nsHTMLReflowMetrics& aDesiredSize, |
michael@0 | 459 | const nsHTMLReflowState& aReflowState, |
michael@0 | 460 | nsReflowStatus& aStatus) |
michael@0 | 461 | { |
michael@0 | 462 | DO_GLOBAL_REFLOW_COUNT("nsCanvasFrame"); |
michael@0 | 463 | DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); |
michael@0 | 464 | NS_FRAME_TRACE_REFLOW_IN("nsCanvasFrame::Reflow"); |
michael@0 | 465 | |
michael@0 | 466 | // Initialize OUT parameter |
michael@0 | 467 | aStatus = NS_FRAME_COMPLETE; |
michael@0 | 468 | |
michael@0 | 469 | nsCanvasFrame* prevCanvasFrame = static_cast<nsCanvasFrame*> |
michael@0 | 470 | (GetPrevInFlow()); |
michael@0 | 471 | if (prevCanvasFrame) { |
michael@0 | 472 | AutoFrameListPtr overflow(aPresContext, |
michael@0 | 473 | prevCanvasFrame->StealOverflowFrames()); |
michael@0 | 474 | if (overflow) { |
michael@0 | 475 | NS_ASSERTION(overflow->OnlyChild(), |
michael@0 | 476 | "must have doc root as canvas frame's only child"); |
michael@0 | 477 | nsContainerFrame::ReparentFrameViewList(*overflow, prevCanvasFrame, this); |
michael@0 | 478 | // Prepend overflow to the our child list. There may already be |
michael@0 | 479 | // children placeholders for fixed-pos elements, which don't get |
michael@0 | 480 | // reflowed but must not be lost until the canvas frame is destroyed. |
michael@0 | 481 | mFrames.InsertFrames(this, nullptr, *overflow); |
michael@0 | 482 | } |
michael@0 | 483 | } |
michael@0 | 484 | |
michael@0 | 485 | // Set our size up front, since some parts of reflow depend on it |
michael@0 | 486 | // being already set. Note that the computed height may be |
michael@0 | 487 | // unconstrained; that's ok. Consumers should watch out for that. |
michael@0 | 488 | SetSize(nsSize(aReflowState.ComputedWidth(), aReflowState.ComputedHeight())); |
michael@0 | 489 | |
michael@0 | 490 | // Reflow our one and only normal child frame. It's either the root |
michael@0 | 491 | // element's frame or a placeholder for that frame, if the root element |
michael@0 | 492 | // is abs-pos or fixed-pos. We may have additional children which |
michael@0 | 493 | // are placeholders for continuations of fixed-pos content, but those |
michael@0 | 494 | // don't need to be reflowed. The normal child is always comes before |
michael@0 | 495 | // the fixed-pos placeholders, because we insert it at the start |
michael@0 | 496 | // of the child list, above. |
michael@0 | 497 | nsHTMLReflowMetrics kidDesiredSize(aReflowState); |
michael@0 | 498 | if (mFrames.IsEmpty()) { |
michael@0 | 499 | // We have no child frame, so return an empty size |
michael@0 | 500 | aDesiredSize.Width() = aDesiredSize.Height() = 0; |
michael@0 | 501 | } else { |
michael@0 | 502 | nsIFrame* kidFrame = mFrames.FirstChild(); |
michael@0 | 503 | bool kidDirty = (kidFrame->GetStateBits() & NS_FRAME_IS_DIRTY) != 0; |
michael@0 | 504 | |
michael@0 | 505 | nsHTMLReflowState kidReflowState(aPresContext, aReflowState, kidFrame, |
michael@0 | 506 | nsSize(aReflowState.AvailableWidth(), |
michael@0 | 507 | aReflowState.AvailableHeight())); |
michael@0 | 508 | |
michael@0 | 509 | if (aReflowState.mFlags.mVResize && |
michael@0 | 510 | (kidFrame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT)) { |
michael@0 | 511 | // Tell our kid it's being vertically resized too. Bit of a |
michael@0 | 512 | // hack for framesets. |
michael@0 | 513 | kidReflowState.mFlags.mVResize = true; |
michael@0 | 514 | } |
michael@0 | 515 | |
michael@0 | 516 | nsPoint kidPt(kidReflowState.ComputedPhysicalMargin().left, |
michael@0 | 517 | kidReflowState.ComputedPhysicalMargin().top); |
michael@0 | 518 | |
michael@0 | 519 | kidReflowState.ApplyRelativePositioning(&kidPt); |
michael@0 | 520 | |
michael@0 | 521 | // Reflow the frame |
michael@0 | 522 | ReflowChild(kidFrame, aPresContext, kidDesiredSize, kidReflowState, |
michael@0 | 523 | kidPt.x, kidPt.y, 0, aStatus); |
michael@0 | 524 | |
michael@0 | 525 | // Complete the reflow and position and size the child frame |
michael@0 | 526 | FinishReflowChild(kidFrame, aPresContext, kidDesiredSize, &kidReflowState, |
michael@0 | 527 | kidPt.x, kidPt.y, 0); |
michael@0 | 528 | |
michael@0 | 529 | if (!NS_FRAME_IS_FULLY_COMPLETE(aStatus)) { |
michael@0 | 530 | nsIFrame* nextFrame = kidFrame->GetNextInFlow(); |
michael@0 | 531 | NS_ASSERTION(nextFrame || aStatus & NS_FRAME_REFLOW_NEXTINFLOW, |
michael@0 | 532 | "If it's incomplete and has no nif yet, it must flag a nif reflow."); |
michael@0 | 533 | if (!nextFrame) { |
michael@0 | 534 | nextFrame = aPresContext->PresShell()->FrameConstructor()-> |
michael@0 | 535 | CreateContinuingFrame(aPresContext, kidFrame, this); |
michael@0 | 536 | SetOverflowFrames(nsFrameList(nextFrame, nextFrame)); |
michael@0 | 537 | // Root overflow containers will be normal children of |
michael@0 | 538 | // the canvas frame, but that's ok because there |
michael@0 | 539 | // aren't any other frames we need to isolate them from |
michael@0 | 540 | // during reflow. |
michael@0 | 541 | } |
michael@0 | 542 | if (NS_FRAME_OVERFLOW_IS_INCOMPLETE(aStatus)) { |
michael@0 | 543 | nextFrame->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER); |
michael@0 | 544 | } |
michael@0 | 545 | } |
michael@0 | 546 | |
michael@0 | 547 | // If the child frame was just inserted, then we're responsible for making sure |
michael@0 | 548 | // it repaints |
michael@0 | 549 | if (kidDirty) { |
michael@0 | 550 | // But we have a new child, which will affect our background, so |
michael@0 | 551 | // invalidate our whole rect. |
michael@0 | 552 | // Note: Even though we request to be sized to our child's size, our |
michael@0 | 553 | // scroll frame ensures that we are always the size of the viewport. |
michael@0 | 554 | // Also note: GetPosition() on a CanvasFrame is always going to return |
michael@0 | 555 | // (0, 0). We only want to invalidate GetRect() since Get*OverflowRect() |
michael@0 | 556 | // could also include overflow to our top and left (out of the viewport) |
michael@0 | 557 | // which doesn't need to be painted. |
michael@0 | 558 | nsIFrame* viewport = PresContext()->GetPresShell()->GetRootFrame(); |
michael@0 | 559 | viewport->InvalidateFrame(); |
michael@0 | 560 | } |
michael@0 | 561 | |
michael@0 | 562 | // Return our desired size. Normally it's what we're told, but |
michael@0 | 563 | // sometimes we can be given an unconstrained height (when a window |
michael@0 | 564 | // is sizing-to-content), and we should compute our desired height. |
michael@0 | 565 | aDesiredSize.Width() = aReflowState.ComputedWidth(); |
michael@0 | 566 | if (aReflowState.ComputedHeight() == NS_UNCONSTRAINEDSIZE) { |
michael@0 | 567 | aDesiredSize.Height() = kidFrame->GetRect().height + |
michael@0 | 568 | kidReflowState.ComputedPhysicalMargin().TopBottom(); |
michael@0 | 569 | } else { |
michael@0 | 570 | aDesiredSize.Height() = aReflowState.ComputedHeight(); |
michael@0 | 571 | } |
michael@0 | 572 | |
michael@0 | 573 | aDesiredSize.SetOverflowAreasToDesiredBounds(); |
michael@0 | 574 | aDesiredSize.mOverflowAreas.UnionWith( |
michael@0 | 575 | kidDesiredSize.mOverflowAreas + kidPt); |
michael@0 | 576 | } |
michael@0 | 577 | |
michael@0 | 578 | if (prevCanvasFrame) { |
michael@0 | 579 | ReflowOverflowContainerChildren(aPresContext, aReflowState, |
michael@0 | 580 | aDesiredSize.mOverflowAreas, 0, |
michael@0 | 581 | aStatus); |
michael@0 | 582 | } |
michael@0 | 583 | |
michael@0 | 584 | FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aReflowState, aStatus); |
michael@0 | 585 | |
michael@0 | 586 | NS_FRAME_TRACE_REFLOW_OUT("nsCanvasFrame::Reflow", aStatus); |
michael@0 | 587 | NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); |
michael@0 | 588 | return NS_OK; |
michael@0 | 589 | } |
michael@0 | 590 | |
michael@0 | 591 | nsIAtom* |
michael@0 | 592 | nsCanvasFrame::GetType() const |
michael@0 | 593 | { |
michael@0 | 594 | return nsGkAtoms::canvasFrame; |
michael@0 | 595 | } |
michael@0 | 596 | |
michael@0 | 597 | nsresult |
michael@0 | 598 | nsCanvasFrame::GetContentForEvent(WidgetEvent* aEvent, |
michael@0 | 599 | nsIContent** aContent) |
michael@0 | 600 | { |
michael@0 | 601 | NS_ENSURE_ARG_POINTER(aContent); |
michael@0 | 602 | nsresult rv = nsFrame::GetContentForEvent(aEvent, |
michael@0 | 603 | aContent); |
michael@0 | 604 | if (NS_FAILED(rv) || !*aContent) { |
michael@0 | 605 | nsIFrame* kid = mFrames.FirstChild(); |
michael@0 | 606 | if (kid) { |
michael@0 | 607 | rv = kid->GetContentForEvent(aEvent, |
michael@0 | 608 | aContent); |
michael@0 | 609 | } |
michael@0 | 610 | } |
michael@0 | 611 | |
michael@0 | 612 | return rv; |
michael@0 | 613 | } |
michael@0 | 614 | |
michael@0 | 615 | #ifdef DEBUG_FRAME_DUMP |
michael@0 | 616 | nsresult |
michael@0 | 617 | nsCanvasFrame::GetFrameName(nsAString& aResult) const |
michael@0 | 618 | { |
michael@0 | 619 | return MakeFrameName(NS_LITERAL_STRING("Canvas"), aResult); |
michael@0 | 620 | } |
michael@0 | 621 | #endif |