Bug 820839 - Draw OSX titlebar content into a retained buffer during the ThebesLayer painting phase, before we clear invalidation state. r=roc
authorMatt Woodrow <mwoodrow@mozilla.com>
Thu, 17 Jan 2013 11:59:23 +1300
changeset 119080 29288a6d1876d5e7d4fb825aa8628a0d0ef95bac
parent 119079 5e3c7f176f2c549ad833bdcfa5d0096c05a4b58a
child 119081 be9dfaa088b293672ecbdd98e513ae6ddfb715d5
push id21449
push usermwoodrow@mozilla.com
push dateWed, 16 Jan 2013 23:01:06 +0000
treeherdermozilla-inbound@29288a6d1876 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs820839
milestone21.0a1
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 820839 - Draw OSX titlebar content into a retained buffer during the ThebesLayer painting phase, before we clear invalidation state. r=roc
layout/base/nsDisplayList.cpp
layout/base/nsDisplayList.h
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
layout/base/nsPresShell.cpp
view/public/nsView.h
view/src/nsViewManager.cpp
widget/cocoa/nsChildView.h
widget/cocoa/nsChildView.mm
widget/cocoa/nsCocoaWindow.mm
widget/nsIWidget.h
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -1104,17 +1104,17 @@ void nsDisplayList::PaintForFrame(nsDisp
                                      nullptr);
 
   nsDisplayItem::ContainerParameters containerParameters
     (presShell->GetXResolution(), presShell->GetYResolution());
   nsRefPtr<ContainerLayer> root = layerBuilder->
     BuildContainerLayerFor(aBuilder, layerManager, aForFrame, nullptr, *this,
                            containerParameters, nullptr);
 
-  if (widgetTransaction) {
+  if (widgetTransaction && !(aFlags & PAINT_NO_CLEAR_INVALIDATIONS)) {
     aForFrame->ClearInvalidationStateBits();
   }
 
   if (!root) {
     layerManager->SetUserData(&gLayerManagerLayerBuilder, oldBuilder);
     return;
   }
   // Root is being scaled up by the X/Y resolution. Scale it back down.
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -1385,17 +1385,18 @@ public:
    * This must only be called on the root display list of the display list
    * tree.
    */
   enum {
     PAINT_DEFAULT = 0,
     PAINT_USE_WIDGET_LAYERS = 0x01,
     PAINT_FLUSH_LAYERS = 0x02,
     PAINT_EXISTING_TRANSACTION = 0x04,
-    PAINT_NO_COMPOSITE = 0x08
+    PAINT_NO_COMPOSITE = 0x08,
+    PAINT_NO_CLEAR_INVALIDATIONS = 0x10
   };
   void PaintRoot(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx,
                  uint32_t aFlags) const;
   /**
    * Like PaintRoot, but used for internal display sublists.
    * aForFrame is the frame that the list is associated with.
    */
   void PaintForFrame(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx,
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -1968,16 +1968,19 @@ nsLayoutUtils::PaintFrame(nsRenderingCon
     }
   }
   if (aFlags & PAINT_EXISTING_TRANSACTION) {
     flags |= nsDisplayList::PAINT_EXISTING_TRANSACTION;
   }
   if (aFlags & PAINT_NO_COMPOSITE) {
     flags |= nsDisplayList::PAINT_NO_COMPOSITE;
   }
+  if (aFlags & PAINT_NO_CLEAR_INVALIDATIONS) {
+    flags |= nsDisplayList::PAINT_NO_CLEAR_INVALIDATIONS;
+  }
 
   list.PaintRoot(&builder, aRenderingContext, flags);
 
 #ifdef MOZ_DUMP_PAINTING
   if (gfxUtils::sDumpPaintList || gfxUtils::sDumpPainting) {
     if (gfxUtils::sDumpPaintingToFile) {
       fprintf(gfxUtils::sDumpPaintFile, "</script>");
     }
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -639,17 +639,18 @@ public:
     PAINT_SYNC_DECODE_IMAGES = 0x02,
     PAINT_WIDGET_LAYERS = 0x04,
     PAINT_IGNORE_SUPPRESSION = 0x08,
     PAINT_DOCUMENT_RELATIVE = 0x10,
     PAINT_HIDE_CARET = 0x20,
     PAINT_ALL_CONTINUATIONS = 0x40,
     PAINT_TO_WINDOW = 0x80,
     PAINT_EXISTING_TRANSACTION = 0x100,
-    PAINT_NO_COMPOSITE = 0x200
+    PAINT_NO_COMPOSITE = 0x200,
+    PAINT_NO_CLEAR_INVALIDATIONS = 0x400
   };
 
   /**
    * Given aFrame, the root frame of a stacking context, paint it and its
    * descendants to aRenderingContext.
    * @param aRenderingContext a rendering context translated so that (0,0)
    * is the origin of aFrame; for best results, (0,0) should transform
    * to pixel-aligned coordinates. This can be null, in which case
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -5380,16 +5380,19 @@ PresShell::Paint(nsView*        aViewToP
     frame->ClearPresShellsFromLastPaint();
   }
 
   nscolor bgcolor = ComputeBackstopColor(aViewToPaint);
   uint32_t flags = nsLayoutUtils::PAINT_WIDGET_LAYERS | nsLayoutUtils::PAINT_EXISTING_TRANSACTION;
   if (!(aFlags & PAINT_COMPOSITE)) {
     flags |= nsLayoutUtils::PAINT_NO_COMPOSITE;
   }
+  if (aViewToPaint->InAlternatePaint()) {
+    flags |= nsLayoutUtils::PAINT_NO_CLEAR_INVALIDATIONS;
+  }
 
   if (frame) {
     // Defer invalidates that are triggered during painting, and discard
     // invalidates of areas that are already being repainted.
     // The layer system can trigger invalidates during painting
     // (see FrameLayerBuilder).
     frame->BeginDeferringInvalidatesForDisplayRoot(aDirtyRegion);
 
--- a/view/public/nsView.h
+++ b/view/public/nsView.h
@@ -291,16 +291,23 @@ public:
   
   void SetForcedRepaint(bool aForceRepaint) { 
     if (!mInAlternatePaint) { 
       mForcedRepaint = aForceRepaint; 
     }
   }
 
   /**
+   * Returns true if the view is currently painting
+   * into an alternate destination, such as the titlebar
+   * area on OSX.
+   */
+  bool InAlternatePaint() { return mInAlternatePaint; }
+
+  /**
    * Make aWidget direct its events to this view.
    * The caller must call DetachWidgetEventHandler before this view
    * is destroyed.
    */
   void AttachWidgetEventHandler(nsIWidget* aWidget);
   /**
    * Stop aWidget directing its events to this view.
    */
--- a/view/src/nsViewManager.cpp
+++ b/view/src/nsViewManager.cpp
@@ -385,22 +385,23 @@ void nsViewManager::ProcessPendingUpdate
             mPresShell && mPresShell->IsVisible()) {
           vm->FlushDelayedResize(true);
           vm->InvalidateView(vm->mRootView);
         }
       }
 
       NS_ASSERTION(aView->HasWidget(), "Must have a widget!");
 
