forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			783 lines
		
	
	
	
		
			29 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			783 lines
		
	
	
	
		
			29 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* ***** BEGIN LICENSE BLOCK *****
 | |
|  * Version: NPL 1.1/GPL 2.0/LGPL 2.1
 | |
|  *
 | |
|  * The contents of this file are subject to the Netscape Public License
 | |
|  * Version 1.1 (the "License"); you may not use this file except in
 | |
|  * compliance with the License. You may obtain a copy of the License at
 | |
|  * http://www.mozilla.org/NPL/
 | |
|  *
 | |
|  * Software distributed under the License is distributed on an "AS IS" basis,
 | |
|  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 | |
|  * for the specific language governing rights and limitations under the
 | |
|  * License.
 | |
|  *
 | |
|  * The Original Code is mozilla.org code.
 | |
|  *
 | |
|  * The Initial Developer of the Original Code is 
 | |
|  * Netscape Communications Corporation.
 | |
|  * Portions created by the Initial Developer are Copyright (C) 1998
 | |
|  * the Initial Developer. All Rights Reserved.
 | |
|  *
 | |
|  * Contributor(s):
 | |
|  *
 | |
|  * Alternatively, the contents of this file may be used under the terms of
 | |
|  * either the GNU General Public License Version 2 or later (the "GPL"), or 
 | |
|  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 | |
|  * in which case the provisions of the GPL or the LGPL are applicable instead
 | |
|  * of those above. If you wish to allow use of your version of this file only
 | |
|  * under the terms of either the GPL or the LGPL, and not to allow others to
 | |
|  * use your version of this file under the terms of the NPL, indicate your
 | |
|  * decision by deleting the provisions above and replace them with the notice
 | |
|  * and other provisions required by the GPL or the LGPL. If you do not delete
 | |
|  * the provisions above, a recipient may use your version of this file under
 | |
|  * the terms of any one of the NPL, the GPL or the LGPL.
 | |
|  *
 | |
|  * ***** END LICENSE BLOCK ***** */
 | |
| #include "nsHTMLContainerFrame.h"
 | |
| #include "nsIRenderingContext.h"
 | |
| #include "nsIPresContext.h"
 | |
| #include "nsIPresShell.h"
 | |
| #include "nsIStyleContext.h"
 | |
| #include "nsStyleConsts.h"
 | |
| #include "nsCSSRendering.h"
 | |
| #include "nsIContent.h"
 | |
| #include "nsLayoutAtoms.h"
 | |
| #include "nsIWidget.h"
 | |
| #include "nsILinkHandler.h"
 | |
| #include "nsHTMLValue.h"
 | |
| #include "nsGUIEvent.h"
 | |
| #include "nsIDocument.h"
 | |
| #include "nsIURL.h"
 | |
| #include "nsPlaceholderFrame.h"
 | |
| #include "nsIHTMLContent.h"
 | |
| #include "nsHTMLParts.h"
 | |
| #include "nsIView.h"
 | |
| #include "nsIViewManager.h"
 | |
| #include "nsViewsCID.h"
 | |
| #include "nsIDOMEvent.h"
 | |
| #include "nsIScrollableView.h"
 | |
| #include "nsWidgetsCID.h"
 | |
| #include "nsIStyleSet.h"
 | |
| #include "nsCOMPtr.h"
 | |
| #include "nsReflowPath.h"
 | |
| 
 | |
| static NS_DEFINE_CID(kCChildCID, NS_CHILD_CID);
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsHTMLContainerFrame::Paint(nsIPresContext*      aPresContext,
 | |
|                             nsIRenderingContext& aRenderingContext,
 | |
|                             const nsRect&        aDirtyRect,
 | |
|                             nsFramePaintLayer    aWhichLayer,
 | |
|                             PRUint32             aFlags)
 | |
