Back to index

lightning-sunbird  0.9+nobinonly
nsViewManager.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  *   Patrick C. Beard <beard@netscape.com>
00024  *   Kevin McCluskey  <kmcclusk@netscape.com>
00025  *   Robert O'Callahan <roc+@cs.cmu.edu>
00026  *   Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
00027  *
00028  * Alternatively, the contents of this file may be used under the terms of
00029  * either of the GNU General Public License Version 2 or later (the "GPL"),
00030  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00031  * in which case the provisions of the GPL or the LGPL are applicable instead
00032  * of those above. If you wish to allow use of your version of this file only
00033  * under the terms of either the GPL or the LGPL, and not to allow others to
00034  * use your version of this file under the terms of the MPL, indicate your
00035  * decision by deleting the provisions above and replace them with the notice
00036  * and other provisions required by the GPL or the LGPL. If you do not delete
00037  * the provisions above, a recipient may use your version of this file under
00038  * the terms of any one of the MPL, the GPL or the LGPL.
00039  *
00040  * ***** END LICENSE BLOCK ***** */
00041 
00042 #define PL_ARENA_CONST_ALIGN_MASK (sizeof(void*)-1)
00043 #include "plarena.h"
00044 
00045 #include "nsViewManager.h"
00046 #include "nsUnitConversion.h"
00047 #include "nsIRenderingContext.h"
00048 #include "nsIDeviceContext.h"
00049 #include "nsGfxCIID.h"
00050 #include "nsIScrollableView.h"
00051 #include "nsView.h"
00052 #include "nsISupportsArray.h"
00053 #include "nsICompositeListener.h"
00054 #include "nsCOMPtr.h"
00055 #include "nsIEventQueue.h"
00056 #include "nsIEventQueueService.h"
00057 #include "nsIServiceManager.h"
00058 #include "nsGUIEvent.h"
00059 #include "nsIPrefBranch.h"
00060 #include "nsIPrefService.h"
00061 #include "nsRegion.h"
00062 #include "nsInt64.h"
00063 #include "nsScrollPortView.h"
00064 #include "nsHashtable.h"
00065 #include "nsCOMArray.h"
00066 
00067 static NS_DEFINE_IID(kBlenderCID, NS_BLENDER_CID);
00068 static NS_DEFINE_IID(kRegionCID, NS_REGION_CID);
00069 static NS_DEFINE_IID(kRenderingContextCID, NS_RENDERING_CONTEXT_CID);
00070 static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID);
00071 
00072 #define ARENA_ALLOCATE(var, pool, type) \
00073     {void *_tmp_; PL_ARENA_ALLOCATE(_tmp_, pool, sizeof(type)); \
00074     var = NS_REINTERPRET_CAST(type*, _tmp_); }
00075 
00093 // if defined widget changes like moves and resizes are defered until and done
00094 // all in one pass.
00095 //#define CACHE_WIDGET_CHANGES
00096 
00097 // display list flags
00098 #define VIEW_RENDERED     0x00000001
00099 #define PUSH_CLIP         0x00000002
00100 #define POP_CLIP          0x00000004
00101 #define VIEW_TRANSPARENT  0x00000008
00102 #define VIEW_TRANSLUCENT  0x00000010
00103 #define VIEW_CLIPPED      0x00000020
00104 // used only by CanScrollWithBitBlt
00105 #define VIEW_ISSCROLLED   0x00000040
00106 #define PUSH_FILTER       0x00000080
00107 #define POP_FILTER        0x00000100
00108 
00109 #define NSCOORD_NONE      PR_INT32_MIN
00110 
00111 #define SUPPORT_TRANSLUCENT_VIEWS
00112 
00113 // The following number is used in OptimizeDisplayList to decide when the
00114 // opaque region is getting too complicated.  When we get to an opaque region
00115 // with MAX_OPAQUE_REGION_COMPLEXITY rects in it, we stop adding views' rects
00116 // to the opaque region.  On "normal" pages (those without hundreds to
00117 // thousands of positioned elements) there are usually not that many opaque
00118 // views around; 10 is plenty for such pages.
00119 #define MAX_OPAQUE_REGION_COMPLEXITY 10
00120 
00121 /*
00122   This class represents an offscreen buffer which may have an alpha channel.
00123   Currently, if an alpha channel is required, we implement it by rendering into
00124   two buffers: one with a black background, one with a white background. We can
00125   recover the alpha values by comparing corresponding final values for each pixel.
00126 */
00127 class BlendingBuffers {
00128 public:
00129   BlendingBuffers(nsIRenderingContext* aCleanupContext);
00130   ~BlendingBuffers();
00131 
00132   // used by the destructor to cleanup resources
00133   nsCOMPtr<nsIRenderingContext> mCleanupContext;
00134   // The primary rendering context. When an alpha channel is in use, this
00135   // holds the black background.
00136   nsCOMPtr<nsIRenderingContext> mBlackCX;
00137   // Only used when an alpha channel is required; holds the white background.
00138   nsCOMPtr<nsIRenderingContext> mWhiteCX;
00139 
00140   PRBool mOwnBlackSurface;
00141   // drawing surface for mBlackCX
00142   nsIDrawingSurface*  mBlack;
00143   // drawing surface for mWhiteCX
00144   nsIDrawingSurface*  mWhite;
00145 
00146   // The offset within the current widget at which this buffer will
00147   // eventually be composited
00148   nsPoint mOffset;
00149 };
00150 
00151 // A DisplayListElement2 records the information needed to paint one view.
00152 // Note that child views get their own DisplayListElement2s; painting a view
00153 // paints that view's frame and all its child frames EXCEPT for the child frames
00154 // that have their own views.
00155 struct DisplayListElement2 {
00156   nsView*       mView;        
00157   nsRect        mBounds;      // coordinates relative to the view manager root
00158   nscoord       mAbsX, mAbsY; // coordinates relative to the view that we're Refreshing 
00159   PRUint32      mFlags;       // see above
00160   nsInt64       mZIndex;      // temporary used during z-index processing (see below)
00161 private: // Prevent new/delete of these elements
00162   DisplayListElement2();
00163   ~DisplayListElement2();
00164 };
00165 
00166 /*
00167   IMPLEMENTING Z-INDEX
00168 
00169   Implementing z-index:auto and negative z-indices properly is hard. Here's how we do it.
00170 
00171   In CreateDisplayList, the display list elements above are inserted into a tree rather
00172   than a simple list. The tree structure mimics the view hierarchy (except for fixed-position
00173   stuff, see below for more about that), except that in the tree, only leaf nodes can do
00174   actual painting (i.e., correspond to display list elements). Therefore every leaf view
00175   (i.e., no child views) has one corresponding leaf tree node containing its
00176   DisplayListElement2. OTOH, every non-leaf view corresponds to an interior tree node
00177   which contains the tree nodes for the child views, and which also contains a leaf tree
00178   node which does the painting for the non-leaf view. Initially sibling tree nodes are
00179   ordered in the same order as their views, which Layout should have arranged in document
00180   order.
00181 
00182   For example, given the view hierarchy and z-indices, and assuming lower-
00183   numbered views precede higher-numbered views in the document order,
00184     V0(auto) --> V1(0) --> V2(auto) --> V3(0)
00185      |            |         +---------> V4(2)
00186      |            +------> V5(1)
00187      +---------> V6(1)
00188   CreateDisplayList would create the following z-tree (z-order increases from top to bottom)
00189     Ta(V0) --> Tb*(V0)
00190      +-------> Tc(V1) --> Td*(V1)
00191      |          +-------> Te(V2) --> Tf*(V2)
00192      |          |          +-------> Tg*(V3)
00193      |          |          +-------> Th*(V4)
00194      |          +-------> Ti*(V5)
00195      +-------> Tj*(V6)
00196   (* indicates leaf nodes marked with display list elements)
00197 
00198   Once the Z-tree has been built we call SortByZOrder to compute a real linear display list.
00199   It recursively computes a display list for each tree node, by computing the display lists
00200   for all child nodes, then concatenating those lists and sorting them by z-index. The trick
00201   is that the z-indices for display list elements are updated during the process; after
00202   a display list is calculated for a tree node, the elements of the display list are all
00203   assigned the z-index specified for the tree node's view (unless the view has z-index
00204   'auto'). This ensures that a tree node's display list elements will be sorted correctly
00205   relative to the siblings of the tree node.
00206 
00207   The above example is processed as follows:
00208   The child nodes of Te(V2) are display list elements [ Tf*(V2)(0), Tg*(V3)(0), Th*(V4)(2) ].
00209   (The display list element for the frames of a non-leaf view always has z-index 0 relative
00210   to the children of the view.)
00211   Te(V2) is 'auto' so its display list is [ Tf*(V2)(0), Tg*(V3)(0), Th*(V4)(2) ].
00212   Tc(V1)'s child display list elements are [ Td*(V1)(0), Tf*(V2)(0), Tg*(V3)(0), Th*(V4)(2),
00213   Ti*(V5)(1) ].
00214   The nodes are sorted and then reassigned z-index 0, so Tc(V1) is replaced with the list
00215   [ Td*(V1)(0), Tf*(V2)(0), Tg*(V3)(0), Ti*(V5)(0), Th*(V4)(0) ].
00216   Finally we collect the elements for Ta(V0):
00217   [ Tb*(V0), Td*(V1)(0), Tf*(V2)(0), Tg*(V3)(0), Ti*(V5)(0), Th*(V4)(0), Tj*(V6)(1) ].
00218 
00219   The z-tree code is called frequently and was a heavy user of the heap.
00220   In order to reduce the amount of time spent allocating and deallocating
00221   memory, the code was changed so that all the memory used to build the z-tree,
00222   including the DisplayListElement2 elements, is allocated from an Arena.
00223   This greatly reduces the number of function calls to new and delete,
00224   and eleminates a final call to DestroyZTreeNode which was needed to
00225   recursivly walk and free the tree.
00226 
00227   In order to ensure that all the z-tree elements are allocated from the Arena,
00228   the DisplayZTreeNode and DisplayListElement2 structures have private (and
00229   unimplemented) constructors and destructors.  This will ensure a compile
00230   error if someone attempts to create or destroy one of these structures
00231   using new or delete.
00232 */
00233 
00234 struct DisplayZTreeNode {
00235   nsView*              mView;            // Null for tree leaf nodes
00236   DisplayZTreeNode*    mZSibling;
00237 
00238   // We can't have BOTH an mZChild and an mDisplayElement
00239   DisplayZTreeNode*    mZChild;          // tree interior nodes
00240   DisplayListElement2* mDisplayElement;  // tree leaf nodes
00241 private: // Prevent new/delete of these elements
00242   DisplayZTreeNode();
00243   ~DisplayZTreeNode();
00244 };
00245 
00246 #ifdef DEBUG
00247 static PRInt32 PrintDisplayListElement(DisplayListElement2* element,
00248                                        PRInt32 aNestCount) {
00249     nsView*              view = element->mView;
00250     nsRect               rect = element->mBounds;
00251     PRUint32             flags = element->mFlags;
00252     PRUint32             viewFlags = view ? view->GetViewFlags() : 0;
00253     nsRect               dim;
00254     if (view) {
00255       view->GetDimensions(dim);
00256     }
00257     nsPoint              v = view ? view->GetPosition() : nsPoint(0, 0);
00258     nsView* parent = view ? view->GetParent() : nsnull;
00259     PRInt32 zindex = view ? view->GetZIndex() : 0;
00260     nsView* zParent = view ? view->GetZParent() : nsnull;
00261     nsViewManager* viewMan = view ? view->GetViewManager() : nsnull;
00262 
00263     printf("%*snsIView@%p{%d,%d,%d,%d @ %d,%d; p=%p,m=%p z=%d,zp=%p} [x=%d, y=%d, w=%d, h=%d, absX=%d, absY=%d]\n",
00264            aNestCount*2, "", (void*)view,
00265            dim.x, dim.y, dim.width, dim.height,
00266            v.x, v.y,
00267            (void*)parent, (void*)viewMan, zindex, (void*)zParent,
00268            rect.x, rect.y, rect.width, rect.height,
00269            element->mAbsX, element->mAbsY);
00270 
00271     PRInt32 newnestcnt = aNestCount;
00272 
00273     if (flags)
00274       {
00275         printf("%*s", aNestCount*2, "");
00276 
00277         if (flags & POP_CLIP) {
00278           printf("POP_CLIP ");
00279           newnestcnt--;
00280         }
00281 
00282         if (flags & PUSH_CLIP) {
00283           printf("PUSH_CLIP ");
00284           newnestcnt++;
00285         }
00286 
00287         if (flags & POP_FILTER) {
00288           printf("POP_FILTER ");
00289           newnestcnt--;
00290         }
00291 
00292         if (flags & PUSH_FILTER) {
00293           printf("PUSH_FILTER ");
00294           newnestcnt++;
00295         }
00296 
00297         if (flags & VIEW_RENDERED) 
00298           printf("VIEW_RENDERED ");
00299 
00300         if (flags & VIEW_ISSCROLLED)
00301           printf("VIEW_ISSCROLLED ");
00302 
00303         if (flags & VIEW_CLIPPED)
00304           printf("VIEW_ISCLIPPED ");
00305 
00306         if (flags & VIEW_TRANSLUCENT)
00307           printf("VIEW_ISTRANSLUCENT ");
00308 
00309         if (flags & VIEW_TRANSPARENT)
00310           printf("VIEW_ISTRANSPARENT ");
00311 
00312         if (viewFlags & NS_VIEW_FLAG_DONT_BITBLT)
00313           printf("NS_VIEW_FLAG_DONT_BITBLT ");
00314 
00315         printf("\n");
00316       }
00317     return newnestcnt;
00318 }
00319 
00320 static void PrintZTreeNode(DisplayZTreeNode* aNode, PRInt32 aIndent) 
00321 {
00322   if (aNode) {
00323     printf("%*sDisplayZTreeNode@%p\n", aIndent*2, "", (void*)aNode);
00324     if (aNode->mDisplayElement) {
00325       PrintDisplayListElement(aNode->mDisplayElement, 0);
00326     }
00327 
00328     aIndent += 2;
00329 
00330     for (DisplayZTreeNode* child = aNode->mZChild; child;
00331          child = child->mZSibling) {
00332       PrintZTreeNode(child, aIndent);
00333     }
00334   }
00335 }
00336 #endif
00337 
00338 #ifdef NS_VM_PERF_METRICS
00339 #include "nsITimeRecorder.h"
00340 #endif
00341 
00342 //-------------- Begin Invalidate Event Definition ------------------------
00343 
00344 struct nsViewManagerEvent : public PLEvent {
00345   nsViewManagerEvent(nsViewManager* aViewManager);
00346   
00347   virtual void HandleEvent() = 0;
00348   
00349   nsViewManager* ViewManager() {
00350     // |owner| is a weak pointer, but the view manager will destroy any
00351     // pending invalidate events in it's destructor.
00352     return NS_STATIC_CAST(nsViewManager*, owner);
00353   }
00354 };
00355 
00356 static void* PR_CALLBACK HandlePLEvent(PLEvent* aEvent)
00357 {
00358   NS_ASSERTION(nsnull != aEvent,"Event is null");
00359   nsViewManagerEvent *event = NS_STATIC_CAST(nsViewManagerEvent*, aEvent);
00360 
00361   event->HandleEvent();
00362   return nsnull;
00363 }
00364 
00365 static void PR_CALLBACK DestroyPLEvent(PLEvent* aEvent)
00366 {
00367   NS_ASSERTION(nsnull != aEvent,"Event is null");
00368   nsViewManagerEvent *event = NS_STATIC_CAST(nsViewManagerEvent*, aEvent);
00369   delete event;
00370 }
00371 
00372 nsViewManagerEvent::nsViewManagerEvent(nsViewManager* aViewManager)
00373 {
00374   NS_ASSERTION(aViewManager, "null parameter");  
00375   PL_InitEvent(this, aViewManager, ::HandlePLEvent, ::DestroyPLEvent);  
00376 }
00377 
00378 struct nsInvalidateEvent : public nsViewManagerEvent {
00379   nsInvalidateEvent(nsViewManager* aViewManager)
00380     : nsViewManagerEvent(aViewManager) { }
00381 
00382   virtual void HandleEvent() {
00383     ViewManager()->ProcessInvalidateEvent();
00384   }
00385 };
00386 
00387 //-------------- End Invalidate Event Definition ---------------------------
00388 
00389 // Compare two Z-index values taking into account topmost and 
00390 // auto flags. the topmost flag is only used when both views are
00391 // zindex:auto.  (XXXldb Lying!)
00392 // 
00393 // returns 0 if equal
00394 //         > 0 if first z-index is greater than the second
00395 //         < 0 if first z-index is less than the second
00396 
00397 static PRInt32 CompareZIndex(PRInt32 aZIndex1, PRBool aTopMost1, PRBool aIsAuto1,
00398                              PRInt32 aZIndex2, PRBool aTopMost2, PRBool aIsAuto2) 
00399 {
00400   NS_ASSERTION(!aIsAuto1 || aZIndex1 == 0,"auto is set and the z-index is not 0");
00401   NS_ASSERTION(!aIsAuto2 || aZIndex2 == 0,"auto is set and the z-index is not 0");
00402 
00403   if (aZIndex1 != aZIndex2) {
00404     return aZIndex1 - aZIndex2;
00405   } else {
00406     return aTopMost1 - aTopMost2;
00407   }
00408 }
00409 
00410 static PRBool IsViewVisible(nsView *aView)
00411 {
00412   for (nsIView *view = aView; view; view = view->GetParent()) {
00413     // We don't check widget visibility here because in the future (with
00414     // the better approach to this that's in attachment 160801 on bug
00415     // 227361), callers of the equivalent to this function should be able
00416     // to rely on being notified when the result of this function changes.
00417     if (view->GetVisibility() == nsViewVisibility_kHide)
00418       return PR_FALSE;
00419   }
00420   // Find out if the root view is visible by asking the view observer
00421   // (this won't be needed anymore if we link view trees across chrome /
00422   // content boundaries in DocumentViewerImpl::MakeWindow).
00423   nsIViewObserver* vo = aView->GetViewManager()->GetViewObserver();
00424   return vo && vo->IsVisible();
00425 }
00426 
00427 void
00428 nsViewManager::PostInvalidateEvent()
00429 {
00430   NS_ASSERTION(IsRootVM(), "Caller screwed up");
00431   
00432   nsCOMPtr<nsIEventQueue> eventQueue;
00433   mEventQueueService->GetSpecialEventQueue(
00434     nsIEventQueueService::UI_THREAD_EVENT_QUEUE, getter_AddRefs(eventQueue));
00435   NS_ASSERTION(nsnull != eventQueue, "Event queue is null");
00436 
00437   if (eventQueue != mInvalidateEventQueue) {
00438     nsInvalidateEvent* ev = new nsInvalidateEvent(this);
00439     eventQueue->PostEvent(ev);
00440     mInvalidateEventQueue = eventQueue;
00441   }
00442 }
00443 
00444 #undef DEBUG_MOUSE_LOCATION
00445 
00446 PRInt32 nsViewManager::mVMCount = 0;
00447 nsIRenderingContext* nsViewManager::gCleanupContext = nsnull;
00448 
00449 // Weakly held references to all of the view managers
00450 nsVoidArray* nsViewManager::gViewManagers = nsnull;
00451 PRUint32 nsViewManager::gLastUserEventTime = 0;
00452 
00453 nsViewManager::nsViewManager()
00454   : mMouseLocation(NSCOORD_NONE, NSCOORD_NONE)
00455   , mDelayedResize(NSCOORD_NONE, NSCOORD_NONE)
00456   , mRootViewManager(this)
00457 {
00458   if (gViewManagers == nsnull) {
00459     NS_ASSERTION(mVMCount == 0, "View Manager count is incorrect");
00460     // Create an array to hold a list of view managers
00461     gViewManagers = new nsVoidArray;
00462   }
00463  
00464   if (gCleanupContext == nsnull) {
00465     /* XXX: This should use a device to create a matching |nsIRenderingContext| object */
00466     CallCreateInstance(kRenderingContextCID, &gCleanupContext);
00467     NS_ASSERTION(gCleanupContext,
00468                  "Wasn't able to create a graphics context for cleanup");
00469   }
00470 
00471   gViewManagers->AppendElement(this);
00472 
00473   mVMCount++;
00474   // NOTE:  we use a zeroing operator new, so all data members are
00475   // assumed to be cleared here.
00476   mDefaultBackgroundColor = NS_RGBA(0, 0, 0, 0);
00477   mAllowDoubleBuffering = PR_TRUE; 
00478   mHasPendingUpdates = PR_FALSE;
00479   mRecursiveRefreshPending = PR_FALSE;
00480   mUpdateBatchFlags = 0;
00481 }
00482 
00483 nsViewManager::~nsViewManager()
00484 {
00485   if (mRootView) {
00486     // Destroy any remaining views
00487     mRootView->Destroy();
00488     mRootView = nsnull;
00489   }
00490 
00491   // Make sure to RevokeEvents for all viewmanagers, since some events
00492   // are posted by a non-root viewmanager.
00493   nsCOMPtr<nsIEventQueue> eventQueue;
00494   mEventQueueService->GetSpecialEventQueue(nsIEventQueueService::UI_THREAD_EVENT_QUEUE,
00495                                            getter_AddRefs(eventQueue));
00496   NS_ASSERTION(eventQueue, "Event queue is null"); 
00497   eventQueue->RevokeEvents(this);
00498   
00499   if (!IsRootVM()) {
00500     // We have a strong ref to mRootViewManager
00501     NS_RELEASE(mRootViewManager);
00502   }
00503   
00504   mInvalidateEventQueue = nsnull;  
00505   mSynthMouseMoveEventQueue = nsnull;  
00506 
00507   mRootScrollable = nsnull;
00508 
00509   NS_ASSERTION((mVMCount > 0), "underflow of viewmanagers");
00510   --mVMCount;
00511 
00512 #ifdef DEBUG
00513   PRBool removed =
00514 #endif
00515     gViewManagers->RemoveElement(this);
00516   NS_ASSERTION(removed, "Viewmanager instance not was not in the global list of viewmanagers");
00517 
00518   if (0 == mVMCount) {
00519     // There aren't any more view managers so
00520     // release the global array of view managers
00521    
00522     NS_ASSERTION(gViewManagers != nsnull, "About to delete null gViewManagers");
00523     delete gViewManagers;
00524     gViewManagers = nsnull;
00525 
00526     // Cleanup all of the offscreen drawing surfaces if the last view manager
00527     // has been destroyed and there is something to cleanup
00528 
00529     // Note: A global rendering context is needed because it is not possible 
00530     // to create a nsIRenderingContext during the shutdown of XPCOM. The last
00531     // viewmanager is typically destroyed during XPCOM shutdown.
00532 
00533     if (gCleanupContext) {
00534 
00535       gCleanupContext->DestroyCachedBackbuffer();
00536     } else {
00537       NS_ASSERTION(PR_FALSE, "Cleanup of drawing surfaces + offscreen buffer failed");
00538     }
00539 
00540     NS_IF_RELEASE(gCleanupContext);
00541   }
00542 
00543   mObserver = nsnull;
00544   mContext = nsnull;
00545 
00546   if (nsnull != mCompositeListeners) {
00547     mCompositeListeners->Clear();
00548     NS_RELEASE(mCompositeListeners);
00549   }
00550 }
00551 
00552 NS_IMPL_ISUPPORTS1(nsViewManager, nsIViewManager)
00553 
00554 nsresult
00555 nsViewManager::CreateRegion(nsIRegion* *result)
00556 {
00557   nsresult rv;
00558 
00559   if (!mRegionFactory) {
00560     mRegionFactory = do_GetClassObject(kRegionCID, &rv);
00561     if (NS_FAILED(rv)) {
00562       *result = nsnull;
00563       return rv;
00564     }
00565   }
00566 
00567   nsIRegion* region = nsnull;
00568   rv = CallCreateInstance(mRegionFactory.get(), &region);
00569   if (NS_SUCCEEDED(rv)) {
00570     rv = region->Init();
00571     *result = region;
00572   }
00573   return rv;
00574 }
00575 
00576 // We don't hold a reference to the presentation context because it
00577 // holds a reference to us.
00578 NS_IMETHODIMP nsViewManager::Init(nsIDeviceContext* aContext)
00579 {
00580   NS_PRECONDITION(nsnull != aContext, "null ptr");
00581 
00582   if (nsnull == aContext) {
00583     return NS_ERROR_NULL_POINTER;
00584   }
00585   if (nsnull != mContext) {
00586     return NS_ERROR_ALREADY_INITIALIZED;
00587   }
00588   mContext = aContext;
00589   mTwipsToPixels = mContext->AppUnitsToDevUnits();
00590   mPixelsToTwips = mContext->DevUnitsToAppUnits();
00591 
00592   mRefreshEnabled = PR_TRUE;
00593 
00594   mMouseGrabber = nsnull;
00595   mKeyGrabber = nsnull;
00596 
00597   if (nsnull == mEventQueueService) {
00598     mEventQueueService = do_GetService(kEventQueueServiceCID);
00599     NS_ASSERTION(mEventQueueService, "couldn't get event queue service");
00600   }
00601   
00602   return NS_OK;
00603 }
00604 
00605 NS_IMETHODIMP_(nsIView *)
00606 nsViewManager::CreateView(const nsRect& aBounds,
00607                           const nsIView* aParent,
00608                           nsViewVisibility aVisibilityFlag)
00609 {
00610   nsView *v = new nsView(this, aVisibilityFlag);
00611   if (v) {
00612     v->SetPosition(aBounds.x, aBounds.y);
00613     nsRect dim(0, 0, aBounds.width, aBounds.height);
00614     v->SetDimensions(dim, PR_FALSE);
00615     v->SetParent(NS_STATIC_CAST(nsView*, NS_CONST_CAST(nsIView*, aParent)));
00616   }
00617   return v;
00618 }
00619 
00620 NS_IMETHODIMP_(nsIScrollableView *)
00621 nsViewManager::CreateScrollableView(const nsRect& aBounds,
00622                                     const nsIView* aParent)
00623 {
00624   nsScrollPortView *v = new nsScrollPortView(this);
00625   if (v) {
00626     v->SetPosition(aBounds.x, aBounds.y);
00627     nsRect dim(0, 0, aBounds.width, aBounds.height);
00628     v->SetDimensions(dim, PR_FALSE);
00629     v->SetParent(NS_STATIC_CAST(nsView*, NS_CONST_CAST(nsIView*, aParent)));
00630   }
00631   return v;
00632 }
00633 
00634 NS_IMETHODIMP nsViewManager::GetRootView(nsIView *&aView)
00635 {
00636   aView = mRootView;
00637   return NS_OK;
00638 }
00639 
00640 NS_IMETHODIMP nsViewManager::SetRootView(nsIView *aView)
00641 {
00642   nsView* view = NS_STATIC_CAST(nsView*, aView);
00643 
00644   NS_PRECONDITION(!view || view->GetViewManager() == this,
00645                   "Unexpected viewmanager on root view");
00646   
00647   // Do NOT destroy the current root view. It's the caller's responsibility
00648   // to destroy it
00649   mRootView = view;
00650 
00651   if (mRootView) {
00652     nsView* parent = mRootView->GetParent();
00653     if (parent) {
00654       // Calling InsertChild on |parent| will InvalidateHierarchy() on us, so
00655       // no need to set mRootViewManager ourselves here.
00656       parent->InsertChild(mRootView, nsnull);
00657     } else {
00658       InvalidateHierarchy();
00659     }
00660 
00661     mRootView->SetZIndex(PR_FALSE, 0, PR_FALSE);
00662   }
00663   // Else don't touch mRootViewManager
00664 
00665   return NS_OK;
00666 }
00667 
00668 NS_IMETHODIMP nsViewManager::GetWindowDimensions(nscoord *aWidth, nscoord *aHeight)
00669 {
00670   if (nsnull != mRootView) {
00671     if (mDelayedResize == nsSize(NSCOORD_NONE, NSCOORD_NONE)) {
00672       nsRect dim;
00673       mRootView->GetDimensions(dim);
00674       *aWidth = dim.width;
00675       *aHeight = dim.height;
00676     } else {
00677       *aWidth = mDelayedResize.width;
00678       *aHeight = mDelayedResize.height;
00679     }
00680   }
00681   else
00682     {
00683       *aWidth = 0;
00684       *aHeight = 0;
00685     }
00686   return NS_OK;
00687 }
00688 
00689 NS_IMETHODIMP nsViewManager::SetWindowDimensions(nscoord aWidth, nscoord aHeight)
00690 {
00691   if (mRootView) {
00692     if (IsViewVisible(mRootView)) {
00693       mDelayedResize.SizeTo(NSCOORD_NONE, NSCOORD_NONE);
00694       DoSetWindowDimensions(aWidth, aHeight);
00695     } else {
00696       mDelayedResize.SizeTo(aWidth, aHeight);
00697     }
00698   }
00699 
00700   return NS_OK;
00701 }
00702 
00703 /* Check the prefs to see whether we should do double buffering or not... */
00704 static
00705 PRBool DoDoubleBuffering(void)
00706 {
00707   static PRBool gotDoublebufferPrefs = PR_FALSE;
00708   static PRBool doDoublebuffering    = PR_TRUE;  /* Double-buffering is ON by default */
00709   
00710   if (!gotDoublebufferPrefs) {
00711     nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID);
00712     if (prefBranch) {
00713       PRBool val;
00714       if (NS_SUCCEEDED(prefBranch->GetBoolPref("viewmanager.do_doublebuffering", &val))) {
00715         doDoublebuffering = val;
00716       }
00717     }
00718 
00719 #ifdef DEBUG
00720     if (!doDoublebuffering) {
00721       printf("nsViewManager: Note: Double-buffering disabled via prefs.\n");
00722     }
00723 #endif /* DEBUG */
00724 
00725     gotDoublebufferPrefs = PR_TRUE;
00726   }
00727   
00728   return doDoublebuffering;
00729 }
00730 
00731 static void ConvertNativeRegionToAppRegion(nsIRegion* aIn, nsRegion* aOut,
00732                                            nsIDeviceContext* context)
00733 {
00734   nsRegionRectSet* rects = nsnull;
00735   aIn->GetRects(&rects);
00736   if (!rects)
00737     return;
00738   
00739   float  p2t;
00740   p2t = context->DevUnitsToAppUnits();
00741 
00742   PRUint32 i;
00743   for (i = 0; i < rects->mNumRects; i++) {
00744     const nsRegionRect& inR = rects->mRects[i];
00745     nsRect outR;
00746     outR.x = NSToIntRound(inR.x * p2t);
00747     outR.y = NSToIntRound(inR.y * p2t);
00748     outR.width = NSToIntRound(inR.width * p2t);
00749     outR.height = NSToIntRound(inR.height * p2t);
00750     aOut->Or(*aOut, outR);
00751   }
00752 
00753   aIn->FreeRects(rects);
00754 }
00755 
00759 void nsViewManager::Refresh(nsView *aView, nsIRenderingContext *aContext,
00760                             nsIRegion *aRegion, PRUint32 aUpdateFlags)
00761 {
00762   NS_ASSERTION(aRegion != nsnull, "Null aRegion");
00763 
00764   if (! IsRefreshEnabled())
00765     return;
00766 
00767   nsRect viewRect;
00768   aView->GetDimensions(viewRect);
00769 
00770   // damageRegion is the damaged area, in twips, relative to the view origin
00771   nsRegion damageRegion;
00772   // convert pixels-relative-to-widget-origin to twips-relative-to-widget-origin
00773   ConvertNativeRegionToAppRegion(aRegion, &damageRegion, mContext);
00774   // move it from widget coordinates into view coordinates
00775   damageRegion.MoveBy(viewRect.x, viewRect.y);
00776 
00777   // Clip it to the view; shouldn't be necessary, but do it for sanity
00778   damageRegion.And(damageRegion, viewRect);
00779   if (damageRegion.IsEmpty()) {
00780 #ifdef DEBUG_roc
00781     nsRect damageRect = damageRegion.GetBounds();
00782     printf("XXX Damage rectangle (%d,%d,%d,%d) does not intersect the widget's view (%d,%d,%d,%d)!\n",
00783            damageRect.x, damageRect.y, damageRect.width, damageRect.height,
00784            viewRect.x, viewRect.y, viewRect.width, viewRect.height);
00785 #endif
00786     return;
00787   }
00788 
00789 #ifdef NS_VM_PERF_METRICS
00790   MOZ_TIMER_DEBUGLOG(("Reset nsViewManager::Refresh(region), this=%p\n", this));
00791   MOZ_TIMER_RESET(mWatch);
00792 
00793   MOZ_TIMER_DEBUGLOG(("Start: nsViewManager::Refresh(region)\n"));
00794   MOZ_TIMER_START(mWatch);
00795 #endif
00796 
00797   NS_ASSERTION(!IsPainting(), "recursive painting not permitted");
00798   if (IsPainting()) {
00799     RootViewManager()->mRecursiveRefreshPending = PR_TRUE;
00800     return;
00801   }  
00802   SetPainting(PR_TRUE);
00803 
00804   // force double buffering in general
00805   aUpdateFlags |= NS_VMREFRESH_DOUBLE_BUFFER;
00806 
00807   if (!DoDoubleBuffering())
00808     aUpdateFlags &= ~NS_VMREFRESH_DOUBLE_BUFFER;
00809 
00810   // check if the rendering context wants double-buffering or not
00811   if (aContext) {
00812     PRBool contextWantsBackBuffer = PR_TRUE;
00813     aContext->UseBackbuffer(&contextWantsBackBuffer);
00814     if (!contextWantsBackBuffer)
00815       aUpdateFlags &= ~NS_VMREFRESH_DOUBLE_BUFFER;
00816   }
00817   
00818   if (PR_FALSE == mAllowDoubleBuffering) {
00819     // Turn off double-buffering of the display
00820     aUpdateFlags &= ~NS_VMREFRESH_DOUBLE_BUFFER;
00821   }
00822 
00823   nsCOMPtr<nsIRenderingContext> localcx;
00824   nsIDrawingSurface*    ds = nsnull;
00825 
00826   if (nsnull == aContext)
00827     {
00828       localcx = CreateRenderingContext(*aView);
00829 
00830       //couldn't get rendering context. this is ok at init time atleast
00831       if (nsnull == localcx) {
00832         SetPainting(PR_FALSE);
00833         return;
00834       }
00835     } else {
00836       // plain assignment grabs another reference.
00837       localcx = aContext;
00838     }
00839 
00840   // notify the listeners.
00841   if (nsnull != mCompositeListeners) {
00842     PRUint32 listenerCount;
00843     if (NS_SUCCEEDED(mCompositeListeners->Count(&listenerCount))) {
00844       nsCOMPtr<nsICompositeListener> listener;
00845       for (PRUint32 i = 0; i < listenerCount; i++) {
00846         if (NS_SUCCEEDED(mCompositeListeners->QueryElementAt(i, NS_GET_IID(nsICompositeListener), getter_AddRefs(listener)))) {
00847           listener->WillRefreshRegion(this, aView, aContext, aRegion, aUpdateFlags);
00848         }
00849       }
00850     }
00851   }
00852 
00853   // damageRect is the clipped damage area bounds, in twips-relative-to-view-origin
00854   nsRect damageRect = damageRegion.GetBounds();
00855   // widgetDamageRectInPixels is the clipped damage area bounds,
00856   // in pixels-relative-to-widget-origin
00857   nsRect widgetDamageRectInPixels = damageRect;
00858   widgetDamageRectInPixels.MoveBy(-viewRect.x, -viewRect.y);
00859   float t2p;
00860   t2p = mContext->AppUnitsToDevUnits();
00861   widgetDamageRectInPixels.ScaleRoundOut(t2p);
00862 
00863   // On the Mac, we normally turn doublebuffering off because Quartz is
00864   // doublebuffering for us. But we need to turn it on anyway if we need
00865   // to use our blender, which requires access to the "current pixel values"
00866   // when it blends onto the canvas.
00867   nsAutoVoidArray displayList;
00868   PLArenaPool displayArena;
00869   PL_INIT_ARENA_POOL(&displayArena, "displayArena", 1024);
00870   PRBool anyTransparentPixels
00871     = BuildRenderingDisplayList(aView, damageRegion, &displayList, displayArena,
00872                                 PR_FALSE, PR_FALSE, nsnull);
00873   PRBool needBlending = PR_FALSE;
00874   for (PRInt32 i = 0; i < displayList.Count(); i++) {
00875     DisplayListElement2* element = NS_STATIC_CAST(DisplayListElement2*, displayList.ElementAt(i));
00876     if (element->mFlags & PUSH_FILTER) {
00877       aUpdateFlags |= NS_VMREFRESH_DOUBLE_BUFFER;
00878       needBlending = PR_TRUE;
00879       break;
00880     }
00881   } 
00882 
00883   if (aUpdateFlags & NS_VMREFRESH_DOUBLE_BUFFER)
00884   {
00885     nsRect maxWidgetSize;
00886     GetMaxWidgetBounds(maxWidgetSize);
00887 
00888     nsRect r(0, 0, widgetDamageRectInPixels.width, widgetDamageRectInPixels.height);
00889     if (NS_FAILED(localcx->GetBackbuffer(r, maxWidgetSize, needBlending, ds))) {
00890       //Failed to get backbuffer so turn off double buffering
00891       aUpdateFlags &= ~NS_VMREFRESH_DOUBLE_BUFFER;
00892     }
00893   }
00894 
00895   // painting will be done in aView's coordinates
00896   PRBool usingDoubleBuffer = (aUpdateFlags & NS_VMREFRESH_DOUBLE_BUFFER) && ds;
00897   if (usingDoubleBuffer) {
00898     // Adjust translations because the backbuffer holds just the damaged area,
00899     // not the whole widget
00900 
00901     // RenderViews draws in view coordinates. We want (damageRect.x, damageRect.y)
00902     // to be translated to (0,0) in the backbuffer. So:
00903     localcx->Translate(-damageRect.x, -damageRect.y);
00904     // We're going to reset the clip region for the backbuffer. We can't
00905     // just use damageRegion because nsIRenderingContext::SetClipRegion doesn't
00906     // translate/scale the coordinates (grrrrrrrrrr)
00907     // So we have to translate the region before we use it. aRegion is in
00908     // pixels-relative-to-widget-origin, so:
00909     aRegion->Offset(-widgetDamageRectInPixels.x, -widgetDamageRectInPixels.y);
00910   } else {
00911     // RenderViews draws in view coordinates. We want (viewRect.x, viewRect.y)
00912     // to be translated to (0,0) in the widget. So:
00913     localcx->Translate(-viewRect.x, -viewRect.y);
00914   }
00915 
00916   // Note that nsIRenderingContext::SetClipRegion always works in pixel coordinates,
00917   // and nsIRenderingContext::SetClipRect always works in app coordinates. Stupid huh?
00918   // Also, SetClipRegion doesn't subject its argument to the current transform, but
00919   // SetClipRect does.
00920   localcx->SetClipRegion(*aRegion, nsClipCombine_kReplace);
00921   localcx->SetClipRect(damageRect, nsClipCombine_kIntersect);
00922 
00923   if (anyTransparentPixels) {
00924     // There are some bits here that aren't going to be completely painted unless we do it now.
00925     // XXX Which color should we use for these bits?
00926     localcx->SetColor(NS_RGB(128, 128, 128));
00927     localcx->FillRect(damageRegion.GetBounds());
00928   }
00929   RenderViews(aView, *localcx, damageRegion, ds, displayList);
00930   PL_FreeArenaPool(&displayArena);
00931   PL_FinishArenaPool(&displayArena);
00932 
00933   if (usingDoubleBuffer) {
00934     // Flush bits back to the screen
00935 
00936     // Restore aRegion to pixels-relative-to-widget-origin
00937     aRegion->Offset(widgetDamageRectInPixels.x, widgetDamageRectInPixels.y);
00938     // Restore translation to its previous state (so that (0,0) is the widget origin)
00939     localcx->Translate(damageRect.x, damageRect.y);
00940     // Make damageRect twips-relative-to-widget-origin
00941     damageRect.MoveBy(-viewRect.x, -viewRect.y);
00942     // Reset clip region to widget-relative
00943     localcx->SetClipRegion(*aRegion, nsClipCombine_kReplace);
00944     localcx->SetClipRect(damageRect, nsClipCombine_kIntersect);
00945     // neither source nor destination are transformed
00946     localcx->CopyOffScreenBits(ds, 0, 0, widgetDamageRectInPixels, NS_COPYBITS_USE_SOURCE_CLIP_REGION);
00947   } else {
00948     // undo earlier translation
00949     localcx->Translate(viewRect.x, viewRect.y);
00950   }
00951 
00952   SetPainting(PR_FALSE);
00953 
00954   // notify the listeners.
00955   if (nsnull != mCompositeListeners) {
00956     PRUint32 listenerCount;
00957     if (NS_SUCCEEDED(mCompositeListeners->Count(&listenerCount))) {
00958       nsCOMPtr<nsICompositeListener> listener;
00959       for (PRUint32 i = 0; i < listenerCount; i++) {
00960         if (NS_SUCCEEDED(mCompositeListeners->QueryElementAt(i, NS_GET_IID(nsICompositeListener), getter_AddRefs(listener)))) {
00961           listener->DidRefreshRegion(this, aView, aContext, aRegion, aUpdateFlags);
00962         }
00963       }
00964     }
00965   }
00966 
00967   if (RootViewManager()->mRecursiveRefreshPending) {
00968     // Unset this flag first, since if aUpdateFlags includes NS_VMREFRESH_IMMEDIATE
00969     // we'll reenter this code from the UpdateAllViews call.
00970     RootViewManager()->mRecursiveRefreshPending = PR_FALSE;
00971     UpdateAllViews(aUpdateFlags);
00972   }
00973 
00974   localcx->ReleaseBackbuffer();
00975 
00976 #ifdef NS_VM_PERF_METRICS
00977   MOZ_TIMER_DEBUGLOG(("Stop: nsViewManager::Refresh(region), this=%p\n", this));
00978   MOZ_TIMER_STOP(mWatch);
00979   MOZ_TIMER_LOG(("vm2 Paint time (this=%p): ", this));
00980   MOZ_TIMER_PRINT(mWatch);
00981 #endif
00982 
00983 }
00984 
00985 void nsViewManager::DefaultRefresh(nsView* aView, const nsRect* aRect)
00986 {
00987   NS_PRECONDITION(aView, "Must have a view to work with!");
00988   nsIWidget* widget = aView->GetNearestWidget(nsnull);
00989   if (! widget)
00990     return;
00991 
00992   nsCOMPtr<nsIRenderingContext> context = CreateRenderingContext(*aView);
00993 
00994   if (! context)
00995     return;
00996 
00997   nscolor bgcolor = mDefaultBackgroundColor;
00998 
00999   if (NS_GET_A(mDefaultBackgroundColor) == 0) {
01000     NS_WARNING("nsViewManager: Asked to paint a default background, but no default background color is set!");
01001     return;
01002   }
01003 
01004   context->SetColor(bgcolor);
01005   context->FillRect(*aRect);
01006 }
01007 
01008 // Perform a *stable* sort of the buffer by increasing Z-index. The common case is
01009 // when many or all z-indices are equal and the list is mostly sorted; make sure
01010 // that's fast (should be linear time if all z-indices are equal).
01011 static void ApplyZOrderStableSort(nsVoidArray &aBuffer, nsVoidArray &aMergeTmp, PRInt32 aStart, PRInt32 aEnd) {
01012   if (aEnd - aStart <= 6) {
01013     // do a fast bubble sort for the small sizes
01014     for (PRInt32 i = aEnd - 1; i > aStart; i--) {
01015       PRBool sorted = PR_TRUE;
01016       for (PRInt32 j = aStart; j < i; j++) {
01017         DisplayListElement2* e1 = NS_STATIC_CAST(DisplayListElement2*, aBuffer.ElementAt(j));
01018         DisplayListElement2* e2 = NS_STATIC_CAST(DisplayListElement2*, aBuffer.ElementAt(j + 1));
01019         if (e1->mZIndex > e2->mZIndex) {
01020           sorted = PR_FALSE;
01021           // We could use aBuffer.MoveElement(), but it wouldn't be much of
01022           // a win if any for swapping two elements.
01023           aBuffer.ReplaceElementAt(e2, j);
01024           aBuffer.ReplaceElementAt(e1, j + 1);
01025         }
01026       }
01027       if (sorted) {
01028         return;
01029       }
01030     }
01031   } else {
01032     // merge sort for the rest
01033     PRInt32 mid = (aEnd + aStart)/2;
01034 
01035     ApplyZOrderStableSort(aBuffer, aMergeTmp, aStart, mid);
01036     ApplyZOrderStableSort(aBuffer, aMergeTmp, mid, aEnd);
01037 
01038     DisplayListElement2* e1 = NS_STATIC_CAST(DisplayListElement2*, aBuffer.ElementAt(mid - 1));
01039     DisplayListElement2* e2 = NS_STATIC_CAST(DisplayListElement2*, aBuffer.ElementAt(mid));
01040 
01041     // fast common case: the list is already completely sorted
01042     if (e1->mZIndex <= e2->mZIndex) {
01043       return;
01044     }
01045     // we have some merging to do.
01046 
01047     PRInt32 i1 = aStart;
01048     PRInt32 i2 = mid;
01049 
01050     e1 = NS_STATIC_CAST(DisplayListElement2*, aBuffer.ElementAt(i1));
01051     e2 = NS_STATIC_CAST(DisplayListElement2*, aBuffer.ElementAt(i2));
01052     while (i1 < mid || i2 < aEnd) {
01053       if (i1 < mid && (i2 == aEnd || e1->mZIndex <= e2->mZIndex)) {
01054         aMergeTmp.AppendElement(e1);
01055         i1++;
01056         if (i1 < mid) {
01057           e1 = NS_STATIC_CAST(DisplayListElement2*, aBuffer.ElementAt(i1));
01058         }
01059       } else {
01060         aMergeTmp.AppendElement(e2);
01061         i2++;
01062         if (i2 < aEnd) {
01063           e2 = NS_STATIC_CAST(DisplayListElement2*, aBuffer.ElementAt(i2));
01064         }
01065       }
01066     }
01067 
01068     for (PRInt32 i = aStart; i < aEnd; i++) {
01069       aBuffer.ReplaceElementAt(aMergeTmp.ElementAt(i - aStart), i);
01070     }
01071 
01072     aMergeTmp.Clear();
01073   }
01074 }
01075 
01076 static nsInt64 BuildExtendedZIndex(nsView* aView) {
01077   return (nsInt64(aView->GetZIndex()) << 1) | nsInt64(aView->IsTopMost() ? 1 : 0);
01078 }
01079 
01080 // The display-element (indirect) children of aNode are extracted and appended to aBuffer in
01081 // z-order, with the bottom-most elements first.
01082 // Their z-index is set to the z-index they will have in aNode's parent.
01083 // I.e. if aNode's view has "z-index: auto", the nodes will keep their z-index, otherwise
01084 // their z-indices will all be equal to the z-index value of aNode's view.
01085 static void SortByZOrder(DisplayZTreeNode *aNode, nsVoidArray &aBuffer, nsVoidArray &aMergeTmp,
01086                          PRBool aForceSort, PLArenaPool &aPool)
01087 {
01088   PRBool autoZIndex = PR_TRUE;
01089   nsInt64 explicitZIndex = 0;
01090 
01091   if (nsnull != aNode->mView) {
01092     // Hixie says only non-translucent elements can have z-index:auto
01093     autoZIndex = aNode->mView->GetZIndexIsAuto() && aNode->mView->GetOpacity() == 1.0f;
01094     explicitZIndex = BuildExtendedZIndex(aNode->mView);
01095   }
01096 
01097   if (nsnull == aNode->mZChild) {
01098     if (nsnull != aNode->mDisplayElement) {
01099       aBuffer.AppendElement(aNode->mDisplayElement);
01100       aNode->mDisplayElement->mZIndex = explicitZIndex;
01101       aNode->mDisplayElement = nsnull;
01102     }
01103     return;
01104   }
01105 
01106   DisplayZTreeNode *child;
01107   PRInt32 childStartIndex = aBuffer.Count();
01108   for (child = aNode->mZChild; nsnull != child; child = child->mZSibling) {
01109     SortByZOrder(child, aBuffer, aMergeTmp, PR_FALSE, aPool);
01110   }
01111   PRInt32 childEndIndex = aBuffer.Count();
01112   PRInt32 sortStartIndex = childStartIndex;
01113   PRInt32 sortEndIndex = childEndIndex;
01114   PRBool hasClip = PR_FALSE;
01115   DisplayListElement2* ePush = nsnull;
01116   DisplayListElement2* ePop = nsnull;
01117 
01118   // When we sort the children by z-index, don't sort any PUSH_ or POP_ instructions
01119   // which are bracketing the children.
01120   while (sortEndIndex - sortStartIndex >= 2) {
01121     DisplayListElement2* childElem =
01122       NS_STATIC_CAST(DisplayListElement2*, aBuffer.ElementAt(sortStartIndex));
01123     if (childElem->mView == aNode->mView) {
01124       if (childElem->mFlags & PUSH_CLIP) {
01125         hasClip = PR_TRUE;
01126         // remember where the push and pop instructions are so we can
01127         // duplicate them later, if necessary
01128         ePush = NS_STATIC_CAST(DisplayListElement2*, aBuffer.ElementAt(sortStartIndex));
01129         ePop = NS_STATIC_CAST(DisplayListElement2*, aBuffer.ElementAt(sortEndIndex - 1));
01130         sortStartIndex++;
01131         sortEndIndex--;
01132       } else if (childElem->mFlags & PUSH_FILTER) {
01133         NS_ASSERTION(!autoZIndex, "FILTER cannot apply to z-index:auto view");
01134         sortStartIndex++;
01135         sortEndIndex--;
01136       } else {
01137         break;
01138       }
01139     } else {
01140       break;
01141     }
01142   }
01143 
01144   if (hasClip) {
01145     ApplyZOrderStableSort(aBuffer, aMergeTmp, sortStartIndex, sortEndIndex);
01146     
01147     if (autoZIndex && sortEndIndex - sortStartIndex >= 1) {
01148       // If we're an auto-z-index, then we have to worry about the possibility that some of
01149       // our children may be moved by the z-sorter beyond the bounds of the PUSH...POP clip
01150       // instructions. So basically, we ensure that around every group of children of
01151       // equal z-index, there is a PUSH...POP element pair with the same z-index. The stable
01152       // z-sorter will not break up such a group.
01153       // Note that if we're not an auto-z-index set, then our children will never be broken
01154       // up so we don't need to do this.
01155       // We also don't have to worry if we have no real children.
01156       // We don't have to do the same thing for PUSH_FILTER/POP_FILTER because
01157       // a filter always corresponds to non-auto z-index; there is no way children
01158       // can be sorted beyond the PUSH/POP instructions.
01159       DisplayListElement2* eFirstChild = NS_STATIC_CAST(DisplayListElement2*, aBuffer.ElementAt(sortStartIndex));
01160 
01161       ePush->mZIndex = eFirstChild->mZIndex;
01162 
01163       DisplayListElement2* eLastChild = NS_STATIC_CAST(DisplayListElement2*, aBuffer.ElementAt(sortEndIndex - 1));
01164 
01165       ePop->mZIndex = eLastChild->mZIndex;
01166 
01167       DisplayListElement2* e = eFirstChild;
01168       for (PRInt32 i = sortStartIndex; i < sortEndIndex - 1; i++) {
01169         DisplayListElement2* eNext = NS_STATIC_CAST(DisplayListElement2*, aBuffer.ElementAt(i + 1));
01170         NS_ASSERTION(e->mZIndex <= eNext->mZIndex, "Display Z-list is not sorted!!");
01171         if (e->mZIndex != eNext->mZIndex) {
01172           // need to insert a POP for the last sequence and a PUSH for the next sequence
01173           DisplayListElement2 *newPop, *newPush;
01174 
01175           ARENA_ALLOCATE(newPop, &aPool, DisplayListElement2);
01176           ARENA_ALLOCATE(newPush, &aPool, DisplayListElement2);
01177 
01178           *newPop = *ePop;
01179           newPop->mZIndex = e->mZIndex;
01180           *newPush = *ePush;
01181           newPush->mZIndex = eNext->mZIndex;
01182           aBuffer.InsertElementAt(newPop, i + 1);
01183           aBuffer.InsertElementAt(newPush, i + 2);
01184           i += 2;
01185           childEndIndex += 2;
01186           sortEndIndex += 2;
01187         }
01188         e = eNext;
01189       }
01190     }
01191   } else if (aForceSort || !autoZIndex || aNode->mView->IsTopMost()) {
01192     // We must do a sort now if we're going to update all the topmost bits
01193     // below
01194     ApplyZOrderStableSort(aBuffer, aMergeTmp, sortStartIndex, sortEndIndex);
01195   }
01196 
01197   for (PRInt32 i = childStartIndex; i < childEndIndex; i++) {
01198     DisplayListElement2* element = NS_STATIC_CAST(DisplayListElement2*, aBuffer.ElementAt(i));
01199     if (!autoZIndex) {
01200       element->mZIndex = explicitZIndex;
01201     } else if (aNode->mView->IsTopMost()) {
01202       // promote children to topmost if this view is topmost
01203       element->mZIndex |= nsInt64(1);
01204     }
01205   }
01206 }
01207 
01208 static void PushStateAndClip(nsIRenderingContext** aRCs, PRInt32 aCount, nsRect &aRect) {
01209   for (int i = 0; i < aCount; i++) {
01210     if (aRCs[i]) {
01211       aRCs[i]->PushState();
01212       aRCs[i]->SetClipRect(aRect, nsClipCombine_kIntersect);
01213     }
01214   }
01215 }
01216 
01217 static void PopState(nsIRenderingContext **aRCs, PRInt32 aCount) {
01218   for (int i = 0; i < aCount; i++) {
01219     if (aRCs[i])
01220       aRCs[i]->PopState();
01221   }
01222 }
01223 
01224 void nsViewManager::AddCoveringWidgetsToOpaqueRegion(nsRegion &aRgn, nsIDeviceContext* aContext,
01225                                                      nsView* aRootView) {
01226   NS_PRECONDITION(aRootView, "Must have root view");
01227   
01228   // We accumulate the bounds of widgets obscuring aRootView's widget into opaqueRgn.
01229   // In OptimizeDisplayList, display list elements which lie behind obscuring native
01230   // widgets are dropped.
01231   // This shouldn't really be necessary, since the GFX/Widget toolkit should remove these
01232   // covering widgets from the clip region passed into the paint command. But right now
01233   // they only give us a paint rect and not a region, so we can't access that information.
01234   // It's important to identifying areas that are covered by native widgets to avoid
01235   // painting their views many times as we process invalidates from the root widget all the
01236   // way down to the nested widgets.
01237   // 
01238   // NB: we must NOT add widgets that correspond to floating views!
01239   // We may be required to paint behind them
01240   aRgn.SetEmpty();
01241 
01242   nsIWidget* widget = aRootView->GetNearestWidget(nsnull);
01243   if (!widget) {
01244     return;
01245   }
01246 
01247   for (nsIWidget* childWidget = widget->GetFirstChild();
01248        childWidget;
01249        childWidget = childWidget->GetNextSibling()) {
01250     PRBool widgetVisible;
01251     childWidget->IsVisible(widgetVisible);
01252     if (widgetVisible) {
01253       nsView* view = nsView::GetViewFor(childWidget);
01254       if (view && view->GetVisibility() == nsViewVisibility_kShow
01255           && !view->GetFloating()) {
01256         nsRect bounds = view->GetBounds();
01257         if (bounds.width > 0 && bounds.height > 0) {
01258           nsView* viewParent = view->GetParent();
01259             
01260           while (viewParent && viewParent != aRootView) {
01261             viewParent->ConvertToParentCoords(&bounds.x, &bounds.y);
01262             viewParent = viewParent->GetParent();
01263           }
01264             
01265           // maybe we couldn't get the view into the coordinate
01266           // system of aRootView (maybe it's not a descendant
01267           // view of aRootView?); if so, don't use it
01268           if (viewParent) {
01269             aRgn.Or(aRgn, bounds);
01270           }
01271         }
01272       }
01273     }
01274   }
01275 }
01276 
01277 PRBool nsViewManager::BuildRenderingDisplayList(nsIView* aRootView,
01278   const nsRegion& aRegion, nsVoidArray* aDisplayList, PLArenaPool &aPool,
01279   PRBool aIgnoreCoveringWidgets, PRBool aIgnoreOutsideClipping,
01280   nsIView* aSuppressScrolling)
01281 {
01282   BuildDisplayList(NS_STATIC_CAST(nsView*, aRootView),
01283                    aRegion.GetBounds(), PR_FALSE, aIgnoreOutsideClipping,
01284                    aSuppressScrolling,
01285                    aDisplayList, aPool);
01286 
01287   nsRegion opaqueRgn;
01288   if (!aIgnoreCoveringWidgets) {
01289     AddCoveringWidgetsToOpaqueRegion(opaqueRgn, mContext,
01290                                      NS_STATIC_CAST(nsView*, aRootView));
01291   }
01292 
01293   nsRect finalTransparentRect;
01294   OptimizeDisplayList(aDisplayList, aRegion, finalTransparentRect, opaqueRgn, PR_FALSE);
01295 
01296 #ifdef DEBUG_roc
01297   if (!finalTransparentRect.IsEmpty()) {
01298     printf("XXX: Using final transparent rect, x=%d, y=%d, width=%d, height=%d\n",
01299            finalTransparentRect.x, finalTransparentRect.y, finalTransparentRect.width, finalTransparentRect.height);
01300   }
01301 #endif
01302 
01303   return !finalTransparentRect.IsEmpty();
01304 }
01305 
01306 /*
01307   aRCSurface is the drawing surface being used to double-buffer aRC, or null
01308   if no double-buffering is happening. We pass this in here so that we can
01309   blend directly into the double-buffer offscreen memory.
01310 */
01311 void nsViewManager::RenderViews(nsView *aRootView, nsIRenderingContext& aRC,
01312                                 const nsRegion& aRegion, nsIDrawingSurface* aRCSurface,
01313                                 const nsVoidArray& aDisplayList)
01314 {
01315 #ifdef DEBUG_roc
01316   if (getenv("MOZ_SHOW_DISPLAY_LIST")) ShowDisplayList(&aDisplayList);
01317 #endif
01318 
01319   PRInt32 index = 0;
01320   nsRect fakeClipRect;
01321   PRBool anyRendered;
01322   OptimizeDisplayListClipping(&aDisplayList, PR_FALSE, fakeClipRect, index, anyRendered);
01323 
01324   index = 0;
01325   OptimizeTranslucentRegions(aDisplayList, &index, nsnull);
01326 
01327   nsIWidget* widget = aRootView->GetWidget();
01328   PRBool translucentWindow = PR_FALSE;
01329   if (widget) {
01330     widget->GetWindowTranslucency(translucentWindow);
01331     if (translucentWindow) {
01332       NS_WARNING("Transparent window enabled");
01333       NS_ASSERTION(aRCSurface, "Cannot support transparent windows with doublebuffering disabled");
01334     }
01335   }
01336 
01337   // Create a buffer wrapping aRC (which is usually the double-buffering offscreen buffer).
01338   BlendingBuffers* buffers =
01339     CreateBlendingBuffers(&aRC, PR_TRUE, aRCSurface, translucentWindow, aRegion.GetBounds());
01340   NS_ASSERTION(buffers, "Failed to create rendering buffers");
01341   if (!buffers)
01342     return;
01343 
01344   nsAutoVoidArray filterStack;
01345 
01346   // draw all views in the display list, from back to front.
01347   for (PRInt32 i = 0; i < aDisplayList.Count(); i++) {
01348     DisplayListElement2* element = NS_STATIC_CAST(DisplayListElement2*, aDisplayList.ElementAt(i));
01349 
01350     nsIRenderingContext* RCs[2] = { buffers->mBlackCX, buffers->mWhiteCX };
01351 
01352     if (element->mFlags & PUSH_CLIP) {
01353       PushStateAndClip(RCs, 2, element->mBounds);
01354     }
01355     if (element->mFlags & PUSH_FILTER) {
01356       NS_ASSERTION(aRCSurface,
01357                    "Cannot support translucent elements with doublebuffering disabled");
01358       if (aRCSurface) {
01359         // Save current buffer on the stack and start rendering into a new
01360         // offscreen buffer
01361         filterStack.AppendElement(buffers);
01362         buffers = CreateBlendingBuffers(&aRC, PR_FALSE, nsnull,
01363                                         (element->mFlags & VIEW_TRANSPARENT) != 0,
01364                                         element->mBounds);
01365       }
01366     }
01367 
01368     if (element->mFlags & VIEW_RENDERED) {
01369       if (element->mFlags & VIEW_CLIPPED) {
01370         PushStateAndClip(RCs, 2, element->mBounds);
01371       }
01372       
01373       RenderDisplayListElement(element, RCs[0]);
01374       // RenderDisplayListElement won't do anything if the context is null
01375       RenderDisplayListElement(element, RCs[1]);
01376 
01377       if (element->mFlags & VIEW_CLIPPED) {
01378         PopState(RCs, 2);
01379       }
01380     }
01381 
01382     if (element->mFlags & POP_FILTER) {
01383       if (aRCSurface) {
01384         // Pop the last buffer off the stack and composite the current buffer into
01385         // the last buffer
01386         BlendingBuffers* doneBuffers = buffers;
01387         buffers = NS_STATIC_CAST(BlendingBuffers*,
01388                                  filterStack.ElementAt(filterStack.Count() - 1));
01389         filterStack.RemoveElementAt(filterStack.Count() - 1);
01390         
01391         // perform the blend itself.
01392         nsRect damageRectInPixels = element->mBounds;
01393         damageRectInPixels -= buffers->mOffset;
01394         damageRectInPixels *= mTwipsToPixels;
01395         if (damageRectInPixels.width > 0 && damageRectInPixels.height > 0) {
01396           nsIRenderingContext* targets[2] = { buffers->mBlackCX, buffers->mWhiteCX };
01397           for (int j = 0; j < 2; j++) {
01398             if (targets[j]) {
01399               mBlender->Blend(0, 0,
01400                               damageRectInPixels.width, damageRectInPixels.height,
01401                               doneBuffers->mBlackCX, targets[j],
01402                               damageRectInPixels.x, damageRectInPixels.y,
01403                               element->mView->GetOpacity(), doneBuffers->mWhiteCX,
01404                               NS_RGB(0, 0, 0), NS_RGB(255, 255, 255));
01405             }
01406           }
01407         }
01408         // probably should recycle these so we don't eat the cost of graphics memory
01409         // allocation
01410         delete doneBuffers;
01411       }
01412     }
01413     if (element->mFlags & POP_CLIP) {
01414       PopState(RCs, 2);
01415     }
01416       
01417     // The element is destroyed when the pool is finished
01418     // delete element;
01419   }
01420     
01421   if (translucentWindow) {
01422     // Get the alpha channel into an array so we can send it to the widget
01423     nsRect r = aRegion.GetBounds();
01424     r *= mTwipsToPixels;
01425     nsRect bufferRect(0, 0, r.width, r.height);
01426     PRUint8* alphas = nsnull;
01427     nsresult rv = mBlender->GetAlphas(bufferRect, buffers->mBlack,
01428                                       buffers->mWhite, &alphas);
01429     
01430     if (NS_SUCCEEDED(rv)) {
01431       widget->UpdateTranslucentWindowAlpha(r, alphas);
01432     }
01433     delete[] alphas;
01434   }
01435 
01436   delete buffers;
01437 }
01438 
01439 void nsViewManager::RenderDisplayListElement(DisplayListElement2* element,
01440                                              nsIRenderingContext* aRC) {
01441   if (!aRC)
01442     return;
01443   
01444   PRBool clipEmpty;
01445   nsRect r;
01446   nsView* view = element->mView;
01447 
01448   view->GetDimensions(r);
01449 
01450   aRC->PushState();
01451 
01452   nscoord x = element->mAbsX - r.x, y = element->mAbsY - r.y;
01453   aRC->Translate(x, y);
01454 
01455   nsRect drect(element->mBounds.x - x, element->mBounds.y - y,
01456                element->mBounds.width, element->mBounds.height);
01457   
01458   element->mView->Paint(*aRC, drect, 0, clipEmpty);
01459   
01460   aRC->PopState();
01461 }
01462 
01463 void nsViewManager::PaintView(nsView *aView, nsIRenderingContext &aRC, nscoord x, nscoord y,
01464                               const nsRect &aDamageRect)
01465 {
01466   aRC.PushState();
01467   aRC.Translate(x, y);
01468   PRBool unused;
01469   aView->Paint(aRC, aDamageRect, 0, unused);
01470   aRC.PopState();
01471 }
01472 
01473 static nsresult NewOffscreenContext(nsIDeviceContext* deviceContext, nsIDrawingSurface* surface,
01474                                     const nsRect& aRect, nsIRenderingContext* *aResult)
01475 {
01476   nsresult             rv;
01477   nsIRenderingContext *context = nsnull;
01478 
01479   rv = deviceContext->CreateRenderingContext(surface, context);
01480   if (NS_FAILED(rv))
01481     return rv;
01482 
01483   // always initialize clipping, linux won't draw images otherwise.
01484   nsRect clip(0, 0, aRect.width, aRect.height);
01485   context->SetClipRect(clip, nsClipCombine_kReplace);
01486 
01487   context->Translate(-aRect.x, -aRect.y);
01488   
01489   *aResult = context;
01490   return NS_OK;
01491 }
01492 
01493 BlendingBuffers::BlendingBuffers(nsIRenderingContext* aCleanupContext) {
01494   mCleanupContext = aCleanupContext;
01495 
01496   mOwnBlackSurface = PR_FALSE;
01497   mWhite = nsnull;
01498   mBlack = nsnull;
01499 }
01500 
01501 BlendingBuffers::~BlendingBuffers() {
01502   if (mWhite)
01503     mCleanupContext->DestroyDrawingSurface(mWhite);
01504 
01505   if (mBlack && mOwnBlackSurface)
01506     mCleanupContext->DestroyDrawingSurface(mBlack);
01507 }
01508 
01509 /*
01510 @param aBorrowContext set to PR_TRUE if the BlendingBuffers' "black" context
01511   should be just aRC; set to PR_FALSE if we should create a new offscreen context
01512 @param aBorrowSurface if aBorrowContext is PR_TRUE, then this is the offscreen surface
01513   corresponding to aRC, or nsnull if aRC doesn't have one; if aBorrowContext is PR_FALSE,
01514   this parameter is ignored
01515 @param aNeedAlpha set to PR_FALSE if the caller guarantees that every pixel of the
01516   BlendingBuffers will be drawn with opacity 1.0, PR_TRUE otherwise
01517 @param aRect the screen rectangle covered by the new BlendingBuffers, in app units, and
01518   relative to the origin of aRC
01519 */
01520 BlendingBuffers*
01521 nsViewManager::CreateBlendingBuffers(nsIRenderingContext *aRC,
01522                                      PRBool aBorrowContext,
01523                                      nsIDrawingSurface* aBorrowSurface,
01524                                      PRBool aNeedAlpha,
01525                                      const nsRect& aRect)
01526 {
01527   nsresult rv;
01528 
01529   // create a blender, if none exists already.
01530   if (!mBlender) {
01531     mBlender = do_CreateInstance(kBlenderCID, &rv);
01532     if (NS_FAILED(rv))
01533       return nsnull;
01534     rv = mBlender->Init(mContext);
01535     if (NS_FAILED(rv)) {
01536       mBlender = nsnull;
01537       return nsnull;
01538     }
01539   }
01540 
01541   BlendingBuffers* buffers = new BlendingBuffers(aRC);
01542   if (!buffers)
01543     return nsnull;
01544 
01545   buffers->mOffset = nsPoint(aRect.x, aRect.y);
01546 
01547   nsRect offscreenBounds(0, 0, aRect.width, aRect.height);
01548   offscreenBounds.ScaleRoundOut(mTwipsToPixels);
01549 
01550   if (aBorrowContext) {
01551     buffers->mBlackCX = aRC;
01552     buffers->mBlack = aBorrowSurface;
01553   } else {
01554     rv = aRC->CreateDrawingSurface(offscreenBounds, NS_CREATEDRAWINGSURFACE_FOR_PIXEL_ACCESS, buffers->mBlack);
01555     if (NS_FAILED(rv)) {
01556       delete buffers;
01557       return nsnull;
01558     }
01559     buffers->mOwnBlackSurface = PR_TRUE;
01560     
01561     rv = NewOffscreenContext(mContext, buffers->mBlack, aRect, getter_AddRefs(buffers->mBlackCX));
01562     if (NS_FAILED(rv)) {
01563       delete buffers;
01564       return nsnull;
01565     }
01566   }
01567 
01568   if (aNeedAlpha) {
01569     rv = aRC->CreateDrawingSurface(offscreenBounds, NS_CREATEDRAWINGSURFACE_FOR_PIXEL_ACCESS, buffers->mWhite);
01570     if (NS_FAILED(rv)) {
01571       delete buffers;
01572       return nsnull;
01573     }
01574     
01575     rv = NewOffscreenContext(mContext, buffers->mWhite, aRect, getter_AddRefs(buffers->mWhiteCX));
01576     if (NS_FAILED(rv)) {
01577       delete buffers;
01578       return nsnull;
01579     }
01580     
01581     // Note that we only need to fill mBlackCX with black when some pixels are going
01582     // to be transparent.
01583     buffers->mBlackCX->SetColor(NS_RGB(0, 0, 0));
01584     buffers->mBlackCX->FillRect(aRect);
01585     buffers->mWhiteCX->SetColor(NS_RGB(255, 255, 255));
01586     buffers->mWhiteCX->FillRect(aRect);
01587   }
01588 
01589   return buffers;
01590 }
01591 
01592 void nsViewManager::ProcessPendingUpdates(nsView* aView, PRBool aDoInvalidate)
01593 {
01594   NS_ASSERTION(IsRootVM(), "Updates will be missed");
01595 
01596   // Protect against a null-view.
01597   if (!aView) {
01598     return;
01599   }
01600 
01601   if (aView->HasWidget()) {
01602     aView->ResetWidgetBounds(PR_FALSE, PR_FALSE, PR_TRUE);
01603   }
01604 
01605   // process pending updates in child view.
01606   for (nsView* childView = aView->GetFirstChild(); childView;
01607        childView = childView->GetNextSibling()) {
01608     ProcessPendingUpdates(childView, aDoInvalidate);
01609   }
01610 
01611   if (aDoInvalidate && aView->HasNonEmptyDirtyRegion()) {
01612     // Push out updates after we've processed the children; ensures that
01613     // damage is applied based on the final widget geometry
01614     NS_ASSERTION(mRefreshEnabled, "Cannot process pending updates with refresh disabled");
01615     nsRegion* dirtyRegion = aView->GetDirtyRegion();
01616     if (dirtyRegion) {
01617       UpdateWidgetArea(aView, *dirtyRegion, nsnull);
01618       dirtyRegion->SetEmpty();
01619     }
01620   }
01621 }
01622 
01623 NS_IMETHODIMP nsViewManager::Composite()
01624 {
01625   if (!IsRootVM()) {
01626     return RootViewManager()->Composite();
01627   }
01628   
01629   if (UpdateCount() > 0)
01630     {
01631       ForceUpdate();
01632       ClearUpdateCount();
01633     }
01634 
01635   return NS_OK;
01636 }
01637 
01638 NS_IMETHODIMP nsViewManager::UpdateView(nsIView *aView, PRUint32 aUpdateFlags)
01639 {
01640   // Mark the entire view as damaged
01641   nsView* view = NS_STATIC_CAST(nsView*, aView);
01642 
01643   nsRect bounds = view->GetBounds();
01644   view->ConvertFromParentCoords(&bounds.x, &bounds.y);
01645   return UpdateView(view, bounds, aUpdateFlags);
01646 }
01647 
01648 // This method accumulates the intersectons of all dirty regions attached to
01649 // descendants of aSourceView with the cliprect of aTargetView into the dirty
01650 // region of aTargetView, after offseting said intersections by aOffset.
01651 static void
01652 AccumulateIntersectionsIntoDirtyRegion(nsView* aTargetView,
01653                                        nsView* aSourceView,
01654                                        const nsPoint& aOffset)
01655 {
01656   if (aSourceView->HasNonEmptyDirtyRegion()) {
01657     // In most cases, aSourceView is an ancestor of aTargetView, since most
01658     // commonly we have dirty rects on the root view.
01659     nsPoint offset = aTargetView->GetOffsetTo(aSourceView);
01660     nsRegion intersection;
01661     intersection.And(*aSourceView->GetDirtyRegion(),
01662                      aTargetView->GetClippedRect() + offset);
01663     if (!intersection.IsEmpty()) {
01664       nsRegion* targetRegion = aTargetView->GetDirtyRegion();
01665       if (targetRegion) {
01666         intersection.MoveBy(-offset + aOffset);
01667         targetRegion->Or(*targetRegion, intersection);
01668         // Random simplification number...
01669         targetRegion->SimplifyOutward(20);
01670       }
01671     }
01672   }
01673 
01674   if (aSourceView == aTargetView) {
01675     // No need to do this with kids of aTargetView
01676     return;
01677   }
01678   
01679   for (nsView* kid = aSourceView->GetFirstChild();
01680        kid;
01681        kid = kid->GetNextSibling()) {
01682     AccumulateIntersectionsIntoDirtyRegion(aTargetView, kid, aOffset);
01683   }
01684 }
01685 
01686 // This gets around an internal compiler error C1001
01687 // (compiler file 'E:\8799\vc98\p2\src\P2\main.c', line 494)
01688 #ifdef WINCE
01689 #pragma optimize( "", off )
01690 #endif
01691 void
01692 nsViewManager::WillBitBlit(nsView* aView, nsPoint aScrollAmount)
01693 {
01694   if (!IsRootVM()) {
01695     RootViewManager()->WillBitBlit(aView, aScrollAmount);
01696     return;
01697   }
01698 
01699   NS_PRECONDITION(aView, "Must have a view");
01700   NS_PRECONDITION(aView->HasWidget(), "View must have a widget");
01701 
01702   ++mScrollCnt;
01703   
01704   // Since the view is actually moving the widget by -aScrollAmount, that's the
01705   // offset we want to use when accumulating dirty rects.
01706   AccumulateIntersectionsIntoDirtyRegion(aView, GetRootView(), -aScrollAmount);
01707 }
01708 #ifdef WINCE
01709 #pragma optimize( "", on )
01710 #endif
01711 
01712 
01713 // Invalidate all widgets which overlap the view, other than the view's own widgets.
01714 void
01715 nsViewManager::UpdateViewAfterScroll(nsView *aView)
01716 {
01717   NS_ASSERTION(RootViewManager()->mScrollCnt > 0,
01718                "Someone forgot to call WillBitBlit()");
01719   // Look at the view's clipped rect. It may be that part of the view is clipped out
01720   // in which case we don't need to worry about invalidating the clipped-out part.
01721   nsRect damageRect = aView->GetClippedRect();
01722   if (damageRect.IsEmpty()) {
01723     return;
01724   }
01725   damageRect.MoveBy(ComputeViewOffset(aView));
01726 
01727   // if this is a floating view, it isn't covered by any widgets other than
01728   // its children, which are handled by the widget scroller.
01729   if (aView->GetFloating()) {
01730     return;
01731   }
01732 
01733   UpdateWidgetArea(RootViewManager()->GetRootView(), nsRegion(damageRect), aView);
01734 
01735   Composite();
01736   --RootViewManager()->mScrollCnt;
01737 }
01738 
01745 void
01746 nsViewManager::UpdateWidgetArea(nsView *aWidgetView, const nsRegion &aDamagedRegion,
01747                                 nsView* aIgnoreWidgetView)
01748 {
01749   if (!IsRefreshEnabled()) {
01750     // accumulate this rectangle in the view's dirty region, so we can
01751     // process it later.
01752     nsRegion* dirtyRegion = aWidgetView->GetDirtyRegion();
01753     if (!dirtyRegion) return;
01754 
01755     dirtyRegion->Or(*dirtyRegion, aDamagedRegion);
01756     // Don't let dirtyRegion grow beyond 8 rects
01757     dirtyRegion->SimplifyOutward(8);
01758     nsViewManager* rootVM = RootViewManager();
01759     rootVM->mHasPendingUpdates = PR_TRUE;
01760     rootVM->IncrementUpdateCount();
01761     return;
01762     // this should only happen at the top level, and this result
01763     // should not be consumed by top-level callers, so it doesn't
01764     // really matter what we return
01765   }
01766 
01767   // If the bounds don't overlap at all, there's nothing to do
01768   nsRegion intersection;
01769   intersection.And(aWidgetView->GetDimensions(), aDamagedRegion);
01770   if (intersection.IsEmpty()) {
01771     return;
01772   }
01773 
01774   // If the widget is hidden, it don't cover nothing
01775   if (nsViewVisibility_kHide == aWidgetView->GetVisibility()) {
01776 #ifdef DEBUG
01777     // Assert if view is hidden but widget is visible
01778     nsIWidget* widget = aWidgetView->GetNearestWidget(nsnull);
01779     if (widget) {
01780       PRBool visible;
01781       widget->IsVisible(visible);
01782       NS_ASSERTION(!visible, "View is hidden but widget is visible!");
01783     }
01784 #endif
01785     return;
01786   }
01787 
01788   if (aWidgetView == aIgnoreWidgetView) {
01789     // the widget for aIgnoreWidgetView (and its children) should be treated as already updated.
01790     return;
01791   }
01792 
01793   nsIWidget* widget = aWidgetView->GetNearestWidget(nsnull);
01794   if (!widget) {
01795     // The root view or a scrolling view might not have a widget
01796     // (for example, during printing). We get here when we scroll
01797     // during printing to show selected options in a listbox, for example.
01798     return;
01799   }
01800 
01801   // Update all child widgets with the damage. In the process,
01802   // accumulate the union of all the child widget areas, or at least
01803   // some subset of that.
01804   nsRegion children;
01805   for (nsIWidget* childWidget = widget->GetFirstChild();
01806        childWidget;
01807        childWidget = childWidget->GetNextSibling()) {
01808     nsView* view = nsView::GetViewFor(childWidget);
01809     if (view && view->GetVisibility() == nsViewVisibility_kShow) {
01810       // Don't mess with views that are in completely different view
01811       // manager trees
01812       if (view->GetViewManager()->RootViewManager() == RootViewManager()) {
01813         // get the damage region into 'view's coordinate system
01814         nsRegion damage = intersection;
01815         nsPoint offset = view->GetOffsetTo(aWidgetView);
01816         damage.MoveBy(-offset);
01817         UpdateWidgetArea(view, damage, aIgnoreWidgetView);
01818         children.Or(children, view->GetDimensions() + offset);
01819         children.SimplifyInward(20);
01820       }
01821     }
01822   }
01823 
01824   nsRegion leftOver;
01825   leftOver.Sub(intersection, children);
01826 
01827   if (!leftOver.IsEmpty()) {
01828     NS_ASSERTION(IsRefreshEnabled(), "Can only get here with refresh enabled, I hope");
01829 
01830     const nsRect* r;
01831     for (nsRegionRectIterator iter(leftOver); (r = iter.Next());) {
01832       nsRect bounds = *r;
01833       ViewToWidget(aWidgetView, aWidgetView, bounds);
01834       widget->Invalidate(bounds, PR_FALSE);
01835     }
01836   }
01837 }
01838 
01839 NS_IMETHODIMP nsViewManager::UpdateView(nsIView *aView, const nsRect &aRect, PRUint32 aUpdateFlags)
01840 {
01841   NS_PRECONDITION(nsnull != aView, "null view");
01842 
01843   nsView* view = NS_STATIC_CAST(nsView*, aView);
01844 
01845   // Only Update the rectangle region of the rect that intersects the view's non clipped rectangle
01846   nsRect clippedRect = view->GetClippedRect();
01847   if (clippedRect.IsEmpty()) {
01848     return NS_OK;
01849   }
01850 
01851   nsRect damagedRect;
01852   damagedRect.IntersectRect(aRect, clippedRect);
01853 
01854    // If the rectangle is not visible then abort
01855    // without invalidating. This is a performance 
01856    // enhancement since invalidating a native widget
01857    // can be expensive.
01858    // This also checks for silly request like damagedRect.width = 0 or damagedRect.height = 0
01859   nsRectVisibility rectVisibility;
01860   GetRectVisibility(view, damagedRect, 0, &rectVisibility);
01861   if (rectVisibility != nsRectVisibility_kVisible) {
01862     return NS_OK;
01863   }
01864 
01865   // if this is a floating view, it isn't covered by any widgets other than
01866   // its children. In that case we walk up to its parent widget and use
01867   // that as the root to update from. This also means we update areas that
01868   // may be outside the parent view(s), which is necessary for floats.
01869   if (view->GetFloating()) {
01870     nsView* widgetParent = view;
01871 
01872     while (!widgetParent->HasWidget()) {
01873       widgetParent->ConvertToParentCoords(&damagedRect.x, &damagedRect.y);
01874       widgetParent = widgetParent->GetParent();
01875     }
01876 
01877     UpdateWidgetArea(widgetParent, nsRegion(damagedRect), nsnull);
01878   } else {
01879     // Propagate the update to the root widget of the root view manager, since
01880     // iframes, for example, can overlap each other and be translucent.  So we
01881     // have to possibly invalidate our rect in each of the widgets we have
01882     // lying about.
01883     damagedRect.MoveBy(ComputeViewOffset(view));
01884 
01885     UpdateWidgetArea(RootViewManager()->GetRootView(), nsRegion(damagedRect), nsnull);
01886   }
01887 
01888   RootViewManager()->IncrementUpdateCount();
01889 
01890   if (!IsRefreshEnabled()) {
01891     return NS_OK;
01892   }
01893 
01894   // See if we should do an immediate refresh or wait
01895   if (aUpdateFlags & NS_VMREFRESH_IMMEDIATE) {
01896     Composite();
01897   } 
01898 
01899   return NS_OK;
01900 }
01901 
01902 NS_IMETHODIMP nsViewManager::UpdateAllViews(PRUint32 aUpdateFlags)
01903 {
01904   if (RootViewManager() != this) {
01905     return RootViewManager()->UpdateAllViews(aUpdateFlags);
01906   }
01907   
01908   UpdateViews(mRootView, aUpdateFlags);
01909   return NS_OK;
01910 }
01911 
01912 void nsViewManager::UpdateViews(nsView *aView, PRUint32 aUpdateFlags)
01913 {
01914   // update this view.
01915   UpdateView(aView, aUpdateFlags);
01916 
01917   // update all children as well.
01918   nsView* childView = aView->GetFirstChild();
01919   while (nsnull != childView)  {
01920     UpdateViews(childView, aUpdateFlags);
01921     childView = childView->GetNextSibling();
01922   }
01923 }
01924 
01925 NS_IMETHODIMP nsViewManager::DispatchEvent(nsGUIEvent *aEvent, nsEventStatus *aStatus)
01926 {
01927   *aStatus = nsEventStatus_eIgnore;
01928 
01929   switch(aEvent->message)
01930     {
01931     case NS_SIZE:
01932       {
01933         nsView* view = nsView::GetViewFor(aEvent->widget);
01934 
01935         if (nsnull != view)
01936           {
01937             nscoord width = ((nsSizeEvent*)aEvent)->windowSize->width;
01938             nscoord height = ((nsSizeEvent*)aEvent)->windowSize->height;
01939             width = ((nsSizeEvent*)aEvent)->mWinWidth;
01940             height = ((nsSizeEvent*)aEvent)->mWinHeight;
01941 
01942             // The root view may not be set if this is the resize associated with
01943             // window creation
01944 
01945             if (view == mRootView)
01946               {
01947                 // Convert from pixels to twips
01948                 float p2t;
01949                 p2t = mContext->DevUnitsToAppUnits();
01950 
01951                 //printf("resize: (pix) %d, %d\n", width, height);
01952                 SetWindowDimensions(NSIntPixelsToTwips(width, p2t),
01953                                     NSIntPixelsToTwips(height, p2t));
01954                 *aStatus = nsEventStatus_eConsumeNoDefault;
01955               }
01956           }
01957 
01958         break;
01959       }
01960 
01961     case NS_PAINT:
01962       {
01963         nsPaintEvent *event = NS_STATIC_CAST(nsPaintEvent*, aEvent);
01964         nsView *view = nsView::GetViewFor(aEvent->widget);
01965 
01966         if (!view || !mContext)
01967           break;
01968 
01969         *aStatus = nsEventStatus_eConsumeNoDefault;
01970 
01971         // The rect is in device units, and it's in the coordinate space of its
01972         // associated window.
01973         nsCOMPtr<nsIRegion> region = event->region;
01974         if (!region) {
01975           if (NS_FAILED(CreateRegion(getter_AddRefs(region))))
01976             break;
01977 
01978           const nsRect& damrect = *event->rect;
01979           region->SetTo(damrect.x, damrect.y, damrect.width, damrect.height);
01980         }
01981         
01982         if (region->IsEmpty())
01983           break;
01984 
01985         // Refresh the view
01986         if (IsRefreshEnabled()) {
01987           // If an ancestor widget was hidden and then shown, we could
01988           // have a delayed resize to handle.
01989           PRBool didResize = PR_FALSE;
01990           for (nsViewManager *vm = this; vm;
01991                vm = vm->mRootView->GetParent()
01992                       ? vm->mRootView->GetParent()->GetViewManager()
01993                       : nsnull) {
01994             if (vm->mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE) &&
01995                 IsViewVisible(vm->mRootView)) {
01996               vm->DoSetWindowDimensions(vm->mDelayedResize.width,
01997                                         vm->mDelayedResize.height);
01998               vm->mDelayedResize.SizeTo(NSCOORD_NONE, NSCOORD_NONE);
01999 
02000               // Paint later.
02001               vm->UpdateView(vm->mRootView, NS_VMREFRESH_NO_SYNC);
02002               didResize = PR_TRUE;
02003             }
02004           }
02005 
02006           if (!didResize) {
02007             //NS_ASSERTION(IsViewVisible(view), "painting an invisible view");
02008 
02009             // Just notify our own view observer that we're about to paint
02010             // XXXbz do we need to notify other view observers for viewmanagers
02011             // in our tree?
02012             // Make sure to not send WillPaint notifications while scrolling
02013             nsViewManager* rootVM = RootViewManager();
02014 
02015             nsIWidget *widget = mRootView->GetWidget();
02016             PRBool translucentWindow = PR_FALSE;
02017             if (widget)
02018                 widget->GetWindowTranslucency(translucentWindow);
02019 
02020             if (rootVM->mScrollCnt == 0 && !translucentWindow) {
02021               nsIViewObserver* observer = GetViewObserver();
02022               if (observer) {
02023                 // Do an update view batch.  Make sure not to do it DEFERRED,
02024                 // since that would effectively delay any invalidates that are
02025                 // triggered by the WillPaint notification (they'd happen when
02026                 // the invalide event fires, which is later than the reflow
02027                 // event would fire and could end up being after some timer
02028                 // events, leading to frame dropping in DHTML).  Note that the
02029                 // observer may try to reenter this code from inside
02030                 // WillPaint() by trying to do a synchronous paint, but since
02031                 // refresh will be disabled it won't be able to do the paint.
02032                 // We should really sort out the rules on our synch painting
02033                 // api....
02034                 BeginUpdateViewBatch();
02035                 observer->WillPaint();
02036                 EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
02037 
02038                 // Get the view pointer again since the code above might have
02039                 // destroyed it (bug 378273).
02040                 view = nsView::GetViewFor(aEvent->widget);
02041               }
02042             }
02043             // Make sure to sync up any widget geometry changes we
02044             // have pending before we paint.
02045             if (rootVM->mHasPendingUpdates) {
02046               rootVM->ProcessPendingUpdates(mRootView, PR_FALSE);
02047             }
02048             
02049             if (view) {
02050               Refresh(view, event->renderingContext, region,
02051                       NS_VMREFRESH_DOUBLE_BUFFER);
02052             }
02053           }
02054         } else {
02055           // since we got an NS_PAINT event, we need to
02056           // draw something so we don't get blank areas.
02057           nsRect damRect;
02058           region->GetBoundingBox(&damRect.x, &damRect.y, &damRect.width, &damRect.height);
02059           float p2t;
02060           p2t = mContext->DevUnitsToAppUnits();
02061           damRect.ScaleRoundOut(p2t);
02062           DefaultRefresh(view, &damRect);
02063         
02064           // Clients like the editor can trigger multiple
02065           // reflows during what the user perceives as a single
02066           // edit operation, so it disables view manager
02067           // refreshing until the edit operation is complete
02068           // so that users don't see the intermediate steps.
02069           // 
02070           // Unfortunately some of these reflows can trigger
02071           // nsScrollPortView and nsScrollingView Scroll() calls
02072           // which in most cases force an immediate BitBlt and
02073           // synchronous paint to happen even if the view manager's
02074           // refresh is disabled. (Bug 97674)
02075           //
02076           // Calling UpdateView() here, is necessary to add
02077           // the exposed region specified in the synchronous paint
02078           // event to  the view's damaged region so that it gets
02079           // painted properly when refresh is enabled.
02080           //
02081           // Note that calling UpdateView() here was deemed
02082           // to have the least impact on performance, since the
02083           // other alternative was to make Scroll() post an
02084           // async paint event for the *entire* ScrollPort or
02085           // ScrollingView's viewable area. (See bug 97674 for this
02086           // alternate patch.)
02087           
02088           UpdateView(view, damRect, NS_VMREFRESH_NO_SYNC);
02089         }
02090 
02091         break;
02092       }
02093 
02094     case NS_CREATE:
02095     case NS_DESTROY:
02096     case NS_SETZLEVEL:
02097     case NS_MOVE:
02098       /* Don't pass these events through. Passing them through
02099          causes performance problems on pages with lots of views/frames 
02100          @see bug 112861 */
02101       *aStatus = nsEventStatus_eConsumeNoDefault;
02102       break;
02103 
02104 
02105     case NS_DISPLAYCHANGED:
02106 
02107       //Destroy the cached backbuffer to force a new backbuffer
02108       //be constructed with the appropriate display depth.
02109       //@see bugzilla bug 6061
02110       *aStatus = nsEventStatus_eConsumeDoDefault;
02111       if (gCleanupContext) {
02112         gCleanupContext->DestroyCachedBackbuffer();
02113       }
02114       break;
02115 
02116     case NS_SYSCOLORCHANGED:
02117       {
02118         // Hold a refcount to the observer. The continued existence of the observer will
02119         // delay deletion of this view hierarchy should the event want to cause its
02120         // destruction in, say, some JavaScript event handler.
02121         nsView *view = nsView::GetViewFor(aEvent->widget);
02122         nsCOMPtr<nsIViewObserver> obs = GetViewObserver();
02123         if (obs) {
02124           PRBool handled;
02125           obs->HandleEvent(view, aEvent, aStatus, PR_TRUE, handled);
02126         }
02127       }
02128       break; 
02129 
02130     default:
02131       {
02132         if ((NS_IS_MOUSE_EVENT(aEvent) &&
02133              // Ignore moves that we synthesize.
02134              NS_STATIC_CAST(nsMouseEvent*,aEvent)->reason ==
02135                nsMouseEvent::eReal &&
02136              // Ignore mouse exit and enter (we'll get moves if the user
02137              // is really moving the mouse) since we get them when we
02138              // create and destroy widgets.
02139              aEvent->message != NS_MOUSE_EXIT &&
02140              aEvent->message != NS_MOUSE_ENTER) ||
02141             NS_IS_KEY_EVENT(aEvent) ||
02142             NS_IS_IME_EVENT(aEvent)) {
02143           gLastUserEventTime = PR_IntervalToMicroseconds(PR_IntervalNow());
02144         }
02145 
02146         if (aEvent->message == NS_DEACTIVATE) {
02147           PRBool result;
02148           GrabMouseEvents(nsnull, result);
02149           mKeyGrabber = nsnull;
02150         }
02151 
02152         //Find the view whose coordinates system we're in.
02153         nsView* baseView = nsView::GetViewFor(aEvent->widget);
02154         nsView* view = baseView;
02155         PRBool capturedEvent = PR_FALSE;
02156 
02157         //Find the view to which we're initially going to send the event 
02158         //for hittesting.
02159         if (NS_IS_MOUSE_EVENT(aEvent) || NS_IS_DRAG_EVENT(aEvent)) {
02160           nsView* mouseGrabber = GetMouseEventGrabber();
02161           if (mouseGrabber) {
02162             view = mouseGrabber;
02163             capturedEvent = PR_TRUE;
02164           }
02165         }
02166         else if (NS_IS_KEY_EVENT(aEvent) || NS_IS_IME_EVENT(aEvent)) {
02167           if (mKeyGrabber) {
02168             view = mKeyGrabber;
02169             capturedEvent = PR_TRUE;
02170           }
02171         }
02172 
02173         if (nsnull != view) {
02174           float t2p = mContext->AppUnitsToDevUnits();
02175           float p2t = mContext->DevUnitsToAppUnits();
02176 
02177           if ((aEvent->message == NS_MOUSE_MOVE &&
02178                NS_STATIC_CAST(nsMouseEvent*,aEvent)->reason ==
02179                  nsMouseEvent::eReal) ||
02180               aEvent->message == NS_MOUSE_ENTER) {
02181             nsPoint rootOffset(0, 0);
02182             for (nsView *v = baseView; v != mRootView; v = v->GetParent())
02183               v->ConvertToParentCoords(&rootOffset.x, &rootOffset.y);
02184             // aEvent->point is relative to the widget, i.e. the view top-left,
02185             // so we need to add the offset to the view origin
02186             rootOffset += baseView->GetDimensions().TopLeft();
02187             mMouseLocation.MoveTo(NSTwipsToIntPixels(rootOffset.x, t2p) +
02188                                     aEvent->point.x,
02189                                   NSTwipsToIntPixels(rootOffset.y, t2p) +
02190                                     aEvent->point.y);
02191 #ifdef DEBUG_MOUSE_LOCATION
02192             if (aEvent->message == NS_MOUSE_ENTER)
02193               printf("[vm=%p]got mouse enter for %p\n",
02194                      this, aEvent->widget);
02195             printf("[vm=%p]setting mouse location to (%d,%d)\n",
02196                    this, mMouseLocation.x, mMouseLocation.y);
02197 #endif
02198             if (aEvent->message == NS_MOUSE_ENTER)
02199               SynthesizeMouseMove(PR_FALSE);
02200           } else if (aEvent->message == NS_MOUSE_EXIT) {
02201             // Although we only care about the mouse moving into an area
02202             // for which this view manager doesn't receive mouse move
02203             // events, we don't check which view the mouse exit was for
02204             // since this seems to vary by platform.  Hopefully this
02205             // won't matter at all since we'll get the mouse move or
02206             // enter after the mouse exit when the mouse moves from one
02207             // of our widgets into another.
02208             mMouseLocation.MoveTo(NSCOORD_NONE, NSCOORD_NONE);
02209 #ifdef DEBUG_MOUSE_LOCATION
02210             printf("[vm=%p]got mouse exit for %p\n",
02211                    this, aEvent->widget);
02212             printf("[vm=%p]clearing mouse location\n",
02213                    this);
02214 #endif
02215           }
02216 
02217           //Calculate the proper offset for the view we're going to
02218           nsPoint offset(0, 0);
02219 
02220           if (view != baseView) {
02221             //Get offset from root of baseView
02222             nsView *parent;
02223             for (parent = baseView; parent; parent = parent->GetParent())
02224               parent->ConvertToParentCoords(&offset.x, &offset.y);
02225 
02226             //Subtract back offset from root of view
02227             for (parent = view; parent; parent = parent->GetParent())
02228               parent->ConvertFromParentCoords(&offset.x, &offset.y);
02229           }
02230 
02231           //Dispatch the event
02232           //Before we start mucking with coords, make sure we know our baseline
02233           aEvent->refPoint.x = aEvent->point.x;
02234           aEvent->refPoint.y = aEvent->point.y;
02235 
02236           nsRect baseViewDimensions;
02237           if (baseView != nsnull) {
02238             baseView->GetDimensions(baseViewDimensions);
02239           }
02240 
02241           nsPoint oldpt = aEvent->point;
02242           // Set the mouse cursor to the middle of the pixel, because
02243           // that's how we paint --- a frame paints a pixel if it covers
02244           // the center of the pixel
02245           aEvent->point.x = baseViewDimensions.x +
02246             NSFloatPixelsToTwips(float(aEvent->point.x) + 0.5f, p2t);
02247           aEvent->point.y = baseViewDimensions.y +
02248             NSFloatPixelsToTwips(float(aEvent->point.y) + 0.5f, p2t);
02249 
02250           aEvent->point.x += offset.x;
02251           aEvent->point.y += offset.y;
02252 
02253           *aStatus = HandleEvent(view, aEvent, capturedEvent);
02254 
02255           // From here on out, "this" could have been deleted!!!
02256           aEvent->point = oldpt;
02257 
02258           //
02259           // if the event is an nsTextEvent, we need to map the reply back into platform coordinates
02260           //
02261           if (aEvent->message==NS_TEXT_TEXT) {
02262             ((nsTextEvent*)aEvent)->theReply.mCursorPosition.x=NSTwipsToIntPixels(((nsTextEvent*)aEvent)->theReply.mCursorPosition.x, t2p);
02263             ((nsTextEvent*)aEvent)->theReply.mCursorPosition.y=NSTwipsToIntPixels(((nsTextEvent*)aEvent)->theReply.mCursorPosition.y, t2p);
02264             ((nsTextEvent*)aEvent)->theReply.mCursorPosition.width=NSTwipsToIntPixels(((nsTextEvent*)aEvent)->theReply.mCursorPosition.width, t2p);
02265             ((nsTextEvent*)aEvent)->theReply.mCursorPosition.height=NSTwipsToIntPixels(((nsTextEvent*)aEvent)->theReply.mCursorPosition.height, t2p);
02266           }
02267           if((aEvent->message==NS_COMPOSITION_START) ||
02268              (aEvent->message==NS_COMPOSITION_QUERY)) {
02269             ((nsCompositionEvent*)aEvent)->theReply.mCursorPosition.x=NSTwipsToIntPixels(((nsCompositionEvent*)aEvent)->theReply.mCursorPosition.x,t2p);
02270             ((nsCompositionEvent*)aEvent)->theReply.mCursorPosition.y=NSTwipsToIntPixels(((nsCompositionEvent*)aEvent)->theReply.mCursorPosition.y,t2p);
02271             ((nsCompositionEvent*)aEvent)->theReply.mCursorPosition.width=NSTwipsToIntPixels(((nsCompositionEvent*)aEvent)->theReply.mCursorPosition.width,t2p);
02272             ((nsCompositionEvent*)aEvent)->theReply.mCursorPosition.height=NSTwipsToIntPixels(((nsCompositionEvent*)aEvent)->theReply.mCursorPosition.height,t2p);
02273           }
02274           if(aEvent->message==NS_QUERYCARETRECT) {
02275             ((nsQueryCaretRectEvent*)aEvent)->theReply.mCaretRect.x=NSTwipsToIntPixels(((nsQueryCaretRectEvent*)aEvent)->theReply.mCaretRect.x,t2p);
02276             ((nsQueryCaretRectEvent*)aEvent)->theReply.mCaretRect.y=NSTwipsToIntPixels(((nsQueryCaretRectEvent*)aEvent)->theReply.mCaretRect.y,t2p);
02277             ((nsQueryCaretRectEvent*)aEvent)->theReply.mCaretRect.width=NSTwipsToIntPixels(((nsQueryCaretRectEvent*)aEvent)->theReply.mCaretRect.width,t2p);
02278             ((nsQueryCaretRectEvent*)aEvent)->theReply.mCaretRect.height=NSTwipsToIntPixels(((nsQueryCaretRectEvent*)aEvent)->theReply.mCaretRect.height,t2p);
02279           }
02280         }
02281     
02282         break;
02283       }
02284     }
02285 
02286   return NS_OK;
02287 }
02288 
02289 void nsViewManager::ReparentViews(DisplayZTreeNode* aNode,
02290                                   nsHashtable &aMapPlaceholderViewToZTreeNode)
02291 {
02292   DisplayZTreeNode* child;
02293   DisplayZTreeNode** prev = &aNode->mZChild;
02294   for (child = aNode->mZChild; nsnull != child; child = *prev) {
02295     ReparentViews(child, aMapPlaceholderViewToZTreeNode);
02296 
02297     nsZPlaceholderView *zParent = nsnull;
02298     if (nsnull != child->mView) {
02299       zParent = child->mView->GetZParent();
02300     }
02301     if (nsnull != zParent) {
02302       nsVoidKey key(zParent);
02303       DisplayZTreeNode* placeholder = (DisplayZTreeNode *)aMapPlaceholderViewToZTreeNode.Get(&key);
02304 
02305       if (placeholder == child) {
02306         // don't do anything if we already reparented this node;
02307         // just advance to the next child
02308         prev = &child->mZSibling;
02309       } else {
02310         // unlink the child from the tree
02311         *prev = child->mZSibling;
02312         child->mZSibling = nsnull;
02313         
02314         if (nsnull != placeholder) {
02315           NS_ASSERTION((placeholder->mDisplayElement == nsnull), "placeholder already has elements?");
02316           NS_ASSERTION((placeholder->mZChild == nsnull), "placeholder already has Z-children?");
02317           placeholder->mDisplayElement = child->mDisplayElement;
02318           placeholder->mView = child->mView;
02319           placeholder->mZChild = child->mZChild;
02320           // We used to delete the child, but since we switched to Arenas just unreference it
02321           // delete child;
02322         } else {
02323           // the placeholder was not added to the display list
02324           // we don't need the real view then, either
02325 
02326           // We used to call DestroyZTreeNode which would delete the child
02327           // and its children/siblings and remove them from the Placeholder
02328           // hash.  However, we now use an Arena to build the tree.  This
02329           // means that we will never reuse a node (because it is never
02330           // freed), thus we can just leave it in the hash.  It will never
02331           // be looked up again.
02332           // DestroyZTreeNode(child);
02333         }
02334       }
02335     } else {
02336       prev = &child->mZSibling;
02337     }
02338   }
02339 }
02340 
02341 static PRBool ComputePlaceholderContainment(nsView* aView) {
02342   PRBool containsPlaceholder = aView->IsZPlaceholderView();
02343 
02344   nsView* child;
02345   for (child = aView->GetFirstChild(); child != nsnull; child = child->GetNextSibling()) {
02346     if (ComputePlaceholderContainment(child)) {
02347       containsPlaceholder = PR_TRUE;
02348     }
02349   }
02350 
02351   if (containsPlaceholder) {
02352     aView->SetViewFlags(aView->GetViewFlags() | NS_VIEW_FLAG_CONTAINS_PLACEHOLDER);
02353   } else {
02354     aView->SetViewFlags(aView->GetViewFlags() & ~NS_VIEW_FLAG_CONTAINS_PLACEHOLDER);
02355   }
02356 
02357   return containsPlaceholder;
02358 }
02359 
02363 static PRBool IsViewportScrollApparatus(nsView* aView, nsIView* aRootScroll) {
02364   if (!aRootScroll)
02365     return PR_FALSE;
02366 
02367   // aView is NOT part of the scrolling apparatus if and only if it is a descendant
02368   // of aRootScroll, OR it is a fixed position view or descendant
02369   nsIView* aRootScrollframeView = aRootScroll->GetParent();
02370   while (aView) {
02371     if (aView == aRootScroll) {
02372       // We're part of the scrolled document.
02373       return PR_FALSE;
02374     }
02375     if (aView == aRootScrollframeView) {
02376       return PR_TRUE;
02377     }
02378     aView = aView->GetParent();
02379   }
02380   // We're the root view or perhaps the view for a position:fixed element
02381   return PR_FALSE;
02382 }
02383 
02384 /*
02385   Fills aDisplayList with DisplayListElement2* pointers. The caller is responsible
02386   for freeing these structs. The display list elements are ordered by z-order so
02387   that the first element of the array is at the bottom in z-order and the last element
02388   in the array is at the top in z-order.
02389 
02390   This should be changed so that the display list array is passed in as a parameter. There
02391   is no need to have the display list as a member of nsViewManager.
02392 
02393   @param aRect the area in aView which we want to build a display list for
02394   @param aEventProcesing PR_TRUE when the list is required for event processing
02395   @param aCaptured PR_TRUE if the event or painting is being captured by the
02396   given view so only views that are descended from the given view are considered
02397   @param aSuppressScrolling if non-null, scrollbars associated with this scrollable
02398   view are not drawn, and clipping this view and its ancestors is suppressed
02399 */
02400 void nsViewManager::BuildDisplayList(nsView* aView, const nsRect& aRect,
02401                                      PRBool aEventProcessing,
02402                                      PRBool aCaptured, nsIView* aSuppressScrolling,
02403                                      nsVoidArray* aDisplayList, PLArenaPool &aPool)
02404 {
02405   // compute this view's origin
02406   nsPoint origin = ComputeViewOffset(aView);
02407     
02408   nsView *displayRoot = aView;
02409   if (!aCaptured) {
02410     for (;;) {
02411       nsView *displayParent = displayRoot->GetParent();
02412 
02413       if (nsnull == displayParent) {
02414         break;
02415       }
02416 
02417       if (displayRoot->GetFloating() && !displayParent->GetFloating()) {
02418         break;
02419       }
02420       displayRoot = displayParent;
02421     }
02422   }
02423     
02424   DisplayZTreeNode *zTree;
02425 
02426   nsPoint displayRootOrigin = ComputeViewOffset(displayRoot);
02427   displayRoot->ConvertFromParentCoords(&displayRootOrigin.x, &displayRootOrigin.y);
02428     
02429   // Determine, for each view, whether it is or contains a ZPlaceholderView
02430   ComputePlaceholderContainment(displayRoot);
02431 
02432   // Create the Z-ordered view tree
02433   PRBool paintFloats;
02434   if (aEventProcessing) {
02435     paintFloats = PR_TRUE;
02436   } else {
02437     paintFloats = displayRoot->GetFloating();
02438   }
02439 
02440   {
02441     nsHashtable       PlaceholderHash;
02442 
02443     CreateDisplayList(displayRoot, zTree, origin.x, origin.y,
02444                       aView, &aRect, displayRoot,
02445                       displayRootOrigin.x, displayRootOrigin.y,
02446                       paintFloats, aEventProcessing,
02447                       aSuppressScrolling ? aSuppressScrolling->GetFirstChild() : nsnull,
02448                       PlaceholderHash, aPool);
02449 
02450     // Reparent any views that need reparenting in the Z-order tree
02451     if(zTree) {
02452       ReparentViews(zTree, PlaceholderHash);
02453     }
02454   }
02455     
02456   if (nsnull != zTree) {
02457     // Apply proper Z-order handling
02458     nsAutoVoidArray mergeTmp;
02459 
02460     SortByZOrder(zTree, *aDisplayList, mergeTmp, PR_TRUE, aPool);
02461   }
02462 
02463   if (aSuppressScrolling) {
02464     // Don't render viewport scrollbars
02465     for (PRInt32 i = 0; i < aDisplayList->Count(); i++) {
02466       DisplayListElement2* element = NS_STATIC_CAST(DisplayListElement2*,
02467                                                     aDisplayList->ElementAt(i));
02468       if ((element->mFlags & VIEW_RENDERED)
02469           && IsViewportScrollApparatus(element->mView, aSuppressScrolling)) {
02470         element->mFlags &= ~VIEW_RENDERED;
02471       }
02472     }
02473   }
02474 }
02475 
02476 void nsViewManager::BuildEventTargetList(nsVoidArray &aTargets, nsView* aView, nsGUIEvent* aEvent,
02477                                          PRBool aCaptured, PLArenaPool &aPool)
02478 {
02479   NS_ASSERTION(!IsPainting(),
02480                "View manager cannot handle events during a paint");
02481   if (IsPainting()) {
02482     return;
02483   }
02484 
02485   nsRect eventRect(aEvent->point.x, aEvent->point.y, 1, 1);
02486   nsAutoVoidArray displayList;
02487   BuildDisplayList(aView, eventRect, PR_TRUE, aCaptured, nsnull, &displayList, aPool);
02488 
02489 #ifdef DEBUG_roc
02490   if (getenv("MOZ_SHOW_DISPLAY_LIST")) ShowDisplayList(&displayList);
02491 #endif
02492 
02493   // The display list is in order from back to front. We return the target list in order from
02494   // front to back.
02495   for (PRInt32 i = displayList.Count() - 1; i >= 0; --i) {
02496     DisplayListElement2* element = NS_STATIC_CAST(DisplayListElement2*, displayList.ElementAt(i));
02497     if (element->mFlags & VIEW_RENDERED) {
02498       aTargets.AppendElement(element);
02499     }
02500   }
02501 }
02502 
02503 nsEventStatus nsViewManager::HandleEvent(nsView* aView, nsGUIEvent* aEvent, PRBool aCaptured) {
02504 //printf(" %d %d %d %d (%d,%d) \n", this, event->widget, event->widgetSupports, 
02505 //       event->message, event->point.x, event->point.y);
02506 
02507   // Hold a refcount to the observer. The continued existence of the observer will
02508   // delay deletion of this view hierarchy should the event want to cause its
02509   // destruction in, say, some JavaScript event handler.
02510   nsCOMPtr<nsIViewObserver> obs = GetViewObserver();
02511 
02512   // accessibility events and key events are dispatched directly to the focused view
02513   if (aEvent->eventStructType == NS_ACCESSIBLE_EVENT
02514       || aEvent->message == NS_CONTEXTMENU_KEY
02515       || NS_IS_KEY_EVENT(aEvent) || NS_IS_IME_EVENT(aEvent) || NS_IS_FOCUS_EVENT(aEvent)) {
02516     nsEventStatus status = nsEventStatus_eIgnore;
02517     if (obs) {
02518        PRBool handled;
02519        obs->HandleEvent(aView, aEvent, &status, PR_TRUE, handled);
02520     }
02521     return status;
02522   }
02523     
02524   nsAutoVoidArray targetViews;
02525   nsCOMArray<nsIViewObserver> heldRefCountsToOtherVMs;
02526 
02527   // In fact, we only need to take this expensive path when the event is a mouse event ... riiiight?
02528   PLArenaPool displayArena;
02529   PL_INIT_ARENA_POOL(&displayArena, "displayArena", 1024);
02530   BuildEventTargetList(targetViews, aView, aEvent, aCaptured, displayArena);
02531 
02532   nsEventStatus status = nsEventStatus_eIgnore;
02533 
02534   // get a death grip on any view managers' view observers (other than this one)
02535   PRInt32 i;
02536   for (i = 0; i < targetViews.Count(); i++) {
02537     DisplayListElement2* element = NS_STATIC_CAST(DisplayListElement2*, targetViews.ElementAt(i));
02538     nsView* v = element->mView;
02539     nsViewManager* vVM = v->GetViewManager();
02540     if (vVM != this) {
02541       nsIViewObserver* vobs = vVM->GetViewObserver();
02542       if (vobs) {
02543         heldRefCountsToOtherVMs.AppendObject(vobs);
02544       }
02545     }
02546   }
02547 
02548   // Save aEvent->point because child code might change it
02549   nsPoint pt = aEvent->point;
02550   for (i = 0; i < targetViews.Count(); i++) {
02551     DisplayListElement2* element = NS_STATIC_CAST(DisplayListElement2*, targetViews.ElementAt(i));
02552     nsView* v = element->mView;
02553 
02554     if (nsnull != v->GetClientData()) {
02555       PRBool handled = PR_FALSE;
02556       nsRect r;
02557       v->GetDimensions(r);
02558 
02559       aEvent->point = pt - (nsPoint(element->mAbsX, element->mAbsY) - r.TopLeft());
02560 
02561       nsViewManager* vVM = v->GetViewManager();
02562       if (vVM == this) {
02563         if (nsnull != obs) {
02564           obs->HandleEvent(v, aEvent, &status, i == targetViews.Count() - 1, handled);
02565         }
02566       } else {
02567         // Hold a refcount to the observer. The continued existence of the observer will
02568         // delay deletion of this view hierarchy should the event want to cause its
02569         // destruction in, say, some JavaScript event handler.
02570         nsCOMPtr<nsIViewObserver> vobs = vVM->GetViewObserver();
02571         if (vobs) {
02572           vobs->HandleEvent(v, aEvent, &status, i == targetViews.Count() - 1, handled);
02573         }
02574       }
02575 
02576       if (handled) {
02577         break;
02578       }
02579       // if the child says "not handled" but did something which deleted the entire view hierarchy,
02580       // we'll crash in the next iteration. Oh well. The old code would have crashed too.
02581     }
02582   }
02583   // Need to restore the event point here because someone may use it later.
02584   // In particular Windows seems to need this.
02585   aEvent->point = pt;
02586 
02587   PL_FreeArenaPool(&displayArena);
02588   PL_FinishArenaPool(&displayArena);
02589 
02590   return status;
02591 }
02592 
02593 NS_IMETHODIMP nsViewManager::GrabMouseEvents(nsIView *aView, PRBool &aResult)
02594 {
02595   if (!IsRootVM()) {
02596     return RootViewManager()->GrabMouseEvents(aView, aResult);
02597   }
02598 
02599   // Along with nsView::SetVisibility, we enforce that the mouse grabber
02600   // can never be a hidden view.
02601   if (aView && NS_STATIC_CAST(nsView*, aView)->GetVisibility()
02602                == nsViewVisibility_kHide) {
02603     aView = nsnull;
02604   }
02605 
02606 #ifdef DEBUG_mjudge
02607   if (aView)
02608     {
02609       printf("capturing mouse events for view %x\n",aView);
02610     }
02611   printf("removing mouse capture from view %x\n",mMouseGrabber);
02612 #endif
02613 
02614   mMouseGrabber = NS_STATIC_CAST(nsView*, aView);
02615   aResult = PR_TRUE;
02616   return NS_OK;
02617 }
02618 
02619 NS_IMETHODIMP nsViewManager::GrabKeyEvents(nsIView *aView, PRBool &aResult)
02620 {
02621   mKeyGrabber = NS_STATIC_CAST(nsView*, aView);
02622   aResult = PR_TRUE;
02623   return NS_OK;
02624 }
02625 
02626 NS_IMETHODIMP nsViewManager::GetMouseEventGrabber(nsIView *&aView)
02627 {
02628   aView = GetMouseEventGrabber();
02629   return NS_OK;
02630 }
02631 
02632 NS_IMETHODIMP nsViewManager::GetKeyEventGrabber(nsIView *&aView)
02633 {
02634   aView = mKeyGrabber;
02635   return NS_OK;
02636 }
02637 
02638 // Recursively reparent widgets if necessary 
02639 
02640 void nsViewManager::ReparentChildWidgets(nsIView* aView, nsIWidget *aNewWidget)
02641 {
02642   if (aView->HasWidget()) {
02643     // Check to see if the parent widget is the
02644     // same as the new parent. If not then reparent
02645     // the widget, otherwise there is nothing more
02646     // to do for the view and its descendants
02647     nsIWidget* widget = aView->GetWidget();
02648     nsCOMPtr<nsIWidget> parentWidget = getter_AddRefs(widget->GetParent());
02649     if (parentWidget.get() != aNewWidget) {
02650 #ifdef DEBUG
02651       nsresult rv =
02652 #endif
02653         widget->SetParent(aNewWidget);
02654       NS_ASSERTION(NS_SUCCEEDED(rv), "SetParent failed!");
02655     }
02656     return;
02657   }
02658 
02659   // Need to check each of the views children to see
02660   // if they have a widget and reparent it.
02661 
02662   nsView* view = NS_STATIC_CAST(nsView*, aView);
02663   for (nsView *kid = view->GetFirstChild(); kid; kid = kid->GetNextSibling()) {
02664     ReparentChildWidgets(kid, aNewWidget);
02665   }
02666 }
02667 
02668 // Reparent a view and its descendant views widgets if necessary
02669 
02670 void nsViewManager::ReparentWidgets(nsIView* aView, nsIView *aParent)
02671 {
02672   NS_PRECONDITION(aParent, "Must have a parent");
02673   NS_PRECONDITION(aView, "Must have a view");
02674   
02675   // Quickly determine whether the view has pre-existing children or a
02676   // widget. In most cases the view will not have any pre-existing 
02677   // children when this is called.  Only in the case
02678   // where a view has been reparented by removing it from
02679   // a reinserting it into a new location in the view hierarchy do we
02680   // have to consider reparenting the existing widgets for the view and
02681   // it's descendants.
02682   nsView* view = NS_STATIC_CAST(nsView*, aView);
02683   if (view->HasWidget() || view->GetFirstChild()) {
02684     nsIWidget* parentWidget = aParent->GetNearestWidget(nsnull);
02685     if (parentWidget) {
02686       ReparentChildWidgets(aView, parentWidget);
02687       return;
02688     }
02689     NS_WARNING("Can not find a widget for the parent view");
02690   }
02691 }
02692 
02693 NS_IMETHODIMP nsViewManager::InsertChild(nsIView *aParent, nsIView *aChild, nsIView *aSibling,
02694                                          PRBool aAfter)
02695 {
02696   nsView* parent = NS_STATIC_CAST(nsView*, aParent);
02697   nsView* child = NS_STATIC_CAST(nsView*, aChild);
02698   nsView* sibling = NS_STATIC_CAST(nsView*, aSibling);
02699   
02700   NS_PRECONDITION(nsnull != parent, "null ptr");
02701   NS_PRECONDITION(nsnull != child, "null ptr");
02702   NS_ASSERTION(sibling == nsnull || sibling->GetParent() == parent,
02703                "tried to insert view with invalid sibling");
02704   NS_ASSERTION(!IsViewInserted(child), "tried to insert an already-inserted view");
02705 
02706   if ((nsnull != parent) && (nsnull != child))
02707     {
02708       // if aAfter is set, we will insert the child after 'prev' (i.e. after 'kid' in document
02709       // order, otherwise after 'kid' (i.e. before 'kid' in document order).
02710 
02711 #if 1
02712       if (nsnull == aSibling) {
02713         if (aAfter) {
02714           // insert at end of document order, i.e., before first view
02715           // this is the common case, by far
02716           parent->InsertChild(child, nsnull);
02717           ReparentWidgets(child, parent);
02718         } else {
02719           // insert at beginning of document order, i.e., after last view
02720           nsView *kid = parent->GetFirstChild();
02721           nsView *prev = nsnull;
02722           while (kid) {
02723             prev = kid;
02724             kid = kid->GetNextSibling();
02725           }
02726           // prev is last view or null if there are no children
02727           parent->InsertChild(child, prev);
02728           ReparentWidgets(child, parent);
02729         }
02730       } else {
02731         nsView *kid = parent->GetFirstChild();
02732         nsView *prev = nsnull;
02733         while (kid && sibling != kid) {
02734           //get the next sibling view
02735           prev = kid;
02736           kid = kid->GetNextSibling();
02737         }
02738         NS_ASSERTION(kid != nsnull,
02739                      "couldn't find sibling in child list");
02740         if (aAfter) {
02741           // insert after 'kid' in document order, i.e. before in view order
02742           parent->InsertChild(child, prev);
02743           ReparentWidgets(child, parent);
02744         } else {
02745           // insert before 'kid' in document order, i.e. after in view order
02746           parent->InsertChild(child, kid);
02747           ReparentWidgets(child, parent);
02748         }
02749       }
02750 #else // don't keep consistent document order, but order things by z-index instead
02751       // essentially we're emulating the old InsertChild(parent, child, zindex)
02752       PRInt32 zIndex = child->GetZIndex();
02753       while (nsnull != kid)
02754         {
02755           PRInt32 idx = kid->GetZIndex();
02756 
02757           if (CompareZIndex(zIndex, child->IsTopMost(), child->GetZIndexIsAuto(),
02758                             idx, kid->IsTopMost(), kid->GetZIndexIsAuto()) >= 0)
02759             break;
02760 
02761           prev = kid;
02762           kid = kid->GetNextSibling();
02763         }
02764 
02765       parent->InsertChild(child, prev);
02766       ReparentWidgets(child, parent);
02767 #endif
02768 
02769       // if the parent view is marked as "floating", make the newly added view float as well.
02770       if (parent->GetFloating())
02771         child->SetFloating(PR_TRUE);
02772 
02773       //and mark this area as dirty if the view is visible...
02774 
02775       if (nsViewVisibility_kHide != child->GetVisibility())
02776         UpdateView(child, NS_VMREFRESH_NO_SYNC);
02777     }
02778   return NS_OK;
02779 }
02780 
02781 NS_IMETHODIMP nsViewManager::InsertZPlaceholder(nsIView *aParent, nsIView *aChild,
02782                                                 nsIView *aSibling, PRBool aAfter)
02783 {
02784   nsView* parent = NS_STATIC_CAST(nsView*, aParent);
02785   nsView* child = NS_STATIC_CAST(nsView*, aChild);
02786 
02787   NS_PRECONDITION(nsnull != parent, "null ptr");
02788   NS_PRECONDITION(nsnull != child, "null ptr");
02789 
02790   nsZPlaceholderView* placeholder = new nsZPlaceholderView(this);
02791   // mark the placeholder as "shown" so that it will be included in a built display list
02792   placeholder->SetParent(parent);
02793   placeholder->SetReparentedView(child);
02794   placeholder->SetZIndex(child->GetZIndexIsAuto(), child->GetZIndex(), child->IsTopMost());
02795   child->SetZParent(placeholder);
02796   
02797   return InsertChild(parent, placeholder, aSibling, aAfter);
02798 }
02799 
02800 NS_IMETHODIMP nsViewManager::InsertChild(nsIView *aParent, nsIView *aChild, PRInt32 aZIndex)
02801 {
02802   // no-one really calls this with anything other than aZIndex == 0 on a fresh view
02803   // XXX this method should simply be eliminated and its callers redirected to the real method
02804   SetViewZIndex(aChild, PR_FALSE, aZIndex, PR_FALSE);
02805   return InsertChild(aParent, aChild, nsnull, PR_TRUE);
02806 }
02807 
02808 NS_IMETHODIMP nsViewManager::RemoveChild(nsIView *aChild)
02809 {
02810   nsView* child = NS_STATIC_CAST(nsView*, aChild);
02811 
02812   NS_PRECONDITION(nsnull != child, "null ptr");
02813 
02814   nsView* parent = child->GetParent();
02815 
02816   if ((nsnull != parent) && (nsnull != child))
02817     {
02818       UpdateView(child, NS_VMREFRESH_NO_SYNC);
02819       parent->RemoveChild(child);
02820     }
02821 
02822   return NS_OK;
02823 }
02824 
02825 NS_IMETHODIMP nsViewManager::MoveViewBy(nsIView *aView, nscoord aX, nscoord aY)
02826 {
02827   nsView* view = NS_STATIC_CAST(nsView*, aView);
02828 
02829   nsPoint pt = view->GetPosition();
02830   MoveViewTo(view, aX + pt.x, aY + pt.y);
02831   return NS_OK;
02832 }
02833 
02834 NS_IMETHODIMP nsViewManager::MoveViewTo(nsIView *aView, nscoord aX, nscoord aY)
02835 {
02836   nsView* view = NS_STATIC_CAST(nsView*, aView);
02837   nsPoint oldPt = view->GetPosition();
02838   nsRect oldArea = view->GetBounds();
02839   view->SetPosition(aX, aY);
02840 
02841   // only do damage control if the view is visible
02842 
02843   if ((aX != oldPt.x) || (aY != oldPt.y)) {
02844     if (view->GetVisibility() != nsViewVisibility_kHide) {
02845       nsView* parentView = view->GetParent();
02846       UpdateView(parentView, oldArea, NS_VMREFRESH_NO_SYNC);
02847       UpdateView(parentView, view->GetBounds(), NS_VMREFRESH_NO_SYNC);
02848     }
02849   }
02850   return NS_OK;
02851 }
02852 
02853 void nsViewManager::InvalidateHorizontalBandDifference(nsView *aView, const nsRect& aRect, const nsRect& aCutOut,
02854   PRUint32 aUpdateFlags, nscoord aY1, nscoord aY2, PRBool aInCutOut) {
02855   nscoord height = aY2 - aY1;
02856   if (aRect.x < aCutOut.x) {
02857     nsRect r(aRect.x, aY1, aCutOut.x - aRect.x, height);
02858     UpdateView(aView, r, aUpdateFlags);
02859   }
02860   if (!aInCutOut && aCutOut.x < aCutOut.XMost()) {
02861     nsRect r(aCutOut.x, aY1, aCutOut.width, height);
02862     UpdateView(aView, r, aUpdateFlags);
02863   }
02864   if (aCutOut.XMost() < aRect.XMost()) {
02865     nsRect r(aCutOut.XMost(), aY1, aRect.XMost() - aCutOut.XMost(), height);
02866     UpdateView(aView, r, aUpdateFlags);
02867   }
02868 }
02869 
02870 void nsViewManager::InvalidateRectDifference(nsView *aView, const nsRect& aRect, const nsRect& aCutOut,
02871   PRUint32 aUpdateFlags) {
02872   if (aRect.y < aCutOut.y) {
02873     InvalidateHorizontalBandDifference(aView, aRect, aCutOut, aUpdateFlags, aRect.y, aCutOut.y, PR_FALSE);
02874   }
02875   if (aCutOut.y < aCutOut.YMost()) {
02876     InvalidateHorizontalBandDifference(aView, aRect, aCutOut, aUpdateFlags, aCutOut.y, aCutOut.YMost(), PR_TRUE);
02877   }
02878   if (aCutOut.YMost() < aRect.YMost()) {
02879     InvalidateHorizontalBandDifference(aView, aRect, aCutOut, aUpdateFlags, aCutOut.YMost(), aRect.YMost(), PR_FALSE);
02880   }
02881 }
02882 
02883 NS_IMETHODIMP nsViewManager::ResizeView(nsIView *aView, const nsRect &aRect, PRBool aRepaintExposedAreaOnly)
02884 {
02885   nsView* view = NS_STATIC_CAST(nsView*, aView);
02886   nsRect oldDimensions;
02887 
02888   view->GetDimensions(oldDimensions);
02889   // Don't use nsRect's operator== here, since it returns true when
02890   // both rects are empty even if they have different widths and we
02891   // have cases where that sort of thing matters to us.
02892   if (oldDimensions.TopLeft() != aRect.TopLeft() ||
02893       oldDimensions.Size() != aRect.Size()) {
02894     nsView* parentView = view->GetParent();
02895     if (parentView == nsnull)
02896       parentView = view;
02897 
02898     // resize the view.
02899     // Prevent Invalidation of hidden views 
02900     if (view->GetVisibility() == nsViewVisibility_kHide) {  
02901       view->SetDimensions(aRect, PR_FALSE);
02902     } else {
02903       if (!aRepaintExposedAreaOnly) {
02904         //Invalidate the union of the old and new size
02905         view->SetDimensions(aRect, PR_TRUE);
02906 
02907         UpdateView(view, aRect, NS_VMREFRESH_NO_SYNC);
02908         view->ConvertToParentCoords(&oldDimensions.x, &oldDimensions.y);
02909         UpdateView(parentView, oldDimensions, NS_VMREFRESH_NO_SYNC);
02910       } else {
02911         view->SetDimensions(aRect, PR_TRUE);
02912 
02913         InvalidateRectDifference(view, aRect, oldDimensions, NS_VMREFRESH_NO_SYNC);
02914         nsRect r = aRect;
02915         view->ConvertToParentCoords(&r.x, &r.y);
02916         view->ConvertToParentCoords(&oldDimensions.x, &oldDimensions.y);
02917         InvalidateRectDifference(parentView, oldDimensions, r, NS_VMREFRESH_NO_SYNC);
02918       } 
02919     }
02920   }
02921 
02922   // Note that if layout resizes the view and the view has a custom clip
02923   // region set, then we expect layout to update the clip region too. Thus
02924   // in the case where mClipRect has been optimized away to just be a null
02925   // pointer, and this resize is implicitly changing the clip rect, it's OK
02926   // because layout will change it back again if necessary.
02927 
02928   return NS_OK;
02929 }
02930 
02931 NS_IMETHODIMP nsViewManager::SetViewChildClipRegion(nsIView *aView, const nsRegion *aRegion)
02932 {
02933   nsView* view = NS_STATIC_CAST(nsView*, aView);
02934  
02935   NS_ASSERTION(!(nsnull == view), "no view");
02936 
02937   const nsRect* oldClipRect = view->GetClipChildrenToRect();
02938 
02939   nsRect newClipRectStorage = view->GetDimensions();
02940   nsRect* newClipRect = nsnull;
02941   if (aRegion) {
02942     newClipRectStorage = aRegion->GetBounds();
02943     newClipRect = &newClipRectStorage;
02944   }
02945 
02946   if ((oldClipRect != nsnull) == (newClipRect != nsnull)
02947       && (!newClipRect || *newClipRect == *oldClipRect)) {
02948     return NS_OK;
02949   }
02950   nsRect oldClipRectStorage =
02951     oldClipRect ? *oldClipRect : view->GetDimensions();
02952  
02953   // Update the view properties
02954   view->SetClipChildrenToRect(newClipRect);
02955 
02956   if (IsViewInserted(view)) {
02957     // Invalidate changed areas
02958     // Paint (new - old) in the current view
02959     InvalidateRectDifference(view, newClipRectStorage, oldClipRectStorage, NS_VMREFRESH_NO_SYNC);
02960     // Paint (old - new) in the parent view, since it'll be clipped out of the current view
02961     nsView* parent = view->GetParent();
02962     if (parent) {
02963       oldClipRectStorage += view->GetPosition();
02964       newClipRectStorage += view->GetPosition();
02965       InvalidateRectDifference(parent, oldClipRectStorage, newClipRectStorage, NS_VMREFRESH_NO_SYNC);
02966     }
02967   }
02968 
02969   return NS_OK;
02970 }
02971 
02972 /*
02973   Returns PR_TRUE if and only if aView is a (possibly indirect) child of aAncestor.
02974 */
02975 static PRBool IsAncestorOf(const nsIView* aAncestor, const nsIView* aView) 
02976 {
02977   while (nsnull != aView) {
02978     aView = aView->GetParent();
02979     if (aView == aAncestor) {
02980       return PR_TRUE;
02981     }
02982   }
02983   return PR_FALSE;
02984 }
02985 
02986 /*
02987   This function returns TRUE only if we are sure that scrolling
02988   aView's widget and moving its child widgets, followed by
02989   UpdateViewAfterScroll, will result in correct painting (i.e. the
02990   same results as just invalidating the entire view). If we're not
02991   sure then we return FALSE to cause a full update of the view's area.
02992 
02993   The way we check this is quite subtle, because there are all sorts
02994   of complicated things that can happen.
02995 
02996   Conceptually what we do is compute the display list for the "after
02997   scrolling" situation and compare it to a translation of the display
02998   list for the "before scrolling" situation.  If the two lists are
02999   equal then we return TRUE.
03000 
03001   We could implement this directly, but that would be rather
03002   complex. In particular it's hard to build the display list for the
03003   "before scrolling" situation since this function is called after
03004   scrolling. So instead we just build the "after scrolling" display
03005   list and do some analysis of it, making some approximations along
03006   the way.
03007 
03008   Display list elements for aView's descendants will always be the
03009   same "before" (plus translation) and "after", because they get
03010   scrolled by the same amount as the translation. Display list
03011   elements for other views will be in different positions "before"
03012   (plus translation) and "after" because they don't get
03013   scrolled. These elements are the ones that force us to return
03014   PR_FALSE. Unfortunately we can't just scan the "after" display list
03015   and ensure no elements are not descendants of aView: there could
03016   have been some non-aView-descendant which appeared in the "before"
03017   list but does not show up in the "after" list because it ended up
03018   completely hidden by some opaque aView-descendant.
03019 
03020   So here's what we have to do: we compute an "after" display list but
03021   before performing the optimization pass to eliminate completely
03022   covered views, we mark all aView-descendant display list elements as
03023   transparent. Thus we can be sure that any non-aView-descendant
03024   display list elements that would have shown up in the translated "before"
03025   list will still show up in the "after" list (since the only views which
03026   can cover them are non-aView-descendants, which haven't moved). Then
03027   we check the final display list to make sure only aView-descendant
03028   display list elements are included.
03029 
03030   NOTE: We also scan the "after" display list to find views whose contents
03031   depend on their scroll position (e.g., CSS attachment-fixed backgrounds),
03032   which are flagged with NS_VIEW_FLAG_DONT_BITBLT. If any are found, we return
03033   PR_FALSE.
03034 
03035   XXX Post-1.0 we should consider instead of returning a boolean,
03036   actually computing a rectangle or region which can be safely
03037   scrolled with bitblt.  This would be the area which is aView's
03038   rectangle minus the area covered by any non-aView-descendant views
03039   in the translated "before" or "after" lists. Then we could scroll
03040   that area and explicitly invalidate the rest. This would give us
03041   high performance scrolling even in the presence of overlapping
03042   content and eliminate the need for native widgets for fixed position
03043   elements.
03044 */
03045 PRBool nsViewManager::CanScrollWithBitBlt(nsView* aView)
03046 {
03047   NS_ASSERTION(!IsPainting(),
03048                "View manager shouldn't be scrolling during a paint");
03049   if (IsPainting()) {
03050     return PR_FALSE; // do the safe thing
03051   }
03052 
03053   nsRect r = aView->GetClippedRect();
03054   // Only check the area that intersects the view's non clipped rectangle
03055   if (r.IsEmpty()) {
03056     return PR_TRUE; // nothing to scroll
03057   }
03058 
03059   nsAutoVoidArray displayList;
03060   PLArenaPool displayArena;
03061   PL_INIT_ARENA_POOL(&displayArena, "displayArena", 1024);
03062   BuildDisplayList(aView, r, PR_FALSE, PR_FALSE, nsnull, &displayList, displayArena);
03063 
03064   PRInt32 i;
03065   for (i = 0; i < displayList.Count(); i++) {
03066     DisplayListElement2* element = NS_STATIC_CAST(DisplayListElement2*, displayList.ElementAt(i));
03067     if ((element->mFlags & VIEW_RENDERED) != 0) {
03068       if (IsAncestorOf(aView, element->mView)) {
03069         element->mFlags |= (VIEW_ISSCROLLED | VIEW_TRANSPARENT);
03070       }
03071     }
03072   }
03073 
03074   nsRect finalTransparentRect;
03075 
03076   // We DON'T use AddCoveringWidgetsToOpaqueRegion here. Our child widgets are going to be moved
03077   // as if they were scrolled, so we need to examine the display list elements that might be covered by
03078   // child widgets.
03079 
03080   // However, we do want to ignore areas that are covered by widgets which have not moved.
03081   // Unfortunately figuring out that area is not easy, because we don't yet trust the native
03082   // widget layer to tell us the correct z-order of native widgets.
03083   // We still have to handle at least one case well:
03084   // widgets for fixed-position elements when we are scrolling the root scrollable (or something inside
03085   // the root scrollable) are on top.
03086   nsRegion opaqueRegion;
03087   if (mRootScrollable != nsnull) {
03088     if (IsAncestorOf(NS_STATIC_CAST(const nsScrollPortView*, mRootScrollable), aView)) {
03089       // add areas of fixed views to the opaque area.
03090       // This is a bit of a hack. We should not be doing special case processing for fixed views.
03091       nsView* fixedView = mRootView->GetFirstChild();
03092       while (fixedView != nsnull) {
03093         if (fixedView->GetZParent() != nsnull && fixedView->GetZIndex() >= 0) {
03094           opaqueRegion.Or(opaqueRegion, fixedView->GetBounds());
03095         }
03096         fixedView = fixedView->GetNextSibling();
03097       }
03098 
03099       // get the region into the coordinates of aView
03100       nscoord deltaX = 0, deltaY = 0;
03101       for (nsView* v = aView; v; v = v->GetParent()) {
03102         v->ConvertToParentCoords(&deltaX, &deltaY);
03103       }
03104       opaqueRegion.MoveBy(-deltaX, -deltaY);
03105     }
03106   }
03107 
03108   // We DO need to use OptimizeDisplayList here to eliminate views
03109   // that are covered by views we know are opaque. Typically aView's
03110   // scrolled view is opaque and we want to eliminate views behind it,
03111   // such as aView itself, that aren't being moved and would otherwise
03112   // cause us to decide not to blit.  Note that if for some view,
03113   // view->HasUniformBackground(), that's also sufficient to ignore
03114   // views behind 'view'; we've been promised that they produce only a
03115   // uniform background, which is still blittable. So we can treat
03116   // 'view' as opaque.
03117 
03118   // (Note that it's possible for aView's parent to actually be in
03119   // front of aView (if aView has a negative z-index) but if so, this
03120   // code still does the right thing. Yay for the display list based
03121   // approach!)
03122 
03123   OptimizeDisplayList(&displayList, nsRegion(r), finalTransparentRect, opaqueRegion, PR_TRUE);
03124 
03125   PRBool anyUnscrolledViews = PR_FALSE;
03126   PRBool anyUnblittableViews = PR_FALSE;
03127 
03128   for (i = 0; i < displayList.Count(); i++) {
03129     DisplayListElement2* element = NS_STATIC_CAST(DisplayListElement2*, displayList.ElementAt(i));
03130     if ((element->mFlags & VIEW_RENDERED) != 0) {
03131       if ((element->mFlags & VIEW_ISSCROLLED) == 0 && element->mView != aView) {
03132         anyUnscrolledViews = PR_TRUE;
03133       } else if ((element->mView->GetViewFlags() & NS_VIEW_FLAG_DONT_BITBLT) != 0) {
03134         anyUnblittableViews = PR_TRUE;
03135       }
03136     }
03137   }
03138 
03139   PL_FreeArenaPool(&displayArena);
03140   PL_FinishArenaPool(&displayArena);
03141 
03142   return !anyUnscrolledViews && !anyUnblittableViews;
03143 }
03144 
03145 NS_IMETHODIMP nsViewManager::SetViewBitBltEnabled(nsIView *aView, PRBool aEnable)
03146 {
03147   nsView* view = NS_STATIC_CAST(nsView*, aView);
03148 
03149   NS_ASSERTION(!(nsnull == view), "no view");
03150 
03151   if (aEnable) {
03152     view->SetViewFlags(view->GetViewFlags() & ~NS_VIEW_FLAG_DONT_BITBLT);
03153   } else {
03154     view->SetViewFlags(view->GetViewFlags() | NS_VIEW_FLAG_DONT_BITBLT);
03155   }
03156 
03157   return NS_OK;
03158 }
03159 
03160 NS_IMETHODIMP nsViewManager::SetViewCheckChildEvents(nsIView *aView, PRBool aEnable)
03161 {
03162   nsView* view = NS_STATIC_CAST(nsView*, aView);
03163 
03164   NS_ASSERTION(!(nsnull == view), "no view");
03165 
03166   if (aEnable) {
03167     view->SetViewFlags(view->GetViewFlags() & ~NS_VIEW_FLAG_DONT_CHECK_CHILDREN);
03168   } else {
03169     view->SetViewFlags(view->GetViewFlags() | NS_VIEW_FLAG_DONT_CHECK_CHILDREN);
03170   }
03171 
03172   return NS_OK;
03173 }
03174 
03175 NS_IMETHODIMP nsViewManager::SetViewFloating(nsIView *aView, PRBool aFloating)
03176 {
03177   nsView* view = NS_STATIC_CAST(nsView*, aView);
03178 
03179   NS_ASSERTION(!(nsnull == view), "no view");
03180 
03181   view->SetFloating(aFloating);
03182 
03183   return NS_OK;
03184 }
03185 
03186 NS_IMETHODIMP nsViewManager::SetViewVisibility(nsIView *aView, nsViewVisibility aVisible)
03187 {
03188   nsView* view = NS_STATIC_CAST(nsView*, aView);
03189 
03190   if (aVisible != view->GetVisibility()) {
03191     view->SetVisibility(aVisible);
03192 
03193     if (IsViewInserted(view)) {
03194       if (!view->HasWidget()) {
03195         if (nsViewVisibility_kHide == aVisible) {
03196           nsView* parentView = view->GetParent();
03197           if (parentView) {
03198             UpdateView(parentView, view->GetBounds(), NS_VMREFRESH_NO_SYNC);
03199           }
03200         }
03201         else {
03202           UpdateView(view, NS_VMREFRESH_NO_SYNC);
03203         }
03204       }
03205     }
03206 
03207     // Any child views not associated with frames might not get their visibility
03208     // updated, so propagate our visibility to them. This is important because
03209     // hidden views should have all hidden children.
03210     for (nsView* childView = view->GetFirstChild(); childView;
03211          childView = childView->GetNextSibling()) {
03212       if (!childView->GetClientData()) {
03213         childView->SetVisibility(aVisible);
03214       }
03215     }
03216   }
03217   return NS_OK;
03218 }
03219 
03220 void nsViewManager::UpdateWidgetsForView(nsView* aView)
03221 {
03222   NS_PRECONDITION(aView, "Must have view!");
03223 
03224   if (aView->HasWidget()) {
03225     aView->GetWidget()->Update();
03226   }
03227 
03228   for (nsView* childView = aView->GetFirstChild();
03229        childView;
03230        childView = childView->GetNextSibling()) {
03231     UpdateWidgetsForView(childView);
03232   }
03233 }
03234 
03235 PRBool nsViewManager::IsViewInserted(nsView *aView)
03236 {
03237   if (mRootView == aView) {
03238     return PR_TRUE;
03239   } else if (aView->GetParent() == nsnull) {
03240     return PR_FALSE;
03241   } else {
03242     nsView* view = aView->GetParent()->GetFirstChild();
03243     while (view != nsnull) {
03244       if (view == aView) {
03245         return PR_TRUE;
03246       }        
03247       view = view->GetNextSibling();
03248     }
03249     return PR_FALSE;
03250   }
03251 }
03252 
03253 NS_IMETHODIMP nsViewManager::SetViewZIndex(nsIView *aView, PRBool aAutoZIndex, PRInt32 aZIndex, PRBool aTopMost)
03254 {
03255   nsView* view = NS_STATIC_CAST(nsView*, aView);
03256   nsresult  rv = NS_OK;
03257 
03258   NS_ASSERTION((view != nsnull), "no view");
03259 
03260   // don't allow the root view's z-index to be changed. It should always be zero.
03261   // This could be removed and replaced with a style rule, or just removed altogether, with interesting consequences
03262   if (aView == mRootView) {
03263     return rv;
03264   }
03265 
03266   PRBool oldTopMost = view->IsTopMost();
03267   PRBool oldIsAuto = view->GetZIndexIsAuto();
03268 
03269   if (aAutoZIndex) {
03270     aZIndex = 0;
03271   }
03272 
03273   PRInt32 oldidx = view->GetZIndex();
03274   view->SetZIndex(aAutoZIndex, aZIndex, aTopMost);
03275 
03276   if (CompareZIndex(oldidx, oldTopMost, oldIsAuto,
03277                     aZIndex, aTopMost, aAutoZIndex) != 0) {
03278     UpdateView(view, NS_VMREFRESH_NO_SYNC);
03279   }
03280 
03281   nsZPlaceholderView* zParentView = view->GetZParent();
03282   if (nsnull != zParentView) {
03283     SetViewZIndex(zParentView, aAutoZIndex, aZIndex, aTopMost);
03284   }
03285 
03286   return rv;
03287 }
03288 
03289 NS_IMETHODIMP nsViewManager::SetViewContentTransparency(nsIView *aView, PRBool aTransparent)
03290 {
03291   nsView* view = NS_STATIC_CAST(nsView*, aView);
03292 
03293   if (view->IsTransparent() != aTransparent) {
03294     view->SetContentTransparency(aTransparent);
03295 
03296     if (IsViewInserted(view)) {
03297       UpdateView(view, NS_VMREFRESH_NO_SYNC);
03298     }
03299   }
03300 
03301   return NS_OK;
03302 }
03303 
03304 NS_IMETHODIMP nsViewManager::SetViewOpacity(nsIView *aView, float aOpacity)
03305 {
03306   nsView* view = NS_STATIC_CAST(nsView*, aView);
03307 
03308   if (view->GetOpacity() != aOpacity)
03309     {
03310       view->SetOpacity(aOpacity);
03311 
03312       if (IsViewInserted(view)) {
03313         UpdateView(view, NS_VMREFRESH_NO_SYNC);
03314       }
03315     }
03316 
03317   return NS_OK;
03318 }
03319 
03320 NS_IMETHODIMP nsViewManager::SetViewObserver(nsIViewObserver *aObserver)
03321 {
03322   mObserver = aObserver;
03323   return NS_OK;
03324 }
03325 
03326 NS_IMETHODIMP nsViewManager::GetViewObserver(nsIViewObserver *&aObserver)
03327 {
03328   if (nsnull != mObserver) {
03329     aObserver = mObserver;
03330     NS_ADDREF(mObserver);
03331     return NS_OK;
03332   } else
03333     return NS_ERROR_NO_INTERFACE;
03334 }
03335 
03336 NS_IMETHODIMP nsViewManager::GetDeviceContext(nsIDeviceContext *&aContext)
03337 {
03338   NS_IF_ADDREF(mContext);
03339   aContext = mContext;
03340   return NS_OK;
03341 }
03342 
03343 void nsViewManager::GetMaxWidgetBounds(nsRect& aMaxWidgetBounds) const
03344 {
03345   // Go through the list of viewmanagers and get the maximum width and 
03346   // height of their widgets
03347   aMaxWidgetBounds.width = 0;
03348   aMaxWidgetBounds.height = 0;
03349   PRInt32 index = 0;
03350   for (index = 0; index < mVMCount; index++) {
03351 
03352     nsViewManager* vm = (nsViewManager*)gViewManagers->ElementAt(index);
03353     nsCOMPtr<nsIWidget> rootWidget;
03354 
03355     if(NS_SUCCEEDED(vm->GetWidget(getter_AddRefs(rootWidget))) && rootWidget)
03356       {
03357         nsRect widgetBounds;
03358         rootWidget->GetBounds(widgetBounds);
03359         aMaxWidgetBounds.width = PR_MAX(aMaxWidgetBounds.width, widgetBounds.width);
03360         aMaxWidgetBounds.height = PR_MAX(aMaxWidgetBounds.height, widgetBounds.height);
03361       }
03362   }
03363 
03364   //   printf("WIDGET BOUNDS %d %d\n", aMaxWidgetBounds.width, aMaxWidgetBounds.height);
03365 }
03366 
03367 PRInt32 nsViewManager::GetViewManagerCount()
03368 {
03369   return mVMCount;
03370 }
03371 
03372 const nsVoidArray* nsViewManager::GetViewManagerArray() 
03373 {
03374   return gViewManagers;
03375 }
03376 
03377 already_AddRefed<nsIRenderingContext>
03378 nsViewManager::CreateRenderingContext(nsView &aView)
03379 {
03380   nsView              *par = &aView;
03381   nsIWidget*          win;
03382   nsIRenderingContext *cx = nsnull;
03383   nscoord             ax = 0, ay = 0;
03384 
03385   do
03386     {
03387       win = par->GetWidget();
03388       if (win)
03389         break;
03390 
03391       //get absolute coordinates of view, but don't
03392       //add in view pos since the first thing you ever
03393       //need to do when painting a view is to translate
03394       //the rendering context by the views pos and other parts
03395       //of the code do this for us...
03396 
03397       if (par != &aView)
03398         {
03399           par->ConvertToParentCoords(&ax, &ay);
03400         }
03401 
03402       par = par->GetParent();
03403     }
03404   while (nsnull != par);
03405 
03406   if (nsnull != win)
03407     {
03408       mContext->CreateRenderingContext(par, cx);
03409 
03410       if (nsnull != cx)
03411         cx->Translate(ax, ay);
03412     }
03413 
03414   return cx;
03415 }
03416 
03417 NS_IMETHODIMP nsViewManager::DisableRefresh(void)
03418 {
03419   if (!IsRootVM()) {
03420     return RootViewManager()->DisableRefresh();
03421   }
03422   
03423   if (mUpdateBatchCnt > 0)
03424     return NS_OK;
03425 
03426   mRefreshEnabled = PR_FALSE;
03427   return NS_OK;
03428 }
03429 
03430 NS_IMETHODIMP nsViewManager::EnableRefresh(PRUint32 aUpdateFlags)
03431 {
03432   if (!IsRootVM()) {
03433     return RootViewManager()->EnableRefresh(aUpdateFlags);
03434   }
03435   
03436   if (mUpdateBatchCnt > 0)
03437     return NS_OK;
03438 
03439   mRefreshEnabled = PR_TRUE;
03440 
03441   // nested batching can combine IMMEDIATE with DEFERRED. Favour
03442   // IMMEDIATE over DEFERRED and DEFERRED over NO_SYNC.
03443   if (aUpdateFlags & NS_VMREFRESH_IMMEDIATE) {
03444     FlushPendingInvalidates();
03445     Composite();
03446   } else if (aUpdateFlags & NS_VMREFRESH_DEFERRED) {
03447     PostInvalidateEvent();
03448   } else { // NO_SYNC
03449     FlushPendingInvalidates();
03450   }
03451 
03452   return NS_OK;
03453 }
03454 
03455 NS_IMETHODIMP nsViewManager::BeginUpdateViewBatch(void)
03456 {
03457   if (!IsRootVM()) {
03458     return RootViewManager()->BeginUpdateViewBatch();
03459   }
03460   
03461   nsresult result = NS_OK;
03462   
03463   if (mUpdateBatchCnt == 0) {
03464     mUpdateBatchFlags = 0;
03465     result = DisableRefresh();
03466   }
03467 
03468   if (NS_SUCCEEDED(result))
03469     ++mUpdateBatchCnt;
03470 
03471   return result;
03472 }
03473 
03474 NS_IMETHODIMP nsViewManager::EndUpdateViewBatch(PRUint32 aUpdateFlags)
03475 {
03476   if (!IsRootVM()) {
03477     return RootViewManager()->EndUpdateViewBatch(aUpdateFlags);
03478   }
03479   
03480   nsresult result = NS_OK;
03481 
03482   --mUpdateBatchCnt;
03483 
03484   NS_ASSERTION(mUpdateBatchCnt >= 0, "Invalid batch count!");
03485 
03486   if (mUpdateBatchCnt < 0)
03487     {
03488       mUpdateBatchCnt = 0;
03489       return NS_ERROR_FAILURE;
03490     }
03491 
03492   mUpdateBatchFlags |= aUpdateFlags;
03493   if (mUpdateBatchCnt == 0) {
03494     result = EnableRefresh(mUpdateBatchFlags);
03495   }
03496 
03497   return result;
03498 }
03499 
03500 NS_IMETHODIMP nsViewManager::SetRootScrollableView(nsIScrollableView *aScrollable)
03501 {
03502   if (mRootScrollable) {
03503     NS_STATIC_CAST(nsScrollPortView*, mRootScrollable)->
03504       SetClipPlaceholdersToBounds(PR_FALSE);
03505   }
03506   mRootScrollable = aScrollable;
03507   if (mRootScrollable) {
03508     NS_STATIC_CAST(nsScrollPortView*, mRootScrollable)->
03509       SetClipPlaceholdersToBounds(PR_TRUE);
03510   }
03511   return NS_OK;
03512 }
03513 
03514 NS_IMETHODIMP nsViewManager::GetRootScrollableView(nsIScrollableView **aScrollable)
03515 {
03516   *aScrollable = mRootScrollable;
03517   return NS_OK;
03518 }
03519 
03520 NS_IMETHODIMP nsViewManager::RenderOffscreen(nsIView* aView, nsRect aRect,
03521                                              PRBool aUntrusted,
03522                                              PRBool aIgnoreViewportScrolling,
03523                                              nscolor aBackgroundColor,
03524                                              nsIRenderingContext** aRenderedContext)
03525 {
03526   nsView* view = NS_STATIC_CAST(nsView*, aView);
03527 
03528   *aRenderedContext = nsnull;
03529 
03530   NS_ASSERTION(!aUntrusted, "We don't support untrusted yet");
03531   if (aUntrusted)
03532     return NS_ERROR_NOT_IMPLEMENTED;
03533 
03534   nsCOMPtr<nsIRenderingContext> tmpContext
03535     = CreateRenderingContext(*view);
03536   if (!tmpContext)
03537     return NS_ERROR_FAILURE;
03538 
03539   nsRect bounds(nsPoint(0, 0), aRect.Size());
03540   bounds.ScaleRoundOut(mTwipsToPixels);
03541   
03542   nsIDrawingSurface* surface;
03543   nsresult rv
03544     = tmpContext->CreateDrawingSurface(bounds, NS_CREATEDRAWINGSURFACE_FOR_PIXEL_ACCESS,
03545                                        surface);
03546   if (NS_FAILED(rv))
03547     return NS_ERROR_FAILURE;
03548   nsCOMPtr<nsIRenderingContext> localcx;
03549   rv = NewOffscreenContext(mContext, surface, aRect, getter_AddRefs(localcx));
03550   if (NS_FAILED(rv)) {
03551     tmpContext->DestroyDrawingSurface(surface);
03552     return NS_ERROR_FAILURE;
03553   }
03554   // clipping and translation is set by NewOffscreenContext
03555 
03556   localcx->SetColor(aBackgroundColor);
03557   localcx->FillRect(aRect);
03558 
03559   nsRect r = aRect;
03560   nsIView* suppressScrolling = nsnull;
03561   if (aIgnoreViewportScrolling && mRootScrollable) {
03562     // Suppress clipping/scrolling/scrollbar painting due to our
03563     // viewport scrollable view
03564     nscoord x, y;
03565     mRootScrollable->GetScrollPosition(x, y);
03566     localcx->Translate(x, y);
03567     r.MoveBy(-x, -y);
03568     suppressScrolling = mRootScrollable->View();
03569   }
03570 
03571   nsAutoVoidArray displayList;
03572   PLArenaPool displayArena;
03573   PL_INIT_ARENA_POOL(&displayArena, "displayArena", 1024);
03574   BuildRenderingDisplayList(view, nsRegion(r), &displayList, displayArena,
03575                             PR_TRUE, PR_TRUE, suppressScrolling);
03576   RenderViews(view, *localcx, nsRegion(aRect), surface, displayList);
03577   PL_FreeArenaPool(&displayArena);
03578   PL_FinishArenaPool(&displayArena);
03579 
03580   localcx.swap(*aRenderedContext);
03581   return NS_OK;
03582 }
03583 
03584 NS_IMETHODIMP nsViewManager::Display(nsIView* aView, nscoord aX, nscoord aY, const nsRect& aClipRect)
03585 {
03586   nsView              *view = NS_STATIC_CAST(nsView*, aView);
03587   nsIRenderingContext *localcx = nsnull;
03588 
03589   if (! IsRefreshEnabled())
03590     return NS_OK;
03591 
03592   NS_ASSERTION(!IsPainting(), "recursive painting not permitted");
03593 
03594   SetPainting(PR_TRUE);
03595 
03596   mContext->CreateRenderingContext(localcx);
03597 
03598   //couldn't get rendering context. this is ok if at startup
03599   if (nsnull == localcx)
03600     {
03601       SetPainting(PR_FALSE);
03602       return NS_ERROR_FAILURE;
03603     }
03604 
03605   nsRect trect = view->GetBounds();
03606   view->ConvertFromParentCoords(&trect.x, &trect.y);
03607 
03608   localcx->Translate(aX, aY);
03609 
03610   localcx->SetClipRect(aClipRect, nsClipCombine_kReplace);
03611 
03612   // Paint the view. The clipping rect was set above set don't clip again.
03613   //aView->Paint(*localcx, trect, NS_VIEW_FLAG_CLIP_SET, result);
03614   nsAutoVoidArray displayList;
03615   PLArenaPool displayArena;
03616   PL_INIT_ARENA_POOL(&displayArena, "displayArena", 1024);
03617   BuildRenderingDisplayList(view, nsRegion(trect), &displayList, displayArena,
03618                             PR_FALSE, PR_FALSE, nsnull);
03619   RenderViews(view, *localcx, nsRegion(trect), PR_FALSE, displayList);
03620   PL_FreeArenaPool(&displayArena);
03621   PL_FinishArenaPool(&displayArena);
03622 
03623   NS_RELEASE(localcx);
03624 
03625   SetPainting(PR_FALSE);
03626 
03627   return NS_OK;
03628 
03629 }
03630 
03631 NS_IMETHODIMP nsViewManager::AddCompositeListener(nsICompositeListener* aListener)
03632 {
03633   if (nsnull == mCompositeListeners) {
03634     nsresult rv = NS_NewISupportsArray(&mCompositeListeners);
03635     if (NS_FAILED(rv))
03636       return rv;
03637   }
03638   return mCompositeListeners->AppendElement(aListener);
03639 }
03640 
03641 NS_IMETHODIMP nsViewManager::RemoveCompositeListener(nsICompositeListener* aListener)
03642 {
03643   if (nsnull != mCompositeListeners) {
03644     return mCompositeListeners->RemoveElement(aListener);
03645   }
03646   return NS_ERROR_FAILURE;
03647 }
03648 
03649 NS_IMETHODIMP nsViewManager::GetWidget(nsIWidget **aWidget)
03650 {
03651   *aWidget = GetWidget();
03652   NS_IF_ADDREF(*aWidget);
03653   return NS_OK;
03654 }
03655 
03656 NS_IMETHODIMP nsViewManager::ForceUpdate()
03657 {
03658   if (!IsRootVM()) {
03659     return RootViewManager()->ForceUpdate();
03660   }
03661 
03662   // Walk the view tree looking for widgets, and call Update() on each one
03663   UpdateWidgetsForView(mRootView);
03664   return NS_OK;
03665 }
03666 
03667 static nsresult EnsureZTreeNodeCreated(nsView* aView, DisplayZTreeNode* &aNode, PLArenaPool &aPool)
03668 {
03669   if (nsnull == aNode) {
03670     ARENA_ALLOCATE(aNode, &aPool, DisplayZTreeNode);
03671 
03672     if (nsnull == aNode) {
03673       return NS_ERROR_OUT_OF_MEMORY;
03674     }
03675     aNode->mView = aView;
03676     aNode->mDisplayElement = nsnull;
03677     aNode->mZChild = nsnull;
03678     aNode->mZSibling = nsnull;
03679   }
03680   return NS_OK;
03681 }
03682 
03703 PRBool nsViewManager::CreateDisplayList(nsView *aView,
03704                                         DisplayZTreeNode* &aResult,
03705                                         nscoord aOriginX, nscoord aOriginY, nsView *aRealView,
03706                                         const nsRect *aDamageRect, nsView *aTopView,
03707                                         nscoord aX, nscoord aY, PRBool aPaintFloats,
03708                                         PRBool aEventProcessing,
03709                                         nsIView* aSuppressClip,
03710                                         nsHashtable &aMapPlaceholderViewToZTreeNode,
03711                                         PLArenaPool &aPool)
03712 {
03713   PRBool retval = PR_FALSE;
03714 
03715   aResult = nsnull;
03716 
03717   NS_ASSERTION((aView != nsnull), "no view");
03718 
03719   if (nsViewVisibility_kHide == aView->GetVisibility()) {
03720     // bail out if the view is marked invisible; all children are invisible
03721     return retval;
03722   }
03723 
03724   nsRect bounds = aView->GetBounds();
03725   nsPoint pos = aView->GetPosition();
03726 
03727   // -> to global coordinates (relative to aTopView)
03728   bounds.x += aX;
03729   bounds.y += aY;
03730   pos.MoveBy(aX, aY);
03731 
03732   // does this view clip all its children?
03733   PRBool isClipView =
03734     (aView->GetClipChildrenToBounds(PR_FALSE)
03735      && !(aView->GetViewFlags() & NS_VIEW_FLAG_CONTAINS_PLACEHOLDER))
03736     || aView->GetClipChildrenToBounds(PR_TRUE);
03737   
03738   if (isClipView && aSuppressClip && IsAncestorOf(aView, aSuppressClip)) {
03739     isClipView = PR_FALSE;
03740   }
03741   PRBool overlap;
03742   nsRect irect;
03743 
03744   nsIView* clipUpTo = aSuppressClip ? aSuppressClip : aTopView;
03745     
03746   // -> to refresh-frame coordinates (relative to aRealView)
03747   bounds.x -= aOriginX;
03748   bounds.y -= aOriginY;
03749   if (aDamageRect) {
03750     overlap = irect.IntersectRect(bounds, *aDamageRect);
03751     if (isClipView) {
03752       aDamageRect = &irect;
03753     }
03754     if (aEventProcessing && aTopView == aView) {
03755       // Always deliver an event somewhere, at least to the top-level target.
03756       // There may be mouse capturing going on.
03757       overlap = PR_TRUE;
03758     }
03759   }
03760   else
03761     overlap = PR_TRUE;
03762 
03763   // -> to global coordinates (relative to aTopView)
03764   bounds.x += aOriginX;
03765   bounds.y += aOriginY;
03766 
03767   // if there's no overlap between the dirty area and the bounds of
03768   // the view, we should be able to ignore the view and all its
03769   // children. Unfortunately there is the possibility of reparenting:
03770   // some other view in the tree might want to be reparented to one of
03771   // our children, and that view might lie outside our
03772   // bounds. Typically we need to go ahead and add this view and its
03773   // children to the ZTree in case someone wants to reparent into it.
03774   //
03775   // We can avoid this in two cases: if we clip our children to our
03776   // bounds, then even if a view is reparented under us, none of its
03777   // visible area can lie outside our bounds, so it can't intersect
03778   // the dirty area.  Also, if we don't contain any placeholder views,
03779   // then there is no way for anyone to reparent below us.
03780   if (!overlap && !(aView->GetViewFlags() & NS_VIEW_FLAG_CONTAINS_PLACEHOLDER)) {
03781     return PR_FALSE;
03782   }
03783 
03784   // Don't paint floating views unless the root view being painted is a floating view.
03785   // This is important because we may be asked to paint
03786   // a window that's behind a transient float; in this case we must paint the real window
03787   // contents, not the float contents (bug 63496)
03788   if (!aPaintFloats && aView->GetFloating()) {
03789     return PR_FALSE;
03790   }
03791 
03792   PRBool anyChildren = aView->GetFirstChild() != nsnull;
03793 
03794   if (aEventProcessing
03795       && (aView->GetViewFlags() & NS_VIEW_FLAG_DONT_CHECK_CHILDREN) != 0) {
03796     anyChildren = PR_FALSE;
03797   }
03798 
03799   PRBool hasFilter = aView->GetOpacity() != 1.0f;
03800   if (hasFilter) {
03801     // -> to refresh-frame coordinates (relative to aRealView)
03802     bounds.x -= aOriginX;
03803     bounds.y -= aOriginY;
03804     
03805     // Add POP first because the z-tree is in reverse order
03806     retval = AddToDisplayList(aView, aResult, bounds, bounds,
03807                               POP_FILTER, aX - aOriginX, aY - aOriginY, PR_TRUE, aPool,
03808                               clipUpTo);
03809     if (retval)
03810       return retval;
03811     
03812     // -> to global coordinates (relative to aTopView)
03813     bounds.x += aOriginX;
03814     bounds.y += aOriginY;
03815   }
03816 
03817   if (anyChildren) {
03818     if (isClipView) {
03819       // -> to refresh-frame coordinates (relative to aRealView)
03820       bounds.x -= aOriginX;
03821       bounds.y -= aOriginY;
03822 
03823       // Add POP first because the z-tree is in reverse order
03824       retval = AddToDisplayList(aView, aResult, bounds, bounds,
03825                                 POP_CLIP, aX - aOriginX, aY - aOriginY, PR_TRUE, aPool,
03826                                 clipUpTo);
03827 
03828       if (retval)
03829         return retval;
03830 
03831       // -> to global coordinates (relative to aTopView)
03832       bounds.x += aOriginX;
03833       bounds.y += aOriginY;
03834     }
03835 
03836     // Put in all the children before we add this view itself.
03837     // This preserves document order.
03838     nsView *childView = nsnull;
03839     for (childView = aView->GetFirstChild(); nsnull != childView;
03840          childView = childView->GetNextSibling()) {
03841       DisplayZTreeNode* createdNode;
03842       retval = CreateDisplayList(childView, createdNode,
03843                                  aOriginX, aOriginY, aRealView, aDamageRect, aTopView,
03844                                  pos.x, pos.y, aPaintFloats, aEventProcessing,
03845                                  aSuppressClip,
03846                                  aMapPlaceholderViewToZTreeNode, aPool);
03847       if (createdNode != nsnull) {
03848         EnsureZTreeNodeCreated(aView, aResult, aPool);
03849         createdNode->mZSibling = aResult->mZChild;
03850         aResult->mZChild = createdNode;
03851       }
03852 
03853       if (retval)
03854         break;
03855     }
03856   }
03857 
03858   if (!retval) {
03859     if (overlap) {
03860       // -> to refresh-frame coordinates (relative to aRealView)
03861       bounds.x -= aOriginX;
03862       bounds.y -= aOriginY;
03863 
03864       if (aEventProcessing || aView->GetOpacity() > 0.0f) {
03865         PRUint32 flags = VIEW_RENDERED;
03866         if (aView->IsTransparent())
03867           flags |= VIEW_TRANSPARENT;
03868         retval = AddToDisplayList(aView, aResult, bounds, irect, flags,
03869                                   aX - aOriginX, aY - aOriginY,
03870                                   aEventProcessing && aTopView == aView, aPool,
03871                                   clipUpTo);
03872         // We're forcing AddToDisplayList to pick up the view only
03873         // during event processing, and only when aView is back at the
03874         // root of the tree of acceptable views (note that when event
03875         // capturing is in effect, aTopView is the capturing view)
03876       }
03877 
03878       // -> to global coordinates (relative to aTopView)
03879       bounds.x += aOriginX;
03880       bounds.y += aOriginY;
03881     } else {
03882       if (aView->IsZPlaceholderView()) {
03883         EnsureZTreeNodeCreated(aView, aResult, aPool);
03884         nsVoidKey key(aView);
03885         aMapPlaceholderViewToZTreeNode.Put(&key, aResult);
03886       }
03887     }
03888   }
03889 
03890   if (anyChildren && isClipView) {
03891     // -> to refresh-frame coordinates (relative to aRealView)
03892     bounds.x -= aOriginX;
03893     bounds.y -= aOriginY;
03894 
03895     if (AddToDisplayList(aView, aResult, bounds, bounds, PUSH_CLIP,
03896                          aX - aOriginX, aY - aOriginY, PR_TRUE, aPool,
03897                          clipUpTo)) {
03898       retval = PR_TRUE;
03899     }
03900     
03901     // -> to global coordinates (relative to aTopView)
03902     bounds.x += aOriginX;
03903     bounds.y += aOriginY;
03904   }
03905 
03906   if (hasFilter) {
03907     // -> to refresh-frame coordinates (relative to aRealView)
03908     bounds.x -= aOriginX;
03909     bounds.y -= aOriginY;
03910     
03911     retval = AddToDisplayList(aView, aResult, bounds, bounds,
03912                               PUSH_FILTER, aX - aOriginX, aY - aOriginY, PR_TRUE, aPool,
03913                               clipUpTo);
03914     if (retval)
03915       return retval;
03916     
03917     // -> to global coordinates (relative to aTopView)
03918     bounds.x += aOriginX;
03919     bounds.y += aOriginY;
03920   }
03921 
03922   return retval;
03923 }
03924 
03925 PRBool nsViewManager::AddToDisplayList(nsView *aView,
03926                                        DisplayZTreeNode* &aParent, nsRect &aClipRect,
03927                                        nsRect& aDirtyRect, PRUint32 aFlags,
03928                                        nscoord aAbsX, nscoord aAbsY,
03929                                        PRBool aAssumeIntersection, PLArenaPool &aPool,
03930                                        nsIView* aStopClippingAtView)
03931 {
03932   nsRect clipRect = aView->GetClippedRect(aStopClippingAtView);
03933   PRBool clipped = clipRect != aView->GetDimensions();
03934 
03935   // get clipRect into the coordinate system of aView's parent. aAbsX and
03936   // aAbsY give an offset to the origin of aView's parent.
03937   clipRect.MoveBy(aView->GetPosition());
03938   clipRect.x += aAbsX;
03939   clipRect.y += aAbsY;
03940 
03941   if (!clipped) {
03942     // XXX this code can't be right. If we're only clipped by one pixel,
03943     // then that's no reason to use a whole different rectangle.
03944     NS_ASSERTION(clipRect == aClipRect, "gah!!!");
03945     clipRect = aClipRect;
03946   }
03947 
03948   PRBool overlap = clipRect.IntersectRect(clipRect, aDirtyRect);
03949   if (!overlap && !aAssumeIntersection) {
03950     return PR_FALSE;
03951   }
03952 
03953   DisplayListElement2* element;
03954   ARENA_ALLOCATE(element, &aPool, DisplayListElement2);
03955   if (element == nsnull) {
03956     return PR_TRUE;
03957   }
03958   DisplayZTreeNode* node;
03959   ARENA_ALLOCATE(node, &aPool, DisplayZTreeNode);
03960   if (nsnull == node) {
03961     return PR_TRUE;
03962   }
03963 
03964   EnsureZTreeNodeCreated(aView, aParent, aPool);
03965 
03966   node->mDisplayElement = element;
03967   node->mView = nsnull;
03968   node->mZChild = nsnull;
03969   node->mZSibling = aParent->mZChild;
03970   aParent->mZChild = node;
03971 
03972   element->mView = aView;
03973   element->mBounds = clipRect;
03974   element->mAbsX = aClipRect.x;
03975   element->mAbsY = aClipRect.y;
03976   element->mFlags = aFlags;
03977   if (clipped) { 
03978     element->mFlags |= VIEW_CLIPPED;
03979   }
03980   
03981   return PR_FALSE;
03982 }
03983 
03994 void nsViewManager::OptimizeDisplayList(const nsVoidArray* aDisplayList, const nsRegion& aDamageRegion,
03995                                         nsRect& aFinalTransparentRect,
03996                                         nsRegion &aOpaqueRegion, PRBool aTreatUniformAsOpaque)
03997 {
03998   // Mark all views inside a PUSH_FILTER/POP_FILTER as being translucent.
03999   // If we don't do this, we'll incorrectly optimize by thinking views are
04000   // opaque when they really aren't.
04001   PRInt32 i;
04002   PRInt32 filterDepth = 0;
04003   for (i = 0; i < aDisplayList->Count(); ++i) {
04004     DisplayListElement2* element = NS_STATIC_CAST(DisplayListElement2*,
04005                                                   aDisplayList->ElementAt(i));
04006     if (element->mFlags & PUSH_FILTER) {
04007       ++filterDepth;
04008     }
04009     if (filterDepth > 0 && (element->mFlags & VIEW_RENDERED)) {
04010       element->mFlags |= VIEW_TRANSLUCENT;
04011     }
04012     if (element->mFlags & POP_FILTER) {
04013       --filterDepth;
04014     }
04015   }
04016 
04017   for (i = aDisplayList->Count() - 1; i >= 0; --i) {
04018     DisplayListElement2* element = NS_STATIC_CAST(DisplayListElement2*, aDisplayList->ElementAt(i));
04019     if (element->mFlags & VIEW_RENDERED) {
04020       nsRegion tmpRgn;
04021       tmpRgn.Sub(element->mBounds, aOpaqueRegion);
04022       tmpRgn.And(tmpRgn, aDamageRegion);
04023 
04024       if (tmpRgn.IsEmpty()) {
04025         element->mFlags &= ~VIEW_RENDERED;
04026       } else {
04027         element->mBounds = tmpRgn.GetBounds();
04028 
04029         PRBool tooComplex = aOpaqueRegion.GetNumRects() > MAX_OPAQUE_REGION_COMPLEXITY &&
04030           !element->mBounds.Contains(aOpaqueRegion.GetBounds());
04031         // See whether we should add this view's bounds to aOpaqueRegion
04032         if (!tooComplex &&
04033             // a view is opaque if it is neither transparent nor transluscent
04034             (!(element->mFlags & (VIEW_TRANSPARENT | VIEW_TRANSLUCENT))
04035             // also, treat it as opaque if it's drawn onto a uniform background
04036             // and we're doing scrolling analysis; if the background is uniform,
04037             // we don't care what's under it. But the background might be translucent
04038             // because of some enclosing opacity group, in which case we do care
04039             // what's under it. Unfortunately we don't know exactly where the
04040             // background comes from ... it may not even have a view ... but
04041             // the side condition on SetHasUniformBackground ensures that
04042             // if the background is translucent, then this view is also marked
04043             // translucent.
04044             || (element->mView->HasUniformBackground() && aTreatUniformAsOpaque
04045                 && !(element->mFlags & VIEW_TRANSLUCENT)))) {
04046           aOpaqueRegion.Or(aOpaqueRegion, element->mBounds);
04047         }
04048       }
04049     }
04050   }
04051 
04052   nsRegion tmpRgn;
04053   tmpRgn.Sub(aDamageRegion, aOpaqueRegion);
04054   aFinalTransparentRect = tmpRgn.GetBounds();
04055 }
04056 
04057 // Remove redundant PUSH/POP_CLIP pairs. These could be expensive.
04058 void nsViewManager::OptimizeDisplayListClipping(const nsVoidArray* aDisplayList,
04059                                                 PRBool aHaveClip, nsRect& aClipRect, PRInt32& aIndex,
04060                                                 PRBool& aAnyRendered)
04061 {   
04062   aAnyRendered = PR_FALSE;
04063 
04064   while (aIndex < aDisplayList->Count()) {
04065     DisplayListElement2* element =
04066       NS_STATIC_CAST(DisplayListElement2*, aDisplayList->ElementAt(aIndex));
04067     aIndex++;
04068 
04069     if (element->mFlags & VIEW_RENDERED) {
04070       aAnyRendered = PR_TRUE;
04071 
04072       if (aHaveClip && (element->mFlags & VIEW_CLIPPED)) {
04073         nsRect newClip;
04074         newClip.IntersectRect(aClipRect, element->mBounds);
04075         // no need to clip if the clip rect doesn't change
04076         if (newClip == aClipRect) {
04077           element->mFlags &= ~VIEW_CLIPPED;
04078         }
04079       }
04080     }
04081 
04082     if (element->mFlags & PUSH_CLIP) {
04083       nsRect newClip;
04084       if (aHaveClip) {
04085         newClip.IntersectRect(aClipRect, element->mBounds);
04086       } else {
04087         newClip = element->mBounds;
04088       }
04089 
04090       PRBool anyRenderedViews = PR_FALSE;
04091       OptimizeDisplayListClipping(aDisplayList, PR_TRUE, newClip, aIndex, anyRenderedViews);
04092       DisplayListElement2* popElement =
04093         NS_STATIC_CAST(DisplayListElement2*, aDisplayList->ElementAt(aIndex - 1));
04094       NS_ASSERTION(popElement->mFlags & POP_CLIP, "Must end with POP!");
04095 
04096       if (anyRenderedViews) {
04097         aAnyRendered = PR_TRUE;
04098       }
04099       if (!anyRenderedViews || (aHaveClip && newClip == aClipRect)) {
04100         // no need to clip if nothing's going to be rendered
04101         // ... or if the clip rect didn't change
04102         element->mFlags &= ~PUSH_CLIP;
04103         popElement->mFlags &= ~POP_CLIP;
04104       }
04105     }
04106 
04107     if (element->mFlags & POP_CLIP) {
04108       return;
04109     }
04110   }
04111 }
04112 
04113 // Compute the bounds what's rendered between each PUSH_FILTER/POP_FILTER. Also
04114 // compute the region of what's rendered that's actually opaque. Then we can
04115 // decide for each PUSH_FILTER/POP_FILTER pair whether the contents are partially
04116 // transparent or fully opaque. Detecting the fully opaque case is important
04117 // because it's much faster to composite a fully opaque element (i.e., all alphas
04118 // 255).
04119 nsRect nsViewManager::OptimizeTranslucentRegions(
04120   const nsVoidArray& aDisplayList, PRInt32* aIndex, nsRegion* aOpaqueRegion)
04121 {
04122   nsRect r;
04123   while (*aIndex < aDisplayList.Count()) {
04124     DisplayListElement2* element =
04125       NS_STATIC_CAST(DisplayListElement2*, aDisplayList.ElementAt(*aIndex));
04126     (*aIndex)++;
04127 
04128     if (element->mFlags & VIEW_RENDERED) {
04129       r.UnionRect(r, element->mBounds);
04130       // the bounds of a non-transparent element are added to the opaque
04131       // region
04132       if (!element->mView->IsTransparent() && aOpaqueRegion) {
04133         aOpaqueRegion->Or(*aOpaqueRegion, element->mBounds);
04134       }
04135     }
04136 
04137     if (element->mFlags & PUSH_FILTER) {
04138       // the region inside the PUSH/POP pair that's painted opaquely
04139       nsRegion opaqueRegion;
04140       // save the bounds of the area that's painted by elements between the PUSH/POP
04141       element->mBounds = OptimizeTranslucentRegions(aDisplayList, aIndex,
04142                                                     &opaqueRegion);
04143       DisplayListElement2* popElement =
04144         NS_STATIC_CAST(DisplayListElement2*, aDisplayList.ElementAt(*aIndex - 1));
04145       popElement->mBounds = element->mBounds;
04146       NS_ASSERTION(popElement->mFlags & POP_FILTER, "Must end with POP!");
04147 
04148       // don't bother with filters if nothing is visible inside the filter
04149       if (element->mBounds.IsEmpty()) {
04150         element->mFlags &= ~PUSH_FILTER;
04151         popElement->mFlags &= ~POP_FILTER;
04152       } else {
04153         nsRegion tmpRegion;
04154         tmpRegion.Sub(element->mBounds, opaqueRegion);
04155         if (!tmpRegion.IsEmpty()) {
04156           // remember whether this PUSH_FILTER/POP_FILTER contents are fully opaque or not
04157           element->mFlags |= VIEW_TRANSPARENT;
04158         }
04159       }
04160 
04161       // The filter doesn't paint opaquely, so don't add anything to aOpaqueRegion
04162       r.UnionRect(r, element->mBounds);
04163     }
04164     if (element->mFlags & POP_FILTER) {
04165       return r;
04166     }
04167   }
04168 
04169   return r;
04170 }
04171 
04172 void nsViewManager::ShowDisplayList(const nsVoidArray* aDisplayList)
04173 {
04174 #ifdef NS_DEBUG
04175   printf("### display list length=%d ###\n", aDisplayList->Count());
04176 
04177   PRInt32 nestcnt = 0;
04178   for (PRInt32 cnt = 0; cnt < aDisplayList->Count(); cnt++) {
04179     DisplayListElement2* element = (DisplayListElement2*)aDisplayList->ElementAt(cnt);
04180     nestcnt = PrintDisplayListElement(element, nestcnt);
04181   }
04182 #endif
04183 }
04184 
04185 nsPoint nsViewManager::ComputeViewOffset(const nsView *aView)
04186 {
04187   NS_PRECONDITION(aView, "Null view in ComputeViewOffset?");
04188   
04189   nsPoint origin(0, 0);
04190 #ifdef DEBUG
04191   const nsView* rootView;
04192   const nsView* origView = aView;
04193 #endif
04194 
04195   while (aView) {
04196 #ifdef DEBUG
04197     rootView = aView;
04198 #endif
04199     origin += aView->GetPosition();
04200     aView = aView->GetParent();
04201   }
04202   NS_ASSERTION(rootView ==
04203                origView->GetViewManager()->RootViewManager()->GetRootView(),
04204                "Unexpected root view");
04205   return origin;
04206 }
04207 
04208 PRBool nsViewManager::DoesViewHaveNativeWidget(nsView* aView)
04209 {
04210   if (aView->HasWidget())
04211     return (nsnull != aView->GetWidget()->GetNativeData(NS_NATIVE_WIDGET));
04212   return PR_FALSE;
04213 }
04214 
04215 /* static */
04216 nsView* nsViewManager::GetWidgetView(nsView *aView)
04217 {
04218   while (aView) {
04219     if (aView->HasWidget())
04220       return aView;
04221     aView = aView->GetParent();
04222   }
04223   return nsnull;
04224 }
04225 
04226 void nsViewManager::ViewToWidget(nsView *aView, nsView* aWidgetView, nsRect &aRect) const
04227 {
04228   while (aView != aWidgetView) {
04229     aView->ConvertToParentCoords(&aRect.x, &aRect.y);
04230     aView = aView->GetParent();
04231   }
04232   
04233   // intersect aRect with bounds of aWidgetView, to prevent generating any illegal rectangles.
04234   nsRect bounds;
04235   aWidgetView->GetDimensions(bounds);
04236   aRect.IntersectRect(aRect, bounds);
04237   // account for the view's origin not lining up with the widget's
04238   aRect.x -= bounds.x;
04239   aRect.y -= bounds.y;
04240   
04241   // finally, convert to device coordinates.
04242   float t2p;
04243   t2p = mContext->AppUnitsToDevUnits();
04244   aRect.ScaleRoundOut(t2p);
04245 }
04246 
04247 nsresult nsViewManager::GetVisibleRect(nsRect& aVisibleRect)
04248 {
04249   nsresult rv = NS_OK;
04250 
04251   // Get the viewport scroller
04252   nsIScrollableView* scrollingView;
04253   GetRootScrollableView(&scrollingView);
04254 
04255   if (scrollingView) {   
04256     // Determine the visible rect in the scrolled view's coordinate space.
04257     // The size of the visible area is the clip view size
04258     nsScrollPortView* clipView = NS_STATIC_CAST(nsScrollPortView*, scrollingView);
04259     clipView->GetDimensions(aVisibleRect);
04260 
04261     scrollingView->GetScrollPosition(aVisibleRect.x, aVisibleRect.y);
04262   } else {
04263     rv = NS_ERROR_FAILURE;
04264   }
04265 
04266   return rv;
04267 }
04268 
04269 nsresult nsViewManager::GetAbsoluteRect(nsView *aView, const nsRect &aRect, 
04270                                         nsRect& aAbsRect)
04271 {
04272   nsIScrollableView* scrollingView = nsnull;
04273   GetRootScrollableView(&scrollingView);
04274   if (nsnull == scrollingView) { 
04275     return NS_ERROR_FAILURE;
04276   }
04277 
04278   nsIView* scrolledIView = nsnull;
04279   scrollingView->GetScrolledView(scrolledIView);
04280   
04281   nsView* scrolledView = NS_STATIC_CAST(nsView*, scrolledIView);
04282 
04283   // Calculate the absolute coordinates of the aRect passed in.
04284   // aRects values are relative to aView
04285   aAbsRect = aRect;
04286   nsView *parentView = aView;
04287   while ((parentView != nsnull) && (parentView != scrolledView)) {
04288     parentView->ConvertToParentCoords(&aAbsRect.x, &aAbsRect.y);
04289     parentView = parentView->GetParent();
04290   }
04291 
04292   if (parentView != scrolledView) {
04293     return NS_ERROR_FAILURE;
04294   }
04295 
04296   return NS_OK;
04297 }
04298 
04299 
04300 NS_IMETHODIMP nsViewManager::GetRectVisibility(nsIView *aView, 
04301                                                const nsRect &aRect,
04302                                                PRUint16 aMinTwips, 
04303                                                nsRectVisibility *aRectVisibility)
04304 {
04305   nsView* view = NS_STATIC_CAST(nsView*, aView);
04306 
04307   // The parameter aMinTwips determines how many rows/cols of pixels must be visible on each side of the element,
04308   // in order to be counted as visible
04309 
04310   *aRectVisibility = nsRectVisibility_kZeroAreaRect;
04311   if (aRect.width == 0 || aRect.height == 0) {
04312     return NS_OK;
04313   }
04314 
04315   // is this view even visible?
04316   if (view->GetVisibility() == nsViewVisibility_kHide) {
04317     return NS_OK; 
04318   }
04319 
04320   // nsViewManager::InsertChild ensures that descendants of floating views
04321   // are also marked floating.
04322   if (view->GetFloating()) {
04323     *aRectVisibility = nsRectVisibility_kVisible;
04324     return NS_OK;
04325   }
04326 
04327   // Calculate the absolute coordinates for the visible rectangle   
04328   nsRect visibleRect;
04329   if (GetVisibleRect(visibleRect) == NS_ERROR_FAILURE) {
04330     *aRectVisibility = nsRectVisibility_kVisible;
04331     return NS_OK;
04332   }
04333 
04334   // Calculate the absolute coordinates of the aRect passed in.
04335   // aRects values are relative to aView
04336   nsRect absRect;
04337   if ((GetAbsoluteRect(view, aRect, absRect)) == NS_ERROR_FAILURE) {
04338     *aRectVisibility = nsRectVisibility_kVisible;
04339     return NS_OK;
04340   }
04341  
04342   /*
04343    * If aMinTwips > 0, ensure at least aMinTwips of space around object is visible
04344    * The object is not visible if:
04345    * ((objectTop     < windowTop    && objectBottom < windowTop) ||
04346    *  (objectBottom  > windowBottom && objectTop    > windowBottom) ||
04347    *  (objectLeft    < windowLeft   && objectRight  < windowLeft) ||
04348    *  (objectRight   > windowRight  && objectLeft   > windowRight))
04349    */
04350 
04351   if (absRect.y < visibleRect.y  && 
04352       absRect.y + absRect.height < visibleRect.y + aMinTwips)
04353     *aRectVisibility = nsRectVisibility_kAboveViewport;
04354   else if (absRect.y + absRect.height > visibleRect.y + visibleRect.height &&
04355            absRect.y > visibleRect.y + visibleRect.height - aMinTwips)
04356     *aRectVisibility = nsRectVisibility_kBelowViewport;
04357   else if (absRect.x < visibleRect.x && 
04358            absRect.x + absRect.width < visibleRect.x + aMinTwips)
04359     *aRectVisibility = nsRectVisibility_kLeftOfViewport;
04360   else if (absRect.x + absRect.width > visibleRect.x  + visibleRect.width &&
04361            absRect.x > visibleRect.x + visibleRect.width - aMinTwips)
04362     *aRectVisibility = nsRectVisibility_kRightOfViewport;
04363   else
04364     *aRectVisibility = nsRectVisibility_kVisible;
04365 
04366   return NS_OK;
04367 }
04368 
04369 
04370 NS_IMETHODIMP
04371 nsViewManager::AllowDoubleBuffering(PRBool aDoubleBuffer)
04372 {
04373   mAllowDoubleBuffering = aDoubleBuffer;
04374   return NS_OK;
04375 }
04376 
04377 NS_IMETHODIMP
04378 nsViewManager::IsPainting(PRBool& aIsPainting)
04379 {
04380   aIsPainting = IsPainting();
04381   return NS_OK;
04382 }
04383 
04384 void
04385 nsViewManager::FlushPendingInvalidates()
04386 {
04387   NS_ASSERTION(IsRootVM(), "Must be root VM for this to be called!\n");
04388   NS_ASSERTION(mUpdateBatchCnt == 0, "Must not be in an update batch!");
04389   // XXXbz this is probably not quite OK yet, if callers can explicitly
04390   // DisableRefresh while we have an event posted.
04391   // NS_ASSERTION(mRefreshEnabled, "How did we get here?");
04392 
04393   // Let all the view observers of all viewmanagers in this tree know that
04394   // we're about to "paint" (this lets them get in their invalidates now so
04395   // we don't go through two invalidate-processing cycles).
04396   NS_ASSERTION(gViewManagers, "Better have a viewmanagers array!");
04397 
04398   // Make sure to not send WillPaint notifications while scrolling
04399   if (mScrollCnt == 0) {
04400     // Disable refresh while we notify our view observers, so that if they do
04401     // view update batches we don't reenter this code and so that we batch
04402     // all of them together.  We don't use
04403     // BeginUpdateViewBatch/EndUpdateViewBatch, since that would reenter this
04404     // exact code, but we want the effect of a single big update batch.
04405     PRBool refreshEnabled = mRefreshEnabled;
04406     mRefreshEnabled = PR_FALSE;
04407     ++mUpdateBatchCnt;
04408     
04409     PRInt32 index;
04410     for (index = 0; index < mVMCount; index++) {
04411       nsViewManager* vm = (nsViewManager*)gViewManagers->ElementAt(index);
04412       if (vm->RootViewManager() == this) {
04413         // One of our kids
04414         nsIViewObserver* observer = vm->GetViewObserver();
04415         if (observer) {
04416           observer->WillPaint();
04417           NS_ASSERTION(mUpdateBatchCnt == 1,
04418                        "Observer did not end view batch?");
04419         }
04420       }
04421     }
04422     
04423     --mUpdateBatchCnt;
04424     // Someone could have called EnableRefresh on us from inside WillPaint().
04425     // Only reset the old mRefreshEnabled value if the current value is false.
04426     if (!mRefreshEnabled) {
04427       mRefreshEnabled = refreshEnabled;
04428     }
04429   }
04430   
04431   if (mHasPendingUpdates) {
04432     ProcessPendingUpdates(mRootView, PR_TRUE);
04433     mHasPendingUpdates = PR_FALSE;
04434   }
04435 }
04436 
04437 void
04438 nsViewManager::ProcessInvalidateEvent()
04439 {
04440   NS_ASSERTION(IsRootVM(),
04441                "Incorrectly targeted invalidate event");
04442   // If we're in the middle of an update batch, just repost the event,
04443   // to be processed when the batch ends.
04444   PRBool processEvent = (mUpdateBatchCnt == 0);
04445   if (processEvent) {
04446     FlushPendingInvalidates();
04447   }
04448   mInvalidateEventQueue = nsnull;
04449   if (!processEvent) {
04450     // We didn't actually process this event... post a new one
04451     PostInvalidateEvent();
04452   }
04453 }
04454 
04455 NS_IMETHODIMP
04456 nsViewManager::SetDefaultBackgroundColor(nscolor aColor)
04457 {
04458   mDefaultBackgroundColor = aColor;
04459   return NS_OK;
04460 }
04461 
04462 NS_IMETHODIMP
04463 nsViewManager::GetDefaultBackgroundColor(nscolor* aColor)
04464 {
04465   *aColor = mDefaultBackgroundColor;
04466   return NS_OK;
04467 }
04468 
04469 
04470 NS_IMETHODIMP
04471 nsViewManager::GetLastUserEventTime(PRUint32& aTime)
04472 {
04473   aTime = gLastUserEventTime;
04474   return NS_OK;
04475 }
04476 
04477 struct nsSynthMouseMoveEvent : public nsViewManagerEvent {
04478   nsSynthMouseMoveEvent(nsViewManager *aViewManager, PRBool aFromScroll)
04479     : nsViewManagerEvent(aViewManager),
04480       mFromScroll(aFromScroll)
04481   {
04482   }
04483 
04484   virtual void HandleEvent() {
04485     ViewManager()->ProcessSynthMouseMoveEvent(mFromScroll);
04486   }
04487 
04488   PRBool mFromScroll;
04489 };
04490 
04491 NS_IMETHODIMP
04492 nsViewManager::SynthesizeMouseMove(PRBool aFromScroll)
04493 {
04494   if (mMouseLocation == nsPoint(NSCOORD_NONE, NSCOORD_NONE))
04495     return NS_OK;
04496 
04497   nsCOMPtr<nsIEventQueue> eventQueue;
04498   mEventQueueService->GetSpecialEventQueue(
04499     nsIEventQueueService::UI_THREAD_EVENT_QUEUE, getter_AddRefs(eventQueue));
04500   NS_ASSERTION(nsnull != eventQueue, "Event queue is null");
04501 
04502   if (eventQueue != mSynthMouseMoveEventQueue) {
04503     nsSynthMouseMoveEvent *ev = new nsSynthMouseMoveEvent(this, aFromScroll);
04504     eventQueue->PostEvent(ev);
04505     mSynthMouseMoveEventQueue = eventQueue;
04506   }
04507 
04508   return NS_OK;
04509 }
04510 
04523 static nsView* FindFloatingViewContaining(nsView* aView, nsPoint aPt)
04524 {
04525   for (nsView* v = aView->GetFirstChild(); v; v = v->GetNextSibling()) {
04526     nsView* r = FindFloatingViewContaining(v, aPt - v->GetOffsetTo(aView));
04527     if (r)
04528       return r;
04529   }
04530 
04531   if (aView->GetFloating() && aView->HasWidget() &&
04532       aView->GetDimensions().Contains(aPt) && IsViewVisible(aView))
04533     return aView;
04534 
04535   return nsnull;
04536 }
04537 
04538 void
04539 nsViewManager::ProcessSynthMouseMoveEvent(PRBool aFromScroll)
04540 {
04541   // allow new event to be posted while handling this one only if the
04542   // source of the event is a scroll (to prevent infinite reflow loops)
04543   if (aFromScroll)
04544     mSynthMouseMoveEventQueue = nsnull;
04545 
04546   if (mMouseLocation == nsPoint(NSCOORD_NONE, NSCOORD_NONE) || !mRootView) {
04547     mSynthMouseMoveEventQueue = nsnull;
04548     return;
04549   }
04550 
04551   // Hold a ref to ourselves so DispatchEvent won't destroy us (since
04552   // we need to access members after we call DispatchEvent).
04553   nsCOMPtr<nsIViewManager> kungFuDeathGrip(this);
04554   
04555 #ifdef DEBUG_MOUSE_LOCATION
04556   printf("[vm=%p]synthesizing mouse move to (%d,%d)\n",
04557          this, mMouseLocation.x, mMouseLocation.y);
04558 #endif
04559 
04560   nsPoint pt = mMouseLocation;
04561   pt.x = NSToCoordRound(mMouseLocation.x*mPixelsToTwips);
04562   pt.y = NSToCoordRound(mMouseLocation.y*mPixelsToTwips);
04563   // This could be a bit slow (traverses entire view hierarchy)
04564   // but it's OK to do it once per synthetic mouse event
04565   nsView* view = FindFloatingViewContaining(mRootView, pt);
04566   nsPoint offset(0, 0);
04567   if (!view) {
04568     view = mRootView;
04569   } else {
04570     offset = view->GetOffsetTo(mRootView);
04571     offset.x = NSToIntRound(offset.x*mTwipsToPixels);
04572     offset.y = NSToIntRound(offset.y*mTwipsToPixels);
04573   }
04574   nsMouseEvent event(PR_TRUE, NS_MOUSE_MOVE, view->GetWidget(),
04575                      nsMouseEvent::eSynthesized);
04576 
04577   event.point = mMouseLocation - offset;
04578   event.time = PR_IntervalNow();
04579   // XXX set event.isShift, event.isControl, event.isAlt, event.isMeta ?
04580 
04581   nsEventStatus status;
04582   view->GetViewManager()->DispatchEvent(&event, &status);
04583 
04584   if (!aFromScroll)
04585     mSynthMouseMoveEventQueue = nsnull;
04586 }
04587 
04588 void
04589 nsViewManager::InvalidateHierarchy()
04590 {
04591   if (mRootView) {
04592     if (!IsRootVM()) {
04593       NS_RELEASE(mRootViewManager);
04594     }
04595     nsView *parent = mRootView->GetParent();
04596     if (parent) {
04597       mRootViewManager = parent->GetViewManager()->RootViewManager();
04598       NS_ADDREF(mRootViewManager);
04599       NS_ASSERTION(mRootViewManager != this,
04600                    "Root view had a parent, but it has the same view manager");
04601     } else {
04602       mRootViewManager = this;
04603     }
04604   }
04605 }