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