Bug 439354 - OS X toolbar background doesn't have a good gradient. Part 1 (widget). r=hwaara, sr=roc.
authorMarkus Stange <mstange@themasta.com>
Wed, 27 Aug 2008 17:45:24 +0200
changeset 18453 b540013031980d62de62ecb85581ca859c4ee52a
parent 18452 41d9d32ab5a703ddb8a45b786e0e593d4124871b
child 18454 a2709097171f20b76a371006b6807bd8e988f031
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewershwaara, roc
bugs439354
milestone1.9.1a2pre
Bug 439354 - OS X toolbar background doesn't have a good gradient. Part 1 (widget). r=hwaara, sr=roc.
content/base/src/nsContentUtils.cpp
gfx/public/nsThemeConstants.h
layout/style/nsCSSKeywordList.h
layout/style/nsCSSProps.cpp
widget/public/nsIWidget.h
widget/src/cocoa/nsChildView.h
widget/src/cocoa/nsChildView.mm
widget/src/cocoa/nsCocoaWindow.h
widget/src/cocoa/nsCocoaWindow.mm
widget/src/cocoa/nsNativeThemeCocoa.h
widget/src/cocoa/nsNativeThemeCocoa.mm
widget/src/xpwidgets/nsBaseWidget.cpp
widget/src/xpwidgets/nsBaseWidget.h
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -3869,26 +3869,20 @@ nsContentUtils::TriggerLink(nsIContent *
     handler->OnLinkClick(aContent, aLinkURI, aTargetSpec.get());
   }
 }
 
 /* static */
 nsIWidget*
 nsContentUtils::GetTopLevelWidget(nsIWidget* aWidget)
 {
-  if (!aWidget) {
+  if (!aWidget)
     return nsnull;
-  }
-
-  nsIWidget* currWidget = aWidget;
-  nsIWidget* parentWidget;
-  while ((parentWidget = currWidget->GetParent()) != nsnull) {
-    currWidget = parentWidget;
-  }
-  return currWidget;
+
+  return aWidget->GetTopLevelWidget();
 }
 
 /* static */
 const nsDependentString
 nsContentUtils::GetLocalizedEllipsis()
 {
   static PRUnichar sBuf[4] = { 0, 0, 0, 0 };
   if (!sBuf[0]) {
--- a/gfx/public/nsThemeConstants.h
+++ b/gfx/public/nsThemeConstants.h
@@ -230,10 +230,13 @@
 // For text on non-iconic menuitems only
 #define NS_THEME_MENUITEMTEXT                              220
 
 // Vista Rebars
 #define NS_THEME_WIN_COMMUNICATIONS_TOOLBOX                221
 #define NS_THEME_WIN_MEDIA_TOOLBOX                         222
 #define NS_THEME_WIN_BROWSER_TAB_BAR_TOOLBOX               223
 
+// Unified toolbar on the Mac
+#define NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR                   224
+
 // Vista glass
 #define NS_THEME_WIN_GLASS                                 230
--- a/layout/style/nsCSSKeywordList.h
+++ b/layout/style/nsCSSKeywordList.h
@@ -564,16 +564,17 @@ CSS_KEY(checkbox-label, checkboxlabel)
 CSS_KEY(radio-label, radiolabel)
 CSS_KEY(button-focus, buttonfocus)
 CSS_KEY(-moz-win-media-toolbox, _moz_win_media_toolbox)
 CSS_KEY(-moz-win-communications-toolbox, _moz_win_communications_toolbox)
 CSS_KEY(-moz-win-browsertabbar-toolbox, _moz_win_browsertabbar_toolbox)
 CSS_KEY(-moz-win-mediatext, _moz_win_mediatext)
 CSS_KEY(-moz-win-communicationstext, _moz_win_communicationstext)
 CSS_KEY(-moz-win-glass, _moz_win_glass)
+CSS_KEY(-moz-mac-unified-toolbar, _moz_mac_unified_toolbar)
 
 #ifdef MOZ_SVG
 //CSS_KEY(all, all)
 CSS_KEY(alphabetic, alphabetic)
 //CSS_KEY(auto, auto)
 CSS_KEY(bevel, bevel)
 CSS_KEY(butt, butt)
 CSS_KEY(central, central)
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -323,16 +323,17 @@ const PRInt32 nsCSSProps::kAppearanceKTa
   eCSSKeyword_menuseparator,          NS_THEME_MENUSEPARATOR,
   eCSSKeyword_menuarrow,              NS_THEME_MENUARROW,
   eCSSKeyword_menuimage,              NS_THEME_MENUIMAGE,
   eCSSKeyword_menuitemtext,           NS_THEME_MENUITEMTEXT,
   eCSSKeyword__moz_win_media_toolbox, NS_THEME_WIN_MEDIA_TOOLBOX,
   eCSSKeyword__moz_win_communications_toolbox, NS_THEME_WIN_COMMUNICATIONS_TOOLBOX,
   eCSSKeyword__moz_win_browsertabbar_toolbox,  NS_THEME_WIN_BROWSER_TAB_BAR_TOOLBOX,
   eCSSKeyword__moz_win_glass,         NS_THEME_WIN_GLASS,
+  eCSSKeyword__moz_mac_unified_toolbar,        NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR,
   eCSSKeyword_UNKNOWN,-1
 };
 
 // Keyword id tables for variant/enum parsing
 const PRInt32 nsCSSProps::kAzimuthKTable[] = {
   eCSSKeyword_left_side,    NS_STYLE_AZIMUTH_LEFT_SIDE,
   eCSSKeyword_far_left,     NS_STYLE_AZIMUTH_FAR_LEFT,
   eCSSKeyword_left,         NS_STYLE_AZIMUTH_LEFT,
--- a/widget/public/nsIWidget.h
+++ b/widget/public/nsIWidget.h
@@ -88,20 +88,20 @@ typedef nsEventStatus (*PR_CALLBACK EVEN
 #define NS_NATIVE_PLUGIN_PORT 8
 #define NS_NATIVE_SCREEN      9
 #define NS_NATIVE_SHELLWIDGET 10      // Get the shell GtkWidget
 #ifdef XP_MACOSX
 #define NS_NATIVE_PLUGIN_PORT_QD    100
 #define NS_NATIVE_PLUGIN_PORT_CG    101
 #endif
 
-// AE42543F-BF61-4164-96BA-AF8F4EDCEEAD
+// 0e64821f-00a2-4adc-ac3b-3439d61f4491
 #define NS_IWIDGET_IID \
-{ 0xae42543f, 0xbf61, 0x4164, \
-  { 0x96, 0xba, 0xaf, 0x8f, 0x4e, 0xdc, 0xee, 0xad } }
+{ 0x0e64821f, 0x00a2, 0x4adc, \
+  { 0xac, 0x3b, 0x34, 0x39, 0xd6, 0x1f, 0x44, 0x91 } }
 
 // Hide the native window systems real window type so as to avoid
 // including native window system types and APIs. This is necessary
 // to ensure cross-platform code.
 typedef void* nsNativeWidget;
 
 /**
  * Border styles
@@ -379,16 +379,25 @@ class nsIWidget : public nsISupports {
      * top level window
      *
      * @return the parent widget or nsnull if it does not have a parent
      *
      */
     virtual nsIWidget* GetParent(void) = 0;
 
     /**
+     * Return the top level Widget of this Widget
+     *
+     * @param     aLevelsUp   returns the number of GetParent() calls that
+     *                        were necessary to get to the top level widget
+     * @return the top level widget
+     */
+    virtual nsIWidget* GetTopLevelWidget(PRInt32* aLevelsUp = NULL) = 0;
+
+    /**
      * Return the top (non-sheet) parent of this Widget if it's a sheet,
      * or nsnull if this isn't a sheet (or some other error occurred).
      * Sheets are only supported on some platforms (currently only OS X).
      *
      * @return the top (non-sheet) parent widget or nsnull
      *
      */
     virtual nsIWidget* GetSheetWindowParent(void) = 0;
--- a/widget/src/cocoa/nsChildView.h
+++ b/widget/src/cocoa/nsChildView.h
@@ -270,17 +270,16 @@ public:
                               nsNativeWidget aNativeParent = nsnull);
 
   NS_IMETHOD              Destroy();
 
   NS_IMETHOD              Show(PRBool aState);
   NS_IMETHOD              IsVisible(PRBool& outState);
 
   virtual nsIWidget*      GetParent(void);
-  nsIWidget*              GetTopLevelWidget();
 
   NS_IMETHOD              ModalEventFilter(PRBool aRealEvent, void *aEvent,
                                            PRBool *aForWindow);
 
   NS_IMETHOD              ConstrainPosition(PRBool aAllowSlop,
                                             PRInt32 *aX, PRInt32 *aY);
   NS_IMETHOD              Move(PRInt32 aX, PRInt32 aY);
   NS_IMETHOD              Resize(PRInt32 aWidth,PRInt32 aHeight, PRBool aRepaint);
--- a/widget/src/cocoa/nsChildView.mm
+++ b/widget/src/cocoa/nsChildView.mm
@@ -872,24 +872,16 @@ NS_IMETHODIMP nsChildView::Show(PRBool a
 
 
 nsIWidget*
 nsChildView::GetParent(void)
 {
   return mParentWidget;
 }
 
-nsIWidget*
-nsChildView::GetTopLevelWidget()
-{
-  nsIWidget* current = this;
-  for (nsIWidget* parent = GetParent(); parent ; parent = parent->GetParent())
-    current = parent;
-  return current;
-}
 
 NS_IMETHODIMP nsChildView::ModalEventFilter(PRBool aRealEvent, void *aEvent,
                                             PRBool *aForWindow)
 {
   if (aForWindow)
     *aForWindow = PR_FALSE;
   return NS_ERROR_NOT_IMPLEMENTED;
 }
@@ -2716,24 +2708,34 @@ NSEvent* gLastDragEvent = nil;
   // It will set the port back to this port on destruction.
   ::SetPort(NULL);  // todo: only do if a Quickdraw plugin is present in the hierarchy!
   [super lockFocus];
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 
+static BOOL IsPaintingSuppressed(nsIWidget* aWidget)
+{
+  nsIWidget* topLevelWidget = aWidget->GetTopLevelWidget();
+  NSWindow* win = (NSWindow*)topLevelWidget->GetNativeData(NS_NATIVE_WINDOW);
+  return ([win isKindOfClass:[ToolbarWindow class]] &&
+          [(ToolbarWindow*)win isPaintingSuppressed]);
+}
+
+
 // The display system has told us that a portion of our view is dirty. Tell
 // gecko to paint it
 - (void)drawRect:(NSRect)aRect
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   PRBool isVisible;
-  if (!mGeckoChild || NS_FAILED(mGeckoChild->IsVisible(isVisible)) || !isVisible)
+  if (!mGeckoChild || NS_FAILED(mGeckoChild->IsVisible(isVisible)) ||
+      !isVisible || IsPaintingSuppressed(mGeckoChild))
     return;
 
   CGContextRef cgContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
 
   nsRect geckoBounds;
   mGeckoChild->GetBounds(geckoBounds);
 
   NSRect bounds = [self bounds];
--- a/widget/src/cocoa/nsCocoaWindow.h
+++ b/widget/src/cocoa/nsCocoaWindow.h
@@ -113,53 +113,90 @@ typedef struct _nsCocoaWindowList {
 - (void)windowDidResize:(NSNotification*)aNotification;
 - (void)sendFocusEvent:(PRUint32)eventType;
 - (nsCocoaWindow*)geckoWidget;
 - (PRBool)toplevelActiveState;
 - (void)sendToplevelActivateEvents;
 - (void)sendToplevelDeactivateEvents;
 @end
 
+// These are the start and end greys for the unified titlebar and toolbar gradient.
+static const float sLeopardHeaderStartGrey = 197/255.0f;
+static const float sLeopardHeaderEndGrey = 150/255.0f;
+static const float sLeopardHeaderBackgroundStartGrey = 233/255.0f;
+static const float sLeopardHeaderBackgroundEndGrey = 207/255.0f;
+
+// This is the grey for the border at the bottom of the titlebar / toolbar.
+static const float sLeopardTitlebarBorderGrey = 64/255.0f;
+static const float sLeopardTitlebarBackgroundBorderGrey = 135/255.0f;
+
+struct UnifiedGradientInfo {
+  float titlebarHeight;
+  float toolbarHeight;
+  BOOL windowIsMain;
+  BOOL drawTitlebar; // NO for toolbar, YES for titlebar
+};
+
+// Callback used by the default titlebar and toolbar shading.
+// *aIn == 0 at the top of the titlebar/toolbar, *aIn == 1 at the bottom
+static void unifiedShading(void* aInfo, const float* aIn, float* aOut)
+{
+  UnifiedGradientInfo* info = (UnifiedGradientInfo*)aInfo;
+  // The gradient percentage at the bottom of the titlebar / top of the toolbar
+  float start = info->titlebarHeight / (info->titlebarHeight + info->toolbarHeight - 1);
+  const float startGrey = info->windowIsMain ? sLeopardHeaderStartGrey : sLeopardHeaderBackgroundStartGrey;
+  const float endGrey = info->windowIsMain ? sLeopardHeaderEndGrey : sLeopardHeaderBackgroundEndGrey;
+  // *aIn is the gradient percentage of the titlebar or toolbar gradient,
+  // a is the gradient percentage of the whole unified gradient.
+  float a = info->drawTitlebar ? *aIn * start : start + *aIn * (1 - start);
+  float result = (1.0f - a) * startGrey + a * endGrey;
+  aOut[0] = result;
+  aOut[1] = result;
+  aOut[2] = result;
+  aOut[3] = 1.0f;
+}
 
 // NSColor subclass that allows us to draw separate colors both in the titlebar 
 // and for background of the window.
 @interface TitlebarAndBackgroundColor : NSColor
 {
   NSColor *mActiveTitlebarColor;
   NSColor *mInactiveTitlebarColor;
   NSColor *mBackgroundColor;
   NSWindow *mWindow; // [WEAK] (we are owned by the window)
-  float mTitlebarHeight;
 }
 
 - (id)initWithActiveTitlebarColor:(NSColor*)aActiveTitlebarColor
             inactiveTitlebarColor:(NSColor*)aInactiveTitlebarColor
                   backgroundColor:(NSColor*)aBackgroundColor
                         forWindow:(NSWindow*)aWindow;
 
 // Pass nil here to get the default appearance.
 - (void)setTitlebarColor:(NSColor*)aColor forActiveWindow:(BOOL)aActive;
 - (NSColor*)activeTitlebarColor;
 - (NSColor*)inactiveTitlebarColor;
 
 - (void)setBackgroundColor:(NSColor*)aColor;
 - (NSColor*)backgroundColor;
 
 - (NSWindow*)window;
-- (float)titlebarHeight;
 @end
 
 // NSWindow subclass for handling windows with toolbars.
 @interface ToolbarWindow : NSWindow
 {
   TitlebarAndBackgroundColor *mColor;
+  float mUnifiedToolbarHeight;
+  BOOL mSuppressPainting;
 }
 - (void)setTitlebarColor:(NSColor*)aColor forActiveWindow:(BOOL)aActive;
-- (NSColor*)activeTitlebarColor;
-- (NSColor*)inactiveTitlebarColor;
+- (void)setUnifiedToolbarHeight:(float)aToolbarHeight;
+- (float)unifiedToolbarHeight;
+- (float)titlebarHeight;
+- (BOOL)isPaintingSuppressed;
 // This method is also available on NSWindows (via a category), and is the 
 // preferred way to check the background color of a window.
 - (NSColor*)windowBackgroundColor;
 @end
 
 class nsCocoaWindow : public nsBaseWidget, public nsPIWidgetCocoa
 {
 private:
--- a/widget/src/cocoa/nsCocoaWindow.mm
+++ b/widget/src/cocoa/nsCocoaWindow.mm
@@ -1635,42 +1635,73 @@ NS_IMETHODIMP nsCocoaWindow::EndSecureKe
   return [self backgroundColor];
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
 }
 
 @end
 
 
+@interface ToolbarWindow(Private)
+
+- (void)redrawTitlebar;
+
+@end
+
+
 // This class allows us to have a "unified toolbar" style window. It works like this:
 // 1) We set the window's style to textured.
 // 2) Because of this, the background color applies to the entire window, including
 //     the titlebar area. For normal textured windows, the default pattern is a 
-//    "brushed metal" image.
+//    "brushed metal" image on Tiger and a unified gradient on Leopard.
 // 3) We set the background color to a custom NSColor subclass that knows how tall the window is.
 //    When -set is called on it, it sets a pattern (with a draw callback) as the fill. In that callback,
-//    it paints the the titlebar and background colrs in the correct areas of the context its given,
+//    it paints the the titlebar and background colors in the correct areas of the context it's given,
 //    which will fill the entire window (CG will tile it horizontally for us).
+// 4) Whenever the window's main state changes and when [window display] is called,
+//    Cocoa redraws the titlebar using the patternDraw callback function.
 //
 // This class also provides us with a pill button to show/hide the toolbar.
+//
+// Drawing the unified gradient in the titlebar and the toolbar works like this:
+// 1) In the style sheet we set the toolbar's -moz-appearance to -moz-mac-unified-toolbar.
+// 2) When the toolbar is drawn, Gecko calls nsNativeThemeCocoa::DrawWidgetBackground
+//    for the widget type NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR.
+// 3) This calls DrawUnifiedToolbar which finds the toolbar frame's ToolbarWindow
+//    and passes the toolbar frame's height to setUnifiedToolbarHeight.
+// 4) If the toolbar height has changed, a titlebar redraw is triggered by
+//    [self display] and the upper part of the unified gradient is drawn in the
+//    titlebar.
+// 5) DrawUnifiedToolbar draws the lower part of the unified gradient in the toolbar.
+//
+// Whenever the unified gradient is drawn in the titlebar or the toolbar, both
+// titlebar height and toolbar height must be known in order to construct the
+// correct gradient (which is a linear gradient with the length
+// titlebarHeight + toolbarHeight - 1). But you can only get from the toolbar frame
+// to the containing window - the other direction doesn't work. That's why the
+// toolbar height is cached in the ToolbarWindow but nsNativeThemeCocoa can simply
+// query the window for its titlebar height when drawing the toolbar.
 @implementation ToolbarWindow
 
 - (id)initWithContentRect:(NSRect)aContentRect styleMask:(unsigned int)aStyle backing:(NSBackingStoreType)aBufferingType defer:(BOOL)aFlag
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
   aStyle = aStyle | NSTexturedBackgroundWindowMask;
   if ((self = [super initWithContentRect:aContentRect styleMask:aStyle backing:aBufferingType defer:aFlag])) {
     mColor = [[TitlebarAndBackgroundColor alloc] initWithActiveTitlebarColor:nil
                                                        inactiveTitlebarColor:nil
                                                              backgroundColor:[NSColor whiteColor]
                                                                    forWindow:self];
     // Call the superclass's implementation, to avoid our guard method below.
     [super setBackgroundColor:mColor];
 
+    mUnifiedToolbarHeight = 0.0f;
+    mSuppressPainting = NO;
+
     // setBottomCornerRounded: is a private API call, so we check to make sure
     // we respond to it just in case.
     if ([self respondsToSelector:@selector(setBottomCornerRounded:)])
       [self setBottomCornerRounded:NO];
   }
   return self;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
@@ -1718,36 +1749,43 @@ NS_IMETHODIMP nsCocoaWindow::EndSecureKe
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   [mColor setTitlebarColor:aColor forActiveWindow:aActive];
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
-
-- (NSColor*)activeTitlebarColor
+// This is called by nsNativeThemeCocoa.mm's DrawUnifiedToolbar.
+// We need to know the toolbar's height in order to draw the correct
+// unified gradient in the titlebar.
+- (void)setUnifiedToolbarHeight:(float)aToolbarHeight
 {
-  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
-
-  return [mColor activeTitlebarColor];
-
-  NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+  if (mUnifiedToolbarHeight == aToolbarHeight)
+    return;
+  mUnifiedToolbarHeight = aToolbarHeight;
+  [self redrawTitlebar];
 }
 
 
-- (NSColor*)inactiveTitlebarColor
+- (float)unifiedToolbarHeight
+{
+  return mUnifiedToolbarHeight;
+}
+
+- (float)titlebarHeight
 {
-  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
-
-  return [mColor inactiveTitlebarColor];
-
-  NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+  NSRect frameRect = [self frame];
+  return frameRect.size.height - [self contentRectForFrameRect:frameRect].size.height;
 }
 
+- (BOOL)isPaintingSuppressed
+{
+  return mSuppressPainting;
+}
 
 // Always show the toolbar pill button.
 - (BOOL)_hasToolbar
 {
   return YES;
 }
 
 
@@ -1819,16 +1857,29 @@ NS_IMETHODIMP nsCocoaWindow::EndSecureKe
       break;
   }
 
   [super sendEvent:anEvent];
 }
 
 @end
 
+@implementation ToolbarWindow(Private)
+
+// [self display] seems to be the only way to repaint a window's titlebar.
+// The bad thing about it is that it repaints all the window's subviews as well.
+// So we use a guard to prevent unnecessary redrawing.
+- (void)redrawTitlebar
+{
+  mSuppressPainting = YES;
+  [self display];
+  mSuppressPainting = NO;
+}
+
+@end
 
 // Custom NSColor subclass where most of the work takes place for drawing in
 // the titlebar area.
 @implementation TitlebarAndBackgroundColor
 
 - (id)initWithActiveTitlebarColor:(NSColor*)aActiveTitlebarColor
             inactiveTitlebarColor:(NSColor*)aInactiveTitlebarColor
                   backgroundColor:(NSColor*)aBackgroundColor
@@ -1836,21 +1887,16 @@ NS_IMETHODIMP nsCocoaWindow::EndSecureKe
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
   if ((self = [super init])) {
     mActiveTitlebarColor = [aActiveTitlebarColor retain];
     mInactiveTitlebarColor = [aInactiveTitlebarColor retain];
     mBackgroundColor = [aBackgroundColor retain];
     mWindow = aWindow; // weak ref to avoid a cycle
-    NSRect frameRect = [aWindow frame];
-
-    // We cant just use a static because the height can vary by window, and we don't
-    // want to recalculate this every time we draw. A member is the best solution.
-    mTitlebarHeight = frameRect.size.height - [aWindow contentRectForFrameRect:frameRect].size.height;
   }
   return self;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
 }
 
 
 - (void)dealloc
@@ -1863,93 +1909,58 @@ NS_IMETHODIMP nsCocoaWindow::EndSecureKe
   [super dealloc];
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 // Our pattern width is 1 pixel. CoreGraphics can cache and tile for us.
 static const float sPatternWidth = 1.0f;
 
-// These are the start and end greys for the default titlebar gradient.
-static const float sLeopardHeaderStartGrey = 196/255.0f;
-static const float sLeopardHeaderEndGrey = 149/255.0f;
-static const float sLeopardHeaderBackgroundStartGrey = 232/255.0f;
-static const float sLeopardHeaderBackgroundEndGrey = 207/255.0f;
-static const float sTigerHeaderStartGrey = 239/255.0f;
-static const float sTigerHeaderEndGrey = 202/255.0f;
-
-// This is the grey for the border at the bottom of the titlebar.
-static const float sLeopardTitlebarBorderGrey = 64/255.0f;
-static const float sLeopardTitlebarBackgroundBorderGrey = 134/255.0f;
-static const float sTigerTitlebarBorderGrey = 140/255.0f;
-
-// Callback used by the default titlebar shading.
-static void headerShading(void* aInfo, const float* aIn, float* aOut)
-{
-  float startGrey, endGrey;
-  BOOL isMain = *(BOOL*)aInfo;
-  if (nsToolkit::OnLeopardOrLater()) {
-    startGrey = isMain ? sLeopardHeaderStartGrey : sLeopardHeaderBackgroundStartGrey;
-    endGrey = isMain ? sLeopardHeaderEndGrey : sLeopardHeaderBackgroundEndGrey;
-  }
-  else {
-    startGrey = sTigerHeaderStartGrey;
-    endGrey = sTigerHeaderEndGrey;
-  }
-  float result = (*aIn) * startGrey + (1.0f - *aIn) * endGrey;
-  aOut[0] = result;
-  aOut[1] = result;
-  aOut[2] = result;
-  aOut[3] = 1.0f;
-}
-
-
 // Callback where all of the drawing for this color takes place.
 void patternDraw(void* aInfo, CGContextRef aContext)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   TitlebarAndBackgroundColor *color = (TitlebarAndBackgroundColor*)aInfo;
   NSColor *backgroundColor = [color backgroundColor];
-  NSWindow *window = [color window];
+  ToolbarWindow *window = (ToolbarWindow*)[color window];
   BOOL isMain = [window isMainWindow];
   NSColor *titlebarColor = isMain ? [color activeTitlebarColor] : [color inactiveTitlebarColor];
 
   // Remember: this context is NOT flipped, so the origin is in the bottom left.
-  float titlebarHeight = [color titlebarHeight];
+  float titlebarHeight = [window titlebarHeight];
   float titlebarOrigin = [window frame].size.height - titlebarHeight;
 
+  UnifiedGradientInfo info = { titlebarHeight, [window unifiedToolbarHeight], isMain, YES };
+
   [NSGraphicsContext saveGraphicsState];
   [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:aContext flipped:NO]];
 
   // If the titlebar color is nil, draw the default titlebar shading.
   if (!titlebarColor) {
-    // On Tiger when the window is not main, we want to draw a pinstripe pattern instead.
-    if (!nsToolkit::OnLeopardOrLater() && !isMain) {
-      [[NSColor windowBackgroundColor] set];
-      NSRectFill(NSMakeRect(0.0f, titlebarOrigin, 1.0f, titlebarOrigin + titlebarHeight));
-    } else {
-      // Otherwise, create and draw a CGShading that uses headerShading() as its callback.
-      CGFunctionCallbacks callbacks = {0, headerShading, NULL};
-      CGFunctionRef function = CGFunctionCreate(&isMain, 1, NULL, 4, NULL, &callbacks);
-      CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
-      CGShadingRef shading = CGShadingCreateAxial(colorSpace, CGPointMake(0.0f, titlebarOrigin),
-                                                  CGPointMake(0.0f, titlebarOrigin + titlebarHeight),
-                                                  function, NO, NO);
-      CGColorSpaceRelease(colorSpace);
-      CGFunctionRelease(function);
-      CGContextDrawShading(aContext, shading);
-      CGShadingRelease(shading);
-    }
+    // Create and draw a CGShading that uses unifiedShading() as its callback.
+    CGFunctionCallbacks callbacks = {0, unifiedShading, NULL};
+    CGFunctionRef function = CGFunctionCreate(&info, 1, NULL, 4, NULL, &callbacks);
+    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
+    CGShadingRef shading = CGShadingCreateAxial(colorSpace,
+                                                CGPointMake(0.0f, titlebarOrigin + titlebarHeight),
+                                                CGPointMake(0.0f, titlebarOrigin),
+                                                function, NO, NO);
+    CGColorSpaceRelease(colorSpace);
+    CGFunctionRelease(function);
+    CGContextDrawShading(aContext, shading);
+    CGShadingRelease(shading);
 
     // Draw the one pixel border at the bottom of the titlebar.
-    float borderGrey = !nsToolkit::OnLeopardOrLater() ? sTigerTitlebarBorderGrey :
-      (isMain ? sLeopardTitlebarBorderGrey : sLeopardTitlebarBackgroundBorderGrey);
-    [[NSColor colorWithDeviceWhite:borderGrey alpha:1.0f] set];
-    NSRectFill(NSMakeRect(0.0f, titlebarOrigin, sPatternWidth, 1.0f));
+    if ([window unifiedToolbarHeight] == 0) {
+      float borderGrey = isMain ? sLeopardTitlebarBorderGrey :
+                                  sLeopardTitlebarBackgroundBorderGrey;
+      [[NSColor colorWithDeviceWhite:borderGrey alpha:1.0f] set];
+      NSRectFill(NSMakeRect(0.0f, titlebarOrigin, sPatternWidth, 1.0f));
+    }
   } else {
     // if the titlebar color is not nil, just set and draw it normally.
     [titlebarColor set];
     NSRectFill(NSMakeRect(0.0f, titlebarOrigin, sPatternWidth, titlebarHeight));
   }
 
   // Draw the background color of the window everywhere but where the titlebar is.
   [backgroundColor set];
