1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
24 * Alternatively, the contents of this file may be used under the terms of
25 * either of the GNU General Public License Version 2 or later (the "GPL"),
26 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
38 /* rendering object that goes directly inside the document's scrollbars */
40 #include "nsIServiceManager.h"
41 #include "nsHTMLParts.h"
42 #include "nsHTMLContainerFrame.h"
43 #include "nsCSSRendering.h"
44 #include "nsPresContext.h"
45 #include "nsStyleContext.h"
47 #include "nsIViewManager.h"
48 #include "nsIRenderingContext.h"
49 #include "nsGUIEvent.h"
50 #include "nsStyleConsts.h"
51 #include "nsGkAtoms.h"
52 #include "nsIEventStateManager.h"
53 #include "nsIDeviceContext.h"
54 #include "nsIPresShell.h"
55 #include "nsIScrollPositionListener.h"
56 #include "nsDisplayList.h"
59 #include "nsIDOMWindowInternal.h"
60 #include "nsIFocusController.h"
61 #include "nsIScrollableFrame.h"
62 #include "nsIScrollableView.h"
63 #include "nsIDocShell.h"
64 #include "nsICanvasFrame.h"
67 //#define DEBUG_CANVAS_FOCUS
75 * The root frame is the parent frame for the document element's frame.
76 * It only supports having a single child frame which must be an area
79 class CanvasFrame
: public nsHTMLContainerFrame
,
80 public nsIScrollPositionListener
,
81 public nsICanvasFrame
{
83 CanvasFrame(nsStyleContext
* aContext
)
84 : nsHTMLContainerFrame(aContext
), mDoPaintFocus(PR_FALSE
) {}
87 NS_IMETHOD
QueryInterface(const nsIID
& aIID
, void** aInstancePtr
);
89 NS_IMETHOD
Init(nsIContent
* aContent
,
91 nsIFrame
* aPrevInFlow
);
92 virtual void Destroy();
94 NS_IMETHOD
AppendFrames(nsIAtom
* aListName
,
95 nsIFrame
* aFrameList
);
96 NS_IMETHOD
InsertFrames(nsIAtom
* aListName
,
98 nsIFrame
* aFrameList
);
99 NS_IMETHOD
RemoveFrame(nsIAtom
* aListName
,
100 nsIFrame
* aOldFrame
);
102 virtual nscoord
GetMinWidth(nsIRenderingContext
*aRenderingContext
);
103 virtual nscoord
GetPrefWidth(nsIRenderingContext
*aRenderingContext
);
104 NS_IMETHOD
Reflow(nsPresContext
* aPresContext
,
105 nsHTMLReflowMetrics
& aDesiredSize
,
106 const nsHTMLReflowState
& aReflowState
,
107 nsReflowStatus
& aStatus
);
108 virtual PRBool
IsContainingBlock() const { return PR_TRUE
; }
110 NS_IMETHOD
BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
111 const nsRect
& aDirtyRect
,
112 const nsDisplayListSet
& aLists
);
114 void PaintFocus(nsIRenderingContext
& aRenderingContext
, nsPoint aPt
);
116 // nsIScrollPositionListener
117 NS_IMETHOD
ScrollPositionWillChange(nsIScrollableView
* aScrollable
, nscoord aX
, nscoord aY
);
118 NS_IMETHOD
ScrollPositionDidChange(nsIScrollableView
* aScrollable
, nscoord aX
, nscoord aY
);
121 NS_IMETHOD
SetHasFocus(PRBool aHasFocus
);
124 * Get the "type" of the frame
126 * @see nsGkAtoms::canvasFrame
128 virtual nsIAtom
* GetType() const;
131 NS_IMETHOD
GetFrameName(nsAString
& aResult
) const;
133 NS_IMETHOD
GetContentForEvent(nsPresContext
* aPresContext
,
135 nsIContent
** aContent
);
137 nsRect
CanvasArea() const;
140 virtual PRIntn
GetSkipSides() const;
143 PRPackedBool mDoPaintFocus
;
144 nsCOMPtr
<nsIViewManager
> mViewManager
;
147 NS_IMETHOD_(nsrefcnt
) AddRef() { return NS_OK
; }
148 NS_IMETHOD_(nsrefcnt
) Release() { return NS_OK
; }
152 //----------------------------------------------------------------------
155 NS_NewCanvasFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
)
157 return new (aPresShell
)CanvasFrame(aContext
);
160 //--------------------------------------------------------------
161 // Frames are not refcounted, no need to AddRef
163 CanvasFrame::QueryInterface(const nsIID
& aIID
, void** aInstancePtr
)
165 NS_PRECONDITION(aInstancePtr
, "null out param");
167 if (aIID
.Equals(NS_GET_IID(nsIScrollPositionListener
))) {
168 *aInstancePtr
= static_cast<nsIScrollPositionListener
*>(this);
171 if (aIID
.Equals(NS_GET_IID(nsICanvasFrame
))) {
172 *aInstancePtr
= static_cast<nsICanvasFrame
*>(this);
176 return nsHTMLContainerFrame::QueryInterface(aIID
, aInstancePtr
);
180 CanvasFrame::Init(nsIContent
* aContent
,
182 nsIFrame
* aPrevInFlow
)
184 nsresult rv
= nsHTMLContainerFrame::Init(aContent
, aParent
, aPrevInFlow
);
186 mViewManager
= PresContext()->GetViewManager();
188 nsIScrollableView
* scrollingView
= nsnull
;
189 mViewManager
->GetRootScrollableView(&scrollingView
);
191 scrollingView
->AddScrollPositionListener(this);
198 CanvasFrame::Destroy()
200 nsIScrollableView
* scrollingView
= nsnull
;
201 mViewManager
->GetRootScrollableView(&scrollingView
);
203 scrollingView
->RemoveScrollPositionListener(this);
206 nsHTMLContainerFrame::Destroy();
210 CanvasFrame::ScrollPositionWillChange(nsIScrollableView
* aScrollable
, nscoord aX
, nscoord aY
)
212 #ifdef DEBUG_CANVAS_FOCUS
214 PRBool hasFocus
= PR_FALSE
;
215 nsCOMPtr
<nsIViewObserver
> observer
;
216 mViewManager
->GetViewObserver(*getter_AddRefs(observer
));
217 nsCOMPtr
<nsIPresShell
> shell
= do_QueryInterface(observer
);
218 nsCOMPtr
<nsPresContext
> context
;
219 shell
->GetPresContext(getter_AddRefs(context
));
220 nsCOMPtr
<nsISupports
> container
;
221 context
->GetContainer(getter_AddRefs(container
));
222 nsCOMPtr
<nsIDocShell
> docShell(do_QueryInterface(container
));
224 docShell
->GetHasFocus(&hasFocus
);
226 printf("SPWC: %p HF: %s mDoPaintFocus: %s\n", docShell
.get(), hasFocus
?"Y":"N", mDoPaintFocus
?"Y":"N");
231 mDoPaintFocus
= PR_FALSE
;
232 mViewManager
->UpdateAllViews(NS_VMREFRESH_NO_SYNC
);
238 CanvasFrame::ScrollPositionDidChange(nsIScrollableView
* aScrollable
, nscoord aX
, nscoord aY
)
244 CanvasFrame::SetHasFocus(PRBool aHasFocus
)
246 if (mDoPaintFocus
!= aHasFocus
) {
247 mDoPaintFocus
= aHasFocus
;
248 nsIViewManager
* vm
= PresContext()->PresShell()->GetViewManager();
250 vm
->UpdateAllViews(NS_VMREFRESH_NO_SYNC
);
257 CanvasFrame::AppendFrames(nsIAtom
* aListName
,
258 nsIFrame
* aFrameList
)
262 NS_ASSERTION(!aListName
, "unexpected child list name");
263 NS_PRECONDITION(mFrames
.IsEmpty(), "already have a child frame");
265 // We only support unnamed principal child list
266 rv
= NS_ERROR_INVALID_ARG
;
268 } else if (!mFrames
.IsEmpty()) {
269 // We only allow a single child frame
270 rv
= NS_ERROR_FAILURE
;
273 // Insert the new frames
275 nsFrame::VerifyDirtyBitSet(aFrameList
);
277 mFrames
.AppendFrame(nsnull
, aFrameList
);
279 rv
= PresContext()->PresShell()->
280 FrameNeedsReflow(this, nsIPresShell::eTreeChange
,
281 NS_FRAME_HAS_DIRTY_CHILDREN
);
288 CanvasFrame::InsertFrames(nsIAtom
* aListName
,
289 nsIFrame
* aPrevFrame
,
290 nsIFrame
* aFrameList
)
294 // Because we only support a single child frame inserting is the same
296 NS_PRECONDITION(!aPrevFrame
, "unexpected previous sibling frame");
298 rv
= NS_ERROR_UNEXPECTED
;
300 rv
= AppendFrames(aListName
, aFrameList
);
307 CanvasFrame::RemoveFrame(nsIAtom
* aListName
,
312 NS_ASSERTION(!aListName
, "unexpected child list name");
314 // We only support the unnamed principal child list
315 rv
= NS_ERROR_INVALID_ARG
;
317 } else if (aOldFrame
== mFrames
.FirstChild()) {
318 // It's our one and only child frame
319 // Damage the area occupied by the deleted frame
320 // The child of the canvas probably can't have an outline, but why bother
321 // thinking about that?
322 Invalidate(aOldFrame
->GetOverflowRect() + aOldFrame
->GetPosition(), PR_FALSE
);
324 // Remove the frame and destroy it
325 mFrames
.DestroyFrame(aOldFrame
);
327 rv
= PresContext()->PresShell()->
328 FrameNeedsReflow(this, nsIPresShell::eTreeChange
,
329 NS_FRAME_HAS_DIRTY_CHILDREN
);
331 rv
= NS_ERROR_FAILURE
;
337 nsRect
CanvasFrame::CanvasArea() const
339 nsRect
result(GetOverflowRect());
341 nsIScrollableFrame
*scrollableFrame
;
342 CallQueryInterface(GetParent(), &scrollableFrame
);
343 if (scrollableFrame
) {
344 nsIScrollableView
* scrollableView
= scrollableFrame
->GetScrollableView();
345 nsRect vcr
= scrollableView
->View()->GetBounds();
346 result
.UnionRect(result
, nsRect(nsPoint(0, 0), vcr
.Size()));
352 * Override nsDisplayBackground methods so that we pass aBGClipRect to
353 * PaintBackground, covering the whole overflow area.
355 class nsDisplayCanvasBackground
: public nsDisplayBackground
{
357 nsDisplayCanvasBackground(nsIFrame
*aFrame
)
358 : nsDisplayBackground(aFrame
)
362 virtual nsRect
GetBounds(nsDisplayListBuilder
* aBuilder
)
364 CanvasFrame
* frame
= static_cast<CanvasFrame
*>(mFrame
);
365 return frame
->CanvasArea() + aBuilder
->ToReferenceFrame(mFrame
);
368 virtual void Paint(nsDisplayListBuilder
* aBuilder
,
369 nsIRenderingContext
* aCtx
, const nsRect
& aDirtyRect
)
371 CanvasFrame
* frame
= static_cast<CanvasFrame
*>(mFrame
);
372 nsPoint offset
= aBuilder
->ToReferenceFrame(mFrame
);
373 nsRect bgClipRect
= frame
->CanvasArea() + offset
;
374 nsCSSRendering::PaintBackground(mFrame
->PresContext(), *aCtx
, mFrame
,
376 nsRect(offset
, mFrame
->GetSize()),
377 *mFrame
->GetStyleBorder(),
378 *mFrame
->GetStylePadding(),
379 mFrame
->HonorPrintBackgroundSettings(),
383 NS_DISPLAY_DECL_NAME("CanvasBackground")
387 * A display item to paint the focus ring for the document.
389 * The only reason this can't use nsDisplayGeneric is overriding GetBounds.
391 class nsDisplayCanvasFocus
: public nsDisplayItem
{
393 nsDisplayCanvasFocus(CanvasFrame
*aFrame
)
394 : nsDisplayItem(aFrame
)
398 virtual nsRect
GetBounds(nsDisplayListBuilder
* aBuilder
)
400 // This is an overestimate, but that's not a problem.
401 CanvasFrame
* frame
= static_cast<CanvasFrame
*>(mFrame
);
402 return frame
->CanvasArea() + aBuilder
->ToReferenceFrame(mFrame
);
405 virtual void Paint(nsDisplayListBuilder
* aBuilder
,
406 nsIRenderingContext
* aCtx
, const nsRect
& aDirtyRect
)
408 CanvasFrame
* frame
= static_cast<CanvasFrame
*>(mFrame
);
409 frame
->PaintFocus(*aCtx
, aBuilder
->ToReferenceFrame(mFrame
));
412 NS_DISPLAY_DECL_NAME("CanvasFocus")
416 CanvasFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
417 const nsRect
& aDirtyRect
,
418 const nsDisplayListSet
& aLists
)
421 // Force a background to be shown. We may have a background propagated to us,
422 // in which case GetStyleBackground wouldn't have the right background
423 // and the code in nsFrame::DisplayBorderBackgroundOutline might not give us
425 // We don't have any border or outline, and our background draws over
426 // the overflow area, so just add nsDisplayCanvasBackground instead of
427 // calling DisplayBorderBackgroundOutline.
428 if (IsVisibleForPainting(aBuilder
)) {
429 rv
= aLists
.BorderBackground()->AppendNewToTop(new (aBuilder
)
430 nsDisplayCanvasBackground(this));
431 NS_ENSURE_SUCCESS(rv
, rv
);
434 nsIFrame
* kid
= GetFirstChild(nsnull
);
436 // Put our child into its own pseudo-stack.
437 rv
= BuildDisplayListForChild(aBuilder
, kid
, aDirtyRect
, aLists
,
438 DISPLAY_CHILD_FORCE_PSEUDO_STACKING_CONTEXT
);
439 NS_ENSURE_SUCCESS(rv
, rv
);
442 #ifdef DEBUG_CANVAS_FOCUS
443 nsCOMPtr
<nsIContent
> focusContent
;
444 aPresContext
->EventStateManager()->
445 GetFocusedContent(getter_AddRefs(focusContent
));
447 PRBool hasFocus
= PR_FALSE
;
448 nsCOMPtr
<nsISupports
> container
;
449 aPresContext
->GetContainer(getter_AddRefs(container
));
450 nsCOMPtr
<nsIDocShell
> docShell(do_QueryInterface(container
));
452 docShell
->GetHasFocus(&hasFocus
);
453 printf("%p - CanvasFrame::Paint R:%d,%d,%d,%d DR: %d,%d,%d,%d\n", this,
454 mRect
.x
, mRect
.y
, mRect
.width
, mRect
.height
,
455 aDirtyRect
.x
, aDirtyRect
.y
, aDirtyRect
.width
, aDirtyRect
.height
);
457 printf("%p - Focus: %s c: %p DoPaint:%s\n", docShell
.get(), hasFocus
?"Y":"N",
458 focusContent
.get(), mDoPaintFocus
?"Y":"N");
463 // Only paint the focus if we're visible
464 if (!GetStyleVisibility()->IsVisible())
467 return aLists
.Outlines()->AppendNewToTop(new (aBuilder
)
468 nsDisplayCanvasFocus(this));
472 CanvasFrame::PaintFocus(nsIRenderingContext
& aRenderingContext
, nsPoint aPt
)
474 nsRect
focusRect(aPt
, GetSize());
476 nsIScrollableFrame
*scrollableFrame
;
477 CallQueryInterface(GetParent(), &scrollableFrame
);
479 if (scrollableFrame
) {
480 nsIScrollableView
* scrollableView
= scrollableFrame
->GetScrollableView();
481 nsRect vcr
= scrollableView
->View()->GetBounds();
482 focusRect
.width
= vcr
.width
;
483 focusRect
.height
= vcr
.height
;
485 scrollableView
->GetScrollPosition(x
, y
);
490 nsStyleOutline
outlineStyle(PresContext());
491 outlineStyle
.SetOutlineStyle(NS_STYLE_BORDER_STYLE_DOTTED
);
492 outlineStyle
.SetOutlineInitialColor();
494 // XXX use the root frame foreground color, but should we find BODY frame
495 // for HTML documents?
496 nsIFrame
* root
= mFrames
.FirstChild();
497 const nsStyleColor
* color
=
498 root
? root
->GetStyleContext()->GetStyleColor() :
499 mStyleContext
->GetStyleColor();
501 NS_ERROR("current color cannot be found");
505 // XXX the CSS border for links is specified as 2px, but it
506 // is only drawn as 1px. Match this here.
507 nscoord onePixel
= nsPresContext::CSSPixelsToAppUnits(1);
509 nsRect
borderInside(focusRect
.x
+ onePixel
,
510 focusRect
.y
+ onePixel
,
511 focusRect
.width
- 2 * onePixel
,
512 focusRect
.height
- 2 * onePixel
);
514 nsCSSRendering::DrawDashedSides(0, aRenderingContext
,
516 nsnull
, &outlineStyle
,
522 /* virtual */ nscoord
523 CanvasFrame::GetMinWidth(nsIRenderingContext
*aRenderingContext
)
526 DISPLAY_MIN_WIDTH(this, result
);
527 if (mFrames
.IsEmpty())
530 result
= mFrames
.FirstChild()->GetMinWidth(aRenderingContext
);
534 /* virtual */ nscoord
535 CanvasFrame::GetPrefWidth(nsIRenderingContext
*aRenderingContext
)
538 DISPLAY_PREF_WIDTH(this, result
);
539 if (mFrames
.IsEmpty())
542 result
= mFrames
.FirstChild()->GetPrefWidth(aRenderingContext
);
547 CanvasFrame::Reflow(nsPresContext
* aPresContext
,
548 nsHTMLReflowMetrics
& aDesiredSize
,
549 const nsHTMLReflowState
& aReflowState
,
550 nsReflowStatus
& aStatus
)
552 DO_GLOBAL_REFLOW_COUNT("CanvasFrame");
553 DISPLAY_REFLOW(aPresContext
, this, aReflowState
, aDesiredSize
, aStatus
);
554 NS_FRAME_TRACE_REFLOW_IN("CanvasFrame::Reflow");
556 // Initialize OUT parameter
557 aStatus
= NS_FRAME_COMPLETE
;
559 // Reflow our one and only child frame
560 nsHTMLReflowMetrics kidDesiredSize
;
561 if (mFrames
.IsEmpty()) {
562 // We have no child frame, so return an empty size
563 aDesiredSize
.width
= aDesiredSize
.height
= 0;
565 nsIFrame
* kidFrame
= mFrames
.FirstChild();
566 PRBool kidDirty
= (kidFrame
->GetStateBits() & NS_FRAME_IS_DIRTY
) != 0;
568 // We must specify an unconstrained available height, because constrained
569 // is only for when we're paginated...
570 nsHTMLReflowState
kidReflowState(aPresContext
, aReflowState
, kidFrame
,
571 nsSize(aReflowState
.availableWidth
,
572 NS_UNCONSTRAINEDSIZE
));
574 if (aReflowState
.mFlags
.mVResize
&&
575 (kidFrame
->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT
)) {
576 // Tell our kid it's being vertically resized too. Bit of a
577 // hack for framesets.
578 kidReflowState
.mFlags
.mVResize
= PR_TRUE
;
582 ReflowChild(kidFrame
, aPresContext
, kidDesiredSize
, kidReflowState
,
583 kidReflowState
.mComputedMargin
.left
, kidReflowState
.mComputedMargin
.top
,
586 // Complete the reflow and position and size the child frame
587 FinishReflowChild(kidFrame
, aPresContext
, &kidReflowState
, kidDesiredSize
,
588 kidReflowState
.mComputedMargin
.left
,
589 kidReflowState
.mComputedMargin
.top
, 0);
591 // If the child frame was just inserted, then we're responsible for making sure
594 // But we have a new child, which will affect our background, so
595 // invalidate our whole rect.
596 // Note: Even though we request to be sized to our child's size, our
597 // scroll frame ensures that we are always the size of the viewport.
598 // Also note: GetPosition() on a CanvasFrame is always going to return
599 // (0, 0). We only want to invalidate GetRect() since GetOverflowRect()
600 // could also include overflow to our top and left (out of the viewport)
601 // which doesn't need to be painted.
602 nsIFrame
* viewport
= PresContext()->GetPresShell()->GetRootFrame();
603 viewport
->Invalidate(nsRect(nsPoint(0, 0), viewport
->GetSize()));
606 // Return our desired size (which doesn't matter)
607 aDesiredSize
.width
= aReflowState
.availableWidth
;
608 aDesiredSize
.height
= kidDesiredSize
.height
+
609 kidReflowState
.mComputedMargin
.TopBottom();
611 aDesiredSize
.mOverflowArea
.UnionRect(
612 nsRect(0, 0, aDesiredSize
.width
, aDesiredSize
.height
),
613 kidDesiredSize
.mOverflowArea
+
614 nsPoint(kidReflowState
.mComputedMargin
.left
,
615 kidReflowState
.mComputedMargin
.top
));
616 FinishAndStoreOverflow(&aDesiredSize
);
619 NS_FRAME_TRACE_REFLOW_OUT("CanvasFrame::Reflow", aStatus
);
620 NS_FRAME_SET_TRUNCATION(aStatus
, aReflowState
, aDesiredSize
);
625 CanvasFrame::GetSkipSides() const
631 CanvasFrame::GetType() const
633 return nsGkAtoms::canvasFrame
;
637 CanvasFrame::GetContentForEvent(nsPresContext
* aPresContext
,
639 nsIContent
** aContent
)
641 NS_ENSURE_ARG_POINTER(aContent
);
642 nsresult rv
= nsFrame::GetContentForEvent(aPresContext
,
645 if (NS_FAILED(rv
) || !*aContent
) {
646 nsIFrame
* kid
= mFrames
.FirstChild();
648 rv
= kid
->GetContentForEvent(aPresContext
,
659 CanvasFrame::GetFrameName(nsAString
& aResult
) const
661 return MakeFrameName(NS_LITERAL_STRING("Canvas"), aResult
);