-      SetPainting(true);
 #ifdef DEBUG_INVALIDATIONS
       printf("---- PAINT START ----PresShell(%p), nsView(%p), nsIWidget(%p)\n", mPresShell, aView, widget);
 #endif
       nsAutoScriptBlocker scriptBlocker;
       NS_ASSERTION(aView->HasWidget(), "Must have a widget!");
+      aView->GetWidget()->WillPaint();
+      SetPainting(true);
       mPresShell->Paint(aView, nsRegion(),
                         nsIPresShell::PAINT_LAYERS |
                         nsIPresShell::PAINT_WILL_SEND_DID_PAINT);
 #ifdef DEBUG_INVALIDATIONS
       printf("---- PAINT END ----\n");
 #endif
       aView->SetForcedRepaint(false);
       SetPainting(false);
--- a/widget/cocoa/nsChildView.h
+++ b/widget/cocoa/nsChildView.h
@@ -16,16 +16,17 @@
 #include "nsAutoPtr.h"
 #include "nsISupports.h"
 #include "nsBaseWidget.h"
 #include "nsIPluginInstanceOwner.h"
 #include "nsIPluginWidget.h"
 #include "nsWeakPtr.h"
 #include "TextInputHandler.h"
 #include "nsCocoaUtils.h"
+#include "gfxQuartzSurface.h"
 
 #include "nsString.h"
 #include "nsIDragService.h"
 
 #include "npapi.h"
 
 #import <Carbon/Carbon.h>
 #import <Cocoa/Cocoa.h>
@@ -275,16 +276,18 @@ typedef NSInteger NSEventGestureAxis;
 - (void)delayedTearDown;
 
 - (void)sendFocusEvent:(uint32_t)eventType;
 
 - (void)handleMouseMoved:(NSEvent*)aEvent;
 
 - (void)drawRect:(NSRect)aRect inTitlebarContext:(CGContextRef)aContext;
 