@@ -2049,22 +2060,16 @@ void patternDraw(void* aInfo, CGContextR
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   [self setFill];
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
-
-- (float)titlebarHeight
-{
-  return mTitlebarHeight;
-}
-
 @end
 
 
 // This is an internal Apple class, which we need to work around a bug in. It is
 // the class responsible for drawing the titlebar for metal windows. It actually
 // is a few levels deep in the inhertiance graph, but we don't need to know about
 // all its superclasses.
 @interface NSGrayFrame : NSObject
--- a/widget/src/cocoa/nsNativeThemeCocoa.h
+++ b/widget/src/cocoa/nsNativeThemeCocoa.h
@@ -121,16 +121,19 @@ protected:
                    ThemeButtonAdornment inAdornment, PRInt32 inState);
   void DrawSpinButtons (CGContextRef context, ThemeButtonKind inKind,
                         const HIRect& inBoxRect,
                         PRBool inDisabled, ThemeDrawState inDrawState,
                         ThemeButtonAdornment inAdornment, PRInt32 inState);
   void DrawCheckbox(CGContextRef context, ThemeButtonKind inKind,
                     const HIRect& inBoxRect, PRBool inChecked, 
                     PRBool inDisabled, PRInt32 inState);
+  void DrawUnifiedToolbar(CGContextRef cgContext, const HIRect& inBoxRect,
+                          nsIFrame *aFrame);
+
   // Scrollbars
   void DrawScrollbar(CGContextRef aCGContext, const HIRect& aBoxRect, nsIFrame *aFrame);
   void GetScrollbarPressStates (nsIFrame *aFrame, PRInt32 aButtonStates[]);
   void GetScrollbarDrawInfo (HIThemeTrackDrawInfo& aTdi, nsIFrame *aFrame, 
                              const HIRect& aRect, PRBool aShouldGetButtonStates);
   nsIFrame* GetParentScrollbarFrame(nsIFrame *aFrame);
 
   void DrawCellWithScaling(NSCell *cell,
--- a/widget/src/cocoa/nsNativeThemeCocoa.mm
+++ b/widget/src/cocoa/nsNativeThemeCocoa.mm
@@ -52,16 +52,17 @@
 #include "nsIFrame.h"
 #include "nsIAtom.h"
 #include "nsIEventStateManager.h"
 #include "nsINameSpaceManager.h"
 #include "nsPresContext.h"
 #include "nsILookAndFeel.h"
 #include "nsWidgetAtoms.h"
 #include "nsToolkit.h"
+#include "nsCocoaWindow.h"
 
 #include "gfxContext.h"
 #include "gfxQuartzSurface.h"
 #include "gfxQuartzNativeDrawing.h"
 
 #define DRAW_IN_FRAME_DEBUG 0
 #define SCROLLBARS_VISUAL_DEBUG 0
 
@@ -111,16 +112,29 @@ static void InflateControlRect(NSRect* r
   int controlSize = EnumSizeForCocoaSize(cocoaControlSize);
   const float* buttonMargins = marginSet[osIndex][controlSize];
   rect->origin.x -= buttonMargins[leftMargin];
   rect->origin.y -= buttonMargins[bottomMargin];
   rect->size.width += buttonMargins[leftMargin] + buttonMargins[rightMargin];
   rect->size.height += buttonMargins[bottomMargin] + buttonMargins[topMargin];
 }
 
+static NSWindow* NativeWindowForFrame(nsIFrame* aFrame, int* aLevelsUp = NULL)
+{
+  if (!aFrame)
+    return nil;  
+
+  nsIWidget* widget = aFrame->GetWindow();
+  if (!widget)
+    return nil;
+
+  nsIWidget* topLevelWidget = widget->GetTopLevelWidget(aLevelsUp);
+
+  return (NSWindow*)topLevelWidget->GetNativeData(NS_NATIVE_WINDOW);
+}
 
 NS_IMPL_ISUPPORTS1(nsNativeThemeCocoa, nsITheme)
 
 
 nsNativeThemeCocoa::nsNativeThemeCocoa()
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
@@ -976,16 +990,66 @@ nsNativeThemeCocoa::GetParentScrollbarFr
     if (scrollbarFrame->GetType() == nsWidgetAtoms::scrollbarFrame) break;
   } while ((scrollbarFrame = scrollbarFrame->GetParent()));
   
   // We return null if we can't find a parent scrollbar frame
   return scrollbarFrame;
 }
 
 
