Bug 513162 - Widget additions for recycling top level widgets as content containers. r=dbaron.
authorJim Mathies <jmathies@mozilla.com>
Thu, 24 Jun 2010 21:01:06 -0500
changeset 46193 5b1c95b84304f22428b982a19bce997cb738aca6
parent 46192 30ecff316b34836ee80542f3fc732a10d185e497
child 46194 a053227f5f96bc54426522ceb3e46f6e807e8a25
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdbaron
bugs513162
milestone1.9.3a6pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 513162 - Widget additions for recycling top level widgets as content containers. r=dbaron.
view/public/nsIView.h
view/src/nsView.cpp
view/src/nsViewManager.cpp
view/src/nsViewManager.h
--- a/view/public/nsIView.h
+++ b/view/public/nsIView.h
@@ -58,18 +58,18 @@ class nsIWidget;
 //        the layer's parent.
 enum nsViewVisibility {
   nsViewVisibility_kHide = 0,
   nsViewVisibility_kShow = 1
 };
 
 // IID for the nsIView interface
 #define NS_IVIEW_IID    \
-  { 0xe981334b, 0x756e, 0x417a, \
-    { 0xbf, 0x18, 0x47, 0x4a, 0x2d, 0xfe, 0xc3, 0x87 } }
+  { 0xdb512cfa, 0xe00c, 0x4eff, \
+    { 0xa2, 0x9c, 0x18, 0x74, 0x96, 0x63, 0x17, 0x69 } }
 
 // Public view flags are defined in this file
 #define NS_VIEW_FLAGS_PUBLIC              0x00FF
 // Private view flags are private to the view module,
 // and are defined in nsView.h
 #define NS_VIEW_FLAGS_PRIVATE             0xFF00
 
 // Public view flags
@@ -287,16 +287,36 @@ public:
                         nsWidgetInitData *aWidgetInitData = nsnull,
                         nsNativeWidget aNative = nsnull,
                         PRBool aEnableDragDrop = PR_TRUE,
                         PRBool aResetVisibility = PR_TRUE,
                         nsContentType aWindowType = eContentTypeInherit,
                         nsIWidget* aParentWidget = nsnull);
 
   /**
+   * Attach/detach a top level widget from this view. When attached, the view
+   * updates the widget's device context and allows the view to begin receiving
+   * gecko events. The underlying base window associated with the widget will
+   * continues to receive events it expects.
+   *
+   * An attached widget will not be destroyed when the view is destroyed,
+   * allowing the recycling of a single top level widget over multiple views.
+   *
+   * @param aWidget The widget to attach to / detach from.
+   */
+  nsresult AttachToTopLevelWidget(nsIWidget* aWidget);
+  nsresult DetachFromTopLevelWidget();
+
+  /**
+   * Returns a flag indicating whether the view owns it's widget
+   * or is attached to an existing top level widget.
+   */
+  PRBool IsAttachedToTopLevel() const { return mWidgetIsTopLevel; }
+
+  /**
    * In 4.0, the "cutout" nature of a view is queryable.
    * If we believe that all cutout view have a native widget, this
    * could be a replacement.
    * @param aWidget out parameter for widget that this view contains,
    *        or nsnull if there is none.
    */
   nsIWidget* GetWidget() const { return mWindow; }
 
@@ -368,16 +388,17 @@ protected:
   PRInt32           mZIndex;
   nsViewVisibility  mVis;
   nscoord           mPosX, mPosY;
   nsRect            mDimBounds; // relative to parent
   nsPoint           mViewToWidgetOffset;
   float             mOpacity;
   PRUint32          mVFlags;
   nsWeakView*       mDeletionObserver;
+  PRBool            mWidgetIsTopLevel;
 
   virtual ~nsIView() {}
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIView, NS_IVIEW_IID)
 
 // nsWeakViews must *not* be used in heap!
 class nsWeakView
