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#include "ui/compositor/test/test_compositor_host.h"
6
7#import <AppKit/NSApplication.h>
8#import <AppKit/NSOpenGL.h>
9#import <AppKit/NSView.h>
10#import <AppKit/NSWindow.h>
11#import <Foundation/NSAutoreleasePool.h>
12
13#include "base/compiler_specific.h"
14#include "base/mac/scoped_nsobject.h"
15#include "base/memory/scoped_ptr.h"
16#include "base/thread_task_runner_handle.h"
17#include "ui/compositor/compositor.h"
18#include "ui/gfx/rect.h"
19
20// AcceleratedTestView provides an NSView class that delegates drawing to a
21// ui::Compositor delegate, setting up the NSOpenGLContext as required.
22@interface AcceleratedTestView : NSView {
23  ui::Compositor* compositor_;
24}
25// Designated initializer.
26-(id)init;
27-(void)setCompositor:(ui::Compositor*)compositor;
28@end
29
30@implementation AcceleratedTestView
31-(id)init {
32  // The frame will be resized when reparented into the window's view hierarchy.
33  self = [super initWithFrame:NSZeroRect];
34  return self;
35}
36
37-(void)setCompositor:(ui::Compositor*)compositor {
38  compositor_ = compositor;
39}
40
41- (void)drawRect:(NSRect)rect {
42  DCHECK(compositor_) << "Drawing with no compositor set.";
43  compositor_->Draw();
44}
45@end
46
47namespace ui {
48
49// Tests that use Objective-C memory semantics need to have a top-level
50// NSAutoreleasePool set up and initialized prior to execution and drained upon
51// exit.  The tests will leak otherwise.
52class FoundationHost {
53 protected:
54  FoundationHost() {
55    pool_ = [[NSAutoreleasePool alloc] init];
56  }
57  virtual ~FoundationHost() {
58    [pool_ drain];
59  }
60
61 private:
62  NSAutoreleasePool* pool_;
63  DISALLOW_COPY_AND_ASSIGN(FoundationHost);
64};
65
66// Tests that use the AppKit framework need to have the NSApplication
67// initialized prior to doing anything with display objects such as windows,
68// views, or controls.
69class AppKitHost : public FoundationHost {
70 protected:
71  AppKitHost() {
72    [NSApplication sharedApplication];
73  }
74  virtual ~AppKitHost() {
75  }
76 private:
77  DISALLOW_COPY_AND_ASSIGN(AppKitHost);
78};
79
80// TestCompositorHostMac provides a window surface and a coordinated compositor
81// for use in the compositor unit tests.
82class TestCompositorHostMac : public TestCompositorHost,
83                              public AppKitHost {
84 public:
85  TestCompositorHostMac(const gfx::Rect& bounds,
86                        ui::ContextFactory* context_factory);
87  virtual ~TestCompositorHostMac();
88
89 private:
90  // TestCompositorHost:
91  virtual void Show() OVERRIDE;
92  virtual ui::Compositor* GetCompositor() OVERRIDE;
93
94  gfx::Rect bounds_;
95
96  ui::ContextFactory* context_factory_;
97
98  scoped_ptr<ui::Compositor> compositor_;
99
100  // Owned.  Released when window is closed.
101  NSWindow* window_;
102
103  DISALLOW_COPY_AND_ASSIGN(TestCompositorHostMac);
104};
105
106TestCompositorHostMac::TestCompositorHostMac(
107    const gfx::Rect& bounds,
108    ui::ContextFactory* context_factory)
109    : bounds_(bounds), context_factory_(context_factory), window_(nil) {
110}
111
112TestCompositorHostMac::~TestCompositorHostMac() {
113  // Release reference to |compositor_|.  Important because the |compositor_|
114  // holds |this| as its delegate, so that reference must be removed here.
115  [[window_ contentView] setCompositor:NULL];
116  [window_ setContentView:nil];
117
118  [window_ orderOut:nil];
119  [window_ close];
120}
121
122void TestCompositorHostMac::Show() {
123  DCHECK(!window_);
124  window_ = [[NSWindow alloc]
125                initWithContentRect:NSMakeRect(bounds_.x(),
126                                               bounds_.y(),
127                                               bounds_.width(),
128                                               bounds_.height())
129                          styleMask:NSBorderlessWindowMask
130                            backing:NSBackingStoreBuffered
131                              defer:NO];
132  base::scoped_nsobject<AcceleratedTestView> view(
133      [[AcceleratedTestView alloc] init]);
134  compositor_.reset(new ui::Compositor(view,
135                                       context_factory_,
136                                       base::ThreadTaskRunnerHandle::Get()));
137  compositor_->SetScaleAndSize(1.0f, bounds_.size());
138  [view setCompositor:compositor_.get()];
139  [window_ setContentView:view];
140  [window_ orderFront:nil];
141}
142
143ui::Compositor* TestCompositorHostMac::GetCompositor() {
144  return compositor_.get();
145}
146
147// static
148TestCompositorHost* TestCompositorHost::Create(
149    const gfx::Rect& bounds,
150    ui::ContextFactory* context_factory) {
151  return new TestCompositorHostMac(bounds, context_factory);
152}
153
154}  // namespace ui
155