+- (void)drawTitlebar:(NSRect)aRect inTitlebarContext:(CGContextRef)aContext;
+
 - (void)sendMouseEnterOrExitEvent:(NSEvent*)aEvent
                             enter:(BOOL)aEnter
                              type:(nsMouseEvent::exitType)aType;
 
 - (void)update;
 - (void)lockFocus;
 - (void) _surfaceNeedsUpdate:(NSNotification*)notification;
 
@@ -500,16 +503,18 @@ public:
   NSView<mozView>* GetEditorView();
 
   bool IsPluginView() { return (mWindowType == eWindowType_plugin); }
 
   nsCocoaWindow*    GetXULWindowWidget();
 
   NS_IMETHOD        ReparentNativeWidget(nsIWidget* aNewParent);
 
+  virtual void      WillPaint() MOZ_OVERRIDE;
+
   mozilla::widget::TextInputHandler* GetTextInputHandler()
   {
     return mTextInputHandler;
   }
 
   // unit conversion convenience functions
   nsIntPoint        CocoaPointsToDevPixels(const NSPoint& aPt) {
     return nsCocoaUtils::CocoaPointsToDevPixels(aPt, BackingScaleFactor());
@@ -519,16 +524,18 @@ public:
   }
   CGFloat           DevPixelsToCocoaPoints(int32_t aPixels) {
     return nsCocoaUtils::DevPixelsToCocoaPoints(aPixels, BackingScaleFactor());
   }
   NSRect            DevPixelsToCocoaPoints(const nsIntRect& aRect) {
     return nsCocoaUtils::DevPixelsToCocoaPoints(aRect, BackingScaleFactor());
   }
 
+  void CompositeTitlebar(const gfxSize& aSize, CGContextRef aContext);
+
 protected:
 
   void              ReportMoveEvent();
   void              ReportSizeEvent();
 
   // override to create different kinds of child views. Autoreleases, so
   // caller must retain.
   virtual NSView*   CreateCocoaView(NSRect inFrame);
@@ -555,16 +562,19 @@ protected:
   // weak ref to this childview's associated mozAccessible for speed reasons 
   // (we get queried for it *a lot* but don't want to own it)
   nsWeakPtr             mAccessible;
 #endif
 
   nsRefPtr<gfxASurface> mTempThebesSurface;
   nsRefPtr<mozilla::gl::TextureImage> mResizerImage;
 