| {
 | |
|   if (NS_FRAME_IS_UNFLOWABLE & mState) {
 | |
|     return NS_OK;
 | |
|   }
 | |
|   nsCOMPtr<nsIAtom> frameType;
 | |
|   GetFrameType(getter_AddRefs(frameType));
 | |
| 
 | |
|   // Paint inline element backgrounds in the foreground layer, but
 | |
|   // others in the background (bug 36710).
 | |
|   if (((frameType.get() == nsLayoutAtoms::inlineFrame)?NS_FRAME_PAINT_LAYER_FOREGROUND:NS_FRAME_PAINT_LAYER_BACKGROUND) == aWhichLayer) {
 | |
|     const nsStyleVisibility* vis = 
 | |
|       (const nsStyleVisibility*)((nsIStyleContext*)mStyleContext)->GetStyleData(eStyleStruct_Visibility);
 | |
|     if (vis->IsVisible() && mRect.width && mRect.height) {
 | |
|       // Paint our background and border
 | |
|       PRIntn skipSides = GetSkipSides();
 | |
|       const nsStyleBorder* border = (const nsStyleBorder*)
 | |
|         mStyleContext->GetStyleData(eStyleStruct_Border);
 | |
|       const nsStyleOutline* outline = (const nsStyleOutline*)
 | |
|         mStyleContext->GetStyleData(eStyleStruct_Outline);
 | |
| 
 | |
|       nsRect  rect(0, 0, mRect.width, mRect.height);
 | |
|       nsCSSRendering::PaintBackground(aPresContext, aRenderingContext, this,
 | |
|                                       aDirtyRect, rect, *border, 0, 0);
 | |
|       nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, this,
 | |
|                                   aDirtyRect, rect, *border, mStyleContext,
 | |
|                                   skipSides);
 | |
|       nsCSSRendering::PaintOutline(aPresContext, aRenderingContext, this,
 | |
|                                    aDirtyRect, rect, *border, *outline,
 | |
|                                    mStyleContext, 0);
 | |
|       
 | |
|       // The sole purpose of this is to trigger display
 | |
|       //  of the selection window for Named Anchors,
 | |
|       //  which don't have any children and normally don't
 | |
|       //  have any size, but in Editor we use CSS to display
 | |
|       //  an image to represent this "hidden" element.
 | |
|       if (!mFrames.FirstChild())
 | |
|       {
 | |
|         nsFrame::Paint(aPresContext, aRenderingContext, aDirtyRect, aWhichLayer, aFlags);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (frameType.get() == nsLayoutAtoms::canvasFrame) {
 | |
|     // We are wrapping the root frame of a document. We
 | |
|     // need to check the pres shell to find out if painting is locked
 | |
|     // down (because we're still in the early stages of document
 | |
|     // and frame construction.  If painting is locked down, then we
 | |
|     // do not paint our children.  
 | |
|     PRBool paintingSuppressed = PR_FALSE;
 | |
|     nsCOMPtr<nsIPresShell> shell;
 | |
|     aPresContext->GetShell(getter_AddRefs(shell));
 | |
|     shell->IsPaintingSuppressed(&paintingSuppressed);
 | |
|     if (paintingSuppressed)
 | |
|       return NS_OK;
 | |
|   }
 | |
| 
 | |
|   // Now paint the kids. Note that child elements have the opportunity to
 | |
|   // override the visibility property and display even if their parent is
 | |
|   // hidden
 | |
| 
 | |
|   PaintChildren(aPresContext, aRenderingContext, aDirtyRect, aWhichLayer, aFlags);
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Create a next-in-flow for aFrame. Will return the newly created
 | |
|  * frame in aNextInFlowResult <b>if and only if</b> a new frame is
 | |
|  * created; otherwise nsnull is returned in aNextInFlowResult.
 | |
|  */
 | |
| nsresult
 | |
| nsHTMLContainerFrame::CreateNextInFlow(nsIPresContext* aPresContext,
 | |
|                                        nsIFrame* aOuterFrame,
 | |
|                                        nsIFrame* aFrame,
 | |
|                                        nsIFrame*& aNextInFlowResult)
 | |
| {
 | |
|   aNextInFlowResult = nsnull;
 | |
| 
 | |
|   nsIFrame* nextInFlow;
 | |
|   aFrame->GetNextInFlow(&nextInFlow);
 | |
|   if (nsnull == nextInFlow) {
 | |
|     // Create a continuation frame for the child frame and insert it
 | |
|     // into our lines child list.
 | |
|     nsIFrame* nextFrame;
 | |
|     aFrame->GetNextSibling(&nextFrame);
 | |
| 
 | |
|     nsIPresShell* presShell;
 | |
|     nsIStyleSet*  styleSet;
 | |
|     aPresContext->GetShell(&presShell);
 | |
|     presShell->GetStyleSet(&styleSet);
 | |
|     NS_RELEASE(presShell);
 | |
|     styleSet->CreateContinuingFrame(aPresContext, aFrame, aOuterFrame, &nextInFlow);
 | |
|     NS_RELEASE(styleSet);
 | |
| 
 | |
|     if (nsnull == nextInFlow) {
 | |
|       return NS_ERROR_OUT_OF_MEMORY;
 | |
|     }
 | |
|     aFrame->SetNextSibling(nextInFlow);
 | |
|     nextInFlow->SetNextSibling(nextFrame);
 | |
| 
 | |
|     NS_FRAME_LOG(NS_FRAME_TRACE_NEW_FRAMES,
 | |
|        ("nsHTMLContainerFrame::MaybeCreateNextInFlow: frame=%p nextInFlow=%p",
 | |
|         aFrame, nextInFlow));
 | |
| 
 | |
|     aNextInFlowResult = nextInFlow;
 | |
|   }
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| static nsresult
 | |
| ReparentFrameViewTo(nsIPresContext* aPresContext,
 | |
|                     nsIFrame*       aFrame,
 | |
|                     nsIViewManager* aViewManager,
 | |
|                     nsIView*        aNewParentView,
 | |
|                     nsIView*        aOldParentView)
 | |
| {
 | |
|   nsIView*  view;
 | |
| 
 | |
|   // XXX What to do about placeholder views for "position: fixed" elements?
 | |
|   // They should be reparented too.
 | |
| 
 | |
|   // Does aFrame have a view?
 | |
|   aFrame->GetView(aPresContext, &view);
 | |
|   if (view) {
 | |
|     // Verify that the current parent view is what we think it is
 | |
|     //nsIView*  parentView;
 | |
|     //NS_ASSERTION(parentView == aOldParentView, "unexpected parent view");
 | |
| 
 | |
|     aViewManager->RemoveChild(view);
 | |
|     
 | |
|     // The view will remember the Z-order and other attributes that have been set on it.
 | |
|     // XXX Pretend this view is last of the parent's views in document order
 | |
|     aViewManager->InsertChild(aNewParentView, view, nsnull, PR_TRUE);
 | |
|   } else {
 | |
|     // Iterate the child frames, and check each child frame to see if it has
 | |
|     // a view
 | |
|     nsIFrame* childFrame;
 | |
|     aFrame->FirstChild(aPresContext, nsnull, &childFrame);
 | |
|     while (childFrame) {
 | |
|       ReparentFrameViewTo(aPresContext, childFrame, aViewManager, aNewParentView, aOldParentView);
 | |
|       childFrame->GetNextSibling(&childFrame);
 | |
|     }
 | |
| 
 | |
|     // Also check the overflow-list
 | |
|     aFrame->FirstChild(aPresContext, nsLayoutAtoms::overflowList, &childFrame);
 | |
|     while (childFrame) {
 | |
|       ReparentFrameViewTo(aPresContext, childFrame, aViewManager, aNewParentView, aOldParentView);
 | |
|       childFrame->GetNextSibling(&childFrame);
 | |
|     }
 | |
| 
 | |
|       // Also check the floater-list
 | |
|     aFrame->FirstChild(aPresContext, nsLayoutAtoms::floaterList, &childFrame);
 | |
|     while (childFrame) {
 | |
|       ReparentFrameViewTo(aPresContext, childFrame, aViewManager, aNewParentView, aOldParentView);
 | |
|       childFrame->GetNextSibling(&childFrame);
 | |
|     }
 | |
| 
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| // Helper function that returns the nearest view to this frame. Checks
 | |
| // this frame, its parent frame, its parent frame, ...
 | |
| static nsIView*
 | |
| GetClosestViewFor(nsIPresContext* aPresContext, nsIFrame* aFrame)
 | |
| {
 | |
|   NS_PRECONDITION(aFrame, "null frame pointer");
 | |
|   nsIView*  view;
 | |
| 
 | |
|   do {
 | |
|     aFrame->GetView(aPresContext, &view);
 | |
|     if (view) {
 | |
|       break;
 | |
|     }
 | |
|     aFrame->GetParent(&aFrame);
 | |
|   } while (aFrame);
 | |
| 
 | |
|   NS_POSTCONDITION(view, "no containing view");
 | |
|   return view;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| nsHTMLContainerFrame::ReparentFrameView(nsIPresContext* aPresContext,
 | |
|                                         nsIFrame*       aChildFrame,
 | |
|                                         nsIFrame*       aOldParentFrame,
 | |
|                                         nsIFrame*       aNewParentFrame)
 | |
| {
 | |
|   NS_PRECONDITION(aChildFrame, "null child frame pointer");
 | |
|   NS_PRECONDITION(aOldParentFrame, "null old parent frame pointer");
 | |
|   NS_PRECONDITION(aNewParentFrame, "null new parent frame pointer");
 | |
|   NS_PRECONDITION(aOldParentFrame != aNewParentFrame, "same old and new parent frame");
 | |
| 
 | |
|   nsIView*  childView;
 | |
|   nsIView*  oldParentView;
 | |
|   nsIView*  newParentView;
 | |
|   
 | |
|   // This code is called often and we need it to be as fast as possible, so
 | |
|   // see if we can trivially detect that no work needs to be done
 | |
|   aChildFrame->GetView(aPresContext, &childView);
 | |
|   if (!childView) {
 | |
|     // Child frame doesn't have a view. See if it has any child frames
 | |
|     nsIFrame* firstChild;
 | |
|     aChildFrame->FirstChild(aPresContext, nsnull, &firstChild);
 | |
|     if (!firstChild) {
 | |
|       return NS_OK;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // See if either the old parent frame or the new parent frame have a view
 | |
|   aOldParentFrame->GetView(aPresContext, &oldParentView);
 | |
|   aNewParentFrame->GetView(aPresContext, &newParentView);
 | |
| 
 | |
|   if (!oldParentView && !newParentView) {
 | |
|     // Walk up both the old parent frame and the new parent frame nodes
 | |
|     // stopping when we either find a common parent or views for one
 | |
|     // or both of the frames.
 | |
|     //
 | |
|     // This works well in the common case where we push/pull and the old parent
 | |
|     // frame and the new parent frame are part of the same flow. They will
 | |
|     // typically be the same distance (height wise) from the
 | |
|     do {
 | |
|       aOldParentFrame->GetParent(&aOldParentFrame);
 | |
|       aNewParentFrame->GetParent(&aNewParentFrame);
 | |
|       
 | |
|       // We should never walk all the way to the root frame without finding
 | |
|       // a view
 | |
|       NS_ASSERTION(aOldParentFrame && aNewParentFrame, "didn't find view");
 | |
| 
 | |
|       // See if we reached a common parent
 | |
|       if (aOldParentFrame == aNewParentFrame) {
 | |
|         break;
 | |
|       }
 | |
| 
 | |
|       // Get the views
 | |
|       aOldParentFrame->GetView(aPresContext, &oldParentView);
 | |
|       aNewParentFrame->GetView(aPresContext, &newParentView);
 | |
|     } while (!(oldParentView || newParentView));
 | |
|   }
 | |
| 
 | |
| 
 | |
|   // See if we found a common parent frame
 | |
|   if (aOldParentFrame == aNewParentFrame) {
 | |
|     // We found a common parent and there are no views between the old parent
 | |
|     // and the common parent or the new parent frame and the common parent.
 | |
|     // Because neither the old parent frame nor the new parent frame have views,
 | |
|     // then any child views don't need reparenting
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   // We found views for one or both of the parent frames before we found a
 | |
|   // common parent
 | |
|   NS_ASSERTION(oldParentView || newParentView, "internal error");
 | |
|   if (!oldParentView) {
 | |
|     oldParentView = GetClosestViewFor(aPresContext, aOldParentFrame);
 | |
|   }
 | |
|   if (!newParentView) {
 | |
|     newParentView = GetClosestViewFor(aPresContext, aNewParentFrame);
 | |
|   }
 | |
|   
 | |
|   // See if the old parent frame and the new parent frame are in the
 | |
|   // same view sub-hierarchy. If they are then we don't have to do
 | |
|   // anything
 | |
|   if (oldParentView != newParentView) {
 | |
|     nsCOMPtr<nsIViewManager>  viewManager;
 | |
|     oldParentView->GetViewManager(*getter_AddRefs(viewManager));
 | |
| 
 | |
|     // They're not so we need to reparent any child views
 | |
|     return ReparentFrameViewTo(aPresContext, aChildFrame, viewManager, newParentView,
 | |
|                                oldParentView);
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| nsHTMLContainerFrame::ReparentFrameViewList(nsIPresContext* aPresContext,
 | |
|                                             nsIFrame*       aChildFrameList,
 | |
|                                             nsIFrame*       aOldParentFrame,
 | |
|                                             nsIFrame*       aNewParentFrame)
 | |
| {
 | |
|   NS_PRECONDITION(aChildFrameList, "null child frame list");
 | |
|   NS_PRECONDITION(aOldParentFrame, "null old parent frame pointer");
 | |
|   NS_PRECONDITION(aNewParentFrame, "null new parent frame pointer");
 | |
|   NS_PRECONDITION(aOldParentFrame != aNewParentFrame, "same old and new parent frame");
 | |
| 
 | |
|   nsIView*  oldParentView;
 | |
|   nsIView*  newParentView;
 | |
|   
 | |
|   // See if either the old parent frame or the new parent frame have a view
 | |
|   aOldParentFrame->GetView(aPresContext, &oldParentView);
 | |
|   aNewParentFrame->GetView(aPresContext, &newParentView);
 | |
| 
 | |
|   if (!oldParentView && !newParentView) {
 | |
|     // Walk up both the old parent frame and the new parent frame nodes
 | |
|     // stopping when we either find a common parent or views for one
 | |
|     // or both of the frames.
 | |
|     //
 | |
|     // This works well in the common case where we push/pull and the old parent
 | |
|     // frame and the new parent frame are part of the same flow. They will
 | |
|     // typically be the same distance (height wise) from the
 | |
|     do {
 | |
|       aOldParentFrame->GetParent(&aOldParentFrame);
 | |
|       aNewParentFrame->GetParent(&aNewParentFrame);
 | |
|       
 | |
|       // We should never walk all the way to the root frame without finding
 | |
|       // a view
 | |
|       NS_ASSERTION(aOldParentFrame && aNewParentFrame, "didn't find view");
 | |
| 
 | |
|       // See if we reached a common parent
 | |
|       if (aOldParentFrame == aNewParentFrame) {
 | |
|         break;
 | |
|       }
 | |
| 
 | |
|       // Get the views
 | |
|       aOldParentFrame->GetView(aPresContext, &oldParentView);
 | |
|       aNewParentFrame->GetView(aPresContext, &newParentView);
 | |
|     } while (!(oldParentView || newParentView));
 | |
|   }
 | |
| 
 | |
| 
 | |
|   // See if we found a common parent frame
 | |
|   if (aOldParentFrame == aNewParentFrame) {
 | |
|     // We found a common parent and there are no views between the old parent
 | |
|     // and the common parent or the new parent frame and the common parent.
 | |
|     // Because neither the old parent frame nor the new parent frame have views,
 | |
|     // then any child views don't need reparenting
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   // We found views for one or both of the parent frames before we found a
 | |
|   // common parent
 | |
|   NS_ASSERTION(oldParentView || newParentView, "internal error");
 | |
|   if (!oldParentView) {
 | |
|     oldParentView = GetClosestViewFor(aPresContext, aOldParentFrame);
 | |
|   }
 | |
|   if (!newParentView) {
 | |
|     newParentView = GetClosestViewFor(aPresContext, aNewParentFrame);
 | |
|   }
 | |
|   
 | |
|   // See if the old parent frame and the new parent frame are in the
 | |
|   // same view sub-hierarchy. If they are then we don't have to do
 | |
|   // anything
 | |
|   if (oldParentView != newParentView) {
 | |
|     nsCOMPtr<nsIViewManager>  viewManager;
 | |
|     oldParentView->GetViewManager(*getter_AddRefs(viewManager));
 | |
| 
 | |
|     // They're not so we need to reparent any child views
 | |
|     for (nsIFrame* f = aChildFrameList; f; f->GetNextSibling(&f)) {
 | |
|       ReparentFrameViewTo(aPresContext, f, viewManager, newParentView,
 | |
|                           oldParentView);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| static PRBool
 | |
| IsContainerContent(nsIFrame* aFrame)
 | |
| {
 | |
|   nsIContent* content;
 | |
|   PRBool      result = PR_FALSE;
 | |
| 
 | |
|   aFrame->GetContent(&content);
 | |
|   if (content) {
 | |
|     content->CanContainChildren(result);
 | |
|     NS_RELEASE(content);
 | |
|   }
 | |
| 
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| nsHTMLContainerFrame::CreateViewForFrame(nsIPresContext* aPresContext,
 | |
|                                          nsIFrame* aFrame,
 | |
|                                          nsIStyleContext* aStyleContext,
 | |
|                                          nsIFrame* aContentParentFrame,
 | |
|                                          PRBool aForce)
 | |
| {
 | |
|   nsIView* view;
 | |
|   aFrame->GetView(aPresContext, &view);
 | |
|   // If we don't yet have a view, see if we need a view
 | |
|   if (nsnull == view) {
 | |
|     PRBool  fixedBackgroundAttachment = PR_FALSE;
 | |
| 
 | |
|     // Get nsStyleColor and nsStyleDisplay
 | |
|     const nsStyleDisplay* display = (const nsStyleDisplay*)
 | |
|       aStyleContext->GetStyleData(eStyleStruct_Display);
 | |
|     const nsStylePosition* position = (const nsStylePosition*)
 | |
|       aStyleContext->GetStyleData(eStyleStruct_Position);
 | |
|     const nsStyleVisibility* vis = (const nsStyleVisibility*)
 | |
|       aStyleContext->GetStyleData(eStyleStruct_Visibility);
 | |
|     
 | |
|     if (vis->mOpacity != 1.0f) {
 | |
|       NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
 | |
|         ("nsHTMLContainerFrame::CreateViewForFrame: frame=%p opacity=%g",
 | |
|          aFrame, vis->mOpacity));
 | |
|       aForce = PR_TRUE;
 | |
|     }
 | |
| 
 | |
|     // See if the frame has a fixed background attachment
 | |
|     const nsStyleBackground *color;
 | |
|     PRBool isCanvas;
 | |
|     PRBool hasBackground = 
 | |
|       nsCSSRendering::FindBackground(aPresContext, aFrame, &color, &isCanvas);
 | |
|     if (hasBackground &&
 | |
|         NS_STYLE_BG_ATTACHMENT_FIXED == color->mBackgroundAttachment) {
 | |
|       aForce = PR_TRUE;
 | |
|       fixedBackgroundAttachment = PR_TRUE;
 | |
|     }
 | |
|     
 | |
|     // See if the frame is being relatively positioned or absolutely
 | |
|     // positioned
 | |
|     if (!aForce) {
 | |
|       if (NS_STYLE_POSITION_RELATIVE == display->mPosition) {
 | |
|         NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
 | |
|           ("nsHTMLContainerFrame::CreateViewForFrame: frame=%p relatively positioned",
 | |
|           aFrame));
 | |
|         aForce = PR_TRUE;
 | |
|       } else if (display->IsAbsolutelyPositioned()) {
 | |
|         NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
 | |
|           ("nsHTMLContainerFrame::CreateViewForFrame: frame=%p absolutely positioned",
 | |
|           aFrame));
 | |
|         aForce = PR_TRUE;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // See if the frame is a scrolled frame
 | |
|     if (!aForce) {
 | |
|       nsIAtom*  pseudoTag;
 | |
|       aStyleContext->GetPseudoType(pseudoTag);
 | |
|       if (pseudoTag == nsLayoutAtoms::scrolledContentPseudo) {
 | |
|         NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
 | |
|           ("nsHTMLContainerFrame::CreateViewForFrame: scrolled frame=%p", aFrame));
 | |
|         aForce = PR_TRUE;
 | |
|       }
 | |
|       NS_IF_RELEASE(pseudoTag);
 | |
|     }
 | |
| 
 | |
|     // See if the frame is block-level and has 'overflow' set to 'hidden'. If
 | |
|     // so and it can have child frames, then we need to give it a view so clipping
 | |
|     // of any child views works correctly. Note that if it's floated it is also
 | |
|     // block-level, but we can't trust that the style context 'display' value is
 | |
|     // set correctly
 | |
|     if (!aForce) {
 | |
|       if ((display->IsBlockLevel() || display->IsFloating()) &&
 | |
|           (display->mOverflow == NS_STYLE_OVERFLOW_HIDDEN)) {
 | |
| 
 | |
|         // The reason for the check of whether it can contain children is just
 | |
|         // to avoid giving it a view unnecessarily
 | |
|         if (::IsContainerContent(aFrame)) {
 | |
|           // XXX Check for the frame being a block frame and only force a view
 | |
|           // in that case, because adding a view for box frames seems to cause
 | |
|           // problems for XUL...
 | |
|           nsIAtom*  frameType;
 | |
| 
 | |
|           aFrame->GetFrameType(&frameType);
 | |
|           if ((frameType == nsLayoutAtoms::blockFrame) ||
 | |
|               (frameType == nsLayoutAtoms::areaFrame)) {
 | |
|             aForce = PR_TRUE;
 | |
|           }
 | |
|           NS_IF_RELEASE(frameType);
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (aForce) {
 | |
|       // Create a view
 | |
|       nsIFrame* parent = nsnull;
 | |
|       nsIView* parentView = nsnull;
 | |
| 
 | |
|       aFrame->GetParentWithView(aPresContext, &parent);
 | |
|       NS_ASSERTION(parent, "GetParentWithView failed");
 | |
|       parent->GetView(aPresContext, &parentView);
 | |
|       NS_ASSERTION(parentView, "no parent with view");
 | |
| 
 | |
|       // Create a view
 | |
|       static NS_DEFINE_CID(kViewCID, NS_VIEW_CID);
 | |
| 
 | |
|       nsresult result = nsComponentManager::CreateInstance(kViewCID, 
 | |
|                                                      nsnull, 
 | |
|                                                      NS_GET_IID(nsIView), 
 | |
|                                                      (void **)&view);
 | |
|       if (NS_OK == result) {
 | |
|         nsIViewManager* viewManager;
 | |
|         parentView->GetViewManager(viewManager);
 | |
|         NS_ASSERTION(nsnull != viewManager, "null view manager");
 | |
| 
 | |
|         // Initialize the view
 | |
|         nsRect bounds;
 | |
|         aFrame->GetRect(bounds);
 | |
|         view->Init(viewManager, bounds, parentView);
 | |
| 
 | |
|         // If the frame has a fixed background attachment, then indicate that the
 | |
|         // view's contents should be repainted and not bitblt'd
 | |
|         if (fixedBackgroundAttachment) {
 | |
|           viewManager->SetViewBitBltEnabled(view, PR_FALSE);
 | |
|         }
 | |
|         
 | |
|         // Insert the view into the view hierarchy. If the parent view is a
 | |
|         // scrolling view we need to do this differently
 | |
|         nsIScrollableView*  scrollingView;
 | |
|         if (NS_SUCCEEDED(parentView->QueryInterface(NS_GET_IID(nsIScrollableView), (void**)&scrollingView))) {
 | |
|           scrollingView->SetScrolledView(view);
 | |
|         } else {
 | |
|           PRInt32 zIndex = 0;
 | |
|           PRBool  autoZIndex = PR_FALSE;
 | |
| 
 | |
|           // Get the z-index to use
 | |
|           if (position->mZIndex.GetUnit() == eStyleUnit_Integer) {
 | |
|             zIndex = position->mZIndex.GetIntValue();
 | |
|           } else if (position->mZIndex.GetUnit() == eStyleUnit_Auto) {
 | |
|             autoZIndex = PR_TRUE;
 | |
|           }
 | |
|           
 | |
|           viewManager->SetViewZIndex(view, autoZIndex, zIndex);
 | |
|           // XXX Drop it at the end of the document order until we can do better
 | |
|           viewManager->InsertChild(parentView, view, nsnull, PR_TRUE);
 | |
| 
 | |
|           if (nsnull != aContentParentFrame) {
 | |
|             // If, for some reason, GetView below fails to initialize zParentView,
 | |
|             // then ensure that we completely bypass InsertZPlaceholder below.
 | |
|             // The effect will be as if we never knew about aContentParentFrame
 | |
|             // in the first place, so at least this code won't be doing any damage.
 | |
|             nsIView* zParentView = parentView;
 | |
|             
 | |
|             aContentParentFrame->GetView(aPresContext, &zParentView);
 | |
|             
 | |
|             if (nsnull == zParentView) {
 | |
|               nsIFrame* zParentFrame = nsnull;
 | |
|               
 | |
|               aContentParentFrame->GetParentWithView(aPresContext, &zParentFrame);
 | |
|               NS_ASSERTION(zParentFrame, "GetParentWithView failed");
 | |
|               zParentFrame->GetView(aPresContext, &zParentView);
 | |
|               NS_ASSERTION(zParentView, "no parent with view");
 | |
|             }
 | |
|             
 | |
|             if (zParentView != parentView) {
 | |
|               viewManager->InsertZPlaceholder(zParentView, view, nsnull, PR_TRUE);
 | |
|             }
 | |
|           }
 | |
|         }
 | |
| 
 | |
|         // See if the view should be hidden
 | |
|         PRBool  viewIsVisible = PR_TRUE;
 | |
|         PRBool  viewHasTransparentContent =
 | |
|             !isCanvas &&
 | |
|             (!hasBackground ||
 | |
|              (color->mBackgroundFlags & NS_STYLE_BG_COLOR_TRANSPARENT));
 | |
|               
 | |
| 
 | |
|         if (NS_STYLE_VISIBILITY_COLLAPSE == vis->mVisible) {
 | |
|           viewIsVisible = PR_FALSE;
 | |
|         }
 | |
|         else if (NS_STYLE_VISIBILITY_HIDDEN == vis->mVisible) {
 | |
|           // If it has a widget, hide the view because the widget can't deal with it
 | |
|           nsIWidget* widget = nsnull;
 | |
|           view->GetWidget(widget);
 | |
|           if (widget) {
 | |
|             viewIsVisible = PR_FALSE;
 | |
|             NS_RELEASE(widget);
 | |
|           }
 | |
|           else {
 | |
|             // If it's a container element, then leave the view visible, but
 | |
|             // mark it as having transparent content. The reason we need to
 | |
|             // do this is that child elements can override their parent's
 | |
|             // hidden visibility and be visible anyway.
 | |
|             //
 | |
|             // Because this function is called before processing the content
 | |
|             // object's child elements, we can't tell if it's a leaf by looking
 | |
|             // at whether the frame has any child frames
 | |
|             if (::IsContainerContent(aFrame)) {
 | |
|               // The view needs to be visible, but marked as having transparent
 | |
|               // content
 | |
|               viewHasTransparentContent = PR_TRUE;
 | |
|             } else {
 | |
|               // Go ahead and hide the view
 | |
|               viewIsVisible = PR_FALSE;
 | |
|             }
 | |
|           }
 | |
|         }
 | |
| 
 | |
|         if (viewIsVisible) {
 | |
|           if (viewHasTransparentContent) {
 | |
|             viewManager->SetViewContentTransparency(view, PR_TRUE);
 | |
|           }
 | |
| 
 | |
|         } else {
 | |
|           viewManager->SetViewVisibility(view, nsViewVisibility_kHide);
 | |
|         }
 | |
| 
 | |
|         // XXX If it's fixed positioned, then create a widget so it floats
 | |
|         // above the scrolling area
 | |
|         if (NS_STYLE_POSITION_FIXED == display->mPosition) {
 | |
|           view->CreateWidget(kCChildCID);
 | |
|         }
 | |
| 
 | |
|         viewManager->SetViewOpacity(view, vis->mOpacity);
 | |
|         NS_RELEASE(viewManager);
 | |
|       }
 | |
| 
 | |
|       // Remember our view
 | |
|       aFrame->SetView(aPresContext, view);
 | |
| 
 | |
|       NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
 | |
|         ("nsHTMLContainerFrame::CreateViewForFrame: frame=%p view=%p",
 | |
|          aFrame));
 | |
|       return result;
 | |
|     }
 | |
|   }
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| void
 | |
| nsHTMLContainerFrame::CheckInvalidateBorder(nsIPresContext* aPresContext,
 | |
|                                             nsHTMLReflowMetrics& aDesiredSize,
 | |
|                                             const nsHTMLReflowState& aReflowState)
 | |
| {
 | |
|   // XXX This method ought to deal with padding as well
 | |
|   // If this is a style change reflow targeted at this frame, we must repaint
 | |
|   // everything (well, we really just have to repaint the borders but we're
 | |
|   // a bunch of lazybones).
 | |
|   if (aReflowState.reason == eReflowReason_Incremental) {
 | |
|     nsHTMLReflowCommand *command = aReflowState.path->mReflowCommand;
 | |
|     if (command) {
 | |
|       nsReflowType type;
 | |
|       command->GetType(type);
 | |
|       if (type == eReflowType_StyleChanged) {
 | |
| 
 | |
| #ifdef NOISY_BLOCK_INVALIDATE
 | |
|         printf("%p invalidate 1 (%d, %d, %d, %d)\n",
 | |
|                this, 0, 0, mRect.width, mRect.height);
 | |
| #endif
 | |
| 
 | |
|         // Lots of things could have changed so damage our entire bounds
 | |
|         nsRect damageRect(0, 0, mRect.width, mRect.height);
 | |
|         if (!damageRect.IsEmpty()) {
 | |
|           Invalidate(aPresContext,damageRect);
 | |
|         }
 | |
| 
 | |
|         return;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // If we changed size, we must invalidate the parts of us that have changed
 | |
|   // to make the border show up.
 | |
|   if ((aReflowState.reason == eReflowReason_Incremental ||
 | |
|        aReflowState.reason == eReflowReason_Dirty)) {
 | |
|     nsMargin border = aReflowState.mComputedBorderPadding -
 | |
|                       aReflowState.mComputedPadding;
 | |
| 
 | |
|     // See if our width changed
 | |
|     if ((aDesiredSize.width != mRect.width) && (border.right > 0)) {
 | |
|       nsRect damageRect;
 | |
| 
 | |
|       if (aDesiredSize.width < mRect.width) {
 | |
|         // Our new width is smaller, so we need to make sure that
 | |
|         // we paint our border in its new position
 | |
|         damageRect.x = aDesiredSize.width - border.right;
 | |
|         damageRect.width = border.right;
 | |
|         damageRect.y = 0;
 | |
|         damageRect.height = aDesiredSize.height;
 | |
|       } else {
 | |
|         // Our new width is larger, so we need to erase our border in its
 | |
|         // old position
 | |
|         damageRect.x = mRect.width - border.right;
 | |
|         damageRect.width = border.right;
 | |
|         damageRect.y = 0;
 | |
|         damageRect.height = mRect.height;
 | |
|       }
 | |
| 
 | |
| #ifdef NOISY_BLOCK_INVALIDATE
 | |
|       printf("%p invalidate 2 (%d, %d, %d, %d)\n",
 | |
|              this, damageRect.x, damageRect.y, damageRect.width, damageRect.height);
 | |
| #endif
 | |
|       if (!damageRect.IsEmpty()) {
 | |
|         Invalidate(aPresContext, damageRect);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // See if our height changed
 | |
|     if ((aDesiredSize.height != mRect.height) && (border.bottom > 0)) {
 | |
|       nsRect  damageRect;
 | |
| 
 | |
|       if (aDesiredSize.height < mRect.height) {
 | |
|         // Our new height is smaller, so we need to make sure that
 | |
|         // we paint our border in its new position
 | |
|         damageRect.x = 0;
 | |
|         damageRect.width = aDesiredSize.width;
 | |
|         damageRect.y = aDesiredSize.height - border.bottom;
 | |
|         damageRect.height = border.bottom;
 | |
| 
 | |
|       } else {
 | |
|         // Our new height is larger, so we need to erase our border in its
 | |
|         // old position
 | |
|         damageRect.x = 0;
 | |
|         damageRect.width = mRect.width;
 | |
|         damageRect.y = mRect.height - border.bottom;
 | |
|         damageRect.height = border.bottom;
 | |
|       }
 | |
| #ifdef NOISY_BLOCK_INVALIDATE
 | |
|       printf("%p invalidate 3 (%d, %d, %d, %d)\n",
 | |
|              this, damageRect.x, damageRect.y, damageRect.width, damageRect.height);
 | |
| #endif
 | |
|       if (!damageRect.IsEmpty()) {
 | |
|         Invalidate(aPresContext, damageRect);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | 
