• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#import "chrome/browser/ui/cocoa/custom_frame_view.h"
6
7#import <Carbon/Carbon.h>
8#include <crt_externs.h>
9#import <objc/runtime.h>
10#include <string.h>
11
12#include "base/logging.h"
13#include "base/mac/mac_util.h"
14#include "base/mac/scoped_nsautorelease_pool.h"
15
16namespace {
17BOOL gCanDrawTitle = NO;
18BOOL gCanGetCornerRadius = NO;
19}  // namespace
20
21@interface NSView (Swizzles)
22- (void)drawRectOriginal:(NSRect)rect;
23- (NSPoint)_fullScreenButtonOriginOriginal;
24@end
25
26@interface NSWindow (FramedBrowserWindow)
27- (NSPoint)fullScreenButtonOriginAdjustment;
28@end
29
30@implementation NSWindow (CustomFrameView)
31- (void)drawCustomFrameRect:(NSRect)rect forView:(NSView*)view {
32  [view drawRectOriginal:rect];
33}
34@end
35
36@interface CustomFrameView : NSView
37
38@end
39
40@implementation CustomFrameView
41
42// This is where we swizzle drawRect, and add in two methods that we
43// need. If any of these fail it shouldn't affect the functionality of the
44// others. If they all fail, we will lose window frame theming and
45// roll overs for our close widgets, but things should still function
46// correctly.
47+ (void)load {
48  // Swizzling should only happen in the browser process. Interacting with
49  // AppKit will run +[borderViewClass initialize] in the renderer, which
50  // may establish Mach IPC with com.apple.windowserver.
51  // Note that CommandLine has not been initialized yet, since this is running
52  // as a module initializer.
53  const char* const* const argv = *_NSGetArgv();
54  const int argc = *_NSGetArgc();
55  const char kType[] = "--type=";
56  for (int i = 1; i < argc; ++i) {
57    const char* arg = argv[i];
58    if (strncmp(arg, kType, strlen(kType)) == 0)
59      return;
60  }
61
62  base::mac::ScopedNSAutoreleasePool pool;
63
64  // On 10.8+ the background for textured windows are no longer drawn by
65  // NSGrayFrame, and NSThemeFrame is used instead <http://crbug.com/114745>.
66  Class borderViewClass = NSClassFromString(
67      base::mac::IsOSMountainLionOrLater() ? @"NSThemeFrame" : @"NSGrayFrame");
68  DCHECK(borderViewClass);
69  if (!borderViewClass) return;
70
71  // Exchange draw rect.
72  Method m0 = class_getInstanceMethod([self class], @selector(drawRect:));
73  DCHECK(m0);
74  if (m0) {
75    BOOL didAdd = class_addMethod(borderViewClass,
76                                  @selector(drawRectOriginal:),
77                                  method_getImplementation(m0),
78                                  method_getTypeEncoding(m0));
79    DCHECK(didAdd);
80    if (didAdd) {
81      Method m1 = class_getInstanceMethod(borderViewClass,
82                                          @selector(drawRect:));
83      Method m2 = class_getInstanceMethod(borderViewClass,
84                                          @selector(drawRectOriginal:));
85      DCHECK(m1 && m2);
86      if (m1 && m2) {
87        method_exchangeImplementations(m1, m2);
88      }
89    }
90  }
91
92  // Swizzle the method that sets the origin for the Lion fullscreen button. Do
93  // nothing if it cannot be found.
94  m0 = class_getInstanceMethod([self class],
95                               @selector(_fullScreenButtonOrigin));
96  if (m0) {
97    BOOL didAdd = class_addMethod(borderViewClass,
98                                  @selector(_fullScreenButtonOriginOriginal),
99                                  method_getImplementation(m0),
100                                  method_getTypeEncoding(m0));
101    if (didAdd) {
102      Method m1 = class_getInstanceMethod(borderViewClass,
103                                          @selector(_fullScreenButtonOrigin));
104      Method m2 = class_getInstanceMethod(borderViewClass,
105          @selector(_fullScreenButtonOriginOriginal));
106      if (m1 && m2) {
107        method_exchangeImplementations(m1, m2);
108      }
109    }
110  }
111}
112
113+ (BOOL)canDrawTitle {
114  return gCanDrawTitle;
115}
116
117+ (BOOL)canGetCornerRadius {
118  return gCanGetCornerRadius;
119}
120
121- (id)initWithFrame:(NSRect)frame {
122  // This class is not for instantiating.
123  [self doesNotRecognizeSelector:_cmd];
124  return nil;
125}
126
127- (id)initWithCoder:(NSCoder*)coder {
128  // This class is not for instantiating.
129  [self doesNotRecognizeSelector:_cmd];
130  return nil;
131}
132
133// Here is our custom drawing for our frame.
134- (void)drawRect:(NSRect)rect {
135  // Delegate drawing to the window, whose default implementation (above) is to
136  // call into the original implementation.
137  [[self window] drawCustomFrameRect:rect forView:self];
138}
139
140// Override to move the fullscreen button to the left of the profile avatar.
141- (NSPoint)_fullScreenButtonOrigin {
142  NSWindow* window = [self window];
143  NSPoint offset = NSZeroPoint;
144
145  if ([window respondsToSelector:@selector(fullScreenButtonOriginAdjustment)])
146    offset = [window fullScreenButtonOriginAdjustment];
147
148  NSPoint origin = [self _fullScreenButtonOriginOriginal];
149  origin.x += offset.x;
150  origin.y += offset.y;
151  return origin;
152}
153
154@end
155