+void
+nsNativeThemeCocoa::DrawUnifiedToolbar(CGContextRef cgContext, const HIRect& inBoxRect,
+                                       nsIFrame *aFrame)
+{
+  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+  float titlebarHeight = 0;
+  int levelsUp = 0;
+  NSWindow* win = NativeWindowForFrame(aFrame, &levelsUp);
+
+  // If the toolbar is directly below the titlebar in the top level view of a ToolbarWindow
+  if ([win isKindOfClass:[ToolbarWindow class]] && levelsUp == 0 &&
+      inBoxRect.origin.y <= 0) {
+    // Consider the titlebar height when calculating the gradient.
+    titlebarHeight = [(ToolbarWindow*)win titlebarHeight];
+    // Notify the window about the toolbar's height so that it can draw the
+    // correct gradient in the titlebar.
+    [(ToolbarWindow*)win setUnifiedToolbarHeight:inBoxRect.size.height];
+  }
+  
+  BOOL isMain = win ? [win isMainWindow] : YES;
+
+  // Draw the gradient
+  UnifiedGradientInfo info = { titlebarHeight, inBoxRect.size.height, isMain, NO };
+  struct CGFunctionCallbacks callbacks = { 0, unifiedShading, NULL };
+  CGFunctionRef function = CGFunctionCreate(&info, 1,  NULL, 4, NULL, &callbacks);
+  float srcY = inBoxRect.origin.y;
+  float dstY = srcY + inBoxRect.size.height - 1;
+  CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
+  CGShadingRef shading = CGShadingCreateAxial(colorSpace,
+                                              CGPointMake(0, srcY),
+                                              CGPointMake(0, dstY), function,
+                                              NO, NO);
+  CGColorSpaceRelease(colorSpace);
+  CGFunctionRelease(function);
+  CGContextClipToRect(cgContext, inBoxRect);
+  CGContextDrawShading(cgContext, shading);
+  CGShadingRelease(shading);
+
+  // Draw the border at the bottom of the toolbar.
+  float borderGrey = isMain ? sLeopardTitlebarBorderGrey :
+                              sLeopardTitlebarBackgroundBorderGrey;
+  [[NSColor colorWithDeviceWhite:borderGrey alpha:1.0f] set];
+  NSRectFill(NSMakeRect(inBoxRect.origin.x, inBoxRect.origin.y +
+                        inBoxRect.size.height - 1.0f, inBoxRect.size.width, 1.0f));
+
+  NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+
 NS_IMETHODIMP
 nsNativeThemeCocoa::DrawWidgetBackground(nsIRenderingContext* aContext, nsIFrame* aFrame,
                                          PRUint8 aWidgetType, const nsRect& aRect,
                                          const nsRect& aDirtyRect)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
 
   // setup to draw into the correct port
@@ -1144,16 +1208,20 @@ nsNativeThemeCocoa::DrawWidgetBackground
       break;
 
     case NS_THEME_TOOLBAR_SEPARATOR: {
       HIThemeSeparatorDrawInfo sdi = { 0, kThemeStateActive };
       HIThemeDrawSeparator(&macRect, &sdi, cgContext, HITHEME_ORIENTATION);
     }
       break;
 
+    case NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR:
+      DrawUnifiedToolbar(cgContext, macRect, aFrame);
+      break;
+
     case NS_THEME_TOOLBAR:
     case NS_THEME_TOOLBOX:
     case NS_THEME_STATUSBAR: {
       HIThemeHeaderDrawInfo hdi = { 0, kThemeStateActive, kHIThemeHeaderKindWindow };
       HIThemeDrawHeader(&macRect, &hdi, cgContext, HITHEME_ORIENTATION);
     }
       break;
       
@@ -1431,16 +1499,20 @@ nsNativeThemeCocoa::GetWidgetBorder(nsID
 
         if (isHorizontal)
           aResult->SizeTo(endcapSize, 0, 0, 0);
         else
           aResult->SizeTo(0, endcapSize, 0, 0);
       }
       break;
     }