--- a/view/src/nsView.cpp
+++ b/view/src/nsView.cpp
@@ -139,25 +139,57 @@ static ViewWrapper* GetWrapperFor(nsIWid
       if (wrapper)
         wrapper->Release();
       return wrapper;
     }
   }
   return nsnull;
 }
 
-//
 // Main events handler
-//
-nsEventStatus HandleEvent(nsGUIEvent *aEvent)
+static nsEventStatus HandleEvent(nsGUIEvent *aEvent)
+{
+#if 0
+  printf(" %d %d %d (%d,%d) \n", aEvent->widget, aEvent->widgetSupports, 
+         aEvent->message, aEvent->point.x, aEvent->point.y);
+#endif
+  nsEventStatus result = nsEventStatus_eIgnore;
+  nsView *view = nsView::GetViewFor(aEvent->widget);
+
+  if (view)
+  {
+    nsCOMPtr<nsIViewManager> vm = view->GetViewManager();
+    vm->DispatchEvent(aEvent, view, &result);
+  }
+
+  return result;
+}
+
+// Attached widget event helpers
+static ViewWrapper* GetAttachedWrapperFor(nsIWidget* aWidget)
+{
+  NS_PRECONDITION(nsnull != aWidget, "null widget ptr");
+  return aWidget->GetAttachedViewPtr();
+}
+
+static nsView* GetAttachedViewFor(nsIWidget* aWidget)
+{           
+  NS_PRECONDITION(nsnull != aWidget, "null widget ptr");
+
+  ViewWrapper* wrapper = GetAttachedWrapperFor(aWidget);
+  if (!wrapper)
+    return nsnull;
+  return wrapper->GetView();
+}
+
+// event handler
+static nsEventStatus AttachedHandleEvent(nsGUIEvent *aEvent)
 { 
-//printf(" %d %d %d (%d,%d) \n", aEvent->widget, aEvent->widgetSupports, 
-//       aEvent->message, aEvent->point.x, aEvent->point.y);
   nsEventStatus result = nsEventStatus_eIgnore;
-  nsView       *view = nsView::GetViewFor(aEvent->widget);
+  nsView *view = GetAttachedViewFor(aEvent->widget);
 
   if (view)
   {
     nsCOMPtr<nsIViewManager> vm = view->GetViewManager();
     vm->DispatchEvent(aEvent, view, &result);
   }
 
   return result;
@@ -171,16 +203,17 @@ nsView::nsView(nsViewManager* aViewManag
   // Views should be transparent by default. Not being transparent is
   // a promise that the view will paint all its pixels opaquely. Views
   // should make this promise explicitly by calling
   // SetViewContentTransparency.
   mVFlags = 0;
   mViewManager = aViewManager;
   mDirtyRegion = nsnull;
   mDeletionObserver = nsnull;
+  mWidgetIsTopLevel = PR_FALSE;
 }
 
 void nsView::DropMouseGrabbing()
 {
   nsCOMPtr<nsIViewObserver> viewObserver = mViewManager->GetViewObserver();
   if (viewObserver) {
     viewObserver->ClearMouseCapture(this);
   }
@@ -235,18 +268,31 @@ nsView::~nsView()
 
   // Destroy and release the widget
   if (mWindow)
   {
     // Release memory for the view wrapper
     ViewWrapper* wrapper = GetWrapperFor(mWindow);
     NS_IF_RELEASE(wrapper);
 
-    mWindow->SetClientData(nsnull);
-    mWindow->Destroy();
+    // If we are not attached to a base window, we're going to tear down our
+    // widget here. However, if we're attached to somebody elses widget, we
+    // want to leave the widget alone: don't reset the client data or call
+    // Destroy. Just clear our event view ptr and free our reference to it. 
+    if (mWidgetIsTopLevel) {
+      ViewWrapper* wrapper = GetAttachedWrapperFor(mWindow);
+      NS_IF_RELEASE(wrapper);
+
+      mWindow->SetAttachedViewPtr(nsnull);
+    }
+    else {
+      mWindow->SetClientData(nsnull);
+      mWindow->Destroy();
+    }
+
     NS_RELEASE(mWindow);
   }
   delete mDirtyRegion;
 
   if (mDeletionObserver) {
     mDeletionObserver->Clear();
   }
 }
@@ -270,18 +316,23 @@ nsresult nsView::QueryInterface(const ns
   return NS_NOINTERFACE;
 }
 
 nsIView* nsIView::GetViewFor(nsIWidget* aWidget)
 {           
   NS_PRECONDITION(nsnull != aWidget, "null widget ptr");
 
   ViewWrapper* wrapper = GetWrapperFor(aWidget);
+
+  if (!wrapper)
+    wrapper = GetAttachedWrapperFor(aWidget);
+
   if (wrapper)
-    return wrapper->GetView();  
+    return wrapper->GetView();
+
   return nsnull;
 }
 
 void nsIView::Destroy()
 {
   delete this;
 }
 
@@ -349,17 +400,17 @@ nsIntRect nsIView::CalcWidgetBounds(nsWi
   if (GetParent()) {
     nsPoint offset;
     nsIWidget* parentWidget = GetParent()->GetNearestWidget(&offset);
     // make viewBounds be relative to the parent widget, in appunits
     viewBounds += offset;
 
     if (parentWidget && aType == eWindowType_popup &&
         IsEffectivelyVisible()) {
-      // put offset into screen coordinates
+      // put offset into screen coordinates. (based on client area origin)
       nsIntPoint screenPoint = parentWidget->WidgetToScreenOffset();
       viewBounds += nsPoint(NSIntPixelsToAppUnits(screenPoint.x, p2a),
                             NSIntPixelsToAppUnits(screenPoint.y, p2a));
     }
   }
 
   // Compute widget bounds in device pixels
   nsIntRect newBounds = viewBounds.ToNearestPixels(p2a);
@@ -385,16 +436,17 @@ void nsView::DoResetWidgetBounds(PRBool 
   // The geometry of a root view's widget is controlled externally,
   // NOT by sizing or positioning the view
   if (mViewManager->GetRootView() == this) {
     return;
   }
   
   nsIntRect curBounds;
   mWindow->GetBounds(curBounds);
+
   nsWindowType type;
   mWindow->GetWindowType(type);
 
   if (curBounds.IsEmpty() && mDimBounds.IsEmpty() && type == eWindowType_popup) {
     // Don't manipulate empty popup widgets. For example there's no point
     // moving hidden comboboxes around, or doing X server roundtrips
     // to compute their true screen position. This could mean that WidgetToScreen
     // operations on these widgets don't return up-to-date values, but popup
@@ -404,16 +456,17 @@ void nsView::DoResetWidgetBounds(PRBool 
 
   NS_PRECONDITION(mWindow, "Why was this called??");
 
   nsIntRect newBounds = CalcWidgetBounds(type);
 
   PRBool changedPos = curBounds.TopLeft() != newBounds.TopLeft();
   PRBool changedSize = curBounds.Size() != newBounds.Size();
 
+  // Child views are never attached to top level widgets, this is safe.
   if (changedPos) {
     if (changedSize && !aMoveOnly) {
       mWindow->Resize(newBounds.x, newBounds.y, newBounds.width, newBounds.height,
                       aInvalidateChangedSize);
     } else {
       mWindow->Move(newBounds.x, newBounds.y);
     }
   } else {
@@ -699,16 +752,72 @@ nsresult nsIView::CreateWidget(const nsI
 
   if (aResetVisibility) {
     v->SetVisibility(GetVisibility());
   }
 
   return NS_OK;
 }
 
+// Attach to a top level widget and start receiving mirrored events.
+nsresult nsIView::AttachToTopLevelWidget(nsIWidget* aWidget)
+{
+  NS_PRECONDITION(nsnull != aWidget, "null widget ptr");
+  /// XXXjimm This is a temporary workaround to an issue w/document
+  // viewer (bug 513162).
+  nsIView *oldView = GetAttachedViewFor(aWidget);
+  if (oldView) {
+    oldView->DetachFromTopLevelWidget();
+  }
+
+  nsCOMPtr<nsIDeviceContext> dx;
+  mViewManager->GetDeviceContext(*getter_AddRefs(dx));
+
+  // Note, the previous device context will be released. Detaching
+  // will not restore the old one.
+  nsresult rv = aWidget->AttachViewToTopLevel(::AttachedHandleEvent, dx);
+  if (NS_FAILED(rv))
+    return rv;
+
+  mWindow = aWidget;
+  NS_ADDREF(mWindow);
+
+  nsView* v = static_cast<nsView*>(this);
+  ViewWrapper* wrapper = new ViewWrapper(v);
+  NS_ADDREF(wrapper);
+  mWindow->SetAttachedViewPtr(wrapper);
+  mWindow->EnableDragDrop(PR_TRUE);
+  mWidgetIsTopLevel = PR_TRUE;
+
+  // Refresh the view bounds
+  nsWindowType type;
+  mWindow->GetWindowType(type);
+  CalcWidgetBounds(type);
+
+  return NS_OK;
+}
+
+// Detach this view from an attached widget. 
+nsresult nsIView::DetachFromTopLevelWidget()
+{
+  NS_PRECONDITION(mWidgetIsTopLevel, "Not attached currently!");
+  NS_PRECONDITION(mWindow, "null mWindow for DetachFromTopLevelWidget!");
+
+  // Release memory for the view wrapper
+  ViewWrapper* wrapper = GetAttachedWrapperFor(mWindow);
+  NS_IF_RELEASE(wrapper);
+
+  mWindow->SetAttachedViewPtr(nsnull);
+  NS_RELEASE(mWindow);
+
+  mWidgetIsTopLevel = PR_FALSE;
+  
+  return NS_OK;
+}
+
 void nsView::SetZIndex(PRBool aAuto, PRInt32 aZIndex, PRBool aTopMost)
 {
   PRBool oldIsAuto = GetZIndexIsAuto();
   mVFlags = (mVFlags & ~NS_VIEW_FLAG_AUTO_ZINDEX) | (aAuto ? NS_VIEW_FLAG_AUTO_ZINDEX : 0);
   mZIndex = aZIndex;
   SetTopMost(aTopMost);
   
   if (HasWidget() || !oldIsAuto || !aAuto) {
@@ -829,40 +938,45 @@ nsIntPoint nsIView::GetScreenPosition() 
   nsPoint toWidgetOffset(0,0);
   nsIWidget* widget = GetNearestWidget(&toWidgetOffset);
   if (widget) {
     nsCOMPtr<nsIDeviceContext> dx;
     mViewManager->GetDeviceContext(*getter_AddRefs(dx));
     PRInt32 p2a = dx->AppUnitsPerDevPixel();
     nsIntPoint ourPoint(NSAppUnitsToIntPixels(toWidgetOffset.x, p2a),
                         NSAppUnitsToIntPixels(toWidgetOffset.y, p2a));
+    // WidgetToScreenOffset is at the origin of the client area of
+    // the widget.
     screenPoint = ourPoint + widget->WidgetToScreenOffset();
   }
   
   return screenPoint;
 }
 
 nsIWidget* nsIView::GetNearestWidget(nsPoint* aOffset) const
 {
+  // aOffset is based on the view's position, which ignores any chrome on
+  // attached parent widgets.
+
   nsPoint pt(0, 0);
   const nsView* v;
   for (v = static_cast<const nsView*>(this);
        v && !v->HasWidget(); v = v->GetParent()) {
     pt += v->GetPosition();
   }
   if (!v) {
     if (aOffset) {
       *aOffset = pt;
     }
     return nsnull;
   }
 
-  // pt is now the offset from v's origin to this's origin
-  // The widget's origin is the top left corner of v's bounds, which may
-  // not coincide with v's origin
+  // pt is now the offset from v's origin to this view's origin. The widget's
+  // origin is the top left corner of v's bounds, which may not coincide with
+  // the view's origin.
   if (aOffset) {
     nsRect vBounds = v->GetBounds();
     *aOffset = pt + v->GetPosition() -  nsPoint(vBounds.x, vBounds.y) +
                v->ViewToWidgetOffset();
   }
   return v->GetWidget();
 }
 
--- a/view/src/nsViewManager.cpp
+++ b/view/src/nsViewManager.cpp
@@ -299,16 +299,30 @@ NS_IMETHODIMP nsViewManager::GetWindowDi
   else
     {
       *aWidth = 0;
       *aHeight = 0;
     }
   return NS_OK;
 }
 
+void nsViewManager::DoSetWindowDimensions(nscoord aWidth, nscoord aHeight)
+{
+  nsRect oldDim;
+  nsRect newDim(0, 0, aWidth, aHeight);
+  mRootView->GetDimensions(oldDim);
+  // We care about resizes even when one dimension is already zero.
+  if (!oldDim.IsExactEqual(newDim)) {
+    // Don't resize the widget. It is already being set elsewhere.
+    mRootView->SetDimensions(newDim, PR_TRUE, PR_FALSE);
+    if (mObserver)
+      mObserver->ResizeReflow(mRootView, aWidth, aHeight);
+  }
+}
+
 NS_IMETHODIMP nsViewManager::SetWindowDimensions(nscoord aWidth, nscoord aHeight)
 {
   if (mRootView) {
     if (IsViewVisible(mRootView)) {
       mDelayedResize.SizeTo(NSCOORD_NONE, NSCOORD_NONE);
       DoSetWindowDimensions(aWidth, aHeight);
     } else {
       mDelayedResize.SizeTo(aWidth, aHeight);
@@ -573,16 +587,23 @@ IsWidgetDrawnByPlugin(nsIWidget* aWidget
  * @param aIgnoreWidgetView if non-null, the aIgnoreWidgetView's widget and its
  * children are not updated.
  */
 void
 nsViewManager::UpdateWidgetArea(nsView *aWidgetView, nsIWidget* aWidget,
                                 const nsRegion &aDamagedRegion,
                                 nsView* aIgnoreWidgetView)
 {
+#if 0
+  nsRect dbgBounds = aDamagedRegion.GetBounds();
+  printf("UpdateWidgetArea view:%X (%d) widget:%X region: %d, %d, %d, %d\n",
+    aWidgetView, aWidgetView->IsAttachedToTopLevel(),
+    aWidget, dbgBounds.x, dbgBounds.y, dbgBounds.width, dbgBounds.height);
+#endif
+
   if (!IsRefreshEnabled()) {
     // accumulate this rectangle in the view's dirty region, so we can
     // process it later.
     nsRegion* dirtyRegion = aWidgetView->GetDirtyRegion();
     if (!dirtyRegion) return;
 
     dirtyRegion->Or(*dirtyRegion, aDamagedRegion);
     // Don't let dirtyRegion grow beyond 8 rects
@@ -634,24 +655,29 @@ nsViewManager::UpdateWidgetArea(nsView *
       nsView* view = nsView::GetViewFor(childWidget);
       NS_ASSERTION(view != aWidgetView, "will recur infinitely");
       PRBool visible;
       childWidget->IsVisible(visible);
       if (view && visible && !IsWidgetDrawnByPlugin(childWidget, view)) {
         // Don't mess with views that are in completely different view
         // manager trees
         if (view->GetViewManager()->RootViewManager() == RootViewManager()) {
-          // get the damage region into 'view's coordinate system
+          // get the damage region into view's coordinate system
           nsRegion damage = intersection;
+
           nsPoint offset = view->GetOffsetTo(aWidgetView);
           damage.MoveBy(-offset);
+
+          // Update the child and it's children
           UpdateWidgetArea(view, childWidget, damage, aIgnoreWidgetView);
 
+          // GetBounds should compensate for chrome on a toplevel widget
           nsIntRect bounds;
           childWidget->GetBounds(bounds);
+
           nsTArray<nsIntRect> clipRects;
           childWidget->GetWindowClipRegion(&clipRects);
           for (PRUint32 i = 0; i < clipRects.Length(); ++i) {
             nsRect rr = (clipRects[i] + bounds.TopLeft()).
               ToAppUnits(AppUnitsPerDevPixel());
             children.Or(children, rr - aWidgetView->ViewToWidgetOffset()); 
             children.SimplifyInward(20);
           }
@@ -736,20 +762,19 @@ NS_IMETHODIMP nsViewManager::DispatchEve
   *aStatus = nsEventStatus_eIgnore;
 
   switch(aEvent->message)
     {
     case NS_SIZE:
       {
         if (aView)
           {
+            // client area dimensions are set on the view
             nscoord width = ((nsSizeEvent*)aEvent)->windowSize->width;
             nscoord height = ((nsSizeEvent*)aEvent)->windowSize->height;
-            width = ((nsSizeEvent*)aEvent)->mWinWidth;
-            height = ((nsSizeEvent*)aEvent)->mWinHeight;
 
             // The root view may not be set if this is the resize associated with
             // window creation
 
             if (aView == mRootView)
               {
                 PRInt32 p2a = AppUnitsPerDevPixel();
                 SetWindowDimensions(NSIntPixelsToAppUnits(width, p2a),
@@ -1006,19 +1031,20 @@ NS_IMETHODIMP nsViewManager::DispatchEve
       }
     }
 
   return NS_OK;
 }
 
 nsEventStatus nsViewManager::HandleEvent(nsView* aView, nsGUIEvent* aEvent)
 {
-//printf(" %d %d %d %d (%d,%d) \n", this, event->widget, event->widgetSupports, 
-//       event->message, event->point.x, event->point.y);
-
+#if 0
+  printf(" %d %d %d %d (%d,%d) \n", this, event->widget, event->widgetSupports, 
+         event->message, event->point.x, event->point.y);
+#endif
   // Hold a refcount to the observer. The continued existence of the observer will
   // delay deletion of this view hierarchy should the event want to cause its
   // destruction in, say, some JavaScript event handler.
   nsCOMPtr<nsIViewObserver> obs = aView->GetViewManager()->GetViewObserver();
   nsEventStatus status = nsEventStatus_eIgnore;
   if (obs) {
      obs->HandleEvent(aView, aEvent, &status);
   }
--- a/view/src/nsViewManager.h
+++ b/view/src/nsViewManager.h
@@ -204,29 +204,17 @@ private:
 
   /**
    * Transforms a rectangle from aView's coordinate system to the coordinate
    * system of the widget attached to aWidgetView, which should be an ancestor
    * of aView.
    */
   nsIntRect ViewToWidget(nsView *aView, nsView* aWidgetView, const nsRect &aRect) const;
 
-  void DoSetWindowDimensions(nscoord aWidth, nscoord aHeight)
-  {
-    nsRect oldDim;
-    nsRect newDim(0, 0, aWidth, aHeight);
-    mRootView->GetDimensions(oldDim);
-    // We care about resizes even when one dimension is already zero.
-    if (!oldDim.IsExactEqual(newDim)) {
-      // Don't resize the widget. It is already being set elsewhere.
-      mRootView->SetDimensions(newDim, PR_TRUE, PR_FALSE);
-      if (mObserver)
-        mObserver->ResizeReflow(mRootView, aWidth, aHeight);
-    }
-  }
+  void DoSetWindowDimensions(nscoord aWidth, nscoord aHeight);
 
   // Safety helpers
   void IncrementUpdateCount() {
     NS_ASSERTION(IsRootVM(),
                  "IncrementUpdateCount called on non-root viewmanager");
     ++mUpdateCnt;
   }