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 "chrome/browser/ui/panels/base_panel_browser_test.h" 6 7 #include "base/bind.h" 8 #include "base/command_line.h" 9 #include "base/memory/weak_ptr.h" 10 #include "base/message_loop/message_loop.h" 11 #include "base/path_service.h" 12 #include "base/strings/string_number_conversions.h" 13 #include "chrome/browser/chrome_notification_types.h" 14 #include "chrome/browser/extensions/extension_service.h" 15 #include "chrome/browser/profiles/profile.h" 16 #include "chrome/browser/ui/browser.h" 17 #include "chrome/browser/ui/browser_window.h" 18 #include "chrome/browser/ui/panels/detached_panel_collection.h" 19 #include "chrome/browser/ui/panels/native_panel.h" 20 #include "chrome/browser/ui/panels/panel_collection.h" 21 #include "chrome/browser/ui/panels/panel_mouse_watcher.h" 22 #include "chrome/browser/ui/panels/stacked_panel_collection.h" 23 #include "chrome/browser/ui/panels/test_panel_active_state_observer.h" 24 #include "chrome/browser/ui/panels/test_panel_mouse_watcher.h" 25 #include "chrome/common/chrome_paths.h" 26 #include "chrome/common/chrome_switches.h" 27 #include "chrome/test/base/interactive_test_utils.h" 28 #include "chrome/test/base/ui_test_utils.h" 29 #include "content/public/browser/notification_service.h" 30 #include "content/public/common/url_constants.h" 31 #include "content/public/test/web_contents_tester.h" 32 #include "extensions/browser/extension_prefs.h" 33 #include "extensions/browser/extension_system.h" 34 #include "extensions/browser/install_flag.h" 35 #include "extensions/common/manifest_constants.h" 36 #include "sync/api/string_ordinal.h" 37 38 #if defined(OS_LINUX) 39 #include "ui/base/x/x11_util.h" 40 #endif 41 42 #if defined(OS_MACOSX) 43 #include "base/mac/scoped_nsautorelease_pool.h" 44 #include "chrome/browser/ui/cocoa/run_loop_testing.h" 45 #endif 46 47 using content::WebContentsTester; 48 using extensions::Extension; 49 50 namespace { 51 52 const gfx::Rect kTestingPrimaryDisplayArea = gfx::Rect(0, 0, 800, 600); 53 const gfx::Rect kTestingPrimaryWorkArea = gfx::Rect(0, 0, 800, 580); 54 55 struct MockDesktopBar { 56 bool auto_hiding_enabled; 57 DisplaySettingsProvider::DesktopBarVisibility visibility; 58 int thickness; 59 }; 60 61 class MockDisplaySettingsProviderImpl : 62 public BasePanelBrowserTest::MockDisplaySettingsProvider { 63 public: 64 explicit MockDisplaySettingsProviderImpl(); 65 virtual ~MockDisplaySettingsProviderImpl() { } 66 67 // Overridden from DisplaySettingsProvider: 68 virtual gfx::Rect GetPrimaryDisplayArea() const OVERRIDE; 69 virtual gfx::Rect GetPrimaryWorkArea() const OVERRIDE; 70 virtual gfx::Rect GetDisplayAreaMatching( 71 const gfx::Rect& bounds) const OVERRIDE; 72 virtual gfx::Rect GetWorkAreaMatching( 73 const gfx::Rect& bounds) const OVERRIDE; 74 virtual bool IsAutoHidingDesktopBarEnabled( 75 DesktopBarAlignment alignment) OVERRIDE; 76 virtual int GetDesktopBarThickness( 77 DesktopBarAlignment alignment) const OVERRIDE; 78 virtual DesktopBarVisibility GetDesktopBarVisibility( 79 DesktopBarAlignment alignment) const OVERRIDE; 80 virtual bool IsFullScreen() OVERRIDE; 81 82 // Overridden from MockDisplaySettingsProvider: 83 virtual void SetPrimaryDisplay( 84 const gfx::Rect& display_area, const gfx::Rect& work_area) OVERRIDE; 85 virtual void SetSecondaryDisplay( 86 const gfx::Rect& display_area, const gfx::Rect& work_area) OVERRIDE; 87 virtual void EnableAutoHidingDesktopBar(DesktopBarAlignment alignment, 88 bool enabled, 89 int thickness) OVERRIDE; 90 virtual void SetDesktopBarVisibility( 91 DesktopBarAlignment alignment, DesktopBarVisibility visibility) OVERRIDE; 92 virtual void SetDesktopBarThickness(DesktopBarAlignment alignment, 93 int thickness) OVERRIDE; 94 virtual void EnableFullScreenMode(bool enabled) OVERRIDE; 95 96 private: 97 gfx::Rect primary_display_area_; 98 gfx::Rect primary_work_area_; 99 gfx::Rect secondary_display_area_; 100 gfx::Rect secondary_work_area_; 101 MockDesktopBar mock_desktop_bars[3]; 102 bool full_screen_enabled_; 103 104 DISALLOW_COPY_AND_ASSIGN(MockDisplaySettingsProviderImpl); 105 }; 106 107 108 MockDisplaySettingsProviderImpl::MockDisplaySettingsProviderImpl() 109 : full_screen_enabled_(false) { 110 memset(mock_desktop_bars, 0, sizeof(mock_desktop_bars)); 111 } 112 113 gfx::Rect MockDisplaySettingsProviderImpl::GetPrimaryDisplayArea() const { 114 return primary_display_area_; 115 } 116 117 gfx::Rect MockDisplaySettingsProviderImpl::GetPrimaryWorkArea() const { 118 return primary_work_area_; 119 } 120 121 gfx::Rect MockDisplaySettingsProviderImpl::GetDisplayAreaMatching( 122 const gfx::Rect& bounds) const { 123 if (secondary_display_area_.IsEmpty()) 124 return primary_display_area_; 125 126 gfx::Rect primary_intersection = 127 gfx::IntersectRects(bounds, primary_display_area_); 128 int primary_intersection_size = 129 primary_intersection.width() * primary_intersection.height(); 130 131 gfx::Rect secondary_intersection = 132 gfx::IntersectRects(bounds, secondary_display_area_); 133 int secondary_intersection_size = 134 secondary_intersection.width() * secondary_intersection.height(); 135 136 return primary_intersection_size >= secondary_intersection_size ? 137 primary_display_area_ : secondary_display_area_; 138 } 139 140 gfx::Rect MockDisplaySettingsProviderImpl::GetWorkAreaMatching( 141 const gfx::Rect& bounds) const { 142 if (secondary_work_area_.IsEmpty()) 143 return primary_work_area_; 144 145 gfx::Rect primary_intersection = 146 gfx::IntersectRects(bounds, primary_work_area_); 147 int primary_intersection_size = 148 primary_intersection.width() * primary_intersection.height(); 149 150 gfx::Rect secondary_intersection = 151 gfx::IntersectRects(bounds, secondary_work_area_); 152 int secondary_intersection_size = 153 secondary_intersection.width() * secondary_intersection.height(); 154 155 return primary_intersection_size >= secondary_intersection_size ? 156 primary_work_area_ : secondary_work_area_; 157 } 158 159 bool MockDisplaySettingsProviderImpl::IsAutoHidingDesktopBarEnabled( 160 DesktopBarAlignment alignment) { 161 return mock_desktop_bars[static_cast<int>(alignment)].auto_hiding_enabled; 162 } 163 164 int MockDisplaySettingsProviderImpl::GetDesktopBarThickness( 165 DesktopBarAlignment alignment) const { 166 return mock_desktop_bars[static_cast<int>(alignment)].thickness; 167 } 168 169 DisplaySettingsProvider::DesktopBarVisibility 170 MockDisplaySettingsProviderImpl::GetDesktopBarVisibility( 171 DesktopBarAlignment alignment) const { 172 return mock_desktop_bars[static_cast<int>(alignment)].visibility; 173 } 174 175 bool MockDisplaySettingsProviderImpl::IsFullScreen() { 176 return full_screen_enabled_; 177 } 178 179 void MockDisplaySettingsProviderImpl::EnableAutoHidingDesktopBar( 180 DesktopBarAlignment alignment, bool enabled, int thickness) { 181 MockDesktopBar* bar = &(mock_desktop_bars[static_cast<int>(alignment)]); 182 bar->auto_hiding_enabled = enabled; 183 bar->thickness = thickness; 184 } 185 186 void MockDisplaySettingsProviderImpl::SetPrimaryDisplay( 187 const gfx::Rect& display_area, const gfx::Rect& work_area) { 188 DCHECK(display_area.Contains(work_area)); 189 primary_display_area_ = display_area; 190 primary_work_area_ = work_area; 191 OnDisplaySettingsChanged(); 192 } 193 194 void MockDisplaySettingsProviderImpl::SetSecondaryDisplay( 195 const gfx::Rect& display_area, const gfx::Rect& work_area) { 196 DCHECK(display_area.Contains(work_area)); 197 secondary_display_area_ = display_area; 198 secondary_work_area_ = work_area; 199 OnDisplaySettingsChanged(); 200 } 201 202 void MockDisplaySettingsProviderImpl::SetDesktopBarVisibility( 203 DesktopBarAlignment alignment, DesktopBarVisibility visibility) { 204 MockDesktopBar* bar = &(mock_desktop_bars[static_cast<int>(alignment)]); 205 if (!bar->auto_hiding_enabled) 206 return; 207 if (visibility == bar->visibility) 208 return; 209 bar->visibility = visibility; 210 FOR_EACH_OBSERVER( 211 DesktopBarObserver, 212 desktop_bar_observers(), 213 OnAutoHidingDesktopBarVisibilityChanged(alignment, visibility)); 214 } 215 216 void MockDisplaySettingsProviderImpl::SetDesktopBarThickness( 217 DesktopBarAlignment alignment, int thickness) { 218 MockDesktopBar* bar = &(mock_desktop_bars[static_cast<int>(alignment)]); 219 if (!bar->auto_hiding_enabled) 220 return; 221 if (thickness == bar->thickness) 222 return; 223 bar->thickness = thickness; 224 FOR_EACH_OBSERVER( 225 DesktopBarObserver, 226 desktop_bar_observers(), 227 OnAutoHidingDesktopBarThicknessChanged(alignment, thickness)); 228 } 229 230 void MockDisplaySettingsProviderImpl::EnableFullScreenMode(bool enabled) { 231 full_screen_enabled_ = enabled; 232 CheckFullScreenMode(PERFORM_FULLSCREEN_CHECK); 233 } 234 235 } // namespace 236 237 const base::FilePath::CharType* BasePanelBrowserTest::kTestDir = 238 FILE_PATH_LITERAL("panels"); 239 240 BasePanelBrowserTest::BasePanelBrowserTest() 241 : InProcessBrowserTest(), 242 mock_display_settings_enabled_(true) { 243 } 244 245 BasePanelBrowserTest::~BasePanelBrowserTest() { 246 } 247 248 void BasePanelBrowserTest::SetUpCommandLine(CommandLine* command_line) { 249 command_line->AppendSwitch(switches::kEnablePanels); 250 } 251 252 void BasePanelBrowserTest::SetUpOnMainThread() { 253 InProcessBrowserTest::SetUpOnMainThread(); 254 255 // Setup the work area and desktop bar so that we have consistent testing 256 // environment for all panel related tests. 257 if (mock_display_settings_enabled_) { 258 mock_display_settings_provider_ = new MockDisplaySettingsProviderImpl(); 259 mock_display_settings_provider_->SetPrimaryDisplay( 260 kTestingPrimaryDisplayArea, kTestingPrimaryWorkArea); 261 PanelManager::SetDisplaySettingsProviderForTesting( 262 mock_display_settings_provider_); 263 } 264 265 PanelManager* panel_manager = PanelManager::GetInstance(); 266 panel_manager->enable_auto_sizing(false); 267 268 PanelManager::shorten_time_intervals_for_testing(); 269 270 // Simulate the mouse movement so that tests are not affected by actual mouse 271 // events. 272 PanelMouseWatcher* mouse_watcher = new TestPanelMouseWatcher(); 273 panel_manager->SetMouseWatcherForTesting(mouse_watcher); 274 275 // This is needed so the subsequently created panels can be activated. 276 // On a Mac, it transforms background-only test process into foreground one. 277 ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser())); 278 } 279 280 void BasePanelBrowserTest::WaitForPanelActiveState( 281 Panel* panel, ActiveState expected_state) { 282 DCHECK(expected_state == SHOW_AS_ACTIVE || 283 expected_state == SHOW_AS_INACTIVE); 284 285 #if defined(OS_MACOSX) 286 scoped_ptr<NativePanelTesting> panel_testing( 287 CreateNativePanelTesting(panel)); 288 ASSERT_TRUE(panel_testing->EnsureApplicationRunOnForeground()) << 289 "Failed to bring application to foreground. Bail out."; 290 #endif 291 292 PanelActiveStateObserver signal(panel, expected_state == SHOW_AS_ACTIVE); 293 signal.Wait(); 294 } 295 296 void BasePanelBrowserTest::WaitForWindowSizeAvailable(Panel* panel) { 297 scoped_ptr<NativePanelTesting> panel_testing( 298 CreateNativePanelTesting(panel)); 299 content::WindowedNotificationObserver signal( 300 chrome::NOTIFICATION_PANEL_WINDOW_SIZE_KNOWN, 301 content::Source<Panel>(panel)); 302 if (panel_testing->IsWindowSizeKnown()) 303 return; 304 signal.Wait(); 305 EXPECT_TRUE(panel_testing->IsWindowSizeKnown()); 306 } 307 308 void BasePanelBrowserTest::WaitForBoundsAnimationFinished(Panel* panel) { 309 scoped_ptr<NativePanelTesting> panel_testing( 310 CreateNativePanelTesting(panel)); 311 // Sometimes there are several animations in sequence due to content 312 // auto resizing. Wait for all animations to finish. 313 while (panel_testing->IsAnimatingBounds()) { 314 content::WindowedNotificationObserver signal( 315 chrome::NOTIFICATION_PANEL_BOUNDS_ANIMATIONS_FINISHED, 316 content::Source<Panel>(panel)); 317 if (!panel_testing->IsAnimatingBounds()) 318 return; 319 signal.Wait(); 320 } 321 } 322 323 BasePanelBrowserTest::CreatePanelParams::CreatePanelParams( 324 const std::string& name, 325 const gfx::Rect& bounds, 326 ActiveState show_flag) 327 : name(name), 328 bounds(bounds), 329 show_flag(show_flag), 330 wait_for_fully_created(true), 331 expected_active_state(show_flag), 332 create_mode(PanelManager::CREATE_AS_DOCKED), 333 profile(NULL) { 334 } 335 336 Panel* BasePanelBrowserTest::CreatePanelWithParams( 337 const CreatePanelParams& params) { 338 #if defined(OS_MACOSX) 339 // Opening panels on a Mac causes NSWindowController of the Panel window 340 // to be autoreleased. We need a pool drained after it's done so the test 341 // can close correctly. The NSWindowController of the Panel window controls 342 // lifetime of the Panel object so we want to release it as soon as 343 // possible. In real Chrome, this is done by message pump. 344 // On non-Mac platform, this is an empty class. 345 base::mac::ScopedNSAutoreleasePool autorelease_pool; 346 #endif 347 348 content::WindowedNotificationObserver observer( 349 content::NOTIFICATION_LOAD_STOP, 350 content::NotificationService::AllSources()); 351 352 PanelManager* manager = PanelManager::GetInstance(); 353 Panel* panel = manager->CreatePanel( 354 params.name, 355 params.profile ? params.profile : browser()->profile(), 356 params.url, 357 params.bounds, 358 params.create_mode); 359 360 if (!params.url.is_empty()) 361 observer.Wait(); 362 363 if (!manager->auto_sizing_enabled() || 364 params.bounds.width() || params.bounds.height()) { 365 EXPECT_FALSE(panel->auto_resizable()); 366 } else { 367 EXPECT_TRUE(panel->auto_resizable()); 368 } 369 370 if (params.show_flag == SHOW_AS_ACTIVE) { 371 panel->Show(); 372 } else { 373 panel->ShowInactive(); 374 } 375 376 if (params.wait_for_fully_created) { 377 base::MessageLoopForUI::current()->RunUntilIdle(); 378 379 #if defined(OS_LINUX) && defined(USE_X11) 380 // On bots, we might have a simple window manager which always activates new 381 // windows, and can't always deactivate them. Re-activate the main tabbed 382 // browser to "deactivate" the newly created panel. 383 if (params.expected_active_state == SHOW_AS_INACTIVE && 384 ui::GuessWindowManager() == ui::WM_ICE_WM) { 385 // Wait for new panel to become active before deactivating to ensure 386 // the activated notification is consumed before we wait for the panel 387 // to become inactive. 388 WaitForPanelActiveState(panel, SHOW_AS_ACTIVE); 389 browser()->window()->Activate(); 390 } 391 #endif 392 // More waiting, because gaining or losing focus may require inter-process 393 // asynchronous communication, and it is not enough to just run the local 394 // message loop to make sure this activity has completed. 395 WaitForPanelActiveState(panel, params.expected_active_state); 396 397 // On Linux, window size is not available right away and we should wait 398 // before moving forward with the test. 399 WaitForWindowSizeAvailable(panel); 400 401 // Wait for the bounds animations on creation to finish. 402 WaitForBoundsAnimationFinished(panel); 403 } 404 405 return panel; 406 } 407 408 Panel* BasePanelBrowserTest::CreatePanelWithBounds( 409 const std::string& panel_name, const gfx::Rect& bounds) { 410 CreatePanelParams params(panel_name, bounds, SHOW_AS_ACTIVE); 411 return CreatePanelWithParams(params); 412 } 413 414 Panel* BasePanelBrowserTest::CreatePanel(const std::string& panel_name) { 415 CreatePanelParams params(panel_name, gfx::Rect(), SHOW_AS_ACTIVE); 416 return CreatePanelWithParams(params); 417 } 418 419 Panel* BasePanelBrowserTest::CreateDockedPanel(const std::string& name, 420 const gfx::Rect& bounds) { 421 Panel* panel = CreatePanelWithBounds(name, bounds); 422 EXPECT_EQ(PanelCollection::DOCKED, panel->collection()->type()); 423 return panel; 424 } 425 426 Panel* BasePanelBrowserTest::CreateDetachedPanel(const std::string& name, 427 const gfx::Rect& bounds) { 428 Panel* panel = CreatePanelWithBounds(name, bounds); 429 PanelManager* panel_manager = panel->manager(); 430 panel_manager->MovePanelToCollection(panel, 431 panel_manager->detached_collection(), 432 PanelCollection::DEFAULT_POSITION); 433 EXPECT_EQ(PanelCollection::DETACHED, panel->collection()->type()); 434 // The panel is first created as docked panel, which ignores the specified 435 // origin in |bounds|. We need to reposition the panel after it becomes 436 // detached. 437 panel->SetPanelBounds(bounds); 438 WaitForBoundsAnimationFinished(panel); 439 return panel; 440 } 441 442 Panel* BasePanelBrowserTest::CreateStackedPanel(const std::string& name, 443 const gfx::Rect& bounds, 444 StackedPanelCollection* stack) { 445 Panel* panel = CreateDetachedPanel(name, bounds); 446 panel->manager()->MovePanelToCollection( 447 panel, 448 stack, 449 static_cast<PanelCollection::PositioningMask>( 450 PanelCollection::DEFAULT_POSITION | 451 PanelCollection::COLLAPSE_TO_FIT)); 452 EXPECT_EQ(PanelCollection::STACKED, panel->collection()->type()); 453 WaitForBoundsAnimationFinished(panel); 454 return panel; 455 } 456 457 Panel* BasePanelBrowserTest::CreateInactivePanel(const std::string& name) { 458 // Create an active panel first, instead of inactive panel. This is because 459 // certain window managers on Linux, like icewm, will always activate the 460 // new window. 461 Panel* panel = CreatePanel(name); 462 463 DeactivatePanel(panel); 464 WaitForPanelActiveState(panel, SHOW_AS_INACTIVE); 465 466 return panel; 467 } 468 469 Panel* BasePanelBrowserTest::CreateInactiveDockedPanel( 470 const std::string& name, const gfx::Rect& bounds) { 471 // Create an active panel first, instead of inactive panel. This is because 472 // certain window managers on Linux, like icewm, will always activate the 473 // new window. 474 Panel* panel = CreateDockedPanel(name, bounds); 475 476 DeactivatePanel(panel); 477 WaitForPanelActiveState(panel, SHOW_AS_INACTIVE); 478 479 return panel; 480 } 481 482 Panel* BasePanelBrowserTest::CreateInactiveDetachedPanel( 483 const std::string& name, const gfx::Rect& bounds) { 484 // Create an active panel first, instead of inactive panel. This is because 485 // certain window managers on Linux, like icewm, will always activate the 486 // new window. 487 Panel* panel = CreateDetachedPanel(name, bounds); 488 489 DeactivatePanel(panel); 490 WaitForPanelActiveState(panel, SHOW_AS_INACTIVE); 491 492 return panel; 493 } 494 495 void BasePanelBrowserTest::ActivatePanel(Panel* panel) { 496 // For certain window managers on Linux, the window activation/deactivation 497 // signals might not be sent. To work around this, we explicitly deactivate 498 // all other panels first. 499 #if defined(OS_LINUX) 500 std::vector<Panel*> panels = PanelManager::GetInstance()->panels(); 501 for (std::vector<Panel*>::const_iterator iter = panels.begin(); 502 iter != panels.end(); ++iter) { 503 Panel* current_panel = *iter; 504 if (panel != current_panel) 505 current_panel->Deactivate(); 506 } 507 #endif 508 509 panel->Activate(); 510 } 511 512 void BasePanelBrowserTest::DeactivatePanel(Panel* panel) { 513 #if defined(OS_LINUX) 514 // For certain window managers on Linux, like icewm, panel activation and 515 // deactivation notification might not get tiggered when non-panel window is 516 // activated or deactivated. So we deactivate the panel directly. 517 panel->Deactivate(); 518 #else 519 // Make the panel lose focus by activating the browser window. This is 520 // because: 521 // 1) On Windows, deactivating the panel window might cause the application 522 // to lose the foreground status. When this occurs, trying to activate 523 // the panel window again will not be allowed by the system. 524 // 2) On MacOS, deactivating a window is not supported by Cocoa. 525 browser()->window()->Activate(); 526 #endif 527 } 528 529 // static 530 NativePanelTesting* BasePanelBrowserTest::CreateNativePanelTesting( 531 Panel* panel) { 532 return panel->native_panel()->CreateNativePanelTesting(); 533 } 534 535 scoped_refptr<Extension> BasePanelBrowserTest::CreateExtension( 536 const base::FilePath::StringType& path, 537 extensions::Manifest::Location location, 538 const base::DictionaryValue& extra_value) { 539 extensions::ExtensionPrefs* extension_prefs = 540 extensions::ExtensionPrefs::Get(browser()->profile()); 541 base::FilePath full_path = extension_prefs->install_directory().Append(path); 542 543 scoped_ptr<base::DictionaryValue> input_value(extra_value.DeepCopy()); 544 input_value->SetString(extensions::manifest_keys::kVersion, "1.0.0.0"); 545 input_value->SetString(extensions::manifest_keys::kName, "Sample Extension"); 546 547 std::string error; 548 scoped_refptr<Extension> extension = Extension::Create( 549 full_path, location, *input_value, Extension::NO_FLAGS, &error); 550 EXPECT_TRUE(extension.get()); 551 EXPECT_STREQ("", error.c_str()); 552 extensions::ExtensionSystem::Get( 553 browser()->profile())->extension_service()->OnExtensionInstalled( 554 extension.get(), 555 syncer::StringOrdinal(), 556 extensions::kInstallFlagInstallImmediately); 557 return extension; 558 } 559 560 void BasePanelBrowserTest::CloseWindowAndWait(Panel* panel) { 561 // Closing a panel may involve several async tasks. Need to use 562 // message pump and wait for the notification. 563 PanelManager* manager = PanelManager::GetInstance(); 564 int panel_count = manager->num_panels(); 565 content::WindowedNotificationObserver signal( 566 chrome::NOTIFICATION_PANEL_CLOSED, 567 content::Source<Panel>(panel)); 568 panel->Close(); 569 signal.Wait(); 570 // Now we have one less panel. 571 EXPECT_EQ(panel_count - 1, manager->num_panels()); 572 573 #if defined(OS_MACOSX) 574 // Mac window controllers may be autoreleased, and in the non-test 575 // environment, may actually depend on the autorelease pool being recycled 576 // with the run loop in order to perform important work. Replicate this in 577 // the test environment. 578 AutoreleasePool()->Recycle(); 579 580 // Make sure that everything has a chance to run. 581 chrome::testing::NSRunLoopRunAllPending(); 582 #endif // OS_MACOSX 583 } 584 585 void BasePanelBrowserTest::MoveMouseAndWaitForExpansionStateChange( 586 Panel* panel, 587 const gfx::Point& position) { 588 content::WindowedNotificationObserver signal( 589 chrome::NOTIFICATION_PANEL_CHANGED_EXPANSION_STATE, 590 content::Source<Panel>(panel)); 591 MoveMouse(position); 592 signal.Wait(); 593 } 594 595 void BasePanelBrowserTest::MoveMouse(const gfx::Point& position) { 596 PanelManager::GetInstance()->mouse_watcher()->NotifyMouseMovement(position); 597 } 598 599 std::string BasePanelBrowserTest::MakePanelName(int index) { 600 std::string panel_name("Panel"); 601 return panel_name + base::IntToString(index); 602 } 603 604 bool BasePanelBrowserTest::WmSupportWindowActivation() { 605 return true; 606 } 607