1 // Copyright (c) 2011 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 "chrome/browser/renderer_host/render_widget_host_view_mac.h" 6 7 #include "base/mac/scoped_nsautorelease_pool.h" 8 #import "chrome/browser/ui/cocoa/cocoa_test_helper.h" 9 #include "chrome/browser/ui/cocoa/test_event_utils.h" 10 #include "content/browser/browser_thread.h" 11 #include "content/browser/renderer_host/test_render_view_host.h" 12 #include "testing/gtest/include/gtest/gtest.h" 13 #include "webkit/plugins/npapi/webplugin.h" 14 15 class RenderWidgetHostViewMacTest : public RenderViewHostTestHarness { 16 public: 17 RenderWidgetHostViewMacTest() : old_rwhv_(NULL), rwhv_mac_(NULL) {} 18 19 virtual void SetUp() { 20 // Set up Cocoa. 21 [CrApplication sharedApplication]; 22 23 RenderViewHostTestHarness::SetUp(); 24 25 // TestRenderViewHost's destruction assumes that its view is a 26 // TestRenderWidgetHostView, so store its view and reset it back to the 27 // stored view in |TearDown()|. 28 old_rwhv_ = rvh()->view(); 29 30 // Owned by its |native_view()|, i.e. |rwhv_cocoa_|. 31 rwhv_mac_ = new RenderWidgetHostViewMac(rvh()); 32 rwhv_cocoa_.reset([rwhv_mac_->native_view() retain]); 33 } 34 virtual void TearDown() { 35 // See comment in SetUp(). 36 rvh()->set_view(old_rwhv_); 37 38 // Make sure the rwhv_mac_ is gone once the superclass's |TearDown()| runs. 39 rwhv_cocoa_.reset(); 40 pool_.Recycle(); 41 MessageLoop::current()->RunAllPending(); 42 pool_.Recycle(); 43 44 RenderViewHostTestHarness::TearDown(); 45 } 46 protected: 47 // Adds an accelerated plugin view to |rwhv_cocoa_|. Returns a handle to the 48 // newly-added view. Callers must ensure that a UI thread is present and 49 // running before calling this function. 50 gfx::PluginWindowHandle AddAcceleratedPluginView(int w, int h) { 51 // Create an accelerated view the size of the rhwvmac. 52 [rwhv_cocoa_.get() setFrame:NSMakeRect(0, 0, w, h)]; 53 gfx::PluginWindowHandle accelerated_handle = 54 rwhv_mac_->AllocateFakePluginWindowHandle(/*opaque=*/false, 55 /*root=*/false); 56 rwhv_mac_->AcceleratedSurfaceSetIOSurface(accelerated_handle, w, h, 0); 57 58 // The accelerated view isn't shown until it has a valid rect and has been 59 // painted to. 60 rwhv_mac_->AcceleratedSurfaceBuffersSwapped(accelerated_handle, 61 0, 0, 0, 0, 0); 62 webkit::npapi::WebPluginGeometry geom; 63 gfx::Rect rect(0, 0, w, h); 64 geom.window = accelerated_handle; 65 geom.window_rect = rect; 66 geom.clip_rect = rect; 67 geom.visible = true; 68 geom.rects_valid = true; 69 rwhv_mac_->MovePluginWindows( 70 std::vector<webkit::npapi::WebPluginGeometry>(1, geom)); 71 72 return accelerated_handle; 73 } 74 private: 75 // This class isn't derived from PlatformTest. 76 base::mac::ScopedNSAutoreleasePool pool_; 77 78 RenderWidgetHostView* old_rwhv_; 79 80 protected: 81 RenderWidgetHostViewMac* rwhv_mac_; 82 scoped_nsobject<RenderWidgetHostViewCocoa> rwhv_cocoa_; 83 84 private: 85 DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewMacTest); 86 }; 87 88 TEST_F(RenderWidgetHostViewMacTest, Basic) { 89 } 90 91 // Regression test for http://crbug.com/60318 92 TEST_F(RenderWidgetHostViewMacTest, FocusAcceleratedView) { 93 // The accelerated view methods want to be called on the UI thread. 94 scoped_ptr<BrowserThread> ui_thread_( 95 new BrowserThread(BrowserThread::UI, MessageLoop::current())); 96 97 int w = 400, h = 300; 98 gfx::PluginWindowHandle accelerated_handle = AddAcceleratedPluginView(w, h); 99 EXPECT_FALSE([rwhv_cocoa_.get() isHidden]); 100 NSView* accelerated_view = static_cast<NSView*>( 101 rwhv_mac_->ViewForPluginWindowHandle(accelerated_handle)); 102 EXPECT_FALSE([accelerated_view isHidden]); 103 104 // Take away first responder from the rwhvmac, then simulate the effect of a 105 // click on the accelerated view. The rwhvmac should be first responder 106 // again. 107 scoped_nsobject<NSWindow> window([[CocoaTestHelperWindow alloc] init]); 108 scoped_nsobject<NSView> other_view( 109 [[NSTextField alloc] initWithFrame:NSMakeRect(0, h, w, 40)]); 110 [[window contentView] addSubview:rwhv_cocoa_.get()]; 111 [[window contentView] addSubview:other_view.get()]; 112 113 EXPECT_TRUE([rwhv_cocoa_.get() acceptsFirstResponder]); 114 [window makeFirstResponder:rwhv_cocoa_.get()]; 115 EXPECT_EQ(rwhv_cocoa_.get(), [window firstResponder]); 116 EXPECT_FALSE([accelerated_view acceptsFirstResponder]); 117 118 EXPECT_TRUE([other_view acceptsFirstResponder]); 119 [window makeFirstResponder:other_view]; 120 EXPECT_NE(rwhv_cocoa_.get(), [window firstResponder]); 121 122 EXPECT_TRUE([accelerated_view acceptsFirstResponder]); 123 [window makeFirstResponder:accelerated_view]; 124 EXPECT_EQ(rwhv_cocoa_.get(), [window firstResponder]); 125 126 // Clean up. 127 rwhv_mac_->DestroyFakePluginWindowHandle(accelerated_handle); 128 } 129 130 TEST_F(RenderWidgetHostViewMacTest, AcceptsFirstResponder) { 131 // The RWHVCocoa should normally accept first responder status. 132 EXPECT_TRUE([rwhv_cocoa_.get() acceptsFirstResponder]); 133 134 // Unless we tell it not to. 135 rwhv_mac_->SetTakesFocusOnlyOnMouseDown(true); 136 EXPECT_FALSE([rwhv_cocoa_.get() acceptsFirstResponder]); 137 138 // But we can set things back to the way they were originally. 139 rwhv_mac_->SetTakesFocusOnlyOnMouseDown(false); 140 EXPECT_TRUE([rwhv_cocoa_.get() acceptsFirstResponder]); 141 } 142 143 TEST_F(RenderWidgetHostViewMacTest, TakesFocusOnMouseDown) { 144 scoped_nsobject<CocoaTestHelperWindow> 145 window([[CocoaTestHelperWindow alloc] init]); 146 [[window contentView] addSubview:rwhv_cocoa_.get()]; 147 148 // Even if the RWHVCocoa disallows first responder, clicking on it gives it 149 // focus. 150 [window setPretendIsKeyWindow:YES]; 151 [window makeFirstResponder:nil]; 152 ASSERT_NE(rwhv_cocoa_.get(), [window firstResponder]); 153 154 rwhv_mac_->SetTakesFocusOnlyOnMouseDown(true); 155 EXPECT_FALSE([rwhv_cocoa_.get() acceptsFirstResponder]); 156 157 std::pair<NSEvent*, NSEvent*> clicks = 158 test_event_utils::MouseClickInView(rwhv_cocoa_.get(), 1); 159 [rwhv_cocoa_.get() mouseDown:clicks.first]; 160 EXPECT_EQ(rwhv_cocoa_.get(), [window firstResponder]); 161 } 162 163 // Regression test for http://crbug.com/64256 164 TEST_F(RenderWidgetHostViewMacTest, TakesFocusOnMouseDownWithAcceleratedView) { 165 // The accelerated view methods want to be called on the UI thread. 166 scoped_ptr<BrowserThread> ui_thread_( 167 new BrowserThread(BrowserThread::UI, MessageLoop::current())); 168 169 int w = 400, h = 300; 170 gfx::PluginWindowHandle accelerated_handle = AddAcceleratedPluginView(w, h); 171 EXPECT_FALSE([rwhv_cocoa_.get() isHidden]); 172 NSView* accelerated_view = static_cast<NSView*>( 173 rwhv_mac_->ViewForPluginWindowHandle(accelerated_handle)); 174 EXPECT_FALSE([accelerated_view isHidden]); 175 176 // Add the RWHVCocoa to the window and remove first responder status. 177 scoped_nsobject<CocoaTestHelperWindow> 178 window([[CocoaTestHelperWindow alloc] init]); 179 [[window contentView] addSubview:rwhv_cocoa_.get()]; 180 [window setPretendIsKeyWindow:YES]; 181 [window makeFirstResponder:nil]; 182 EXPECT_NE(rwhv_cocoa_.get(), [window firstResponder]); 183 184 // Tell the RWHVMac to not accept first responder status. The accelerated 185 // view should also stop accepting first responder. 186 rwhv_mac_->SetTakesFocusOnlyOnMouseDown(true); 187 EXPECT_FALSE([accelerated_view acceptsFirstResponder]); 188 189 // A click on the accelerated view should focus the RWHVCocoa. 190 std::pair<NSEvent*, NSEvent*> clicks = 191 test_event_utils::MouseClickInView(accelerated_view, 1); 192 [rwhv_cocoa_.get() mouseDown:clicks.first]; 193 EXPECT_EQ(rwhv_cocoa_.get(), [window firstResponder]); 194 195 // Clean up. 196 rwhv_mac_->DestroyFakePluginWindowHandle(accelerated_handle); 197 } 198