D133426
[gecko.git] / 
blobd1334262293567dea9177ad03c6cc719c1d479fa
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  *
3  * The contents of this file are subject to the Netscape Public License
4  * Version 1.0 (the "NPL"); you may not use this file except in
5  * compliance with the NPL.  You may obtain a copy of the NPL at
6  * http://www.mozilla.org/NPL/
7  *
8  * Software distributed under the NPL is distributed on an "AS IS" basis,
9  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
10  * for the specific language governing rights and limitations under the
11  * NPL.
12  *
13  * The Initial Developer of this code under the NPL is Netscape
14  * Communications Corporation.  Portions created by Netscape are
15  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
16  * Reserved.
17  */
18 #include "nsIPresShell.h"
19 #include "nsIPresContext.h"
20 #include "nsIContent.h"
21 #include "nsIDocument.h"
22 #include "nsIDocumentObserver.h"
23 #include "nsIStyleSet.h"
24 #include "nsIStyleContext.h"
25 #include "nsFrame.h"
26 #include "nsIReflowCommand.h"
27 #include "nsIViewManager.h"
28 #include "nsCRT.h"
29 #include "plhash.h"
30 #include "prlog.h"
31 #include "nsVoidArray.h"
32 #include "nsIPref.h"
33 #include "nsIViewObserver.h"
34 #include "nsContainerFrame.h"
35 #include "nsHTMLIIDs.h"
36 #include "nsIDeviceContext.h"
37 #include "nsIEventStateManager.h"
38 #include "nsDOMEvent.h"
39 #include "nsHTMLParts.h"
41 static PRBool gsNoisyRefs = PR_FALSE;
42 #undef NOISY
44 #if 0
45 static PLHashNumber
46 HashKey(nsIFrame* key)
48   return (PLHashNumber) key;
51 static PRIntn
52 CompareKeys(nsIFrame* key1, nsIFrame* key2)
54   return key1 == key2;
57 class FrameHashTable {
58 public:
59   FrameHashTable();
60   ~FrameHashTable();
62   void* Get(nsIFrame* aKey);
63   void* Put(nsIFrame* aKey, void* aValue);
64   void* Remove(nsIFrame* aKey);
66 protected:
67   PLHashTable* mTable;
70 FrameHashTable::FrameHashTable()
72   mTable = PL_NewHashTable(8, (PLHashFunction) HashKey,
73                            (PLHashComparator) CompareKeys,
74                            (PLHashComparator) nsnull,
75                            nsnull, nsnull);
78 FrameHashTable::~FrameHashTable()
80   // XXX if debugging then we should assert that the table is empty
81   PL_HashTableDestroy(mTable);
84 /**
85  * Get the data associated with a frame.
86  */
87 void*
88 FrameHashTable::Get(nsIFrame* aKey)
90   PRInt32 hashCode = (PRInt32) aKey;
91   PLHashEntry** hep = PL_HashTableRawLookup(mTable, hashCode, aKey);
92   PLHashEntry* he = *hep;
93   if (nsnull != he) {
94     return he->value;
95   }
96   return nsnull;
99 /**
100  * Create an association between a frame and some data. This call
101  * returns an old association if there was one (or nsnull if there
102  * wasn't).
103  */
104 void*
105 FrameHashTable::Put(nsIFrame* aKey, void* aData)
107   PRInt32 hashCode = (PRInt32) aKey;
108   PLHashEntry** hep = PL_HashTableRawLookup(mTable, hashCode, aKey);
109   PLHashEntry* he = *hep;
110   if (nsnull != he) {
111     void* oldValue = he->value;
112     he->value = aData;
113     return oldValue;
114   }
115   PL_HashTableRawAdd(mTable, hep, hashCode, aKey, aData);
116   return nsnull;
120  * Remove an association between a frame and it's data. This returns
121  * the old associated data.
122  */
123 void*
124 FrameHashTable::Remove(nsIFrame* aKey)
126   PRInt32 hashCode = (PRInt32) aKey;
127   PLHashEntry** hep = PL_HashTableRawLookup(mTable, hashCode, aKey);
128   PLHashEntry* he = *hep;
129   void* oldValue = nsnull;
130   if (nsnull != he) {
131     oldValue = he->value;
132     PL_HashTableRawRemove(mTable, hep, he);
133   }
134   return oldValue;
136 #endif
138 //----------------------------------------------------------------------
140 static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
141 static NS_DEFINE_IID(kIPresShellIID, NS_IPRESSHELL_IID);
142 static NS_DEFINE_IID(kIDocumentObserverIID, NS_IDOCUMENT_OBSERVER_IID);
143 static NS_DEFINE_IID(kIViewObserverIID, NS_IVIEWOBSERVER_IID);
145 class PresShell : public nsIPresShell, public nsIViewObserver,
146                   private nsIDocumentObserver
149 public:
150   PresShell();
152   void* operator new(size_t sz) {
153     void* rv = new char[sz];
154     nsCRT::zero(rv, sz);
155     return rv;
156   }
158   // nsISupports
159   NS_DECL_ISUPPORTS
161   // nsIDocumentObserver
162   NS_IMETHOD BeginUpdate(nsIDocument *aDocument);
163   NS_IMETHOD EndUpdate(nsIDocument *aDocument);
164   NS_IMETHOD BeginLoad(nsIDocument *aDocument);
165   NS_IMETHOD EndLoad(nsIDocument *aDocument);
166   NS_IMETHOD BeginReflow(nsIDocument *aDocument, nsIPresShell* aShell);
167   NS_IMETHOD EndReflow(nsIDocument *aDocument, nsIPresShell* aShell);
168   NS_IMETHOD ContentChanged(nsIDocument *aDocument,
169                             nsIContent* aContent,
170                             nsISupports* aSubContent);
171   NS_IMETHOD AttributeChanged(nsIDocument *aDocument,
172                               nsIContent*  aContent,
173                               nsIAtom*     aAttribute,
174                               PRInt32      aHint);
175   NS_IMETHOD ContentAppended(nsIDocument *aDocument,
176                              nsIContent* aContainer,
177                              PRInt32     aNewIndexInContainer);
178   NS_IMETHOD ContentInserted(nsIDocument *aDocument,
179                              nsIContent* aContainer,
180                              nsIContent* aChild,
181                              PRInt32 aIndexInContainer);
182   NS_IMETHOD ContentReplaced(nsIDocument *aDocument,
183                              nsIContent* aContainer,
184                              nsIContent* aOldChild,
185                              nsIContent* aNewChild,
186                              PRInt32 aIndexInContainer);
187   NS_IMETHOD ContentRemoved(nsIDocument *aDocument,
188                             nsIContent* aContainer,
189                             nsIContent* aChild,
190                             PRInt32 aIndexInContainer);
191   NS_IMETHOD StyleSheetAdded(nsIDocument *aDocument,
192                              nsIStyleSheet* aStyleSheet);
193   NS_IMETHOD StyleSheetDisabledStateChanged(nsIDocument *aDocument,
194                                             nsIStyleSheet* aStyleSheet,
195                                             PRBool aDisabled);
196   NS_IMETHOD DocumentWillBeDestroyed(nsIDocument *aDocument);
198   // nsIPresShell
199   NS_IMETHOD Init(nsIDocument* aDocument,
200                   nsIPresContext* aPresContext,
201                   nsIViewManager* aViewManager,
202                   nsIStyleSet* aStyleSet);
203   virtual nsIDocument* GetDocument();
204   virtual nsIPresContext* GetPresContext();
205   virtual nsIViewManager* GetViewManager();
206   virtual nsIStyleSet* GetStyleSet();
207   NS_IMETHOD EnterReflowLock();
208   NS_IMETHOD ExitReflowLock();
209   virtual void BeginObservingDocument();
210   virtual void EndObservingDocument();
211   NS_IMETHOD InitialReflow(nscoord aWidth, nscoord aHeight);
212   NS_IMETHOD ResizeReflow(nscoord aWidth, nscoord aHeight);
213   NS_IMETHOD StyleChangeReflow();
214   virtual nsIFrame* GetRootFrame();
215   virtual nsIFrame* FindFrameWithContent(nsIContent* aContent);
216   virtual void AppendReflowCommand(nsIReflowCommand* aReflowCommand);
217   virtual void ProcessReflowCommands();
218   virtual void ClearFrameRefs(nsIFrame*);
219   NS_IMETHOD CreateRenderingContext(nsIFrame *aFrame, nsIRenderingContext *&aContext);
221   //nsIViewObserver interface
223   NS_IMETHOD Paint(nsIView *aView,
224                    nsIRenderingContext& aRenderingContext,
225                    const nsRect&        aDirtyRect);
226   NS_IMETHOD HandleEvent(nsIView*        aView,
227                          nsGUIEvent*     aEvent,
228                          nsEventStatus&  aEventStatus);
229   NS_IMETHOD Scrolled(nsIView *aView);
230   NS_IMETHOD ResizeReflow(nsIView *aView, nscoord aWidth, nscoord aHeight);
232 protected:
233   ~PresShell();
235 #ifdef NS_DEBUG
236   void VerifyIncrementalReflow();
237 #endif
239   nsIDocument* mDocument;
240   nsIPresContext* mPresContext;
241   nsIStyleSet* mStyleSet;
242   nsIFrame* mRootFrame;
243   nsIViewManager* mViewManager;
244   PRUint32 mUpdateCount;
245   nsVoidArray mReflowCommands;
246   PRUint32 mReflowLockCount;
247   PRBool mIsDestroying;
248   nsIFrame* mCurrentEventFrame;
251 #ifdef NS_DEBUG
253  * Note: the log module is created during library initialization which
254  * means that you cannot perform logging before then.
255  */
256 static PRLogModuleInfo* gLogModule = PR_NewLogModule("verifyreflow");
257 #endif
259 static PRBool gVerifyReflow = PRBool(0x55);
261 NS_LAYOUT PRBool
262 nsIPresShell::GetVerifyReflowEnable()
264 #ifdef NS_DEBUG
265   if (gVerifyReflow == PRBool(0x55)) {
266     gVerifyReflow = 0 != gLogModule->level;
267     printf("Note: verifyreflow is %sabled\n",
268            gVerifyReflow ? "en" : "dis");
269   }
270 #endif
271   return gVerifyReflow;
274 NS_LAYOUT void
275 nsIPresShell::SetVerifyReflowEnable(PRBool aEnabled)
277   gVerifyReflow = aEnabled;
280 //----------------------------------------------------------------------
282 NS_LAYOUT nsresult
283 NS_NewPresShell(nsIPresShell** aInstancePtrResult)
285   NS_PRECONDITION(nsnull != aInstancePtrResult, "null ptr");
286   if (nsnull == aInstancePtrResult) {
287     return NS_ERROR_NULL_POINTER;
288   }
289   PresShell* it = new PresShell();
290   if (nsnull == it) {
291     return NS_ERROR_OUT_OF_MEMORY;
292   }
293   return it->QueryInterface(kIPresShellIID, (void **) aInstancePtrResult);
296 PresShell::PresShell()
298   //XXX joki 11/17 - temporary event hack.
299   mIsDestroying = PR_FALSE;
302 #ifdef NS_DEBUG
303 // for debugging only
304 nsrefcnt PresShell::AddRef(void)
306   if (gsNoisyRefs) printf("PresShell: AddRef: %x, cnt = %d \n",this, mRefCnt+1);
307   return ++mRefCnt;
310 // for debugging only
311 nsrefcnt PresShell::Release(void)
313   if (gsNoisyRefs==PR_TRUE) printf("PresShell Release: %x, cnt = %d \n",this, mRefCnt-1);
314   if (--mRefCnt == 0) {
315     if (gsNoisyRefs==PR_TRUE) printf("PresShell Delete: %x, \n",this);
316     delete this;
317     return 0;
318   }
319   return mRefCnt;
321 #else
322 NS_IMPL_ADDREF(PresShell)
323 NS_IMPL_RELEASE(PresShell)
324 #endif
326 nsresult
327 PresShell::QueryInterface(const nsIID& aIID, void** aInstancePtr)
329   if (aIID.Equals(kIPresShellIID)) {
330     nsIPresShell* tmp = this;
331     *aInstancePtr = (void*) tmp;
332     NS_ADDREF_THIS();
333     return NS_OK;
334   }
335   if (aIID.Equals(kIDocumentObserverIID)) {
336     nsIDocumentObserver* tmp = this;
337     *aInstancePtr = (void*) tmp;
338     NS_ADDREF_THIS();
339     return NS_OK;
340   }
341   if (aIID.Equals(kIViewObserverIID)) {
342     nsIViewObserver* tmp = this;
343     *aInstancePtr = (void*) tmp;
344     NS_ADDREF_THIS();
345     return NS_OK;
346   }
347   if (aIID.Equals(kISupportsIID)) {
348     nsIPresShell* tmp = this;
349     nsISupports* tmp2 = tmp;
350     *aInstancePtr = (void*) tmp2;
351     NS_ADDREF_THIS();
352     return NS_OK;
353   }
354   return NS_NOINTERFACE;
357 PresShell::~PresShell()
359   mRefCnt = 99;/* XXX hack! get around re-entrancy bugs */
360   mIsDestroying = PR_TRUE;
361   if (nsnull != mRootFrame) {
362     mRootFrame->DeleteFrame(*mPresContext);
363   }
364   NS_IF_RELEASE(mViewManager);
365   //Release mPresContext after mViewManager
366   NS_IF_RELEASE(mPresContext);
367   NS_IF_RELEASE(mStyleSet);
368   if (nsnull != mDocument) {
369     mDocument->DeleteShell(this);
370     NS_RELEASE(mDocument);
371   }
372   mRefCnt = 0;
376  * Initialize the presentation shell. Create view manager and style
377  * manager.
378  */
379 nsresult
380 PresShell::Init(nsIDocument* aDocument,
381                 nsIPresContext* aPresContext,
382                 nsIViewManager* aViewManager,
383                 nsIStyleSet* aStyleSet)
385   NS_PRECONDITION(nsnull != aDocument, "null ptr");
386   NS_PRECONDITION(nsnull != aPresContext, "null ptr");
387   NS_PRECONDITION(nsnull != aViewManager, "null ptr");
388   if ((nsnull == aDocument) || (nsnull == aPresContext) ||
389       (nsnull == aViewManager)) {
390     return NS_ERROR_NULL_POINTER;
391   }
392   if (nsnull != mDocument) {
393     return NS_ERROR_ALREADY_INITIALIZED;
394   }
396   mDocument = aDocument;
397   NS_ADDREF(aDocument);
398   mViewManager = aViewManager;
399   NS_ADDREF(mViewManager);
401   //doesn't add a ref since we own it... MMP
402   mViewManager->SetViewObserver((nsIViewObserver *)this);
404   // Bind the context to the presentation shell.
405   mPresContext = aPresContext;
406   NS_ADDREF(aPresContext);
407   aPresContext->SetShell(this);
409   mStyleSet = aStyleSet;
410   NS_ADDREF(aStyleSet);
412   return NS_OK;
415 NS_METHOD
416 PresShell::EnterReflowLock()
418   ++mReflowLockCount;
419   return NS_OK;
422 NS_METHOD
423 PresShell::ExitReflowLock()
425   PRUint32 newReflowLockCount = mReflowLockCount - 1;
426   if (newReflowLockCount == 0) {
427     ProcessReflowCommands();
428   }
429   mReflowLockCount = newReflowLockCount;
430   return NS_OK;
433 nsIDocument*
434 PresShell::GetDocument()
436   NS_IF_ADDREF(mDocument);
437   return mDocument;
440 nsIPresContext*
441 PresShell::GetPresContext()
443   NS_IF_ADDREF(mPresContext);
444   return mPresContext;
447 nsIViewManager*
448 PresShell::GetViewManager()
450   NS_IF_ADDREF(mViewManager);
451   return mViewManager;
454 nsIStyleSet*
455 PresShell::GetStyleSet()
457   NS_IF_ADDREF(mStyleSet);
458   return mStyleSet;
461 // Make shell be a document observer
462 void
463 PresShell::BeginObservingDocument()
465   if (nsnull != mDocument) {
466     mDocument->AddObserver(this);
467   }
470 // Make shell stop being a document observer
471 void
472 PresShell::EndObservingDocument()
474   if (nsnull != mDocument) {
475     mDocument->RemoveObserver(this);
476   }
479 NS_IMETHODIMP
480 PresShell::InitialReflow(nscoord aWidth, nscoord aHeight)
482   NS_PRECONDITION(nsnull == mRootFrame, "unexpected root frame");
484   EnterReflowLock();
486   if (nsnull != mPresContext) {
487     nsRect r(0, 0, aWidth, aHeight);
488     mPresContext->SetVisibleArea(r);
489   }
491   if (nsnull == mRootFrame) {
492     if (nsnull != mDocument) {
493       nsIContent* root = mDocument->GetRootContent();
494       if (nsnull != root) {
495         // Have style sheet processor construct a frame for the
496         // root content object
497         mStyleSet->ConstructFrame(mPresContext, root, nsnull, mRootFrame);
498         NS_RELEASE(root);
499       }
500     }
501   }
503   if (nsnull != mRootFrame) {
504     // Kick off a top-down reflow
505     NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
506                  ("enter nsPresShell::InitialReflow: %d,%d", aWidth, aHeight));
507 #ifdef NS_DEBUG
508     if (nsIFrame::GetVerifyTreeEnable()) {
509       mRootFrame->VerifyTree();
510     }
511 #endif
512     nsRect                bounds;
513     mPresContext->GetVisibleArea(bounds);
514     nsSize                maxSize(bounds.width, bounds.height);
515     nsHTMLReflowMetrics   desiredSize(nsnull);
516     nsReflowStatus        status;
517     nsIHTMLReflow*        htmlReflow;
518     nsIRenderingContext*  rcx = nsnull;
520     CreateRenderingContext(mRootFrame, rcx);
522     nsHTMLReflowState reflowState(*mPresContext, mRootFrame,
523                                   eReflowReason_Initial, maxSize, rcx);
525     if (NS_OK == mRootFrame->QueryInterface(kIHTMLReflowIID, (void**)&htmlReflow)) {
526       htmlReflow->Reflow(*mPresContext, desiredSize, reflowState, status);
527       mRootFrame->SizeTo(desiredSize.width, desiredSize.height);
528 #ifdef NS_DEBUG
529       if (nsIFrame::GetVerifyTreeEnable()) {
530         mRootFrame->VerifyTree();
531       }
532 #endif
533     }
534     NS_IF_RELEASE(rcx);
535     NS_FRAME_LOG(NS_FRAME_TRACE_CALLS, ("exit nsPresShell::InitialReflow"));
536   }
538   ExitReflowLock();
540   return NS_OK; //XXX this needs to be real. MMP
543 NS_IMETHODIMP
544 PresShell::ResizeReflow(nscoord aWidth, nscoord aHeight)
546   EnterReflowLock();
548   if (nsnull != mPresContext) {
549     nsRect r(0, 0, aWidth, aHeight);
550     mPresContext->SetVisibleArea(r);
551   }
553   // If we don't have a root frame yet, that means we haven't had our initial
554   // reflow...
555   if (nsnull != mRootFrame) {
556     // Kick off a top-down reflow
557     NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
558                  ("enter nsPresShell::ResizeReflow: %d,%d", aWidth, aHeight));
559 #ifdef NS_DEBUG
560     if (nsIFrame::GetVerifyTreeEnable()) {
561       mRootFrame->VerifyTree();
562     }
563 #endif
564     nsRect                bounds;
565     mPresContext->GetVisibleArea(bounds);
566     nsSize                maxSize(bounds.width, bounds.height);
567     nsHTMLReflowMetrics   desiredSize(nsnull);
568     nsReflowStatus        status;
569     nsIHTMLReflow*        htmlReflow;
570     nsIRenderingContext*  rcx = nsnull;
572     CreateRenderingContext(mRootFrame, rcx);
574     nsHTMLReflowState reflowState(*mPresContext, mRootFrame,
575                                   eReflowReason_Resize, maxSize, rcx);
577     if (NS_OK == mRootFrame->QueryInterface(kIHTMLReflowIID, (void**)&htmlReflow)) {
578       htmlReflow->Reflow(*mPresContext, desiredSize, reflowState, status);
579       mRootFrame->SizeTo(desiredSize.width, desiredSize.height);
580 #ifdef NS_DEBUG
581       if (nsIFrame::GetVerifyTreeEnable()) {
582         mRootFrame->VerifyTree();
583       }
584 #endif
585     }
586     NS_IF_RELEASE(rcx);
587     NS_FRAME_LOG(NS_FRAME_TRACE_CALLS, ("exit nsPresShell::ResizeReflow"));
589     // XXX if debugging then we should assert that the cache is empty
590   } else {
591 #ifdef NOISY
592     printf("PresShell::ResizeReflow: null root frame\n");
593 #endif
594   }
596   ExitReflowLock();
598   return NS_OK; //XXX this needs to be real. MMP
601 NS_IMETHODIMP
602 PresShell::StyleChangeReflow()
604   EnterReflowLock();
606   if (nsnull != mRootFrame) {
607     // Kick off a top-down reflow
608     NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
609                  ("enter nsPresShell::StyleChangeReflow"));
610 #ifdef NS_DEBUG
611     if (nsIFrame::GetVerifyTreeEnable()) {
612       mRootFrame->VerifyTree();
613     }
614 #endif
615     nsRect                bounds;
616     mPresContext->GetVisibleArea(bounds);
617     nsSize                maxSize(bounds.width, bounds.height);
618     nsHTMLReflowMetrics   desiredSize(nsnull);
619     nsReflowStatus        status;
620     nsIHTMLReflow*        htmlReflow;
621     nsIRenderingContext*  rcx = nsnull;
623     CreateRenderingContext(mRootFrame, rcx);
625     // XXX We should be using eReflowReason_StyleChange
626     nsHTMLReflowState reflowState(*mPresContext, mRootFrame,
627                                   eReflowReason_Resize, maxSize, rcx);
629     if (NS_OK == mRootFrame->QueryInterface(kIHTMLReflowIID, (void**)&htmlReflow)) {
630       htmlReflow->Reflow(*mPresContext, desiredSize, reflowState, status);
631       mRootFrame->SizeTo(desiredSize.width, desiredSize.height);
632 #ifdef NS_DEBUG
633       if (nsIFrame::GetVerifyTreeEnable()) {
634         mRootFrame->VerifyTree();
635       }
636 #endif
637     }
638     NS_IF_RELEASE(rcx);
639     NS_FRAME_LOG(NS_FRAME_TRACE_CALLS, ("exit nsPresShell::StyleChangeReflow"));
640   }
642   ExitReflowLock();
644   return NS_OK; //XXX this needs to be real. MMP
647 nsIFrame*
648 PresShell::GetRootFrame()
650   return mRootFrame;
653 NS_IMETHODIMP
654 PresShell::BeginUpdate(nsIDocument *aDocument)
656   mUpdateCount++;
657   return NS_OK;
660 NS_IMETHODIMP
661 PresShell::EndUpdate(nsIDocument *aDocument)
663   NS_PRECONDITION(0 != mUpdateCount, "too many EndUpdate's");
664   if (--mUpdateCount == 0) {
665     // XXX do something here
666   }
667   return NS_OK;
670 NS_IMETHODIMP
671 PresShell::BeginLoad(nsIDocument *aDocument)
673   return NS_OK;
676 NS_IMETHODIMP
677 PresShell::EndLoad(nsIDocument *aDocument)
679   return NS_OK;
682 NS_IMETHODIMP
683 PresShell::BeginReflow(nsIDocument *aDocument, nsIPresShell* aShell)
685   return NS_OK;
688 NS_IMETHODIMP
689 PresShell::EndReflow(nsIDocument *aDocument, nsIPresShell* aShell)
691   return NS_OK;
694 void
695 PresShell::AppendReflowCommand(nsIReflowCommand* aReflowCommand)
697   mReflowCommands.AppendElement(aReflowCommand);
698   NS_ADDREF(aReflowCommand);
701 void
702 PresShell::ProcessReflowCommands()
704   if (0 != mReflowCommands.Count()) {
705     nsHTMLReflowMetrics   desiredSize(nsnull);
706     nsIRenderingContext*  rcx;
708     CreateRenderingContext(mRootFrame, rcx);
710     while (0 != mReflowCommands.Count()) {
711       nsIReflowCommand* rc = (nsIReflowCommand*) mReflowCommands.ElementAt(0);
712       mReflowCommands.RemoveElementAt(0);
714       // Dispatch the reflow command
715       nsSize          maxSize;
716       mRootFrame->GetSize(maxSize);
717 #ifdef NS_DEBUG
718       nsIReflowCommand::ReflowType type;
719       rc->GetType(type);
720       NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
721          ("PresShell::ProcessReflowCommands: begin reflow command type=%d",
722           type));
723 #endif
724       rc->Dispatch(*mPresContext, desiredSize, maxSize, *rcx);
725       NS_RELEASE(rc);
726       NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
727          ("PresShell::ProcessReflowCommands: end reflow command"));
728     }
730     // Place and size the root frame
731     mRootFrame->SizeTo(desiredSize.width, desiredSize.height);
732 #ifdef NS_DEBUG
733     if (nsIFrame::GetVerifyTreeEnable()) {
734       mRootFrame->VerifyTree();
735     }
736     if (GetVerifyReflowEnable()) {
737       VerifyIncrementalReflow();
738     }
739 #endif
740     NS_IF_RELEASE(rcx);
741   }
744 void
745 PresShell::ClearFrameRefs(nsIFrame* aFrame)
747   nsIEventStateManager *manager;
748   if (NS_OK == mPresContext->GetEventStateManager(&manager)) {
749     manager->ClearFrameRefs(aFrame);
750     NS_RELEASE(manager);
751   }
752   if (aFrame == mCurrentEventFrame) {
753     mCurrentEventFrame = nsnull;
754   }
757 NS_IMETHODIMP PresShell :: CreateRenderingContext(nsIFrame *aFrame,
758                                                   nsIRenderingContext *&aContext)
760   nsIWidget *widget = nsnull;
761   nsIView   *view = nsnull;
762   nsPoint   pt;
763   nsresult  rv;
765   aFrame->GetView(view);
767   if (nsnull == view)
768     aFrame->GetOffsetFromView(pt, view);
770   while (nsnull != view)
771   {
772     view->GetWidget(widget);
774     if (nsnull != widget)
775     {
776       NS_RELEASE(widget);
777       break;
778     }
780     view->GetParent(view);
781   }
783   nsIDeviceContext  *dx;
785   dx = mPresContext->GetDeviceContext();
787   if (nsnull != view)
788     rv = dx->CreateRenderingContext(view, aContext);
789   else
790     rv = dx->CreateRenderingContext(aContext);
792   NS_RELEASE(dx);
794   return rv;
797 #ifdef NS_DEBUG
798 static char*
799 ContentTag(nsIContent* aContent, PRIntn aSlot)
801   static char buf0[100], buf1[100], buf2[100];
802   static char* bufs[] = { buf0, buf1, buf2 };
803   char* buf = bufs[aSlot];
804   nsIAtom* atom;
805   aContent->GetTag(atom);
806   if (nsnull != atom) {
807     nsAutoString tmp;
808     atom->ToString(tmp);
809     tmp.ToCString(buf, 100);
810   }
811   else {
812     buf[0] = 0;
813   }
814   return buf;
816 #endif
818 NS_IMETHODIMP
819 PresShell::ContentChanged(nsIDocument *aDocument,
820                           nsIContent*  aContent,
821                           nsISupports* aSubContent)
823   NS_PRECONDITION(nsnull != mRootFrame, "null root frame");
825   EnterReflowLock();
826   nsresult rv = mStyleSet->ContentChanged(mPresContext, aContent, aSubContent);
827   ExitReflowLock();
828   return rv;
831 NS_IMETHODIMP
832 PresShell::AttributeChanged(nsIDocument *aDocument,
833                             nsIContent*  aContent,
834                             nsIAtom*     aAttribute,
835                             PRInt32      aHint)
837   NS_PRECONDITION(nsnull != mRootFrame, "null root frame");
839   EnterReflowLock();
840   nsresult rv = mStyleSet->AttributeChanged(mPresContext, aContent, aAttribute, aHint);
841   ExitReflowLock();
842   return rv;
845 NS_IMETHODIMP
846 PresShell::ContentAppended(nsIDocument *aDocument,
847                            nsIContent* aContainer,
848                            PRInt32     aNewIndexInContainer)
850   EnterReflowLock();
851   nsresult  rv = mStyleSet->ContentAppended(mPresContext, aContainer, aNewIndexInContainer);
852   ExitReflowLock();
853   return rv;
856 NS_IMETHODIMP
857 PresShell::ContentInserted(nsIDocument* aDocument,
858                            nsIContent*  aContainer,
859                            nsIContent*  aChild,
860                            PRInt32      aIndexInContainer)
862   EnterReflowLock();
863   nsresult  rv = mStyleSet->ContentInserted(mPresContext, aContainer, aChild, aIndexInContainer);
864   ExitReflowLock();
865   return rv;
868 NS_IMETHODIMP
869 PresShell::ContentReplaced(nsIDocument* aDocument,
870                            nsIContent*  aContainer,
871                            nsIContent*  aOldChild,
872                            nsIContent*  aNewChild,
873                            PRInt32      aIndexInContainer)
875   EnterReflowLock();
876   nsresult  rv = mStyleSet->ContentReplaced(mPresContext, aContainer, aOldChild,
877                                             aNewChild, aIndexInContainer);
878   ExitReflowLock();
879   return rv;
882 NS_IMETHODIMP
883 PresShell::ContentRemoved(nsIDocument *aDocument,
884                           nsIContent* aContainer,
885                           nsIContent* aChild,
886                           PRInt32     aIndexInContainer)
888   EnterReflowLock();
889   nsresult  rv = mStyleSet->ContentRemoved(mPresContext, aContainer,
890                                            aChild, aIndexInContainer);
891   ExitReflowLock();
892   return rv;
895 NS_IMETHODIMP
896 PresShell::StyleSheetAdded(nsIDocument *aDocument,
897                            nsIStyleSheet* aStyleSheet)
899   return NS_OK;
902 NS_IMETHODIMP
903 PresShell::StyleSheetDisabledStateChanged(nsIDocument *aDocument,
904                                           nsIStyleSheet* aStyleSheet,
905                                           PRBool aDisabled)
907   nsresult rv = NS_OK;
908   if (nsnull != mRootFrame) {
909     nsIFrame* childFrame;
910     rv = mRootFrame->FirstChild(nsnull, childFrame);
911     
912     if (nsnull != mDocument) {
913       nsIContent* root = mDocument->GetRootContent();
914       if (nsnull != root) {
915         
916         EnterReflowLock();
917         rv = mStyleSet->ReconstructFrames(mPresContext, root,
918                                           mRootFrame, childFrame);
919         ExitReflowLock();
920         NS_RELEASE(root);
921       }
922     }
923   }
924   return rv;
927 NS_IMETHODIMP
928 PresShell::DocumentWillBeDestroyed(nsIDocument *aDocument)
930   return NS_OK;
933 static PRBool
934 IsZeroSizedFrame(nsIFrame *aFrame)
936   nsSize size;
937   aFrame->GetSize(size);
938   return ((0 == size.width) && (0 == size.height));
941 static nsIFrame*
942 FindFrameWithContent(nsIFrame* aFrame, nsIContent* aContent)
944   nsIContent* frameContent;
945    
946   aFrame->GetContent(frameContent);
947   if (frameContent == aContent) {
948     // XXX Sleazy hack to check whether this is a placeholder frame.
949     // If it is, we skip it and go on to (hopefully) find the 
950     // absolutely positioned frame.
951     const nsStylePosition*  position;
953     aFrame->GetStyleData(eStyleStruct_Position, (nsStyleStruct*&)position);
954     if ((NS_STYLE_POSITION_ABSOLUTE != position->mPosition) ||
955         !IsZeroSizedFrame(aFrame)) {
956       NS_RELEASE(frameContent);
957       return aFrame;
958     }
959   }
960   NS_IF_RELEASE(frameContent);
962   // Search for the frame in each child list that aFrame supports
963   nsIAtom* listName = nsnull;
964   PRInt32 listIndex = 0;
965   do {
966     nsIFrame* kid;
967     aFrame->FirstChild(listName, kid);
968     while (nsnull != kid) {
969       nsIFrame* result = FindFrameWithContent(kid, aContent);
970       if (nsnull != result) {
971         NS_IF_RELEASE(listName);
972         return result;
973       }
974       kid->GetNextSibling(kid);
975     }
976     NS_IF_RELEASE(listName);
977     aFrame->GetAdditionalChildListName(listIndex++, listName);
978   } while(nsnull != listName);
980   return nsnull;
983 nsIFrame*
984 PresShell::FindFrameWithContent(nsIContent* aContent)
986   // For the time being do a brute force depth-first search of
987   // the frame tree
988   return ::FindFrameWithContent(mRootFrame, aContent);
991 //nsIViewObserver
993 NS_IMETHODIMP PresShell :: Paint(nsIView              *aView,
994                                  nsIRenderingContext& aRenderingContext,
995                                  const nsRect&        aDirtyRect)
997   void*     clientData;
998   nsIFrame* frame;
999   nsresult  rv = NS_OK;
1001   NS_ASSERTION(!(nsnull == aView), "null view");
1003   aView->GetClientData(clientData);
1004   frame = (nsIFrame *)clientData;
1006   if (nsnull != frame) {
1007     rv = frame->Paint(*mPresContext, aRenderingContext, aDirtyRect);
1008 #ifdef NS_DEBUG
1009     // Draw a border around the frame
1010     if (nsIFrame::GetShowFrameBorders()) {
1011       nsRect r;
1012       frame->GetRect(r);
1013       aRenderingContext.SetColor(NS_RGB(0,0,255));
1014       aRenderingContext.DrawRect(0, 0, r.width, r.height);
1015     }
1016 #endif
1017   }
1018   return rv;
1021 NS_IMETHODIMP PresShell :: HandleEvent(nsIView         *aView,
1022                                        nsGUIEvent*     aEvent,
1023                                        nsEventStatus&  aEventStatus)
1025   void*     clientData;
1026   nsIFrame* frame;
1027   nsresult  rv = NS_OK;
1028   
1029   NS_ASSERTION(!(nsnull == aView), "null view");
1031   if (mIsDestroying || mReflowLockCount > 0) {
1032     return NS_OK;
1033   }
1035   aView->GetClientData(clientData);
1036   frame = (nsIFrame *)clientData;
1038   if (nsnull != frame) {
1039     frame->GetFrameForPoint(aEvent->point, &mCurrentEventFrame);
1040     if (nsnull != mCurrentEventFrame) {
1041       //Once we have the targetFrame, handle the event in this order
1042       nsIEventStateManager *manager;
1043       if (NS_OK == mPresContext->GetEventStateManager(&manager)) {
1044         //1. Give event to event manager for pre event state changes and generation of synthetic events.
1045         rv = manager->PreHandleEvent(*mPresContext, aEvent, mCurrentEventFrame, aEventStatus);
1047         //2. Give event to the DOM for third party and JS use.
1048         if (nsnull != mCurrentEventFrame && NS_OK == rv) {
1049           nsIContent* targetContent;
1050           if (NS_OK == mCurrentEventFrame->GetContent(targetContent) && nsnull != targetContent) {
1051             rv = targetContent->HandleDOMEvent(*mPresContext, (nsEvent*)aEvent, nsnull, 
1052                                                DOM_EVENT_INIT, aEventStatus);
1053             NS_RELEASE(targetContent);
1054           }
1056           //3. Give event to the Frames for browser default processing.
1057           // XXX The event isn't translated into the local coordinate space
1058           // of the frame...
1059           if (nsnull != mCurrentEventFrame && NS_OK == rv) {
1060             rv = mCurrentEventFrame->HandleEvent(*mPresContext, aEvent, aEventStatus);
1062             //4. Give event to event manager for post event state changes and generation of synthetic events.
1063             if (nsnull != mCurrentEventFrame && NS_OK == rv) {
1064               rv = manager->PostHandleEvent(*mPresContext, aEvent, mCurrentEventFrame, aEventStatus);
1065             }
1066           }
1067         }
1068         NS_RELEASE(manager);
1069       }
1070     }
1071   }
1072   else {
1073     rv = NS_OK;
1074   }
1076   return rv;
1079 NS_IMETHODIMP PresShell :: Scrolled(nsIView *aView)
1081   void*     clientData;
1082   nsIFrame* frame;
1083   nsresult  rv;
1084   
1085   NS_ASSERTION(!(nsnull == aView), "null view");
1087   aView->GetClientData(clientData);
1088   frame = (nsIFrame *)clientData;
1090   if (nsnull != frame)
1091     rv = frame->Scrolled(aView);
1092   else
1093     rv = NS_OK;
1095   return rv;
1098 NS_IMETHODIMP PresShell :: ResizeReflow(nsIView *aView, nscoord aWidth, nscoord aHeight)
1100   return ResizeReflow(aWidth, aHeight);
1103 #ifdef NS_DEBUG
1104 #include "nsViewsCID.h"
1105 #include "nsWidgetsCID.h"
1106 #include "nsIScrollableView.h"
1107 #include "nsIDeviceContext.h"
1108 #include "nsIURL.h"
1109 #include "nsICSSParser.h"
1110 #include "nsIStyleSheet.h"
1112 static NS_DEFINE_IID(kViewManagerCID, NS_VIEW_MANAGER_CID);
1113 static NS_DEFINE_IID(kIViewManagerIID, NS_IVIEWMANAGER_IID);
1114 static NS_DEFINE_IID(kScrollingViewCID, NS_SCROLLING_VIEW_CID);
1115 static NS_DEFINE_IID(kIViewIID, NS_IVIEW_IID);
1116 static NS_DEFINE_IID(kScrollViewIID, NS_ISCROLLABLEVIEW_IID);
1117 static NS_DEFINE_IID(kWidgetCID, NS_CHILD_CID);
1119 static void
1120 ShowDiffs(nsIFrame* k1, nsIFrame* k2, const nsRect& r1, const nsRect& r2)
1122   printf("verifyreflow: ");
1123   nsAutoString name;
1124   k1->GetFrameName(name);
1125   fputs(name, stdout);
1126   printf(" ");
1127   stdout << r1;
1128   printf(" != ");
1129   k2->GetFrameName(name);
1130   fputs(name, stdout);
1131   printf(" ");
1132   stdout << r2;
1133   printf("\n");
1136 static void
1137 CompareTrees(nsIFrame* aA, nsIFrame* aB)
1139   nsIFrame* k1, *k2;
1140   aA->FirstChild(nsnull, k1);
1141   aB->FirstChild(nsnull, k2);
1142   NS_ASSERTION(nsContainerFrame::LengthOf(k1) == nsContainerFrame::LengthOf(k2),
1143                "child counts don't match");
1145   nsRect r1, r2;
1146   nsIView* v1, *v2;
1147   nsIWidget* w1, *w2;
1148   for (;;) {
1149     if (nsnull == k1) {
1150       NS_ASSERTION(nsnull == k2, "child lists are different");
1151       break;
1152     }
1153     NS_ASSERTION(nsnull != k2, "child lists are different");
1155     // Verify that the frames are the same size
1156     k1->GetRect(r1);
1157     k2->GetRect(r2);
1158     if (r1 != r2) {
1159       ShowDiffs(k1, k2, r1, r2);
1160     }
1161     else {
1162       // Make sure either both have views or neither have views; if they
1163       // do have views, make sure the views are the same size. If the
1164       // views have widgets, make sure they both do or neither does. If
1165       // they do, make sure the widgets are the same size.
1166       k1->GetView(v1);
1167       k2->GetView(v2);
1168       if (nsnull != v1) {
1169         NS_ASSERTION(nsnull != v2, "child views are not matched");
1170         v1->GetBounds(r1);
1171         v2->GetBounds(r2);
1172         NS_ASSERTION(r1 == r2, "child views are different sizes");
1174         v1->GetWidget(w1);
1175         v2->GetWidget(w2);
1176         if (nsnull != w1) {
1177           NS_ASSERTION(nsnull != w2, "child widgets are not matched");
1178           w1->GetBounds(r1);
1179           w2->GetBounds(r2);
1180           NS_ASSERTION(r1 == r2, "child widgets are different sizes");
1181         }
1182         else {
1183           NS_ASSERTION(nsnull == w2, "child widgets are not matched");
1184         }
1185       }
1186       else {
1187         NS_ASSERTION(nsnull == v2, "child views are not matched");
1188       }
1190       // Compare the sub-trees too
1191       CompareTrees(k1, k2);
1192     }
1194     // Advance to next sibling
1195     k1->GetNextSibling(k1);
1196     k2->GetNextSibling(k2);
1197   }
1200 // XXX: copy of nsWebWidget's ua.css loading code!!!
1201 #define UA_CSS_URL "resource:/res/ua.css"
1203 static nsIStyleSheet* gUAStyleSheet;
1205 static nsresult
1206 InitUAStyleSheet()
1208   nsresult rv = NS_OK;
1210   if (nsnull == gUAStyleSheet) {  // snarf one
1211     nsIURL* uaURL;
1212     rv = NS_NewURL(&uaURL, nsnull, UA_CSS_URL); // XXX this bites, fix it
1213     if (NS_OK == rv) {
1214       // Get an input stream from the url
1215       PRInt32 ec;
1216       nsIInputStream* in = uaURL->Open(&ec);
1217       if (nsnull != in) {
1218         // Translate the input using the argument character set id into unicode
1219         nsIUnicharInputStream* uin;
1220         rv = NS_NewConverterStream(&uin, nsnull, in);
1221         if (NS_OK == rv) {
1222           // Create parser and set it up to process the input file
1223           nsICSSParser* css;
1224           rv = NS_NewCSSParser(&css);
1225           if (NS_OK == rv) {
1226             // Parse the input and produce a style set
1227             // XXX note: we are ignoring rv until the error code stuff in the
1228             // input routines is converted to use nsresult's
1229             css->Parse(uin, uaURL, gUAStyleSheet);
1230             NS_RELEASE(css);
1231           }
1232           NS_RELEASE(uin);
1233         }
1234         NS_RELEASE(in);
1235       }
1236       else {
1237 //        printf("open of %s failed: error=%x\n", UA_CSS_URL, ec);
1238         rv = NS_ERROR_ILLEGAL_VALUE;  // XXX need a better error code here
1239       }
1241       NS_RELEASE(uaURL);
1242     }
1243   }
1244   return rv;
1247 static nsresult
1248 CreateStyleSet(nsIDocument* aDocument, nsIStyleSet** aStyleSet)
1250   nsresult rv = InitUAStyleSheet();
1251   if (NS_OK != rv) {
1252     NS_WARNING("unable to load UA style sheet");
1253   }
1255   rv = NS_NewStyleSet(aStyleSet);
1256   if (NS_OK == rv) {
1257     PRInt32 index = aDocument->GetNumberOfStyleSheets();
1258     while (0 < index--) {
1259       // NOTE: turn the order around for the set
1260       nsIStyleSheet* sheet = aDocument->GetStyleSheetAt(index);
1261       (*aStyleSet)->AppendDocStyleSheet(sheet);
1262       NS_RELEASE(sheet);
1263     }
1264     // XXX this is just wrong, the UA style sheet should be owned by the UA
1265     // for that matter, the style set should be created by the UA too
1266     if (nsnull != gUAStyleSheet) {
1267       (*aStyleSet)->AppendBackstopStyleSheet(gUAStyleSheet);
1268     }
1269   }
1270   return rv;
1273 // After an incremental reflow, we verify the correctness by doing a
1274 // full reflow into a fresh frame tree.
1275 void
1276 PresShell::VerifyIncrementalReflow()
1278   // All the stuff we are creating that needs releasing
1279   nsIPresContext* cx;
1280   nsIViewManager* vm;
1281   nsIView* view;
1282   nsIPresShell* sh;
1283   nsIStyleSet* ss;
1285   // Create a presentation context to view the new frame tree
1286   nsresult rv;
1287   if (mPresContext->IsPaginated()) {
1288     rv = NS_NewPrintPreviewContext(&cx);
1289   }
1290   else {
1291     rv = NS_NewGalleyContext(&cx);
1292   }
1293   NS_ASSERTION(NS_OK == rv, "failed to create presentation context");
1294   nsIDeviceContext* dc = mPresContext->GetDeviceContext();
1295   nsIPref* prefs; 
1296   mPresContext->GetPrefs(prefs);
1297   cx->Init(dc, prefs);
1298   NS_IF_RELEASE(prefs);
1300   rv = CreateStyleSet(mDocument, &ss);
1301   NS_ASSERTION(NS_OK == rv, "failed to create style set");
1303   // Get our scrolling preference
1304   nsScrollPreference scrolling;
1305   nsIView* rootView;
1306   mViewManager->GetRootView(rootView);
1307   nsIScrollableView* scrollView;
1308   rv = rootView->QueryInterface(kScrollViewIID, (void**)&scrollView);
1309   if (NS_OK == rv) {
1310     scrollView->GetScrollPreference(scrolling);
1311   }
1312   nsIWidget* rootWidget;
1313   rootView->GetWidget(rootWidget);
1314   void* nativeParentWidget = rootWidget->GetNativeData(NS_NATIVE_WIDGET);
1316   // Create a new view manager.
1317   rv = nsRepository::CreateInstance(kViewManagerCID, nsnull, kIViewManagerIID,
1318                                     (void**) &vm);
1319   if ((NS_OK != rv) || (NS_OK != vm->Init(dc))) {
1320     NS_ASSERTION(NS_OK == rv, "failed to create view manager");
1321   }
1323   NS_RELEASE(dc);
1325   vm->SetViewObserver((nsIViewObserver *)this);
1327   // Create a child window of the parent that is our "root view/window"
1328   // Create a view
1329   nsRect tbounds;
1330   mPresContext->GetVisibleArea(tbounds);
1331 //  tbounds *= mPresContext->GetPixelsToTwips();
1332   rv = nsRepository::CreateInstance(kScrollingViewCID, nsnull, kIViewIID,
1333                                     (void **) &view);
1334   if ((NS_OK != rv) || (NS_OK != view->Init(vm, tbounds, nsnull))) {
1335     NS_ASSERTION(NS_OK == rv, "failed to create scroll view");
1336   }
1338   //now create the widget for the view
1339   rv = view->CreateWidget(kWidgetCID, nsnull, nativeParentWidget);
1340   if (NS_OK != rv) {
1341     NS_ASSERTION(NS_OK == rv, "failed to create scroll view widget");
1342   }
1344   rv = view->QueryInterface(kScrollViewIID, (void**)&scrollView);
1345   if (NS_OK == rv) {
1346     scrollView->CreateScrollControls(nativeParentWidget);
1347     scrollView->SetScrollPreference(scrolling);
1348   }
1349   else {
1350     NS_ASSERTION(0, "invalid scrolling view");
1351   }
1353   // Setup hierarchical relationship in view manager
1354   vm->SetRootView(view);
1356   // Make the new presentation context the same size as our
1357   // presentation context.
1358   nsRect r;
1359   mPresContext->GetVisibleArea(r);
1360   cx->SetVisibleArea(r);
1362   // Create a new presentation shell to view the document
1363   rv = mDocument->CreateShell(cx, vm, ss, &sh);
1364   NS_ASSERTION(NS_OK == rv, "failed to create presentation shell");
1365   sh->ResizeReflow(r.width, r.height);
1367   // Now that the document has been reflowed, use its frame tree to
1368   // compare against our frame tree.
1369   CompareTrees(GetRootFrame(), sh->GetRootFrame());
1371   NS_RELEASE(vm);
1373   NS_RELEASE(cx);
1374   NS_RELEASE(sh);
1376   NS_RELEASE(ss);
1378 #endif