+  nsRefPtr<gfxQuartzSurface> mTitlebarSurf;
+  gfxSize mTitlebarSize;
+
   // Cached value of [mView backingScaleFactor], to avoid sending two obj-c
   // messages (respondsToSelector, backingScaleFactor) every time we need to
   // use it.
   // ** We'll need to reinitialize this if the backing resolution changes. **
   CGFloat               mBackingScaleFactor;
 
   bool                  mVisible;
   bool                  mDrawing;
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -648,16 +648,55 @@ nsChildView::ReparentNativeWidget(nsIWid
   [mView removeFromSuperview];
   mParentView = newParentView;
   [mParentView addSubview:mView];
   return NS_OK;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
 
+void
+nsChildView::WillPaint()
+{
+  if (!mView || ![mView isKindOfClass:[ChildView class]])
+    return;
+  NSWindow* win = [mView window];
+  if (!win || ![win isKindOfClass:[ToolbarWindow class]])
+    return;
+  if (![(ToolbarWindow*)win drawsContentsIntoWindowFrame])
+    return;
+
+  NSRect titlebarRect = [(ToolbarWindow*)win titlebarRect];
+  gfxSize titlebarSize(titlebarRect.size.width, titlebarRect.size.height);
+  if (!mTitlebarSurf || mTitlebarSize != titlebarSize) {
+    mTitlebarSize = titlebarSize;
+    mTitlebarSurf = new gfxQuartzSurface(titlebarSize, gfxASurface::ImageFormatARGB32);
+  }
+  NSRect flippedTitlebarRect = { NSZeroPoint, titlebarRect.size };
+  CGContextRef context = mTitlebarSurf->GetCGContext();
+  [(ChildView*)mView drawRect:flippedTitlebarRect inTitlebarContext:context];
+}
+
+void
+nsChildView::CompositeTitlebar(const gfxSize& aSize, CGContextRef aContext)
+{
+  NS_ASSERTION(mTitlebarSurf, "Must have titlebar surface");
+  if (!mTitlebarSurf) {
+    return;
+  }
+
+  CGImageRef image = CGBitmapContextCreateImage(mTitlebarSurf->GetCGContext());
+
+  CGContextDrawImage(aContext, 
+                     CGRectMake(0, 0, mTitlebarSize.width, mTitlebarSize.height), 
+                     image);
+
+  CGImageRelease(image);
+}
+
 void nsChildView::ResetParent()
 {
   if (!mOnDestroyCalled) {
     if (mParentWidget)
       mParentWidget->RemoveChild(this);
     if (mView)
       [mView removeFromSuperview];
   }
@@ -2413,16 +2452,24 @@ NSEvent* gLastDragMouseDownEvent = nil;
   if (mGeckoChild) {
     // actually, it could be the color space that's changed,
     // but we can't tell the difference here except by retrieving
     // the backing scale factor and comparing to the old value
     mGeckoChild->BackingScaleFactorChanged();
   }
 }
 
+- (void)drawTitlebar:(NSRect)aRect inTitlebarContext:(CGContextRef)aContext
+{
+  if (mGeckoChild) {
+    gfxSize size(aRect.size.width, aRect.size.height);
+    mGeckoChild->CompositeTitlebar(size, aContext);
+  }
+}
+
 // The display system has told us that a portion of our view is dirty. Tell
 // gecko to paint it
 - (void)drawRect:(NSRect)aRect
 {
   CGContextRef cgContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
   [self drawRect:aRect inContext:cgContext alternate:false];
 
   // If we're a transparent window and our contents have changed, we need
--- a/widget/cocoa/nsCocoaWindow.mm
+++ b/widget/cocoa/nsCocoaWindow.mm
@@ -2965,18 +2965,17 @@ TitlebarDrawCallback(void* aInfo, CGCont
 
     // Gecko drawing assumes flippedness, but the current context isn't flipped
     // (because we're painting into the window's border view, which is not a
     // ChildView, so it isn't flipped).
     // So we need to set a flip transform.
     CGContextScaleCTM(aContext, 1.0f, -1.0f);
     CGContextTranslateCTM(aContext, 0.0f, -[window frame].size.height);
 
-    NSRect flippedTitlebarRect = { NSZeroPoint, titlebarRect.size };
-    [(ChildView*)view drawRect:flippedTitlebarRect inTitlebarContext:aContext];
+    [(ChildView*)view drawTitlebar:[window frame] inTitlebarContext:aContext];
   } else {
     BOOL isMain = [window isMainWindow];
     NSColor *titlebarColor = [window titlebarColorForActiveWindow:isMain];
     if (!titlebarColor) {
       // If the titlebar color is nil, draw the default titlebar shading.
       DrawNativeTitlebar(aContext, NSRectToCGRect(titlebarRect),
                          [window unifiedToolbarHeight], isMain);
     } else {
--- a/widget/nsIWidget.h
+++ b/widget/nsIWidget.h
@@ -87,18 +87,18 @@ typedef nsEventStatus (* EVENT_CALLBACK)
 #ifdef XP_WIN
 #define NS_NATIVE_TSF_THREAD_MGR       100
 #define NS_NATIVE_TSF_CATEGORY_MGR     101
 #define NS_NATIVE_TSF_DISPLAY_ATTR_MGR 102
 #define NS_NATIVE_ICOREWINDOW          103 // winrt specific
 #endif
 
 #define NS_IWIDGET_IID \
-  { 0x476D5716, 0xE225, 0x4497, \
-    { 0x80, 0x41, 0x92, 0xF8, 0x67, 0x59, 0xC4, 0x38 } }
+  { 0xDAEE334D, 0x9031, 0x4928, \
+    { 0x9D, 0xD7, 0x75, 0x2E, 0x8B, 0x6B, 0xF7, 0x0E } }
 
 /*
  * Window shadow styles
  * Also used for the -moz-window-shadow CSS property
  */
 
 #define NS_STYLE_WINDOW_SHADOW_NONE             0
 #define NS_STYLE_WINDOW_SHADOW_DEFAULT          1
@@ -1638,16 +1638,24 @@ class nsIWidget : public nsISupports {
      * that we want to be visible under the page, so layout should not force
      * a default background.
      */
     virtual bool WidgetPaintsBackground() { return false; }
 
     virtual bool NeedsPaint() {
       return true;
     }
+
+    /**
+     * This function is called by nsViewManager right before the retained layer 
+     * tree for this widget is about to be updated, and any required
+     * ThebesLayer painting occurs.
+     */
+    virtual void WillPaint() { }
+
     /**
      * Get the natural bounds of this widget.  This method is only
      * meaningful for widgets for which Gecko implements screen
      * rotation natively.  When this is the case, GetBounds() returns
      * the widget bounds taking rotation into account, and
      * GetNaturalBounds() returns the bounds *not* taking rotation
      * into account.
      *