// Copyright 2014 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. #include "ui/views/widget/native_widget_mac.h" #import #import "base/mac/mac_util.h" #import "base/mac/scoped_nsobject.h" #include "base/macros.h" #import "ui/base/test/windowed_nsnotification_observer.h" #include "ui/views/test/test_widget_observer.h" #include "ui/views/test/widget_test.h" namespace views { namespace test { // Tests for NativeWidgetMac that rely on global window manager state, and can // not be parallelized. class NativeWidgetMacInteractiveUITest : public WidgetTest, public ::testing::WithParamInterface { public: class Observer; NativeWidgetMacInteractiveUITest() : activationCount_(0), deactivationCount_(0) {} Widget* MakeWidget() { return GetParam() ? CreateTopLevelFramelessPlatformWidget() : CreateTopLevelPlatformWidget(); } protected: scoped_ptr observer_; int activationCount_; int deactivationCount_; private: DISALLOW_COPY_AND_ASSIGN(NativeWidgetMacInteractiveUITest); }; class NativeWidgetMacInteractiveUITest::Observer : public TestWidgetObserver { public: Observer(NativeWidgetMacInteractiveUITest* parent, Widget* widget) : TestWidgetObserver(widget), parent_(parent) {} void OnWidgetActivationChanged(Widget* widget, bool active) override { if (active) parent_->activationCount_++; else parent_->deactivationCount_++; } private: NativeWidgetMacInteractiveUITest* parent_; DISALLOW_COPY_AND_ASSIGN(Observer); }; // Test that showing a window causes it to attain global keyWindow status. TEST_P(NativeWidgetMacInteractiveUITest, ShowAttainsKeyStatus) { Widget* widget = MakeWidget(); observer_.reset(new Observer(this, widget)); EXPECT_FALSE(widget->IsActive()); EXPECT_EQ(0, activationCount_); widget->Show(); EXPECT_TRUE(widget->IsActive()); RunPendingMessages(); EXPECT_TRUE([widget->GetNativeWindow() isKeyWindow]); EXPECT_EQ(1, activationCount_); EXPECT_EQ(0, deactivationCount_); // Now check that losing and gaining key status due events outside of Widget // works correctly. Widget* widget2 = MakeWidget(); // Note: not observed. EXPECT_EQ(0, deactivationCount_); widget2->Show(); EXPECT_EQ(1, deactivationCount_); RunPendingMessages(); EXPECT_FALSE(widget->IsActive()); EXPECT_EQ(1, deactivationCount_); EXPECT_EQ(1, activationCount_); [widget->GetNativeWindow() makeKeyAndOrderFront:nil]; RunPendingMessages(); EXPECT_TRUE(widget->IsActive()); EXPECT_EQ(1, deactivationCount_); EXPECT_EQ(2, activationCount_); widget2->CloseNow(); widget->CloseNow(); EXPECT_EQ(1, deactivationCount_); EXPECT_EQ(2, activationCount_); } // Test that ShowInactive does not take keyWindow status. TEST_P(NativeWidgetMacInteractiveUITest, ShowInactiveIgnoresKeyStatus) { Widget* widget = MakeWidget(); base::scoped_nsobject waiter( [[WindowedNSNotificationObserver alloc] initForNotification:NSWindowDidBecomeKeyNotification object:widget->GetNativeWindow()]); EXPECT_FALSE(widget->IsVisible()); EXPECT_FALSE([widget->GetNativeWindow() isVisible]); EXPECT_FALSE(widget->IsActive()); EXPECT_FALSE([widget->GetNativeWindow() isKeyWindow]); widget->ShowInactive(); EXPECT_TRUE(widget->IsVisible()); EXPECT_TRUE([widget->GetNativeWindow() isVisible]); EXPECT_FALSE(widget->IsActive()); EXPECT_FALSE([widget->GetNativeWindow() isKeyWindow]); // If the window were to become active, this would activate it. RunPendingMessages(); EXPECT_FALSE(widget->IsActive()); EXPECT_FALSE([widget->GetNativeWindow() isKeyWindow]); EXPECT_EQ(0, [waiter notificationCount]); // Activating the inactive widget should make it key, asynchronously. widget->Activate(); [waiter wait]; EXPECT_EQ(1, [waiter notificationCount]); EXPECT_TRUE(widget->IsActive()); EXPECT_TRUE([widget->GetNativeWindow() isKeyWindow]); widget->CloseNow(); } namespace { // Show |widget| and wait for it to become the key window. void ShowKeyWindow(Widget* widget) { base::scoped_nsobject waiter( [[WindowedNSNotificationObserver alloc] initForNotification:NSWindowDidBecomeKeyNotification object:widget->GetNativeWindow()]); widget->Show(); EXPECT_TRUE([waiter wait]); EXPECT_TRUE([widget->GetNativeWindow() isKeyWindow]); } NSData* ViewAsTIFF(NSView* view) { NSBitmapImageRep* bitmap = [view bitmapImageRepForCachingDisplayInRect:[view bounds]]; [view cacheDisplayInRect:[view bounds] toBitmapImageRep:bitmap]; return [bitmap TIFFRepresentation]; } } // namespace // Test that parent windows keep their traffic lights enabled when showing // dialogs. TEST_F(NativeWidgetMacInteractiveUITest, ParentWindowTrafficLights) { // Snow leopard doesn't have -[NSWindow _sharesParentKeyState]. if (base::mac::IsOSSnowLeopard()) return; Widget* parent_widget = CreateTopLevelPlatformWidget(); parent_widget->SetBounds(gfx::Rect(100, 100, 100, 100)); ShowKeyWindow(parent_widget); NSWindow* parent = parent_widget->GetNativeWindow(); EXPECT_TRUE([parent isMainWindow]); NSButton* button = [parent standardWindowButton:NSWindowCloseButton]; EXPECT_TRUE(button); NSData* active_button_image = ViewAsTIFF(button); EXPECT_TRUE(active_button_image); // Create an activatable frameless child. Frameless so that it doesn't have // traffic lights of its own, and activatable so that it can take key status. Widget* child_widget = new Widget; Widget::InitParams params(Widget::InitParams::TYPE_WINDOW_FRAMELESS); params.native_widget = new NativeWidgetMac(child_widget); params.bounds = gfx::Rect(130, 130, 100, 100); params.parent = parent_widget->GetNativeView(); child_widget->Init(params); ShowKeyWindow(child_widget); // Ensure the button instance is still valid. EXPECT_EQ(button, [parent standardWindowButton:NSWindowCloseButton]); // Parent window should still be main, and have its traffic lights active. EXPECT_TRUE([parent isMainWindow]); EXPECT_FALSE([parent isKeyWindow]); // Enabled status doesn't actually change, but check anyway. EXPECT_TRUE([button isEnabled]); NSData* button_image_with_child = ViewAsTIFF(button); EXPECT_TRUE([active_button_image isEqualToData:button_image_with_child]); // Verify that activating some other random window does change the button. Widget* other_widget = CreateTopLevelPlatformWidget(); other_widget->SetBounds(gfx::Rect(200, 200, 100, 100)); ShowKeyWindow(other_widget); EXPECT_FALSE([parent isMainWindow]); EXPECT_FALSE([parent isKeyWindow]); EXPECT_TRUE([button isEnabled]); NSData* inactive_button_image = ViewAsTIFF(button); EXPECT_FALSE([active_button_image isEqualToData:inactive_button_image]); other_widget->CloseNow(); parent_widget->CloseNow(); } INSTANTIATE_TEST_CASE_P(NativeWidgetMacInteractiveUITestInstance, NativeWidgetMacInteractiveUITest, ::testing::Bool()); } // namespace test } // namespace views