Back to index

lightning-sunbird  0.9+nobinonly
TestInlineFrame.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 #include <stdio.h>
00038 #include "nscore.h"
00039 #include "nsCRT.h"
00040 #include "nsHTMLParts.h"
00041 #include "nsIDocument.h"
00042 #include "nsVoidArray.h"
00043 #include "nsDocument.h"
00044 #include "nsHTMLTagContent.h"
00045 #include "nsCoord.h"
00046 #include "nsSplittableFrame.h"
00047 #include "nsIContentDelegate.h"
00048 #include "nsPresContext.h"
00049 #include "nsInlineFrame.h"
00050 #include "nsIAtom.h"
00051 #include "nsAutoPtr.h"
00052 #include "nsStyleSet.h"
00053 
00055 //
00056 
00057 class MyDocument : public nsDocument {
00058 public:
00059   MyDocument();
00060   NS_IMETHOD StartDocumentLoad(const char* aCommand,
00061                                nsIChannel* aChannel,
00062                                nsILoadGroup* aLoadGroup,
00063                                nsISupports* aContainer,
00064                                nsIStreamListener **aDocListener)
00065   {
00066     return NS_OK;
00067   }
00068 
00069 protected:
00070   virtual ~MyDocument();
00071 };
00072 
00073 MyDocument::MyDocument()
00074 {
00075 }
00076 
00077 MyDocument::~MyDocument()
00078 {
00079 }
00080 
00081 
00083 //
00084 
00085 // Frame with a fixed width, but that's optionally splittable
00086 class FixedSizeFrame : public nsSplittableFrame {
00087 public:
00088   FixedSizeFrame(nsIContent* aContent,
00089                  nsIFrame* aParentFrame);
00090 
00091   NS_IMETHOD Reflow(nsPresContext* aPresContext,
00092                     nsReflowMetrics& aDesiredSize,
00093                     const nsReflowState& aReflowState,
00094                     nsReflowStatus& aStatus);
00095 
00096   PRBool       IsSplittable() const;
00097 };
00098 
00099 class FixedSizeContent : public nsHTMLTagContent {
00100 public:
00101   FixedSizeContent(nscoord aWidth,
00102                    nscoord aHeight,
00103                    PRBool  aIsSplittable = PR_FALSE);
00104 
00105   // Accessors
00106   nscoord   GetWidth() {return mWidth;}
00107   void      SetWidth(nscoord aWidth);
00108   nscoord   GetHeight() {return mHeight;}
00109   void      SetHeight(nscoord aHeight);
00110 
00111   void      ToHTML(nsString& out);
00112   PRBool    IsSplittable() const {return mIsSplittable;}
00113   void      SetIsSplittable(PRBool aIsSplittable) {mIsSplittable = aIsSplittable;}
00114 
00115 
00116 private:
00117   nscoord mWidth, mHeight;
00118   PRBool  mIsSplittable;
00119 };
00120 
00122 //
00123 
00124 FixedSizeFrame::FixedSizeFrame(nsIContent* aContent,
00125                                nsIFrame* aParentFrame)
00126   : nsSplittableFrame(aContent, aParentFrame)
00127 {
00128 }
00129 
00130 NS_METHOD FixedSizeFrame::Reflow(nsPresContext*      aPresContext,
00131                                  nsReflowMetrics&     aDesiredSize,
00132                                  const nsReflowState& aReflowState,
00133                                  nsReflowStatus&      aStatus)
00134 {
00135   NS_PRECONDITION((aReflowState.availableWidth > 0) && (aReflowState.availableHeight > 0),
00136                   "bad max size");
00137   FixedSizeContent* content = (FixedSizeContent*)mContent;
00138   nsReflowStatus    status = NS_FRAME_COMPLETE;
00139   FixedSizeFrame*   prevInFlow = (FixedSizeFrame*)mPrevInFlow;
00140 
00141   aDesiredSize.width = content->GetWidth();
00142   aDesiredSize.height = content->GetHeight();
00143 
00144   // We can split once horizontally
00145   if (nsnull != prevInFlow) {
00146     aDesiredSize.width -= prevInFlow->mRect.width;
00147   } else if ((aDesiredSize.width > aReflowState.maxSize.width) && content->IsSplittable()) {
00148     aDesiredSize.width = aReflowState.maxSize.width;
00149     status = NS_FRAME_NOT_COMPLETE;
00150   }
00151 
00152   aDesiredSize.ascent = aDesiredSize.height;
00153   aDesiredSize.descent = 0;
00154   if (nsnull != aDesiredSize.maxElementSize) {
00155     aDesiredSize.maxElementSize->width = aDesiredSize.width;
00156     aDesiredSize.maxElementSize->height = aDesiredSize.height;
00157   }
00158 
00159   return status;
00160 }
00161 
00162 PRBool FixedSizeFrame::IsSplittable() const
00163 {
00164   FixedSizeContent* content = (FixedSizeContent*)mContent;
00165 
00166   return content->IsSplittable();
00167 }
00168 
00170 //
00171 
00172 FixedSizeContent::FixedSizeContent(nscoord aWidth,
00173                                    nscoord aHeight,
00174                                    PRBool  aIsSplittable)
00175   : nsHTMLTagContent()
00176 {
00177   mWidth = aWidth;
00178   mHeight = aHeight;
00179   mIsSplittable = aIsSplittable;
00180 }
00181 
00182 // Change the width of the content triggering an incremental reflow
00183 void FixedSizeContent::SetWidth(nscoord aWidth)
00184 {
00185   NS_NOTYETIMPLEMENTED("set width");
00186 }
00187 
00188 // Change the width of the content triggering an incremental reflow
00189 void FixedSizeContent::SetHeight(nscoord aHeight)
00190 {
00191   NS_NOTYETIMPLEMENTED("set height");
00192 }
00193 
00194 void FixedSizeContent::ToHTML(nsString& out)
00195 {
00196 }
00197 
00199 //
00200 
00201 class InlineFrame : public nsInlineFrame
00202 {
00203 public:
00204   InlineFrame(nsIContent* aContent,
00205               nsIFrame* aParent);
00206 
00207   // Accessors to return protected state
00208   nsIFrame* OverflowList() {return mOverflowList;}
00209   void      ClearOverflowList() {mOverflowList = nsnull;}
00210   PRBool    LastContentIsComplete() {return mLastContentIsComplete;}
00211 
00212   // Helper member functions
00213   PRInt32   MaxChildWidth();
00214   PRInt32   MaxChildHeight();
00215 };
00216 
00217 InlineFrame::InlineFrame(nsIContent* aContent,
00218                          nsIFrame*   aParent)
00219   : nsInlineFrame(aContent, aParent)
00220 {
00221 }
00222 
00223 #if 0
00224 PRInt32 InlineFrame::MaxChildWidth()
00225 {
00226   PRInt32 maxWidth = 0;
00227 
00228   nsIFrame* f;
00229   for (f = GetFirstChild(); nsnull != f; f->GetNextSibling(f)) {
00230     if (f->GetWidth() > maxWidth) {
00231       maxWidth = f->GetWidth();
00232     }
00233   }
00234 
00235   return maxWidth;
00236 }
00237 
00238 PRInt32 InlineFrame::MaxChildHeight()
00239 {
00240   PRInt32 maxHeight = 0;
00241 
00242   for (nsIFrame* f = GetFirstChild(); nsnull != f; f = f->GetNextSibling()) {
00243     if (f->GetHeight() > maxHeight) {
00244       maxHeight = f->GetHeight();
00245     }
00246   }
00247 
00248   return maxHeight;
00249 }
00250 
00252 // Helper functions
00253 
00254 // Compute the number of frames in the list
00255 static PRInt32
00256 LengthOf(nsIFrame* aChildList)
00257 {
00258   PRInt32 result = 0;
00259 
00260   while (nsnull != aChildList) {
00261     aChildList = aChildList->GetNextSibling();
00262     result++;
00263   }
00264 
00265   return result;
00266 }
00267 
00269 //
00270 
00271 // Test basic reflowing of unmapped children. No borders/padding or child
00272 // margins involved, and no splitting of child frames. b is an empty
00273 // HTML container
00274 //
00275 // Here's what this function tests:
00276 // - reflow unmapped maps all children and returns that it's complete
00277 // - each child frame is placed and sized properly
00278 // - the inline frame's desired width and  height are correct
00279 static PRBool
00280 TestReflowUnmapped(nsPresContext* presContext)
00281 {
00282   // Create an HTML container
00283   nsIContent* b;
00284   NS_NewHTMLContainer(&b, NS_NewAtom("span"));
00285 
00286   // Append three fixed width elements.
00287   b->AppendChildTo(new FixedSizeContent(100, 100));
00288   b->AppendChildTo(new FixedSizeContent(300, 300));
00289   b->AppendChildTo(new FixedSizeContent(200, 200));
00290 
00291   // Create an inline frame for the HTML container and set its
00292   // style context
00293   InlineFrame*      f = new InlineFrame(b, 0, nsnull);
00294   nsRefPtr<nsStyleContext> styleContext;
00295   styleContext = presContext->StyleSet()->ResolveStyleFor(b, nsnull);
00296 
00297   f->SetStyleContext(presContext,styleContext);
00298 
00299   // Reflow the HTML container
00300   nsReflowMetrics   reflowMetrics;
00301   nsSize            maxSize(1000, 1000);
00302   nsReflowStatus    status;
00303 
00304   status = f->ResizeReflow(presContext, reflowMetrics, maxSize, nsnull);
00305 
00306   // Make sure the frame is complete
00307   if (NS_FRAME_IS_NOT_COMPLETE(status)) {
00308     printf("ReflowUnmapped: initial reflow not complete: %d\n", status);
00309     return PR_FALSE;
00310   }
00311 
00312   // Verify that all the children were mapped
00313   if (f->ChildCount() != b->ChildCount()) {
00314     printf("ReflowUnmapped: wrong number of child frames: %d\n", f->ChildCount());
00315     return PR_FALSE;
00316   }
00317 
00318   // and that the last content offset is correct
00319   if (f->GetLastContentOffset() != (b->ChildCount() - 1)) {
00320     printf("ReflowUnmapped: wrong last content offset: %d\n", f->GetLastContentOffset());
00321     return PR_FALSE;
00322   }
00323 
00324   // Verify that the width/height of each child frame is correct
00325   for (PRInt32 i = 0; i < b->ChildCount(); i++) {
00326     FixedSizeContent* childContent = (FixedSizeContent*)b->ChildAt(i);
00327     nsIFrame*         childFrame = f->ChildAt(i);
00328 
00329     if ((childFrame->GetWidth() != childContent->GetWidth()) ||
00330         (childFrame->GetHeight() != childContent->GetHeight())) {
00331       printf("ReflowUnmapped: child frame size incorrect: %d\n", i);
00332       return PR_FALSE;
00333     }
00334   }
00335 
00336   // Verify that the inline frame's desired height is correct
00337   if (reflowMetrics.height != f->MaxChildHeight()) {
00338     printf("ReflowUnmapped: wrong frame height: %d\n", reflowMetrics.height);
00339     return PR_FALSE;
00340   }
00341 
00342   // Verify that the frames are tiled horizontally with no space between frames
00343   nscoord x = 0;
00344   for (nsIFrame* child = f->FirstChild(); child; child = child->GetNextSibling()) {
00345     nsPoint origin;
00346 
00347     child->GetOrigin(origin);
00348     if (origin.x != x) {
00349       printf("ReflowUnmapped: child x-origin is incorrect: %d\n", x);
00350       return PR_FALSE;
00351     }
00352 
00353     x += child->GetWidth();
00354   }
00355 
00356   // and that the inline frame's desired width is correct
00357   if (reflowMetrics.width != x) {
00358     printf("ReflowUnmapped: wrong frame width: %d\n", reflowMetrics.width);
00359     return PR_FALSE;
00360   }
00361 
00362   NS_RELEASE(b);
00363   return PR_TRUE;
00364 }
00365 
00366 // Test the case where the frame max size is too small for even one child frame.
00367 //
00368 // Here's what this function tests:
00369 // 1. reflow unmapped with a max width narrower than the first child
00370 //   a) when there's only one content child
00371 //   b) when there's more than one content child
00372 // 2. reflow unmapped when the height's too small
00373 // 3. reflow mapped when the height's too small
00374 // 4. reflow mapped with a max width narrower than the first child
00375 static PRBool
00376 TestChildrenThatDontFit(nsPresContext* presContext)
00377 {
00378   // Create an HTML container
00379   nsIContent* b;
00380   NS_NewHTMLContainer(&b, NS_NewAtom("span"));
00381 
00382   // Add one fixed width element.
00383   b->AppendChildTo(new FixedSizeContent(100, 100));
00384 
00385   // Create an inline frame for the HTML container and set its
00386   // style context
00387   InlineFrame*      f = new InlineFrame(b, 0, nsnull);
00388   nsRefPtr<nsStyleContext> styleContext;
00389   styleContext = presContext->StyleSet()->ResolveStyleFor(b, nsnull);
00390 
00391   f->SetStyleContext(presContext,styleContext);
00392 
00394   // Test #1a
00395 
00396   // Reflow the frame with a width narrower than the first child frame. This
00397   // tests how we handle one child that doesn't fit when reflowing unmapped
00398   nsReflowMetrics   reflowMetrics;
00399   nsSize            maxSize(10, 1000);
00400   nsReflowStatus    status;
00401 
00402   status = f->ResizeReflow(presContext, reflowMetrics, maxSize, nsnull);
00403 
00404   // Verify that the inline frame is complete
00405   if (NS_FRAME_IS_NOT_COMPLETE(status)) {
00406     printf("ChildrenThatDontFIt: reflow unmapped isn't complete (#1a): %d\n", status);
00407     return PR_FALSE;
00408   }
00409 
00410   // Verify that the desired width is the width of the first frame
00411   if (reflowMetrics.width != f->FirstChild()->GetWidth()) {
00412     printf("ChildrenThatDontFit: frame width is wrong (#1a): %d\n", f->FirstChild()->GetWidth());
00413     return PR_FALSE;
00414   }
00415 
00417   // Test #1b
00418 
00419   // Append two more fixed width elements.
00420   b->AppendChildTo(new FixedSizeContent(300, 300));
00421   b->AppendChildTo(new FixedSizeContent(200, 200));
00422 
00423   // Create a new inline frame for the HTML container
00424   InlineFrame*  f1 = new InlineFrame(b, 0, nsnull);
00425   f1->SetStyleContext(presContext,styleContext);
00426 
00427   // Reflow the frame with a width narrower than the first child frame. This
00428   // tests how we handle children that don't fit when reflowing unmapped
00429   maxSize.SizeTo(10, 1000);
00430   status = f1->ResizeReflow(presContext, reflowMetrics, maxSize, nsnull);
00431 
00432   // Verify that the frame is not complete
00433   if (NS_FRAME_IS_NOT_COMPLETE(status)) {
00434     printf("ChildrenThatDontFIt: reflow unmapped isn't not complete (#1b): %d\n", status);
00435     return PR_FALSE;
00436   }
00437 
00438   // Verify that the desired width is the width of the first frame
00439   if (reflowMetrics.width != f1->FirstChild()->GetWidth()) {
00440     printf("ChildrenThatDontFit: frame width is wrong (#1b): %d\n", f1->FirstChild()->GetWidth());
00441     return PR_FALSE;
00442   }
00443 
00444   // Verify that the child count is only 1
00445   if (f1->ChildCount() != 1) {
00446     printf("ChildrenThatDontFit: more than one child frame (#1b): %d\n", f1->ChildCount());
00447     return PR_FALSE;
00448   }
00449 
00450   // and that there's no overflow list. If there's an overflow list that means
00451   // we reflowed a second frame and we would have passed it a negative max size
00452   // which is not a very nice thing to do
00453   if (nsnull != f1->OverflowList()) {
00454     printf("ChildrenThatDontFit: unexpected overflow list (#1b)\n");
00455     return PR_FALSE;
00456   }
00457 
00459   // Test #2
00460 
00461   // Now reflow the frame wide enough so that all the frames fit. Make
00462   // the height small enough so that no frames fit, so we can test that
00463   // reflow unmapped handles that check correctly
00464   maxSize.width = 1000;
00465   maxSize.height = 10;
00466   reflowMetrics.height = 0;
00467   status = f1->ResizeReflow(presContext, reflowMetrics, maxSize, nsnull);
00468 
00469   // Verify the frame is complete
00470   if (NS_FRAME_IS_NOT_COMPLETE(status)) {
00471     printf("ChildrenThatDontFit: resize isn't complete (#2): %d\n", status);
00472     return PR_FALSE;
00473   }
00474 
00475   // Verify that all child frames are now there
00476   if (f1->ChildCount() != b->ChildCount()) {
00477     printf("ChildrenThatDontFit: wrong child count (#2): %d\n", f1->ChildCount());
00478     return PR_FALSE;
00479   }
00480 
00481   // and that the frame's height is the right size
00482   if (reflowMetrics.height != f1->MaxChildHeight()) {
00483     printf("ChildrenThatDontFit: height is wrong (#2): %d\n", reflowMetrics.height);
00484     return PR_FALSE;
00485   }
00486 
00488   // Test #3
00489 
00490   // Now test reflow mapped against a height that's too small
00491   nscoord oldHeight = reflowMetrics.height;
00492   maxSize.height = 10;
00493   reflowMetrics.height = 0;
00494   status = f1->ResizeReflow(presContext, reflowMetrics, maxSize, nsnull);
00495 
00496   // Verify the frame is complete and we still have all the child frames
00497   if (NS_FRAME_IS_NOT_COMPLETE(status) || (f1->ChildCount() != b->ChildCount())) {
00498     printf("ChildrenThatDontFit: reflow mapped failed (#3)\n");
00499     return PR_FALSE;
00500   }
00501 
00502   // Verify that the desired height of the frame is the same as before
00503   if (reflowMetrics.height != oldHeight) {
00504     printf("ChildrenThatDontFit: height is wrong (#3): %d\n", reflowMetrics.height);
00505     return PR_FALSE;
00506   }
00507 
00509   // Test #4
00510 
00511   // And finally test reflowing mapped with a max width narrower than the first
00512   // child
00513   maxSize.width = 10;
00514   maxSize.height = 1000;
00515   status = f1->ResizeReflow(presContext, reflowMetrics, maxSize, nsnull);
00516 
00517   // Verify the frame is not complete
00518   if (NS_FRAME_IS_NOT_COMPLETE(status)) {
00519     printf("ChildrenThatDontFIt: reflow mapped isn't not complete (#4): %d\n", status);
00520     return PR_FALSE;
00521   }
00522 
00523   // Verify that there's only one child
00524   if (f1->ChildCount() > 1) {
00525     printf("ChildrenThatDontFIt: reflow mapped too many children (#4): %d\n", f1->ChildCount());
00526     return PR_FALSE;
00527   }
00528 
00529   // and that the desired width is the width of the first frame
00530   if (reflowMetrics.width != f1->FirstChild()->GetWidth()) {
00531     printf("ChildrenThatDontFit: frame width is wrong (#4): %d\n", f1->FirstChild()->GetWidth());
00532     return PR_FALSE;
00533   }
00534 
00535   NS_RELEASE(b);
00536   return PR_TRUE;
00537 }
00538 
00539 // Test handling of the overflow list
00540 //
00541 // Here's what this function tests:
00542 // 1. reflow unmapped places children whose width doesn't completely fit on
00543 //    the overflow list
00544 // 2. frames use their own overflow list when reflowing mapped children
00545 // 3. continuing frames should use the overflow list from their prev-in-flow
00546 static PRBool
00547 TestOverflow(nsPresContext* presContext)
00548 {
00549   // Create an HTML container
00550   nsIContent* b;
00551   NS_NewHTMLContainer(&b, NS_NewAtom("span"));
00552 
00553   // Append three fixed width elements.
00554   b->AppendChildTo(new FixedSizeContent(100, 100));
00555   b->AppendChildTo(new FixedSizeContent(300, 300));
00556   b->AppendChildTo(new FixedSizeContent(200, 200));
00557 
00558   // Create an inline frame for the HTML container and set its
00559   // style context
00560   InlineFrame*      f = new InlineFrame(b, 0, nsnull);
00561   nsRefPtr<nsStyleContext> styleContext;
00562   styleContext = presContext->StyleSet()->ResolveStyleFor(b, nsnull);
00563 
00564   f->SetStyleContext(presContext,styleContext);
00565 
00567   // Test #1
00568 
00569   // Reflow the frame so only half the second frame fits
00570   nsReflowMetrics   reflowMetrics;
00571   nsSize            maxSize(150, 1000);
00572   nsReflowStatus    status;
00573 
00574   status = f->ResizeReflow(presContext, reflowMetrics, maxSize, nsnull);
00575   NS_ASSERTION(nsIFrame::frNotComplete == status, "bad status");
00576   NS_ASSERTION(f->ChildCount() == 1, "bad child count");
00577 
00578   // Verify that there's one frame on the overflow list
00579   if ((nsnull == f->OverflowList()) || (LengthOf(f->OverflowList()) != 1)) {
00580     printf("Overflow: no overflow list (#1)\n");
00581     return PR_FALSE;
00582   }
00583 
00585   // Test #2
00586 
00587   // Reflow the frame again this time so all the child frames fit. The inline
00588   // frame should use its own overflow list
00589   maxSize.width = 1000;
00590   status = f->ResizeReflow(presContext, reflowMetrics, maxSize, nsnull);
00591   NS_ASSERTION(nsIFrame::frComplete == status, "isn't complete");
00592 
00593   // Verify the overflow list is now empty
00594   if (nsnull != f->OverflowList()) {
00595     printf("Overflow: overflow list should be empty (#2)\n");
00596     return PR_FALSE;
00597   }
00598 
00600   // Test #3
00601 
00602   // Reflow the frame so that only the first frame fits. The rest of the
00603   // children should go on the overflow list. This isn't interesting by
00604   // itself, but it enables the next test
00605   maxSize.width = f->FirstChild()->GetWidth();
00606   status = f->ResizeReflow(presContext, reflowMetrics, maxSize, nsnull);
00607   NS_ASSERTION((f->ChildCount() == 1) && (LengthOf(f->OverflowList()) == 2), "bad overflow list");
00608 
00609   // Create a continuing frame
00610   InlineFrame* f1 = new InlineFrame(b, 0, nsnull);
00611 
00612   f1->SetStyleContext(f->GetStyleContext(presContext));
00613   f->SetNextSibling(f1);
00614   f->SetNextInFlow(f1);
00615   f1->SetPrevInFlow(f);
00616 
00617   // Reflow the continuing frame. It should get its children from the overflow list
00618   maxSize.width = 1000;
00619   status = f1->ResizeReflow(presContext, reflowMetrics, maxSize, nsnull);
00620   NS_ASSERTION(NS_FRAME_IS_COMPLETE(status), "bad continuing frame");
00621 
00622   // Verify that the overflow list is now empty
00623   if (nsnull != f->OverflowList()) {
00624     printf("Overflow: overflow list should be empty (#3)\n");
00625     return PR_FALSE;
00626   }
00627 
00628   // and that the continuing frame has the remaining children
00629   if (f1->ChildCount() != (b->ChildCount() - f->ChildCount())) {
00630     printf("Overflow: continuing frame child count is wrong (#3): %d\n", f1->ChildCount());
00631     return PR_FALSE;
00632   }
00633 
00634   // Verify that the content offsets of the continuing frame are correct
00635   if (f1->GetFirstContentOffset() != f1->FirstChild()->GetIndexInParent()) {
00636     printf("Overflow: continuing frame bad first content offset (#3): %d\n", f1->GetFirstContentOffset());
00637     return PR_FALSE;
00638   }
00639   if (f1->GetLastContentOffset() != (f1->LastChild()->GetIndexInParent())) {
00640     printf("Overflow: continuing frame bad last content offset (#3): %d\n", f1->GetLastContentOffset());
00641     return PR_FALSE;
00642   }
00643 
00644   NS_RELEASE(b);
00645   return PR_TRUE;
00646 }
00647 
00648 // Test handling of pushing/pulling children
00649 //
00650 // Here's what this function tests:
00651 // 1. continuing frames pick up where their prev-in-flow left off
00652 // 2. pulling up a child from a next-in-flow
00653 // 3. pushing a child frame to a next-in-flow that already has children
00654 // 4. pushing a child frame to an empty next-in-flow
00655 // 5. pulling up children from a next-in-flow that has an overflow list
00656 // 6. pulling up all the children across an empty frame
00657 // 7. pulling up only some of the children across an empty frame
00658 // 8. partially pulling up a child from a next-in-flow
00659 static PRBool
00660 TestPushingPulling(nsPresContext* presContext)
00661 {
00662   // Create an HTML container
00663   nsIContent* b;
00664   NS_NewHTMLContainer(&b, NS_NewAtom("span"));
00665 
00666   // Append three fixed width elements.
00667   b->AppendChildTo(new FixedSizeContent(100, 100));
00668   b->AppendChildTo(new FixedSizeContent(300, 300));
00669   b->AppendChildTo(new FixedSizeContent(200, 200));
00670 
00671   // Create an inline frame for the HTML container and set its
00672   // style context
00673   InlineFrame*      f = new InlineFrame(b, 0, nsnull);
00674   nsRefPtr<nsStyleContext> styleContext;
00675   styleContext = presContext->StyleSet()->ResolveStyleFor(b, nsnull);
00676 
00677   f->SetStyleContext(presContext,styleContext);
00678 
00679   // Reflow the inline frame so only the first frame fits
00680   nsReflowMetrics   reflowMetrics;
00681   nsSize            maxSize(100, 1000);
00682   nsReflowStatus    status;
00683 
00684   status = f->ResizeReflow(presContext, reflowMetrics, maxSize, nsnull);
00685   NS_ASSERTION(NS_FRAME_IS_NOT_COMPLETE(status), "bad status");
00686   NS_ASSERTION(f->ChildCount() == 1, "bad child count");
00687 
00689   // Test #1
00690 
00691   // Create a continuing inline frame and test that it picks up with the second
00692   // child frame. See TestOverflow() for checking the case where the continuing
00693   // frame uses the overflow list, and TestSplittableChildren() for the
00694   // case where the last child isn't complete
00695   NS_ASSERTION(nsnull == f->OverflowList(), "unexpected overflow list");
00696   InlineFrame* f1 = new InlineFrame(b, 0, nsnull);
00697 
00698   f1->SetStyleContext(f->GetStyleContext(presContext));
00699   f->SetNextSibling(f1);
00700   f->SetNextInFlow(f1);
00701   f1->SetPrevInFlow(f);
00702 
00703   // Reflow the continuing inline frame. It should have the remainder of the
00704   // children
00705   maxSize.width = 1000;
00706   status = f1->ResizeReflow(presContext, reflowMetrics, maxSize, nsnull);
00707   NS_ASSERTION(NS_FRAME_IS_COMPLETE(status), "bad continuing frame");
00708 
00709   // Verify that the continuing inline frame has the remaining children
00710   if (f1->ChildCount() != (b->ChildCount() - f->ChildCount())) {
00711     printf("PushPull: continuing frame child count is wrong (#1): %d\n", f1->ChildCount());
00712     return PR_FALSE;
00713   }
00714 
00716   // Test #2
00717 
00718   // Reflow the first inline frame so that two child frames now fit. This tests
00719   // pulling up a child frame from the next-in-flow
00720   maxSize.width = 400;
00721   status = f->ResizeReflow(presContext, reflowMetrics, maxSize, nsnull);
00722   NS_ASSERTION(nsIFrame::frNotComplete == status, "bad status");
00723   NS_ASSERTION(f->ChildCount() == 2, "bad child count");
00724   NS_ASSERTION(f1->ChildCount() == 1, "bad continuing child count");
00725 
00726   // Verify the last content offset of the first inline frame
00727   if (f->GetLastContentOffset() != (f->ChildCount() - 1)) {
00728     printf("PushPull: bad last content offset (#2): %d\n", f->GetLastContentOffset());
00729     return PR_FALSE;
00730   }
00731 
00732   // Verify that the first/last content offsets of the continuing inline frame
00733   // were updated correctly after pulling up a child
00734   if (f1->GetFirstContentOffset() != f1->FirstChild()->GetIndexInParent()) {
00735     printf("PushPull: continuing frame bad first content offset (#2): %d\n", f1->GetFirstContentOffset());
00736     return PR_FALSE;
00737   }
00738   if (f1->GetLastContentOffset() != (f1->LastChild()->GetIndexInParent())) {
00739     printf("PushPull: continuing frame bad last content offset (#2): %d\n", f1->GetLastContentOffset());
00740     return PR_FALSE;
00741   }
00742 
00744   // Test #3
00745 
00746   // Now reflow the inline frame so only the first child fits and verify the pushing
00747   // code works correctly.
00748   //
00749   // This tests the case where we're pushing to a next-in-flow that already has
00750   // child frames
00751   maxSize.width = f->FirstChild()->GetWidth();
00752   status = f->ResizeReflow(presContext, reflowMetrics, maxSize, nsnull);
00753   NS_ASSERTION(NS_FRAME_IS_NOT_COMPLETE(status), "bad status");
00754   NS_ASSERTION(f->ChildCount() == 1, "bad child count");
00755 
00756   // Verify the last content offset of the first inline frame
00757   if (f->GetLastContentOffset() != 0) {
00758     printf("PushPull: bad last content offset after push (#3): %d\n",
00759            f->GetLastContentOffset());
00760     return PR_FALSE;
00761   }
00762 
00763   // Verify the continuing inline frame has two children
00764   if (f1->ChildCount() != 2) {
00765     printf("PushPull: continuing child bad child count after push (#3): %d\n",
00766            f1->ChildCount());
00767     return PR_FALSE;
00768   }
00769 
00770   // Verify its first content offset is correct
00771   if (f1->GetFirstContentOffset() != f1->FirstChild()->GetIndexInParent()) {
00772     printf("PushPull: continuing child bad first content offset after push (#3): %d\n",
00773            f1->GetFirstContentOffset());
00774     return PR_FALSE;
00775   }
00776 
00777   // and that the last content offset is correct
00778   if (f1->GetLastContentOffset() != (f1->LastChild()->GetIndexInParent())) {
00779     printf("PushPull: continuing child bad last content offset after push (#3): %d\n",
00780            f1->GetLastContentOffset());
00781     return PR_FALSE;
00782   }
00783 
00784   // Now pull all the frames back to the first inline frame
00785   maxSize.width = 1000;
00786   status = f->ResizeReflow(presContext, reflowMetrics, maxSize, nsnull);
00787   NS_ASSERTION(nsIFrame::frComplete == status, "bad status");
00788 
00789   // Verify the child count and last content offset of the first inline frame
00790   if ((f->ChildCount() != b->ChildCount()) ||
00791       (f->GetLastContentOffset() != (f->ChildCount() - 1))) {
00792     printf("PushPull: failed to pull up all the child frames (#3)\n");
00793     return PR_FALSE;
00794   }
00795 
00796   // The continuing inline frame should have no children
00797   if (f1->ChildCount() != 0) {
00798     printf("PushPull: continuing child is not empty after pulling up all children (#3)\n");
00799     return PR_FALSE;
00800   }
00801 
00803   // Test #4
00804 
00805   // Now reflow the inline frame so only the first child fits and verify the pushing
00806   // code works correctly.
00807   //
00808   // This tests the case where we're pushing to an empty next-in-flow
00809   maxSize.width = f->FirstChild()->GetWidth();
00810   f1->SetFirstContentOffset(f->NextChildOffset());  // don't trigger pre-reflow check of next-in-flow
00811   status = f->ResizeReflow(presContext, reflowMetrics, maxSize, nsnull);
00812   NS_ASSERTION(nsIFrame::frNotComplete == status, "bad status");
00813   NS_ASSERTION(f->ChildCount() == 1, "bad child count");
00814 
00815   // Verify the continuing inline frame has two children
00816   if (f1->ChildCount() != 2) {
00817     printf("PushPull: continuing child bad child count after push (#4): %d\n",
00818            f1->ChildCount());
00819     return PR_FALSE;
00820   }
00821 
00822   // Verify its first content offset is correct
00823   if (f1->GetFirstContentOffset() != f1->FirstChild()->GetIndexInParent()) {
00824     printf("PushPull: continuing child bad first content offset after push (#4): %d\n",
00825            f1->GetFirstContentOffset());
00826     return PR_FALSE;
00827   }
00828 
00829   // and that the last content offset is correct
00830   if (f1->GetLastContentOffset() != (f1->LastChild()->GetIndexInParent())) {
00831     printf("PushPull: continuing child bad last content offset after push (#4): %d\n",
00832            f1->GetLastContentOffset());
00833     return PR_FALSE;
00834   }
00835 
00837   // Test #5
00838 
00839   // Test pulling up children from a next-in-flow that has an overflow list.
00840   // Reflow f1 so only one child fits
00841   maxSize.width = 300;
00842   status = f1->ResizeReflow(presContext, reflowMetrics, maxSize, nsnull);
00843   NS_ASSERTION(nsIFrame::frNotComplete == status, "bad status");
00844   NS_ASSERTION((f1->ChildCount() == 1) && (f1->GetLastContentOffset() == 1), "bad reflow");
00845   NS_ASSERTION(nsnull != f1->OverflowList(), "no overflow list");
00846 
00847   // Now reflow the first inline frame so it's wide enough for all the child frames
00848   maxSize.width = 1000;
00849   status = f->ResizeReflow(presContext, reflowMetrics, maxSize, nsnull);
00850   NS_ASSERTION(nsIFrame::frComplete == status, "bad status");
00851   NS_ASSERTION((f->ChildCount() == 3) && (f->GetLastContentOffset() == 2), "bad mapping");
00852 
00853   // The second frame should be be empty, and it should not have an overflow list
00854   if (f1->ChildCount() != 0) {
00855     printf("PushPull: continuing child isn't empty (#5)\n");
00856     return PR_FALSE;
00857   }
00858   if (nsnull != f1->OverflowList()) {
00859     printf("PushPull: continuing child still has overflow list (#5)\n");
00860     return PR_FALSE;
00861   }
00862 
00864   // Test #6
00865 
00866   // Test pulling up all the child frames across an empty frame, i.e. a frame
00867   // from which we've already pulled up all the children
00868   //
00869   // Set it up so that there's one frame in 'f' and one frame in 'f1'
00870   maxSize.width = 100;
00871   f1->BreakFromPrevFlow();
00872   status = f->ResizeReflow(presContext, reflowMetrics, maxSize, nsnull);
00873   NS_ASSERTION(nsIFrame::frNotComplete == status, "bad status");
00874   NS_ASSERTION((f->ChildCount() == 1) && (f->GetLastContentOffset() == 0), "bad mapping");
00875 
00876   maxSize.width = 300;
00877   f1->AppendToFlow(f);
00878   status = f1->ResizeReflow(presContext, reflowMetrics, maxSize, nsnull);
00879   NS_ASSERTION(nsIFrame::frNotComplete == status, "bad status");
00880   NS_ASSERTION((f1->ChildCount() == 1) && (f1->GetLastContentOffset() == 1), "bad mapping");
00881   NS_ASSERTION(nsnull != f1->OverflowList(), "no overflow list");
00882 
00883   // Now create a third inline frame and have it map the third child frame
00884   InlineFrame* f2 = new InlineFrame(b, 0, nsnull);
00885 
00886   f2->SetStyleContext(f->GetStyleContext(presContext));
00887   f1->SetNextSibling(f2);
00888   f1->SetNextInFlow(f2);
00889   f2->SetPrevInFlow(f1);
00890 
00891   maxSize.width = 1000;
00892   status = f2->ResizeReflow(presContext, reflowMetrics, maxSize, nsnull);
00893   NS_ASSERTION(nsIFrame::frComplete == status, "bad status");
00894   NS_ASSERTION(nsnull == f1->OverflowList(), "overflow list");
00895 
00896   // Now reflow the first inline frame wide enough so all the child frames fit
00897   // XXX This isn't enough and we need one more test. We need to first
00898   // pull up just one child, then reflow the first frame again so all
00899   // the child frames fit.
00900   status = f->ResizeReflow(presContext, reflowMetrics, maxSize, nsnull);
00901 
00902   // Verify that the inline frame is complete
00903   if (NS_FRAME_NOT_IS_COMPLETE(status)) {
00904     printf("PushPull: failed to pull-up across empty frame (#6)\n");
00905     return PR_FALSE;
00906   }
00907 
00908   // Verify the inline frame maps all the children
00909   if ((f->ChildCount() != 3) || (f->GetLastContentOffset() != 2)) {
00910     printf("PushPull: bad last content offset or child count (#6)\n");
00911     return PR_FALSE;
00912   }
00913 
00914   // Verify that the next two inline frames are empty
00915   if (f1->ChildCount() != 0) {
00916     printf("PushPull: second frame isn't empty (#6)\n");
00917     return PR_FALSE;
00918   }
00919   if (f2->ChildCount() != 0) {
00920     printf("PushPull: third frame isn't empty (#6)\n");
00921     return PR_FALSE;
00922   }
00923 
00925   // Test #7
00926 
00927   // Test pulling up only some of the child frames across an empty frame, i.e.
00928   // a frame from which we've already pulled up all the children
00929   //
00930   // Set it up so that there's one child frame in the first inline frame
00931   maxSize.width = 100;
00932   f1->BreakFromPrevFlow();
00933   f2->BreakFromPrevFlow();
00934   status = f->ResizeReflow(presContext, reflowMetrics, maxSize, nsnull);
00935   NS_ASSERTION(nsIFrame::frNotComplete == status, "bad status");
00936   NS_ASSERTION(f->ChildCount() == 1, "bad child count");
00937   NS_ASSERTION(f->GetLastContentOffset() == 0, "bad mapping");
00938 
00939   // And one frame in f1
00940   maxSize.width = 300;
00941   f1->AppendToFlow(f);
00942   status = f1->ResizeReflow(presContext, reflowMetrics, maxSize, nsnull);
00943   NS_ASSERTION(nsIFrame::frNotComplete == status, "bad status");
00944   NS_ASSERTION(f1->ChildCount() == 1, "bad child count");
00945   NS_ASSERTION((f1->GetFirstContentOffset() == 1) && (f1->GetLastContentOffset() == 1), "bad mapping");
00946 
00947   // And one frame in f2
00948   f2->AppendToFlow(f1);
00949   maxSize.width = 1000;
00950   status = f2->ResizeReflow(presContext, reflowMetrics, maxSize, nsnull);
00951   NS_ASSERTION(nsIFrame::frComplete == status, "bad status");
00952   NS_ASSERTION(f2->ChildCount() == 1, "bad child count");
00953   NS_ASSERTION((f2->GetFirstContentOffset() == 2) && (f2->GetLastContentOffset() == 2), "bad mapping");
00954 
00955   // Now reflow the first inline frame wide enough so that the first two child
00956   // frames fit
00957   maxSize.width = 400;
00958   status = f->ResizeReflow(presContext, reflowMetrics, maxSize, nsnull);
00959 
00960   // Verify that the first inline frame is not complete
00961   if (NS_FRAME_IS_COMPLETE(status)) {
00962     printf("PushPull: bad status (#7)\n");
00963     return PR_FALSE;
00964   }
00965 
00966   // Verify the first inline frame maps two children
00967   if ((f->ChildCount() != 2) || (f->GetLastContentOffset() != 1)) {
00968     printf("PushPull: bad last content offset or child count (#7)\n");
00969     return PR_FALSE;
00970   }
00971 
00972   // Verify that the second inline frame is empty
00973   if (f1->ChildCount() != 0) {
00974     printf("PushPull: second frame isn't empty (#7)\n");
00975     return PR_FALSE;
00976   }
00977 
00978   // and that it's content offset must be correct, because the third inline
00979   // frame isn't empty
00980   // Note: don't check the last content offset, because for empty frames the value
00981   // is undefined...
00982   if (f1->GetFirstContentOffset() != f->NextChildOffset()) {
00983     printf("PushPull: second frame bad first content offset (#7): %d\n", f1->GetFirstContentOffset());
00984     return PR_FALSE;
00985   }
00986 
00987   // Verify that the third inline frame has one child frame
00988   if (f2->ChildCount() != 1) {
00989     printf("PushPull: third frame bad child count (#7): %d\n", f2->ChildCount());
00990     return PR_FALSE;
00991   }
00992 
00993   // and that its content offsets are correct
00994   if ((f2->GetFirstContentOffset() != 2) || (f2->GetLastContentOffset() != 2)) {
00995     printf("PushPull: third frame bad content mapping (#7)\n");
00996     return PR_FALSE;
00997   }
00998 
01000   // Test #8
01001 
01002   // Test partially pulling up a child. First get all the child frames back into
01003   // the first inline frame, and get rid of the third inline frame
01004   f2->BreakFromPrevFlow();
01005   f1->SetNextSibling(nsnull);
01006 
01007   maxSize.width = 1000;
01008   status = f->ResizeReflow(presContext, reflowMetrics, maxSize, nsnull);
01009   NS_ASSERTION(nsIFrame::frComplete == status, "bad status");
01010   NS_ASSERTION(f->ChildCount() == 3, "bad child count");
01011   f1->SetFirstContentOffset(f->NextChildOffset());  // don't trigger pre-reflow checks below
01012 
01013   // Now reflow the first inline frame so there are two frames pushed to the
01014   // next-in-flow
01015   maxSize.width = 100;
01016   status = f->ResizeReflow(presContext, reflowMetrics, maxSize, nsnull);
01017   NS_ASSERTION(nsIFrame::frNotComplete == status, "bad status");
01018   NS_ASSERTION((f->ChildCount() == 1) && (f1->ChildCount() == 2), "bad child count");
01019 
01020   // Reflow the first inline frame so that part only part of the second child frame
01021   // fits. In order to test this we need to make the second piece of content
01022   // splittable
01023   FixedSizeContent* c2 = (FixedSizeContent*)b->ChildAt(1);
01024   c2->SetIsSplittable(PR_TRUE);
01025 
01026   maxSize.width = 250;
01027   status = f->ResizeReflow(presContext, reflowMetrics, maxSize, nsnull);
01028   NS_ASSERTION(nsIFrame::frNotComplete == status, "bad status");
01029 
01030   // The first inline frame should have two child frames
01031   if (f->ChildCount() != 2) {
01032     printf("PushPull: bad child count when pulling up (#8): %d\n", f->ChildCount());
01033     return PR_FALSE;
01034   }
01035 
01036   // it should have a last content offset of 1, and it's last content should
01037   // not be complete
01038   if ((f->GetLastContentOffset() != 1) || f->LastContentIsComplete()) {
01039     printf("PushPull: bad content mapping when pulling up (#8)\n");
01040     return PR_FALSE;
01041   }
01042 
01043   // The continuing inline frame should also have two child frame
01044   if (f1->ChildCount() != 2) {
01045     printf("PushPull: continuing frame bad child count (#8): %d\n", f1->ChildCount());
01046     return PR_FALSE;
01047   }
01048 
01049   // and correct content offsets
01050   if ((f1->GetFirstContentOffset() != f1->FirstChild()->GetIndexInParent()) ||
01051       (f1->GetLastContentOffset() != (f1->LastChild()->GetIndexInParent()))) {
01052     printf("PushPull: continuing frame bad mapping (#8)\n");
01053     return PR_FALSE;
01054   }
01055 
01056   // The first child of f1 should be in the flow
01057   if (f1->FirstChild()->GetPrevInFlow() != f->LastChild()) {
01058     printf("PushPull: continuing frame bad flow (#8)\n");
01059     return PR_FALSE;
01060   }
01061 
01062   NS_RELEASE(b);
01063   return PR_TRUE;
01064 }
01065 
01066 // Test handling of splittable children
01067 //
01068 // Here's what this function tests:
01069 // 1. reflow unmapped correctly handles child frames that need to split
01070 // 2. reflow mapped correctly handles when the last child is incomplete
01071 // 3. reflow mapped correctly handles the case of a child that's incomplete
01072 //    and that already has a next-in-flow
01073 // 4. reflow mapped handles the case where a child that's incomplete now fits
01074 //    when reflowed again
01075 //    a) when the child has a next-in-flow
01076 //    b) when the child doesn't have a next-in-flow
01077 // 5. continuing frame checks its prev-in-flow's last child and if it's incomplete
01078 //    creates a continuing frame
01079 // 6. reflow mapped correctly handles child frames that need to be continued
01080 // 7. pulling up across empty frames resulting from deleting a child's next-in-flows
01081 static PRBool
01082 TestSplittableChildren(nsPresContext* presContext)
01083 {
01084   // Create an HTML container
01085   nsIContent* b;
01086   NS_NewHTMLContainer(&b, NS_NewAtom("span"));
01087 
01088   // Append three fixed width elements that can split
01089   b->AppendChildTo(new FixedSizeContent(100, 100, PR_TRUE));
01090   b->AppendChildTo(new FixedSizeContent(300, 300, PR_TRUE));
01091   b->AppendChildTo(new FixedSizeContent(200, 200, PR_TRUE));
01092 
01093   // Create an inline frame for the HTML container and set its
01094   // style context
01095   InlineFrame*      f = new InlineFrame(b, 0, nsnull);
01096   nsRefPtr<nsStyleContext> styleContext;
01097   styleContext = presContext->StyleSet()->ResolveStyleFor(b, nsnull);
01098 
01099   f->SetStyleContext(presContext,styleContext);
01100 
01102   // Test #1
01103 
01104   // Reflow the inline frame so only half of the first frame fits. This tests
01105   // reflow unmapped
01106   nsReflowMetrics         reflowMetrics;
01107   nsSize                  maxSize(50, 1000);
01108   nsIFrame::ReflowStatus  status;
01109 
01110   status = f->ResizeReflow(presContext, reflowMetrics, maxSize, nsnull);
01111   NS_ASSERTION(f->ChildCount() == 1, "bad child count");
01112 
01113   // Verify that the inline frame isn't complete
01114   if (nsIFrame::frNotComplete != status) {
01115     printf("Splittable: inline frame should be incomplete (#1): %d\n", status);
01116     return PR_FALSE;
01117   }
01118 
01119   // Verify the last content offset is correct
01120   if (f->GetLastContentOffset() != 0) {
01121     printf("Splittable: wrong last content offset (#1): %d\n", f->GetLastContentOffset());
01122     return PR_FALSE;
01123   }
01124 
01125   // Verify that the last child isn't complete
01126   if (f->LastContentIsComplete()) {
01127     printf("Splittable: child should not be complete (#1)\n");
01128     return PR_FALSE;
01129   }
01130 
01132   // Test #2
01133 
01134   // Now reflow the inline frame again and test reflow mapped
01135   status = f->ResizeReflow(presContext, reflowMetrics, maxSize, nsnull);
01136   NS_ASSERTION(f->ChildCount() == 1, "bad child count");
01137 
01138   // The inline frame should still be incomplete with a last content offset of 0
01139   if ((nsIFrame::frNotComplete != status) ||
01140       (f->GetLastContentOffset() != 0) || (f->LastContentIsComplete())) {
01141     printf("Splittable: reflow mapped failed (#2)\n");
01142     return PR_FALSE;
01143   }
01144 
01145   // There should be an overflow list containing the first child's next-in-flow
01146   if ((nsnull == f->OverflowList()) ||
01147       (f->OverflowList() != f->FirstChild()->GetNextInFlow())) {
01148     printf("Splittable: should be an overflow list (#2)\n");
01149     return PR_FALSE;
01150   }
01151 
01152   // Verify that the first child has a null next sibling. Children on the overflow
01153   // list should be disconnected from the mapped children
01154   if (nsnull != f->FirstChild()->GetNextSibling()) {
01155     printf("Splittable: first child has overflow list as next sibling (#2)\n");
01156     return PR_FALSE;
01157   }
01158 
01160   // Test #3
01161 
01162   // Reflow it again with the same size and make sure we don't add any more
01163   // frames to the overflow list
01164   status = f->ResizeReflow(presContext, reflowMetrics, maxSize, nsnull);
01165   NS_ASSERTION(nsIFrame::frNotComplete == status, "bad status");
01166   NS_ASSERTION(f->ChildCount() == 1, "bad child count");
01167 
01168   // The inline frame should still have a last content offset of 0 and
01169   // the last content child should still be incomplete
01170   if ((f->GetLastContentOffset() != 0) || (f->LastContentIsComplete())) {
01171     printf("Splittable: reflow mapped again failed (#3)\n");
01172     return PR_FALSE;
01173   }
01174 
01175   // The overflow list should still have only one child
01176   if (LengthOf(f->OverflowList()) != 1) {
01177     printf("Splittable: reflow mapped again has bad overflow list (#3)\n");
01178     return PR_FALSE;
01179   }
01180 
01182   // Test #4a
01183 
01184   // Now reflow the inline frame so that the the first child now completely fits.
01185   // This tests how reflow mapped handles the case of a child that's incomplete
01186   // and has a next-in-flow, and when reflowed now fits.
01187   maxSize.width = 100;
01188   status = f->ResizeReflow(presContext, reflowMetrics, maxSize, nsnull);
01189   NS_ASSERTION(nsIFrame::frNotComplete == status, "bad status");
01190   NS_ASSERTION(f->ChildCount() == 1, "bad child count");
01191 
01192   // The inline frame should have a last content offset of 0
01193   if (f->GetLastContentOffset() != 0) {
01194     printf("Splittable: wrong last content offset (#4a): %d\n",
01195            f->GetLastContentOffset());
01196     return PR_FALSE;
01197   }
01198 
01199   // The first child should now be complete
01200   if (!f->LastContentIsComplete()) {
01201     printf("Splittable: last child isn't complete (#4a)\n");
01202     return PR_FALSE;
01203   }
01204 
01205   // it should not have a next-in-flow
01206   if (nsnull != f->FirstChild()->GetNextInFlow()) {
01207     printf("Splittable: unexpected next-in-flow (#4a)\n");
01208     return PR_FALSE;
01209   }
01210 
01211   // and there should be no overflow list
01212   if (nsnull != f->OverflowList()) {
01213     printf("Splittable: unexpected overflow list (#4a)\n");
01214     return PR_FALSE;
01215   }
01216 
01218   // Test #4b
01219 
01220   // This is a variation of the previous test where the child doesn't have
01221   // a next-in-flow. Reflow the inline frame so the first child is incomplete
01222   maxSize.width = 50;
01223   status = f->ResizeReflow(presContext, reflowMetrics, maxSize, nsnull);
01224   NS_ASSERTION(!f->LastContentIsComplete(), "last child should be incomplete");
01225   NS_ASSERTION(nsnull != f->OverflowList(), "no overflow list");
01226   NS_ASSERTION(f->FirstChild()->GetNextInFlow() == f->OverflowList(), "bad next-in-flow");
01227 
01228   // Now get rid of the overflow list and the first child's next-in-flow
01229   f->FirstChild()->SetNextInFlow(nsnull);
01230   f->ClearOverflowList();
01231 
01232   // Now reflow the inline frame so the first child fits. This tests that we
01233   // correctly reset mLastContentIsComplete when the frame that was incomplete
01234   // doesn't have a next-in-flow
01235   maxSize.width = 100;
01236   status = f->ResizeReflow(presContext, reflowMetrics, maxSize, nsnull);
01237   NS_ASSERTION(nsIFrame::frNotComplete == status, "bad status");
01238   NS_ASSERTION(f->ChildCount() == 1, "bad child count");
01239 
01240   // Verify that the first child is complete
01241   if (!f->LastContentIsComplete()) {
01242     printf("Splittable: last content is NOT complete (#4b)\n");
01243     return PR_FALSE;
01244   }
01245 
01247   // Test #5
01248 
01249   // Create a continuing inline frame and have it pick up with the second
01250   // child
01251   InlineFrame* f1 = new InlineFrame(b, 0, nsnull);
01252 
01253   f1->SetStyleContext(f->GetStyleContext(presContext));
01254   f->SetNextSibling(f1);
01255   f->SetNextInFlow(f1);
01256   f1->SetPrevInFlow(f);
01257 
01258   // Reflow the continuing inline frame so its second frame is not complete
01259   maxSize.width = 150;
01260   status = f1->ResizeReflow(presContext, reflowMetrics, maxSize, nsnull);
01261   NS_ASSERTION(nsIFrame::frNotComplete == status, "bad status");
01262   NS_ASSERTION((f1->GetFirstContentOffset() == 1) && (f1->GetLastContentOffset() == 1), "bad offsets");
01263   NS_ASSERTION(!f1->LastContentIsComplete(), "should not be complete");
01264 
01265   // Create a third continuing inline frame and verify that it picks up by continuing
01266   // the second child frame. This tests the case where the prev-in-flow's last
01267   // child isn't complete and there's no overflow list
01268   NS_ASSERTION(nsnull == f1->OverflowList(), "unexpected overflow list");
01269   InlineFrame* f2 = new InlineFrame(b, 0, nsnull);
01270 
01271   f2->SetStyleContext(f->GetStyleContext(presContext));
01272   f1->SetNextSibling(f2);
01273   f1->SetNextInFlow(f2);
01274   f2->SetPrevInFlow(f1);
01275 
01276   // Make the width large enough for all the remaining frames to fit
01277   maxSize.width = 1000;
01278   status = f2->ResizeReflow(presContext, reflowMetrics, maxSize, nsnull);
01279   NS_ASSERTION(nsIFrame::frComplete == status, "bad status");
01280 
01281   // Verify the first child is a continuing frame of its prev-in-flow's
01282   // child
01283   if (f2->FirstChild()->GetPrevInFlow() != f1->FirstChild()) {
01284     printf("Splittable: child frame bad prev-in-flow (#5)\n");
01285     return PR_FALSE;
01286   }
01287 
01288   // Verify the two child frames have the same content offset and so
01289   // do their parents
01290   if ((f2->FirstChild()->GetIndexInParent() != f1->FirstChild()->GetIndexInParent()) ||
01291       (f2->GetFirstContentOffset() != f1->GetFirstContentOffset())) {
01292     printf("Splittable: bad content offset (#5)\n");
01293     return PR_FALSE;
01294   }
01295 
01296   // Verify that the third continuing inline frame is complete and that it has two children
01297   if ((nsnull != f2->FirstChild()->GetNextInFlow()) || (f2->ChildCount() != 2)) {
01298     printf("Splittable: bad continuing frame (#5)\n");
01299     return PR_FALSE;
01300   }
01301 
01303   // Test #6
01304 
01305   // Test how reflow mapped handles the case where an existing child doesn't
01306   // fit when reflowed. We care about the case where the child doesn't have
01307   // a continuing frame and so we have to create one
01308   //
01309   // Reflow the first inline frame so all the child frames fit
01310   maxSize.width = 1000;
01311   status = f->ResizeReflow(presContext, reflowMetrics, maxSize, nsnull);
01312   NS_ASSERTION(nsIFrame::frComplete == status, "bad status");
01313   NS_ASSERTION((f->ChildCount() == 3) && (f->GetLastContentOffset() == 2), "bad count");
01314 
01315   f1->SetFirstContentOffset(f->NextChildOffset());  // don't trigger pre-reflow checks
01316   f2->SetFirstContentOffset(f->NextChildOffset());  // don't trigger pre-reflow checks
01317 
01318   // Now reflow it so that the first child needs to be continued
01319   maxSize.width = 50;
01320   status = f->ResizeReflow(presContext, reflowMetrics, maxSize, nsnull);
01321   NS_ASSERTION(nsIFrame::frNotComplete == status, "bad status");
01322 
01323   // Verify there's only one child in f
01324   if (f->ChildCount() != 1) {
01325     printf("Splittable: bad child count (#6): %d\n", f->ChildCount());
01326     return PR_FALSE;
01327   }
01328 
01329   // Verify the last content offset and that the last content is not complete
01330   if ((f->GetLastContentOffset() != 0) || f->LastContentIsComplete()) {
01331     printf("Splittable: bad content mapping (#6)\n");
01332     return PR_FALSE;
01333   }
01334 
01335   // Verify that there are three children in the next-in-flow, and that
01336   // the first child is a continuing frame
01337   if (f1->ChildCount() != 3) {
01338     printf("Splittable: continuing frame bad child count (#6): %d\n",
01339            f1->ChildCount());
01340     return PR_FALSE;
01341   }
01342   if (f1->FirstChild()->GetPrevInFlow() != f->LastChild()) {
01343     printf("Splittable: continuing frame bad child flow (#6)\n");
01344     return PR_FALSE;
01345   }
01346 
01347   // Verify the next-in-flow's content offsets
01348   if ((f1->GetFirstContentOffset() != 0) || (f1->GetLastContentOffset() != 2)) {
01349     printf("Splittable: continuing frame bad mapping (#6)\n");
01350     return PR_FALSE;
01351   }
01352 
01354   // Test #7
01355 
01356   // Test pulling up across empty frames resulting from deleting a child
01357   // frame's next-in-flows
01358   //
01359   // Reflow the first inline frame so all the child frames fit
01360   maxSize.width = 1000;
01361   status = f->ResizeReflow(presContext, reflowMetrics, maxSize, nsnull);
01362   NS_ASSERTION(nsIFrame::frComplete == status, "bad status");
01363   NS_ASSERTION((f->ChildCount() == 3) && (f->GetLastContentOffset() == 2), "bad count");
01364 
01365   f1->SetFirstContentOffset(f->NextChildOffset());  // don't trigger pre-reflow checks
01366   f2->SetFirstContentOffset(f->NextChildOffset());  // don't trigger pre-reflow checks
01367 
01368   // Now reflow the first inline frame so that the second child frame is continued
01369   maxSize.width = 200;
01370   status = f->ResizeReflow(presContext, reflowMetrics, maxSize, nsnull);
01371   NS_ASSERTION(nsIFrame::frNotComplete == status, "bad status");
01372 
01373   // Verify the first inline frame's child count is correct
01374   if (f->ChildCount() != 2) {
01375     printf("Splittable: bad child count (#7): %d\n", f->ChildCount());
01376     return PR_FALSE;
01377   }
01378 
01379   // Verify the last content offset and that the last frame isn't complete
01380   if ((f->GetLastContentOffset() != 1) || (f->LastContentIsComplete())) {
01381     printf("Splittable: bad mapping (#7)\n");
01382     return PR_FALSE;
01383   }
01384 
01385   // The second inline frame should have two children as well
01386   if (f1->ChildCount() != 2) {
01387     printf("Splittable: continuing frame bad child count (#7): %d\n", f1->ChildCount());
01388     return PR_FALSE;
01389   }
01390 
01391   // and that its content offsets are correct
01392   if ((f1->GetFirstContentOffset() != 1) || (f1->GetLastContentOffset() != 2)) {
01393     printf("Splittable: continuing frame bad mapping (#7)\n");
01394     return PR_FALSE;
01395   }
01396 
01397   // Now reflow the second inline frame so that only the continuing child frame
01398   // fits and the last frame is pushed to the third inline frame
01399   maxSize.width = 200;
01400   status = f1->ResizeReflow(presContext, reflowMetrics, maxSize, nsnull);
01401   NS_ASSERTION(nsIFrame::frNotComplete == status, "bad status");
01402 
01403   // Verify the second inline frame's child count and last content offset
01404   if ((f1->ChildCount() != 1) || (f1->GetLastContentOffset() != 1)) {
01405     printf("Splittable: continuing frame bad child count or mapping (#7)\n");
01406     return PR_FALSE;
01407   }
01408 
01409   // Verify the third inline frame maps the last child frame and has correct
01410   // content offsets
01411   if ((f2->ChildCount() != 1) ||
01412       (f2->GetFirstContentOffset() != 2) || (f2->GetLastContentOffset() != 2)) {
01413     printf("Splittable: last continuing frame bad child count or mapping (#7)\n");
01414     return PR_FALSE;
01415   }
01416 
01417   // Now the real test. Reflow the first inline frame so all the child frames fit.
01418   // What we're testing is that the pull-up code correctly handles the case where
01419   // there's an empty frame that results from the second child frame's next-in-flow
01420   // being deleted
01421   maxSize.width = 1000;
01422   status = f->ResizeReflow(presContext, reflowMetrics, maxSize, nsnull);
01423 
01424   // The inline frame should be complete and map all three child frames
01425   if ((nsIFrame::frComplete != status) || (f->ChildCount() != 3)) {
01426     printf("Splittable: first inline frame does not map all the child frames (#7)\n");
01427     return PR_FALSE;
01428   }
01429 
01430   NS_RELEASE(b);
01431   return PR_TRUE;
01432 }
01433 
01434 // Test computing the max element size
01435 //
01436 // Here's what this function tests:
01437 // 1. reflow unmapped correctly computes the max element size
01438 // 2. reflow mapped correctly computes the max element size
01439 // 3. reflow mapped/unmapped work together to compute the correct result
01440 // 4. pulling-up children code computes the correct result
01441 static PRBool
01442 TestMaxElementSize(nsPresContext* presContext)
01443 {
01444   // Create an HTML container
01445   nsIContent* b;
01446   NS_NewHTMLContainer(&b, NS_NewAtom("span"));
01447 
01448   // Append three fixed width elements.
01449   b->AppendChildTo(new FixedSizeContent(100, 100));
01450   b->AppendChildTo(new FixedSizeContent(300, 300));
01451   b->AppendChildTo(new FixedSizeContent(200, 200));
01452 
01453   // Create an inline frame for the HTML container and set its
01454   // style context
01455   InlineFrame*      f = new InlineFrame(b, 0, nsnull);
01456   nsRefPtr<nsStyleContext> styleContext;
01457   styleContext = presContext->StyleSet()->ResolveStyleFor(b, nsnull);
01458 
01459   f->SetStyleContext(presContext,styleContext);
01460 
01462   // Test #1
01463 
01464   // Reflow the inline frame and check our max element size computation when reflowing
01465   // unmapped children
01466   //
01467   // Note: we've already tested elsewhere that we correctly handle the case
01468   // where aMaxElementSize is NULL and that we don't GPF...
01469   nsSize                  maxElementSize(0, 0);
01470   nsReflowMetrics         reflowMetrics;
01471   nsSize                  maxSize(1000, 1000);
01472   nsIFrame::ReflowStatus  status;
01473 
01474   status = f->ResizeReflow(presContext, reflowMetrics, maxSize, &maxElementSize);
01475 
01476   // Verify that the max element size is correct
01477   NS_ASSERTION(nsIFrame::frComplete == status, "isn't complete");
01478   if ((maxElementSize.width != f->MaxChildWidth()) ||
01479       (maxElementSize.height != f->MaxChildHeight())) {
01480     printf("MaxElementSize: wrong result in reflow unmapped (#1): (%d, %d)\n",
01481       maxElementSize.width, maxElementSize.height);
01482     return PR_FALSE;
01483   }
01484 
01486   // Test #2
01487 
01488   // Now compute it again and check the computation when reflowing
01489   // mapped children
01490   maxElementSize.SizeTo(0, 0);
01491   status = f->ResizeReflow(presContext, reflowMetrics, maxSize, &maxElementSize);
01492   NS_ASSERTION(nsIFrame::frComplete == status, "isn't complete");
01493 
01494   if ((maxElementSize.width != f->MaxChildWidth()) ||
01495       (maxElementSize.height != f->MaxChildHeight())) {
01496     printf("MaxElementSize: wrong result in reflow mapped (#2): (%d, %d)\n",
01497       maxElementSize.width, maxElementSize.height);
01498     return PR_FALSE;
01499   }
01500 
01502   // Test #3
01503 
01504   // Check that reflow mapped/unmapped work together. Reflow the inline frame
01505   // so that only the first two frames (including the largest frame) fit
01506   nsIFrame* maxChild = f->ChildAt(1);
01507   nsPoint   origin;
01508 
01509   maxChild->GetOrigin(origin);
01510   maxSize.width = origin.x + maxChild->GetWidth();
01511   maxElementSize.SizeTo(0, 0);
01512   status = f->ResizeReflow(presContext, reflowMetrics, maxSize, &maxElementSize);
01513 
01514   if ((nsIFrame::frNotComplete != status) || (f->ChildCount() != 2)) {
01515     printf("MaxElementSize: reflow failed (#3)\n");
01516     return PR_FALSE;
01517   }
01518 
01519   // Now reflow it again and check that the maximum element size is correct
01520   maxSize.width = 1000;
01521   maxElementSize.SizeTo(0, 0);
01522   status = f->ResizeReflow(presContext, reflowMetrics, maxSize, &maxElementSize);
01523   NS_ASSERTION(nsIFrame::frComplete == status, "isn't complete");
01524 
01525   if ((maxElementSize.width != f->MaxChildWidth()) ||
01526       (maxElementSize.height != f->MaxChildHeight())) {
01527     printf("MaxElementSize: wrong result in reflow mapped/unmapped (#3): (%d, %d)\n",
01528            maxElementSize.width, maxElementSize.height);
01529     return PR_FALSE;
01530   }
01531 
01533   // Test #4
01534 
01535   // Last thing to check is pulling up children. Reflow it so only the first two
01536   // frames (including the largest frame) fit
01537   maxChild = f->ChildAt(1);
01538   maxChild->GetOrigin(origin);
01539   maxSize.width = origin.x + maxChild->GetWidth();
01540   maxElementSize.SizeTo(0, 0);
01541   status = f->ResizeReflow(presContext, reflowMetrics, maxSize, &maxElementSize);
01542   NS_ASSERTION(f->ChildCount() == 2, "unexpected child count");
01543 
01544   // Create a continuing inline frame and reflow it
01545   InlineFrame* f1 = new InlineFrame(b, 0, nsnull);
01546 
01547   f1->SetStyleContext(f->GetStyleContext(presContext));
01548   f->SetNextSibling(f1);
01549   f->SetNextInFlow(f1);
01550   f1->SetPrevInFlow(f);
01551   maxSize.width = 1000;
01552   status = f1->ResizeReflow(presContext, reflowMetrics, maxSize, &maxElementSize);
01553   NS_ASSERTION((nsIFrame::frComplete == status) && (f1->ChildCount() == 1), "bad continuing frame");
01554 
01555   // Now reflow the first inline frame so all child frames fit, and verify the
01556   // max element size is correct
01557   maxElementSize.SizeTo(0, 0);
01558   status = f->ResizeReflow(presContext, reflowMetrics, maxSize, &maxElementSize);
01559   NS_ASSERTION(nsIFrame::frComplete == status, "isn't complete");
01560   NS_ASSERTION(f1->ChildCount() == 0, "pull-up failed");
01561 
01562   if ((maxElementSize.width != f->MaxChildWidth()) ||
01563       (maxElementSize.height != f->MaxChildHeight())) {
01564     printf("MaxElementSize: wrong result in reflow mapped/pull-up (#4): (%d, %d)\n",
01565            maxElementSize.width, maxElementSize.height);
01566     return PR_FALSE;
01567   }
01568 
01569   // Now reflow it so that the largest size frame is in the continuing inline frame
01570   f1->SetFirstContentOffset(f->NextChildOffset());  // don't trigger pre-reflow checks
01571   maxSize.width = f->FirstChild()->GetWidth();
01572   status = f->ResizeReflow(presContext, reflowMetrics, maxSize, &maxElementSize);
01573   NS_ASSERTION(f->ChildCount() == 1, "unexpected child count");
01574   NS_ASSERTION(f1->ChildCount() == 2, "unexpected child count");
01575 
01576   // Now reflow the first inline frame so all child frames fit, and verify the
01577   // max element size is correct
01578   maxSize.width = 1000;
01579   maxElementSize.SizeTo(0, 0);
01580   status = f->ResizeReflow(presContext, reflowMetrics, maxSize, &maxElementSize);
01581   NS_ASSERTION(nsIFrame::frComplete == status, "isn't complete");
01582   NS_ASSERTION(f1->ChildCount() == 0, "pull-up failed");
01583 
01584   if ((maxElementSize.width != f->MaxChildWidth()) ||
01585       (maxElementSize.height != f->MaxChildHeight())) {
01586     printf("MaxElementSize: wrong result in reflow mapped/pull-up (#4): (%d, %d)\n",
01587            maxElementSize.width, maxElementSize.height);
01588     return PR_FALSE;
01589   }
01590 
01591   NS_RELEASE(b);
01592   return PR_TRUE;
01593 }
01594 #endif
01595 
01596 int main(int argc, char** argv)
01597 {
01598 #if 0
01599   // Create test document and presentation context
01600   MyDocument *myDoc = new MyDocument();
01601   nsPresContext* presContext;
01602   nsIDeviceContext *dx;
01603   
01604   static NS_DEFINE_IID(kDeviceContextCID, NS_DEVICE_CONTEXT_CID);
01605 
01606   nsresult rv = CallCreateInstance(kDeviceContextCID, &dx);
01607 
01608   if (NS_OK == rv) {
01609     dx->Init(nsull);
01610      dx->SetDevUnitsToAppUnits(dx->DevUnitsToTwips());
01611      dx->SetAppUnitsToDevUnits(dx->TwipsToDevUnits());
01612   }
01613 
01614   NS_NewGalleyContext(&presContext);
01615 
01616   presContext->Init(dx, nsnull);
01617 
01618   // Test basic reflowing of unmapped children
01619   if (!TestReflowUnmapped(presContext)) {
01620     return -1;
01621   }
01622 
01623   // Test the case where the frame max size is too small for even one child frame
01624   if (!TestChildrenThatDontFit(presContext)) {
01625     return -1;
01626   }
01627 
01628   if (!TestOverflow(presContext)) {
01629     return -1;
01630   }
01631 
01632   if (!TestPushingPulling(presContext)) {
01633     return -1;
01634   }
01635 
01636   if (!TestSplittableChildren(presContext)) {
01637     return -1;
01638   }
01639 
01640   if (!TestMaxElementSize(presContext)) {
01641     return -1;
01642   }
01643 
01644   /*
01645   if (!TestIncremental(presContext)) {
01646     return -1;
01647   }
01648 
01649   if (!TestBorderPadding(presContext)) {
01650     return -1;
01651   }
01652 
01653   if (!TestChildMargins(presContext)) {
01654     return -1;
01655   }
01656 
01657   if (!TestAlignment(presContext)) {
01658     return -1;
01659   }
01660 
01661   if (!TestDisplayNone(presContext)) {
01662     return -1;
01663   }
01664 
01665   if (!TestRelativePositioning(presContext)) {
01666     return -1;
01667   }
01668 
01669   if (!TestAbsolutePositiong(presContext)) {
01670     return -1;
01671   }
01672   */
01673 
01674   presContext->Release();
01675   myDoc->Release();
01676 #endif
01677   return 0;
01678 }