+
+    case NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR:
+      aResult->SizeTo(0, 0, 1, 0);
+      break;
   }
 
   return NS_OK;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
 
 
@@ -1743,16 +1815,17 @@ nsNativeThemeCocoa::GetMinimumWidgetSize
 NS_IMETHODIMP
 nsNativeThemeCocoa::WidgetStateChanged(nsIFrame* aFrame, PRUint8 aWidgetType, 
                                      nsIAtom* aAttribute, PRBool* aShouldRepaint)
 {
   // Some widget types just never change state.
   switch (aWidgetType) {
     case NS_THEME_TOOLBOX:
     case NS_THEME_TOOLBAR:
+    case NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR:
     case NS_THEME_TOOLBAR_BUTTON:
     case NS_THEME_SCROLLBAR_TRACK_VERTICAL: 
     case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
     case NS_THEME_STATUSBAR:
     case NS_THEME_STATUSBAR_PANEL:
     case NS_THEME_STATUSBAR_RESIZER_PANEL:
     case NS_THEME_TOOLTIP:
     case NS_THEME_TAB_PANELS:
@@ -1833,16 +1906,17 @@ nsNativeThemeCocoa::ThemeSupportsWidget(
     case NS_THEME_RADIO:
     case NS_THEME_RADIO_SMALL:
     case NS_THEME_RADIO_CONTAINER:
     case NS_THEME_GROUPBOX:
     case NS_THEME_BUTTON:
     case NS_THEME_BUTTON_BEVEL:
     case NS_THEME_SPINNER:
     case NS_THEME_TOOLBAR:
+    case NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR:
     case NS_THEME_STATUSBAR:
     case NS_THEME_TEXTFIELD:
     case NS_THEME_TEXTFIELD_MULTILINE:
     //case NS_THEME_TOOLBOX:
     //case NS_THEME_TOOLBAR_BUTTON:
     case NS_THEME_PROGRESSBAR:
     case NS_THEME_PROGRESSBAR_VERTICAL:
     case NS_THEME_PROGRESSBAR_CHUNK:
--- a/widget/src/xpwidgets/nsBaseWidget.cpp
+++ b/widget/src/xpwidgets/nsBaseWidget.cpp
@@ -286,16 +286,35 @@ NS_IMETHODIMP nsBaseWidget::SetParent(ns
 //-------------------------------------------------------------------------
 nsIWidget* nsBaseWidget::GetParent(void)
 {
   return nsnull;
 }
 
 //-------------------------------------------------------------------------
 //
+// Get this nsBaseWidget top level widget
+//
+//-------------------------------------------------------------------------
+nsIWidget* nsBaseWidget::GetTopLevelWidget(PRInt32* aLevelsUp)
+{
+  nsIWidget *topLevelWidget, *widget = this;
+  if (aLevelsUp)
+    *aLevelsUp = -1;
+  while (widget) {
+    topLevelWidget = widget;
+    widget = widget->GetParent();
+    if (aLevelsUp)
+      ++*aLevelsUp;
+  }
+  return topLevelWidget;
+}
+
+//-------------------------------------------------------------------------
+//
 // Get this nsBaseWidget's top (non-sheet) parent (if it's a sheet)
 //
 //-------------------------------------------------------------------------
 nsIWidget* nsBaseWidget::GetSheetWindowParent(void)
 {
   return nsnull;
 }
 
--- a/widget/src/xpwidgets/nsBaseWidget.h
+++ b/widget/src/xpwidgets/nsBaseWidget.h
@@ -76,16 +76,17 @@ public:
   NS_IMETHOD              CaptureMouse(PRBool aCapture);
   NS_IMETHOD              Validate();
   NS_IMETHOD              InvalidateRegion(const nsIRegion *aRegion, PRBool aIsSynchronous);
   NS_IMETHOD              GetClientData(void*& aClientData);
   NS_IMETHOD              SetClientData(void* aClientData);
   NS_IMETHOD              Destroy();
   NS_IMETHOD              SetParent(nsIWidget* aNewParent);
   virtual nsIWidget*      GetParent(void);
+  virtual nsIWidget*      GetTopLevelWidget(PRInt32* aLevelsUp = NULL);
   virtual nsIWidget*      GetSheetWindowParent(void);
   virtual void            AddChild(nsIWidget* aChild);
   virtual void            RemoveChild(nsIWidget* aChild);
 
   NS_IMETHOD              SetZIndex(PRInt32 aZIndex);
   NS_IMETHOD              GetZIndex(PRInt32* aZIndex);
   NS_IMETHOD              PlaceBehind(nsTopLevelWidgetZPlacement aPlacement,
                                       nsIWidget *aWidget, PRBool aActivate);