--- /dev/null
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import <Cocoa/Cocoa.h>
+
+// CustomFrameView is a class whose methods we swizzle into NSGrayFrame
+// on 10.7 and below, or NSThemeFrame on 10.8 and above, so that we can
+// support custom frame drawing. This is used with a textured window so that
+// AppKit does not draw a title bar.
+// This class is never to be instantiated on its own.
+// We explored a variety of ways to support custom frame drawing and custom
+// window widgets.
+// Our requirements were:
+// a) that we could fall back on standard system drawing at any time for the
+// "default theme"
+// b) We needed to be able to draw both a background pattern, and an overlay
+// graphic, and we need to be able to set the pattern phase of our background
+// window.
+// c) We had to be able to support "transparent" themes, so that you could see
+// through to the underlying windows in places without the system theme
+// getting in the way.
+//
+// Since we want "A" we couldn't just do a transparent borderless window. At
+// least I couldn't find the right combination of HITheme calls to make it draw
+// nicely, and I don't trust that the HITheme calls are going to exist in future
+// system versions.
+// "C" precluded us from inserting a view between the system frame and the
+// the content frame in Z order. To get the transparency we actually need to
+// replace the drawing of the system frame.
+// "B" precluded us from just setting a background color on the window.
+//
+// Originally we tried overriding the private API +frameViewForStyleMask: to
+// add our own subclass of NSGrayView to our window. Turns out that if you
+// subclass NSGrayView it does not draw correctly when you call NSGrayView's
+// drawRect. It appears that NSGrayView's drawRect: method (and that of its
+// superclasses) do lots of "isMemberOfClass/isKindOfClass" calls, and if your
+// class is NOT an instance of NSGrayView (as opposed to a subclass of
+// NSGrayView) then the system drawing will not work correctly.
+//
+// Given all of the above, we found swizzling drawRect in _load to be the
+// easiest and safest method of achieving our goals. We do the best we can to
+// check that everything is safe, and attempt to fallback gracefully if it is
+// not.
+
+@interface NSWindow (CustomFrameView)
+
+// To define custom window drawing behaviour, override this method on an
+// NSWindow subclass. Call the default method (on super) to draw the
+// default frame.
+// NOTE: Always call the default implementation (even if you just immediately
+// draw over it), otherwise the top-left and top-right corners of the window
+// won't be drawn correctly.
+- (void)drawCustomFrameRect:(NSRect)rect forView:(NSView*)view;
+
+@end
\ No newline at end of file
--- /dev/null
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "browser/ui/cocoa/custom_frame_view.h"
+
+#import <objc/runtime.h>
+#import <Carbon/Carbon.h>
+
+#include "base/logging.h"
+#include "base/mac/mac_util.h"
+#include "base/mac/scoped_nsautorelease_pool.h"
+
+namespace {
+BOOL gCanDrawTitle = NO;
+BOOL gCanGetCornerRadius = NO;
+} // namespace
+
+@interface NSView (Swizzles)
+- (void)drawRectOriginal:(NSRect)rect;
+- (NSPoint)_fullScreenButtonOriginOriginal;
+@end
+
+@interface NSWindow (FramedBrowserWindow)
+- (NSPoint)fullScreenButtonOriginAdjustment;
+@end
+
+@implementation NSWindow (CustomFrameView)
+- (void)drawCustomFrameRect:(NSRect)rect forView:(NSView*)view {
+ [view drawRectOriginal:rect];
+}
+@end
+
+@interface CustomFrameView : NSView
+
+@end
+
+@implementation CustomFrameView
+
+// This is where we swizzle drawRect, and add in two methods that we
+// need. If any of these fail it shouldn't affect the functionality of the
+// others. If they all fail, we will lose window frame theming and
+// roll overs for our close widgets, but things should still function
+// correctly.
++ (void)load {
+ base::mac::ScopedNSAutoreleasePool pool;
+
+ // On 10.8+ the background for textured windows are no longer drawn by
+ // NSGrayFrame, and NSThemeFrame is used instead <http://crbug.com/114745>.
+ Class borderViewClass = NSClassFromString(
+ base::mac::IsOSMountainLionOrLater() ? @"NSThemeFrame" : @"NSGrayFrame");
+ DCHECK(borderViewClass);
+ if (!borderViewClass) return;
+
+ // Exchange draw rect.
+ Method m0 = class_getInstanceMethod([self class], @selector(drawRect:));
+ DCHECK(m0);
+ if (m0) {
+ BOOL didAdd = class_addMethod(borderViewClass,
+ @selector(drawRectOriginal:),
+ method_getImplementation(m0),
+ method_getTypeEncoding(m0));
+ DCHECK(didAdd);
+ if (didAdd) {
+ Method m1 = class_getInstanceMethod(borderViewClass,
+ @selector(drawRect:));
+ Method m2 = class_getInstanceMethod(borderViewClass,
+ @selector(drawRectOriginal:));
+ DCHECK(m1 && m2);
+ if (m1 && m2) {
+ method_exchangeImplementations(m1, m2);
+ }
+ }
+ }
+
+ // Swizzle the method that sets the origin for the Lion fullscreen button. Do
+ // nothing if it cannot be found.
+ m0 = class_getInstanceMethod([self class],
+ @selector(_fullScreenButtonOrigin));
+ if (m0) {
+ BOOL didAdd = class_addMethod(borderViewClass,
+ @selector(_fullScreenButtonOriginOriginal),
+ method_getImplementation(m0),
+ method_getTypeEncoding(m0));
+ if (didAdd) {
+ Method m1 = class_getInstanceMethod(borderViewClass,
+ @selector(_fullScreenButtonOrigin));
+ Method m2 = class_getInstanceMethod(borderViewClass,
+ @selector(_fullScreenButtonOriginOriginal));
+ if (m1 && m2) {
+ method_exchangeImplementations(m1, m2);
+ }
+ }
+ }
+}
+
++ (BOOL)canDrawTitle {
+ return gCanDrawTitle;
+}
+
++ (BOOL)canGetCornerRadius {
+ return gCanGetCornerRadius;
+}
+
+- (id)initWithFrame:(NSRect)frame {
+ // This class is not for instantiating.
+ [self doesNotRecognizeSelector:_cmd];
+ return nil;
+}
+
+- (id)initWithCoder:(NSCoder*)coder {
+ // This class is not for instantiating.
+ [self doesNotRecognizeSelector:_cmd];
+ return nil;
+}
+
+// Here is our custom drawing for our frame.
+- (void)drawRect:(NSRect)rect {
+ // Delegate drawing to the window, whose default implementation (above) is to
+ // call into the original implementation.
+ [[self window] drawCustomFrameRect:rect forView:self];
+}
+
+// Override to move the fullscreen button to the left of the profile avatar.
+- (NSPoint)_fullScreenButtonOrigin {
+ NSWindow* window = [self window];
+ NSPoint offset = NSZeroPoint;
+
+ if ([window respondsToSelector:@selector(fullScreenButtonOriginAdjustment)])
+ offset = [window fullScreenButtonOriginAdjustment];
+
+ NSPoint origin = [self _fullScreenButtonOriginOriginal];
+ origin.x += offset.x;
+ origin.y += offset.y;
+ return origin;
+}
+
+@end