| // Copyright (c) 2011 The Chromium OS 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 <gflags/gflags.h> |
| #include <gtest/gtest.h> |
| |
| #include <set> |
| #include <string> |
| #include <vector> |
| |
| #include "base/file_path.h" |
| #include "base/file_util.h" |
| #include "base/logging.h" |
| #include "base/stringprintf.h" |
| #include "cros/chromeos_wm_ipc_enums.h" |
| #include "window_manager/layout2/browser_window.h" |
| #include "window_manager/layout2/layout_manager2.h" |
| #include "window_manager/panels/panel.h" |
| #include "window_manager/panels/panel_manager.h" |
| #include "window_manager/shadow.h" |
| #include "window_manager/stacking_manager.h" |
| #include "window_manager/test_lib.h" |
| #include "window_manager/window.h" |
| |
| DEFINE_bool(logtostderr, false, |
| "Print debugging messages to stderr (suppressed otherwise)"); |
| |
| // Flags from layout/layout_manager2.cc: |
| DECLARE_string(initial_chrome_window_mapped_file); |
| DECLARE_bool(overlapping_windows); |
| DECLARE_bool(overlap_windows_by_default); |
| |
| using std::set; |
| using std::string; |
| using std::vector; |
| |
| namespace window_manager { |
| |
| class LayoutManager2Test : public BasicWindowManagerTest { |
| protected: |
| enum Side { |
| SIDE_LEFT = 0, |
| SIDE_RIGHT, |
| }; |
| |
| // Describes the configuration of browser windows at an edge of the screen. |
| enum EdgeConfiguration { |
| // There is a slice of an offscreen browser window visible at the edge of |
| // the screen. |
| EDGE_HAS_BROWSER = 0, |
| |
| // No slice is visible (i.e. the active or secondary browser window on that |
| // side of the screen is flush with the edge). |
| EDGE_HAS_NO_BROWSER, |
| }; |
| |
| // Mock implementation used to test that LayoutManager2 takes panel docks into |
| // account when determining how much of the screen to use. |
| class MockPanelManager : public PanelManagerAreaChangeNotifier { |
| public: |
| MockPanelManager() : left_width_(0), right_width_(0) {} |
| virtual ~MockPanelManager() {} |
| |
| // PanelManagerAreaChangeNotifier implementation. |
| virtual void RegisterAreaChangeListener( |
| PanelManagerAreaChangeListener* listener) { |
| ASSERT_TRUE(listeners_.insert(listener).second); |
| } |
| virtual void UnregisterAreaChangeListener( |
| PanelManagerAreaChangeListener* listener) { |
| ASSERT_EQ(static_cast<size_t>(1), listeners_.erase(listener)); |
| } |
| virtual void GetArea(int* left_width, int* right_width) const { |
| *left_width = left_width_; |
| *right_width = right_width_; |
| } |
| |
| void set_left_width(int width) { left_width_ = width; } |
| void set_right_width(int width) { right_width_ = width; } |
| |
| void NotifyListeners() { |
| for (set<PanelManagerAreaChangeListener*>::const_iterator it = |
| listeners_.begin(); it != listeners_.end(); ++it) { |
| (*it)->HandlePanelManagerAreaChange(); |
| } |
| } |
| |
| private: |
| int left_width_; |
| int right_width_; |
| set<PanelManagerAreaChangeListener*> listeners_; |
| |
| DISALLOW_COPY_AND_ASSIGN(MockPanelManager); |
| }; |
| |
| LayoutManager2* layout_manager() { return wm_->layout_manager_.get(); } |
| |
| // Computes the expected bounds of a browser window. |
| // Windows are laid out such that switching to a particular window effectively |
| // pans a viewport -- the windows' positions relative to each other don't |
| // change. |
| // |
| // |screen_bounds| contains a particular viewport. For either the active |
| // (focused) or secondary browser window, the viewport is equivalent to the |
| // root window's bounds. For offscreen windows, the viewport is shifted to |
| // the left or right. |
| // |
| // |side| represents the side of |screen_bounds| that the browser is on. |
| // |edge_config| describes whether a slice of the next offscreen browser |
| // window is visible at the edge of this window's side of the screen or not -- |
| // if so, the window is shifted slightly away from the edge. |
| Rect GetExpectedBoundsForBrowser( |
| const Rect& screen_bounds, |
| Side side, |
| EdgeConfiguration edge_config, |
| int browser_width) { |
| const int kGap = |
| (edge_config == EDGE_HAS_BROWSER) ? LayoutManager2::kEdgeGapPixels : 0; |
| |
| switch (side) { |
| case SIDE_LEFT: |
| return Rect( |
| screen_bounds.x + kGap, screen_bounds.y, |
| browser_width, screen_bounds.height); |
| case SIDE_RIGHT: |
| return Rect( |
| screen_bounds.right() - browser_width - kGap, screen_bounds.y, |
| browser_width, screen_bounds.height); |
| default: |
| NOTREACHED() << "Unknown side " << side; |
| return Rect(); |
| } |
| } |
| |
| // Drag the resize handle input window to the left or the right. |
| void DragResizeHandle(int dx) { |
| const XWindow kResizeXid = layout_manager()->resize_handle_xid_; |
| XEvent event; |
| xconn_->set_pointer_grab_xid(kResizeXid); |
| xconn_->InitButtonPressEvent(&event, kResizeXid, Point(0, 0), 1); |
| wm_->HandleEvent(&event); |
| xconn_->InitMotionNotifyEvent(&event, kResizeXid, Point(dx, 0)); |
| wm_->HandleEvent(&event); |
| xconn_->InitButtonReleaseEvent(&event, kResizeXid, Point(dx, 0), 1); |
| wm_->HandleEvent(&event); |
| } |
| |
| // Get the value of the root window's _CHROME_LAYOUT_MODE property, returning |
| // -1 if the property isn't present. |
| int GetLayoutModeProperty() { |
| int value = -1; |
| xconn_->GetIntProperty(xconn_->GetRootWindow(), |
| xconn_->GetAtomOrDie("_CHROME_LAYOUT_MODE"), |
| &value); |
| return value; |
| } |
| }; |
| |
| // Test that we set windows' horizontally- and vertically-maximized state. |
| TEST_F(LayoutManager2Test, LegacyMaximized) { |
| AutoReset<bool> overlapping_windows_flag_resetter( |
| &FLAGS_overlapping_windows, false); |
| CreateAndInitNewWm(); |
| |
| const XAtom kWmStateAtom = xconn_->GetAtomOrDie("_NET_WM_STATE"); |
| const XAtom kWmStateMaximizedHorzAtom = |
| xconn_->GetAtomOrDie("_NET_WM_STATE_MAXIMIZED_HORZ"); |
| const XAtom kWmStateMaximizedVertAtom = |
| xconn_->GetAtomOrDie("_NET_WM_STATE_MAXIMIZED_VERT"); |
| |
| XWindow xid = CreateSimpleWindow(); |
| ASSERT_TRUE(wm_->wm_ipc()->SetWindowType( |
| xid, chromeos::WM_IPC_WINDOW_CHROME_TOPLEVEL, NULL)); |
| SendInitialEventsForWindow(xid); |
| EXPECT_TRUE( |
| IntArrayPropertyContains(xid, kWmStateAtom, kWmStateMaximizedHorzAtom)); |
| EXPECT_TRUE( |
| IntArrayPropertyContains(xid, kWmStateAtom, kWmStateMaximizedVertAtom)); |
| } |
| |
| // Test the window-switching logic when windows are mapped and unmapped and when |
| // the Alt-Tab and Alt-Shift-Tab shortcuts are used. |
| TEST_F(LayoutManager2Test, LegacyWindowSwitching) { |
| AutoReset<bool> overlapping_windows_flag_resetter( |
| &FLAGS_overlapping_windows, false); |
| CreateAndInitNewWm(); |
| |
| const Rect kRootBounds = xconn_->root_bounds(); |
| const Rect kLeftOffscreenBounds( |
| Point(0 - kRootBounds.width, 0), kRootBounds.size()); |
| const Rect kRightOffscreenBounds( |
| Point(kRootBounds.width, 0), kRootBounds.size()); |
| const Rect kFarRightOffscreenBounds( |
| Point(2 * kRootBounds.width, 0), kRootBounds.size()); |
| |
| // Create a window and check that it gets focused and is resized to cover the |
| // entire screen. |
| XWindow xid = CreateSimpleWindow(); |
| ASSERT_TRUE(wm_->wm_ipc()->SetWindowType( |
| xid, chromeos::WM_IPC_WINDOW_CHROME_TOPLEVEL, NULL)); |
| SendInitialEventsForWindow(xid); |
| EXPECT_EQ(xid, xconn_->focused_xid()); |
| EXPECT_EQ(kRootBounds, xconn_->GetWindowInfoOrDie(xid)->bounds); |
| EXPECT_EQ(kRootBounds, GetCompositedWindowBounds(xid)); |
| |
| // Create a second window and check that it's focused and |xid| is pushed |
| // offscreen to the left. |
| XWindow xid2 = CreateSimpleWindow(); |
| SendInitialEventsForWindow(xid2); |
| EXPECT_EQ(xid2, xconn_->focused_xid()); |
| EXPECT_EQ(kLeftOffscreenBounds, xconn_->GetWindowInfoOrDie(xid)->bounds); |
| EXPECT_EQ(kLeftOffscreenBounds, GetCompositedWindowBounds(xid)); |
| EXPECT_EQ(kRootBounds, xconn_->GetWindowInfoOrDie(xid2)->bounds); |
| EXPECT_EQ(kRootBounds, GetCompositedWindowBounds(xid2)); |
| |
| // After sending Alt-Tab, we should wrap around and switch back to |xid|. |
| const KeyBindings::KeyCombo kAltTabKeyCombo(XK_Tab, KeyBindings::kAltMask); |
| const XTime kEventTime = wm_->GetCurrentTimeFromServer(); |
| SendKey(xconn_->GetRootWindow(), kAltTabKeyCombo, kEventTime, kEventTime); |
| EXPECT_EQ(xid, xconn_->focused_xid()); |
| EXPECT_EQ(kRootBounds, xconn_->GetWindowInfoOrDie(xid)->bounds); |
| EXPECT_EQ(kRootBounds, GetCompositedWindowBounds(xid)); |
| EXPECT_EQ(kRightOffscreenBounds, xconn_->GetWindowInfoOrDie(xid2)->bounds); |
| EXPECT_EQ(kRightOffscreenBounds, GetCompositedWindowBounds(xid2)); |
| |
| // Alt-Shift-Tab should make us wrap around the left edge to |xid2|. |
| const KeyBindings::KeyCombo kAltShiftTabKeyCombo( |
| XK_Tab, KeyBindings::kShiftMask | KeyBindings::kAltMask); |
| SendKey(xconn_->GetRootWindow(), |
| kAltShiftTabKeyCombo, kEventTime, kEventTime); |
| EXPECT_EQ(xconn_->focused_xid(), xid2); |
| EXPECT_EQ(kLeftOffscreenBounds, xconn_->GetWindowInfoOrDie(xid)->bounds); |
| EXPECT_EQ(kLeftOffscreenBounds, GetCompositedWindowBounds(xid)); |
| EXPECT_EQ(kRootBounds, xconn_->GetWindowInfoOrDie(xid2)->bounds); |
| EXPECT_EQ(kRootBounds, GetCompositedWindowBounds(xid2)); |
| |
| // Send a _NET_ACTIVE_WINDOW message asking to move back to |xid|. |
| SendActiveWindowMessage(xid); |
| EXPECT_EQ(xconn_->focused_xid(), xid); |
| EXPECT_EQ(kRootBounds, xconn_->GetWindowInfoOrDie(xid)->bounds); |
| EXPECT_EQ(kRootBounds, GetCompositedWindowBounds(xid)); |
| EXPECT_EQ(kRightOffscreenBounds, xconn_->GetWindowInfoOrDie(xid2)->bounds); |
| EXPECT_EQ(kRightOffscreenBounds, GetCompositedWindowBounds(xid2)); |
| |
| // If we open another window, it should be placed between the other two, |
| // giving us the window order |xid|, |xid3|, |xid2|, with |xid3| focused. |
| XWindow xid3 = CreateSimpleWindow(); |
| SendInitialEventsForWindow(xid3); |
| EXPECT_EQ(xid3, xconn_->focused_xid()); |
| EXPECT_EQ(kLeftOffscreenBounds, xconn_->GetWindowInfoOrDie(xid)->bounds); |
| EXPECT_EQ(kLeftOffscreenBounds, GetCompositedWindowBounds(xid)); |
| EXPECT_EQ(kRootBounds, xconn_->GetWindowInfoOrDie(xid3)->bounds); |
| EXPECT_EQ(kRootBounds, GetCompositedWindowBounds(xid3)); |
| EXPECT_EQ(kRightOffscreenBounds, xconn_->GetWindowInfoOrDie(xid2)->bounds); |
| EXPECT_EQ(kRightOffscreenBounds, GetCompositedWindowBounds(xid2)); |
| |
| // If |xid3| is unmapped, we should switch to the window to its left, |xid|. |
| XEvent event; |
| xconn_->InitUnmapEvent(&event, xid3); |
| wm_->HandleEvent(&event); |
| EXPECT_EQ(xid, xconn_->focused_xid()); |
| EXPECT_EQ(kRootBounds, xconn_->GetWindowInfoOrDie(xid)->bounds); |
| EXPECT_EQ(kRootBounds, GetCompositedWindowBounds(xid)); |
| EXPECT_EQ(kRightOffscreenBounds, xconn_->GetWindowInfoOrDie(xid2)->bounds); |
| EXPECT_EQ(kRightOffscreenBounds, GetCompositedWindowBounds(xid2)); |
| |
| // Now unmap |xid| and check that we switch to |xid2|. |
| xconn_->InitUnmapEvent(&event, xid); |
| wm_->HandleEvent(&event); |
| EXPECT_EQ(xid2, xconn_->focused_xid()); |
| EXPECT_EQ(kRootBounds, xconn_->GetWindowInfoOrDie(xid2)->bounds); |
| EXPECT_EQ(kRootBounds, GetCompositedWindowBounds(xid2)); |
| |
| // Finally, unmap |xid2|. |
| xconn_->InitUnmapEvent(&event, xid2); |
| wm_->HandleEvent(&event); |
| } |
| |
| TEST_F(LayoutManager2Test, LegacyTransients) { |
| AutoReset<bool> overlapping_windows_flag_resetter( |
| &FLAGS_overlapping_windows, false); |
| CreateAndInitNewWm(); |
| |
| const XAtom kWmStateAtom = xconn_->GetAtomOrDie("_NET_WM_STATE"); |
| const XAtom kWmStateModalAtom = xconn_->GetAtomOrDie("_NET_WM_STATE_MODAL"); |
| |
| // Map two windows. |
| XWindow xid = CreateSimpleWindow(); |
| SendInitialEventsForWindow(xid); |
| XWindow xid2 = CreateSimpleWindow(); |
| SendInitialEventsForWindow(xid2); |
| ASSERT_EQ(xid2, xconn_->focused_xid()); |
| |
| // Map a transient window on behalf of |xid| and check that we don't switch to |
| // it automatically. |
| const Rect kInitialTransientBounds(0, 0, 300, 200); |
| XWindow transient_xid = CreateBasicWindow(kInitialTransientBounds); |
| MockXConnection::WindowInfo* transient_info = |
| xconn_->GetWindowInfoOrDie(transient_xid); |
| transient_info->transient_for = xid; |
| transient_info->depth = 24; |
| SendInitialEventsForWindow(transient_xid); |
| EXPECT_EQ(xid2, xconn_->focused_xid()); |
| Window* transient_win = wm_->GetWindowOrDie(transient_xid); |
| EXPECT_EQ(Window::VISIBILITY_HIDDEN, transient_win->visibility()); |
| |
| // We should draw a shadow under the window since it's 24-bit. |
| EXPECT_TRUE(transient_win->shadow() != NULL); |
| |
| // Send a _NET_ACTIVE_WINDOW message asking the WM to focus |transient_xid| |
| // and check that it focuses it and switches to |xid|. |
| SendActiveWindowMessage(transient_xid); |
| EXPECT_EQ(transient_xid, xconn_->focused_xid()); |
| const Rect kCenteredTransientBounds( |
| (xconn_->root_bounds().width - kInitialTransientBounds.width) / 2, |
| (xconn_->root_bounds().height - kInitialTransientBounds.height) / 2, |
| kInitialTransientBounds.width, kInitialTransientBounds.height); |
| EXPECT_EQ(kCenteredTransientBounds, |
| xconn_->GetWindowInfoOrDie(transient_xid)->bounds); |
| EXPECT_EQ(kCenteredTransientBounds, GetCompositedWindowBounds(transient_xid)); |
| EXPECT_EQ(Window::VISIBILITY_SHOWN, transient_win->visibility()); |
| EXPECT_EQ(xconn_->root_bounds(), xconn_->GetWindowInfoOrDie(xid)->bounds); |
| EXPECT_EQ(xconn_->root_bounds(), GetCompositedWindowBounds(xid)); |
| |
| // Map an initially-modal transient window for |xid2|. |
| XWindow transient_xid2 = CreateSimpleWindow(); |
| MockXConnection::WindowInfo* transient_info2 = |
| xconn_->GetWindowInfoOrDie(transient_xid2); |
| transient_info2->transient_for = xid2; |
| transient_info2->depth = 32; |
| AppendAtomToProperty(transient_xid2, kWmStateAtom, kWmStateModalAtom); |
| SendInitialEventsForWindow(transient_xid2); |
| Window* transient_win2 = wm_->GetWindowOrDie(transient_xid2); |
| |
| // The second window shouldn't get a shadow since it's 32-bit. |
| EXPECT_TRUE(transient_win2->shadow() == NULL); |
| |
| // We should focus |transient_xid2| and switch to |xid2|. |
| EXPECT_EQ(transient_xid2, xconn_->focused_xid()); |
| EXPECT_EQ(xconn_->root_bounds(), xconn_->GetWindowInfoOrDie(xid2)->bounds); |
| EXPECT_EQ(xconn_->root_bounds(), GetCompositedWindowBounds(xid2)); |
| EXPECT_EQ(Window::VISIBILITY_HIDDEN, transient_win->visibility()); |
| EXPECT_EQ(Window::VISIBILITY_SHOWN, transient_win2->visibility()); |
| |
| // Create another (non-modal) transient window for |xid2| and check that |
| // |transient_xid2| remains focused (since it's modal) and is stacked on top |
| // of |transient_xid3|. We also say that the window supports |
| // WM_DELETE_WINDOW; we use this for another test later. |
| XWindow transient_xid3 = CreateSimpleWindow(); |
| MockXConnection::WindowInfo* transient_info3 = |
| xconn_->GetWindowInfoOrDie(transient_xid3); |
| transient_info3->transient_for = xid2; |
| AppendAtomToProperty(transient_xid3, |
| xconn_->GetAtomOrDie("WM_PROTOCOLS"), |
| xconn_->GetAtomOrDie("WM_DELETE_WINDOW")); |
| SendInitialEventsForWindow(transient_xid3); |
| Window* transient_win3 = wm_->GetWindowOrDie(transient_xid3); |
| EXPECT_EQ(transient_xid2, xconn_->focused_xid()); |
| EXPECT_TRUE(TestWindowIsAboveOtherWindow(transient_win2, transient_win3)); |
| EXPECT_EQ(Window::VISIBILITY_HIDDEN, transient_win->visibility()); |
| EXPECT_EQ(Window::VISIBILITY_SHOWN, transient_win2->visibility()); |
| EXPECT_EQ(Window::VISIBILITY_SHOWN, transient_win3->visibility()); |
| |
| // We should also ignore _NET_ACTIVE_WINDOW messages about |transient_xid| |
| // (again, since |transient_xid2| is modal). |
| SendActiveWindowMessage(transient_xid); |
| EXPECT_EQ(transient_xid2, xconn_->focused_xid()); |
| EXPECT_EQ(xconn_->root_bounds(), xconn_->GetWindowInfoOrDie(xid2)->bounds); |
| EXPECT_EQ(xconn_->root_bounds(), GetCompositedWindowBounds(xid2)); |
| |
| // We should also ignore clicks on |transient_xid3|. |
| xconn_->set_pointer_grab_xid(transient_xid3); |
| XEvent event; |
| xconn_->InitButtonPressEvent(&event, transient_xid3, Point(0, 0), 1); |
| wm_->HandleEvent(&event); |
| EXPECT_EQ(0, xconn_->pointer_grab_xid()); |
| EXPECT_EQ(transient_xid2, xconn_->focused_xid()); |
| |
| // Alt-Tab shouldn't do anything, either. |
| const KeyBindings::KeyCombo kAltTabKeyCombo(XK_Tab, KeyBindings::kAltMask); |
| const XTime kEventTime = wm_->GetCurrentTimeFromServer(); |
| SendKey(xconn_->GetRootWindow(), kAltTabKeyCombo, kEventTime, kEventTime); |
| EXPECT_EQ(transient_xid2, xconn_->focused_xid()); |
| |
| // Send an event asking for |transient_xid3| to be made modal. It should be |
| // focused and raised on top of |transient_xid2| (see |
| // http://crosbug.com/17642). |
| SendWmStateMessage(transient_xid3, kWmStateModalAtom, true); |
| EXPECT_EQ(transient_xid3, xconn_->focused_xid()); |
| EXPECT_TRUE(TestWindowIsAboveOtherWindow(transient_win3, transient_win2)); |
| |
| // Send an event asking for |transient_xid3| to be made non-modal. |
| // Since |transient_xid2| is still modal, it should now be focused and on top. |
| SendWmStateMessage(transient_xid3, kWmStateModalAtom, false); |
| EXPECT_EQ(transient_xid2, xconn_->focused_xid()); |
| EXPECT_TRUE(TestWindowIsAboveOtherWindow(transient_win2, transient_win3)); |
| |
| // Now make |transient_xid2| non-modal as well. |
| SendWmStateMessage(transient_xid2, kWmStateModalAtom, false); |
| EXPECT_EQ(transient_xid2, xconn_->focused_xid()); |
| EXPECT_TRUE(TestWindowIsAboveOtherWindow(transient_win2, transient_win3)); |
| |
| // Click on |xid2| and check that it takes the focus. |
| xconn_->set_pointer_grab_xid(xid2); |
| xconn_->InitButtonPressEvent(&event, xid2, Point(0, 0), 1); |
| wm_->HandleEvent(&event); |
| EXPECT_EQ(0, xconn_->pointer_grab_xid()); |
| EXPECT_EQ(xid2, xconn_->focused_xid()); |
| |
| // Click on |transient_xid3| again. This time, it should be focused and |
| // raised above |transient_xid2|. |
| xconn_->set_pointer_grab_xid(transient_xid3); |
| xconn_->InitButtonPressEvent(&event, transient_xid3, Point(0, 0), 1); |
| wm_->HandleEvent(&event); |
| EXPECT_EQ(0, xconn_->pointer_grab_xid()); |
| EXPECT_EQ(transient_xid3, xconn_->focused_xid()); |
| EXPECT_TRUE(TestWindowIsAboveOtherWindow(transient_win3, transient_win2)); |
| |
| // Send a message asking for |transient_xid| to be made modal. |
| // We should focus it and automatically switch to |xid|. |
| SendWmStateMessage(transient_xid, kWmStateModalAtom, true); |
| EXPECT_EQ(transient_xid, xconn_->focused_xid()); |
| EXPECT_EQ(xconn_->root_bounds(), xconn_->GetWindowInfoOrDie(xid)->bounds); |
| EXPECT_EQ(xconn_->root_bounds(), GetCompositedWindowBounds(xid)); |
| EXPECT_EQ(Window::VISIBILITY_SHOWN, transient_win->visibility()); |
| EXPECT_EQ(Window::VISIBILITY_HIDDEN, transient_win2->visibility()); |
| EXPECT_EQ(Window::VISIBILITY_HIDDEN, transient_win3->visibility()); |
| |
| // Unmap |transient_xid| and check that |xid| gets the focus. |
| xconn_->InitUnmapEvent(&event, transient_xid); |
| wm_->HandleEvent(&event); |
| EXPECT_EQ(xid, xconn_->focused_xid()); |
| |
| // Unmap |xid2| and check that |transient_xid3| got a delete request -- we |
| // should try to close a browser window's transients when the browser window |
| // is unmapped. |
| ASSERT_EQ(0, GetNumDeleteWindowMessagesForWindow(transient_xid3)); |
| xconn_->InitUnmapEvent(&event, xid2); |
| wm_->HandleEvent(&event); |
| EXPECT_EQ(1, GetNumDeleteWindowMessagesForWindow(transient_xid3)); |
| |
| // Map an info bubble and check that its initial position is preserved. |
| const Rect kInitialBubbleBounds(20, 30, 40, 50); |
| XWindow bubble_xid = CreateBasicWindow(kInitialBubbleBounds); |
| ASSERT_TRUE(wm_->wm_ipc()->SetWindowType( |
| bubble_xid, |
| chromeos::WM_IPC_WINDOW_CHROME_INFO_BUBBLE, |
| NULL)); |
| MockXConnection::WindowInfo* bubble_info = |
| xconn_->GetWindowInfoOrDie(bubble_xid); |
| bubble_info->transient_for = xid; |
| SendInitialEventsForWindow(bubble_xid); |
| EXPECT_EQ(bubble_xid, xconn_->focused_xid()); |
| EXPECT_EQ(kInitialBubbleBounds, bubble_info->bounds); |
| EXPECT_EQ(kInitialBubbleBounds, GetCompositedWindowBounds(bubble_xid)); |
| |
| // Ask the WM to move and resize the bubble, and check that it does. |
| const Rect kNewBubbleBounds(60, 70, 80, 90); |
| xconn_->InitConfigureRequestEvent(&event, bubble_xid, kNewBubbleBounds); |
| wm_->HandleEvent(&event); |
| EXPECT_EQ(kNewBubbleBounds, bubble_info->bounds); |
| xconn_->InitConfigureNotifyEvent(&event, bubble_xid); |
| wm_->HandleEvent(&event); |
| EXPECT_EQ(kNewBubbleBounds, GetCompositedWindowBounds(bubble_xid)); |
| } |
| |
| TEST_F(LayoutManager2Test, LegacyFullscreen) { |
| AutoReset<bool> overlapping_windows_flag_resetter( |
| &FLAGS_overlapping_windows, false); |
| CreateAndInitNewWm(); |
| |
| const XAtom kWmStateAtom = xconn_->GetAtomOrDie("_NET_WM_STATE"); |
| const XAtom kWmStateFullscreenAtom = |
| xconn_->GetAtomOrDie("_NET_WM_STATE_FULLSCREEN"); |
| |
| // Create and map a window. |
| XWindow xid = CreateSimpleWindow(); |
| SendInitialEventsForWindow(xid); |
| Window* win = wm_->GetWindowOrDie(xid); |
| MockXConnection::WindowInfo* info = xconn_->GetWindowInfoOrDie(xid); |
| |
| // Request that the window be made fullscreen. Check that the fullscreen hint |
| // is set on it and that it's restacked. |
| SendWmStateMessage(xid, kWmStateFullscreenAtom, true); |
| EXPECT_EQ(xid, xconn_->focused_xid()); |
| EXPECT_EQ(wm_->root_bounds(), info->bounds); |
| EXPECT_EQ(wm_->root_bounds(), GetCompositedWindowBounds(xid)); |
| EXPECT_TRUE( |
| IntArrayPropertyContains(xid, kWmStateAtom, kWmStateFullscreenAtom)); |
| EXPECT_TRUE(WindowIsInLayer(win, StackingManager::LAYER_FULLSCREEN_WINDOW)); |
| |
| // Map a second window. It should take the focus, and the first window should |
| // be unfullscreened. |
| XWindow xid2 = CreateSimpleWindow(); |
| SendInitialEventsForWindow(xid2); |
| EXPECT_EQ(xid2, xconn_->focused_xid()); |
| EXPECT_FALSE( |
| IntArrayPropertyContains(xid, kWmStateAtom, kWmStateFullscreenAtom)); |
| |
| // Map a third, already-fullscreen window. |
| XWindow xid3 = CreateSimpleWindow(); |
| AppendAtomToProperty(xid3, kWmStateAtom, kWmStateFullscreenAtom); |
| SendInitialEventsForWindow(xid3); |
| Window* win3 = wm_->GetWindowOrDie(xid); |
| MockXConnection::WindowInfo* info3 = xconn_->GetWindowInfoOrDie(xid3); |
| EXPECT_EQ(xid3, xconn_->focused_xid()); |
| EXPECT_EQ(wm_->root_bounds(), info3->bounds); |
| EXPECT_EQ(wm_->root_bounds(), GetCompositedWindowBounds(xid3)); |
| EXPECT_TRUE( |
| IntArrayPropertyContains(xid3, kWmStateAtom, kWmStateFullscreenAtom)); |
| EXPECT_TRUE(WindowIsInLayer(win3, StackingManager::LAYER_FULLSCREEN_WINDOW)); |
| |
| // Now press Alt-Tab and check that we unfullscreen the third window and |
| // switch back to the first window. |
| const KeyBindings::KeyCombo kAltTabKeyCombo(XK_Tab, KeyBindings::kAltMask); |
| const XTime kEventTime = wm_->GetCurrentTimeFromServer(); |
| SendKey(xconn_->GetRootWindow(), kAltTabKeyCombo, kEventTime, kEventTime); |
| EXPECT_EQ(xid, xconn_->focused_xid()); |
| EXPECT_TRUE( |
| WindowIsInLayer(win, StackingManager::LAYER_ACTIVE_BROWSER_WINDOW)); |
| EXPECT_FALSE( |
| IntArrayPropertyContains(xid3, kWmStateAtom, kWmStateFullscreenAtom)); |
| |
| // Send a message to make the first window fullscreen again... |
| SendWmStateMessage(xid, kWmStateFullscreenAtom, true); |
| EXPECT_TRUE(WindowIsInLayer(win, StackingManager::LAYER_FULLSCREEN_WINDOW)); |
| EXPECT_TRUE( |
| IntArrayPropertyContains(xid, kWmStateAtom, kWmStateFullscreenAtom)); |
| |
| // ... and then send a message to make it not fullscreen. |
| SendWmStateMessage(xid, kWmStateFullscreenAtom, false); |
| EXPECT_TRUE( |
| WindowIsInLayer(win, StackingManager::LAYER_ACTIVE_BROWSER_WINDOW)); |
| EXPECT_FALSE( |
| IntArrayPropertyContains(xid, kWmStateAtom, kWmStateFullscreenAtom)); |
| |
| // Creating a panel (which will take the focus) should also unfullscreen the |
| // browser. |
| SendWmStateMessage(xid, kWmStateFullscreenAtom, true); |
| Panel* panel = CreatePanel(200, 20, 400); |
| ASSERT_EQ(panel->content_xid(), xconn_->focused_xid()); |
| EXPECT_FALSE( |
| IntArrayPropertyContains(xid, kWmStateAtom, kWmStateFullscreenAtom)); |
| |
| // Check that we don't crash when a fullscreen window is unmapped. |
| SendWmStateMessage(xid, kWmStateFullscreenAtom, true); |
| XEvent event; |
| xconn_->InitUnmapEvent(&event, xid); |
| wm_->HandleEvent(&event); |
| } |
| |
| TEST_F(LayoutManager2Test, ResizeScreen) { |
| const Rect kInitialRootBounds = xconn_->root_bounds(); |
| XWindow xid = CreateSimpleWindow(); |
| MockXConnection::WindowInfo* info = xconn_->GetWindowInfoOrDie(xid); |
| SendInitialEventsForWindow(xid); |
| EXPECT_EQ(kInitialRootBounds, xconn_->GetWindowInfoOrDie(xid)->bounds); |
| EXPECT_EQ(kInitialRootBounds, GetCompositedWindowBounds(xid)); |
| |
| // Resize the root window and check that the browser gets resized. |
| const Rect kNewRootBounds( |
| 0, 0, kInitialRootBounds.width * 2, kInitialRootBounds.height * 2); |
| xconn_->ResizeWindow(xconn_->GetRootWindow(), kNewRootBounds.size()); |
| XEvent event; |
| xconn_->InitConfigureNotifyEvent(&event, xconn_->GetRootWindow()); |
| wm_->HandleEvent(&event); |
| EXPECT_EQ(kNewRootBounds, info->bounds); |
| SendConfigureNotifyEvent(xid); |
| EXPECT_EQ(kNewRootBounds, GetCompositedWindowBounds(xid)); |
| |
| // Register a mock panel manager and check that the window is reconfigured |
| // appropriately. |
| MockPanelManager panel_manager; |
| layout_manager()->SetPanelAreaNotifier(&panel_manager); |
| const int kLeftPanelWidth = 20; |
| const int kRightPanelWidth = 40; |
| panel_manager.set_left_width(kLeftPanelWidth); |
| panel_manager.set_right_width(kRightPanelWidth); |
| panel_manager.NotifyListeners(); |
| const Rect kAvailableBounds( |
| kLeftPanelWidth, 0, |
| kNewRootBounds.width - (kLeftPanelWidth + kRightPanelWidth), |
| kNewRootBounds.height); |
| EXPECT_EQ(kAvailableBounds, info->bounds); |
| SendConfigureNotifyEvent(xid); |
| EXPECT_EQ(kAvailableBounds, GetCompositedWindowBounds(xid)); |
| |
| // Create a second browser and check that both are constrained within the |
| // available area. |
| XWindow xid2 = CreateSimpleWindow(); |
| MockXConnection::WindowInfo* info2 = xconn_->GetWindowInfoOrDie(xid2); |
| SendInitialEventsForWindow(xid2); |
| EXPECT_EQ( |
| GetExpectedBoundsForBrowser( |
| kAvailableBounds, SIDE_LEFT, EDGE_HAS_NO_BROWSER, |
| layout_manager()->FindBrowserWindowByXid(xid)->unmaximized_width()), |
| info->bounds); |
| EXPECT_EQ( |
| GetExpectedBoundsForBrowser( |
| kAvailableBounds, SIDE_RIGHT, EDGE_HAS_NO_BROWSER, |
| layout_manager()->FindBrowserWindowByXid(xid2)->unmaximized_width()), |
| info2->bounds); |
| |
| layout_manager()->SetPanelAreaNotifier(NULL); |
| } |
| |
| TEST_F(LayoutManager2Test, LegacyStackShadowsAtBottomOfLayer) { |
| AutoReset<bool> overlapping_windows_flag_resetter( |
| &FLAGS_overlapping_windows, false); |
| CreateAndInitNewWm(); |
| |
| // Map two windows. The second should be focused. |
| XWindow xid = CreateSimpleWindow(); |
| SendInitialEventsForWindow(xid); |
| Window* win = wm_->GetWindowOrDie(xid); |
| XWindow xid2 = CreateSimpleWindow(); |
| SendInitialEventsForWindow(xid2); |
| Window* win2 = wm_->GetWindowOrDie(xid2); |
| ASSERT_EQ(xid2, xconn_->focused_xid()); |
| |
| // The first window should be stacked under the second (focused) window, but |
| // both windows' shadows should be under the first window. |
| MockCompositor::StageActor* stage = compositor_->GetDefaultStage(); |
| EXPECT_LT(stage->GetStackingIndex(win2->actor()), |
| stage->GetStackingIndex(win->actor())); |
| EXPECT_LT(stage->GetStackingIndex(win->actor()), |
| stage->GetStackingIndex(win->shadow()->group())); |
| EXPECT_LT(stage->GetStackingIndex(win->actor()), |
| stage->GetStackingIndex(win2->shadow()->group())); |
| } |
| |
| // Test that the layout manager handles windows that claim to be transient |
| // for already-transient windows reasonably -- see http://crosbug.com/3316. |
| TEST_F(LayoutManager2Test, NestedTransients) { |
| XWindow xid = CreateSimpleWindow(); |
| SendInitialEventsForWindow(xid); |
| |
| // Map a transient window. |
| XWindow transient_xid = CreateSimpleWindow(); |
| xconn_->GetWindowInfoOrDie(transient_xid)->transient_for = xid; |
| SendInitialEventsForWindow(transient_xid); |
| EXPECT_EQ(transient_xid, xconn_->focused_xid()); |
| EXPECT_EQ(Window::VISIBILITY_SHOWN, |
| wm_->GetWindowOrDie(transient_xid)->visibility()); |
| |
| // Now map another window that's transient for the first transient window, and |
| // check that it gets shown and takes the focus. |
| XWindow transient_xid2 = CreateSimpleWindow(); |
| xconn_->GetWindowInfoOrDie(transient_xid2)->transient_for = transient_xid; |
| SendInitialEventsForWindow(transient_xid2); |
| EXPECT_EQ(transient_xid2, xconn_->focused_xid()); |
| EXPECT_EQ(Window::VISIBILITY_SHOWN, |
| wm_->GetWindowOrDie(transient_xid2)->visibility()); |
| } |
| |
| TEST_F(LayoutManager2Test, InitialBrowserWindow) { |
| ScopedTempDirectory temp_dir; |
| FilePath initial_file_path = |
| temp_dir.path().Append("initial_chrome_window_mapped"); |
| |
| AutoReset<string> file_flag_resetter( |
| &FLAGS_initial_chrome_window_mapped_file, initial_file_path.value()); |
| AutoReset<bool> overlapping_windows_flag_resetter( |
| &FLAGS_overlapping_windows, false); |
| CreateAndInitNewWm(); |
| |
| EXPECT_FALSE(file_util::PathExists(initial_file_path)); |
| EXPECT_FALSE(layout_manager()->key_bindings_group_->enabled()); |
| |
| // Check that the first window doesn't get animated sliding onscreen. |
| XWindow xid = CreateSimpleWindow(); |
| SendInitialEventsForWindow(xid); |
| Window* win = wm_->GetWindowOrDie(xid); |
| EXPECT_FALSE(GetMockActorForWindow(win)->position_was_animated()); |
| |
| // We should write the first browser window's ID to a file after it's mapped. |
| EXPECT_TRUE(file_util::PathExists(initial_file_path)); |
| string file_contents; |
| EXPECT_TRUE(file_util::ReadFileToString(initial_file_path, &file_contents)); |
| EXPECT_EQ(StringPrintf("%lu", xid), file_contents); |
| |
| // We should also enable our key bindings after the first window is mapped. |
| EXPECT_TRUE(layout_manager()->key_bindings_group_->enabled()); |
| |
| // Create a second window and check that both are animated now. |
| XWindow xid2 = CreateSimpleWindow(); |
| SendInitialEventsForWindow(xid2); |
| Window* win2 = wm_->GetWindowOrDie(xid2); |
| EXPECT_TRUE(GetMockActorForWindow(win)->position_was_animated()); |
| EXPECT_TRUE(GetMockActorForWindow(win2)->position_was_animated()); |
| } |
| |
| TEST_F(LayoutManager2Test, CloseTransientWindowWhenOwnerIsUnmapped) { |
| XWindow owner_xid = CreateSimpleWindow(); |
| SendInitialEventsForWindow(owner_xid); |
| |
| XWindow transient_xid = CreateSimpleWindow(); |
| // Say that we support the WM_DELETE_WINDOW protocol. |
| AppendAtomToProperty(transient_xid, |
| xconn_->GetAtomOrDie("WM_PROTOCOLS"), |
| xconn_->GetAtomOrDie("WM_DELETE_WINDOW")); |
| xconn_->GetWindowInfoOrDie(transient_xid)->transient_for = owner_xid; |
| SendInitialEventsForWindow(transient_xid); |
| |
| // After we unmap the owner, the transient should receive a delete request. |
| ASSERT_EQ(0, GetNumDeleteWindowMessagesForWindow(transient_xid)); |
| XEvent event; |
| xconn_->InitUnmapEvent(&event, owner_xid); |
| wm_->HandleEvent(&event); |
| ASSERT_EQ(1, GetNumDeleteWindowMessagesForWindow(transient_xid)); |
| } |
| |
| TEST_F(LayoutManager2Test, MultipleWindowLayout) { |
| // Create and map four browser windows. |
| XWindow xid1 = CreateSimpleWindow(); |
| MockXConnection::WindowInfo* info1 = xconn_->GetWindowInfoOrDie(xid1); |
| SendInitialEventsForWindow(xid1); |
| BrowserWindow* browser1 = layout_manager()->FindBrowserWindowByXid(xid1); |
| ASSERT_TRUE(browser1 != NULL); |
| |
| XWindow xid2 = CreateSimpleWindow(); |
| MockXConnection::WindowInfo* info2 = xconn_->GetWindowInfoOrDie(xid2); |
| SendInitialEventsForWindow(xid2); |
| BrowserWindow* browser2 = layout_manager()->FindBrowserWindowByXid(xid2); |
| ASSERT_TRUE(browser2 != NULL); |
| |
| XWindow xid3 = CreateSimpleWindow(); |
| MockXConnection::WindowInfo* info3 = xconn_->GetWindowInfoOrDie(xid3); |
| SendInitialEventsForWindow(xid3); |
| BrowserWindow* browser3 = layout_manager()->FindBrowserWindowByXid(xid3); |
| ASSERT_TRUE(browser3 != NULL); |
| |
| XWindow xid4 = CreateSimpleWindow(); |
| MockXConnection::WindowInfo* info4 = xconn_->GetWindowInfoOrDie(xid4); |
| SendInitialEventsForWindow(xid4); |
| ASSERT_EQ(xconn_->focused_xid(), xid4); |
| BrowserWindow* browser4 = layout_manager()->FindBrowserWindowByXid(xid4); |
| ASSERT_TRUE(browser4 != NULL); |
| |
| const int kInitialWidth = layout_manager()->GetInitialUnmaximizedWidth(); |
| const int kGap = LayoutManager2::kEdgeGapPixels; |
| const Rect kRootBounds = xconn_->root_bounds(); |
| const double kSecondaryBrightness = |
| LayoutManager2::kSecondaryBrowserWindowBrightness; |
| const double kEdgeBrightness = LayoutManager2::kEdgeBrowserWindowBrightness; |
| |
| // Initially, the fourth window should be focused on the right, with the third |
| // window visible on the left and a strip of the second window visible at the |
| // left edge. |
| // +-+-----+-------+ |
| // | | | | |
| // |2| 3 | 4* | |
| // | | | | |
| // +-+-----+-------+ |
| EXPECT_EQ(xconn_->focused_xid(), xid4); |
| |
| Rect left_bounds = |
| GetExpectedBoundsForBrowser( |
| kRootBounds, SIDE_LEFT, EDGE_HAS_BROWSER, kInitialWidth); |
| Rect right_bounds = |
| GetExpectedBoundsForBrowser( |
| kRootBounds, SIDE_RIGHT, EDGE_HAS_NO_BROWSER, kInitialWidth); |
| Rect left_offscreen_bounds = |
| GetExpectedBoundsForBrowser( |
| Rect(left_bounds.right() + kGap - kRootBounds.width, 0, |
| kRootBounds.width, kRootBounds.height), |
| SIDE_LEFT, EDGE_HAS_BROWSER, kInitialWidth); |
| Rect far_left_offscreen_bounds = |
| GetExpectedBoundsForBrowser( |
| Rect(left_offscreen_bounds.right() + kGap - kRootBounds.width, 0, |
| kRootBounds.width, kRootBounds.height), |
| SIDE_LEFT, EDGE_HAS_NO_BROWSER, kInitialWidth); |
| |
| EXPECT_EQ(far_left_offscreen_bounds, info1->bounds); |
| EXPECT_EQ(left_offscreen_bounds, info2->bounds); |
| EXPECT_EQ(left_bounds, info3->bounds); |
| EXPECT_EQ(right_bounds, info4->bounds); |
| |
| EXPECT_TRUE(TestWindowIsAboveOtherWindow(browser4->win(), browser3->win())); |
| EXPECT_TRUE(TestWindowIsAboveOtherWindow(browser3->win(), browser2->win())); |
| EXPECT_TRUE(TestWindowIsAboveOtherWindow(browser2->win(), browser1->win())); |
| |
| EXPECT_DOUBLE_EQ(kEdgeBrightness, browser1->brightness()); |
| EXPECT_DOUBLE_EQ(kEdgeBrightness, browser2->brightness()); |
| EXPECT_DOUBLE_EQ(kSecondaryBrightness, browser3->brightness()); |
| EXPECT_DOUBLE_EQ(1.0, browser4->brightness()); |
| |
| // Switch back to the second window, which should result in it being focused |
| // on the left, the third window being focused on the right, and strips of the |
| // first and fourth windows visible at the left and right edges of the screen, |
| // respectively. |
| // +-+-------+---+-+ |
| // | | | | | |
| // |1| 2* | 3 |4| |
| // | | | | | |
| // +-+-------+---+-+ |
| SendActiveWindowMessage(xid2); |
| EXPECT_EQ(xconn_->focused_xid(), xid2); |
| |
| left_bounds = |
| GetExpectedBoundsForBrowser( |
| kRootBounds, SIDE_LEFT, EDGE_HAS_BROWSER, kInitialWidth); |
| right_bounds = |
| GetExpectedBoundsForBrowser( |
| kRootBounds, SIDE_RIGHT, EDGE_HAS_BROWSER, kInitialWidth); |
| left_offscreen_bounds = |
| GetExpectedBoundsForBrowser( |
| Rect(left_bounds.right() + kGap - kRootBounds.width, 0, |
| kRootBounds.width, kRootBounds.height), |
| SIDE_LEFT, EDGE_HAS_NO_BROWSER, kInitialWidth); |
| Rect right_offscreen_bounds = |
| GetExpectedBoundsForBrowser( |
| Rect(right_bounds.x - kGap, 0, |
| kRootBounds.width, kRootBounds.height), |
| SIDE_RIGHT, EDGE_HAS_NO_BROWSER, kInitialWidth); |
| |
| EXPECT_EQ(left_offscreen_bounds, info1->bounds); |
| EXPECT_EQ(left_bounds, info2->bounds); |
| EXPECT_EQ(right_bounds, info3->bounds); |
| EXPECT_EQ(right_offscreen_bounds, info4->bounds); |
| |
| EXPECT_TRUE(TestWindowIsAboveOtherWindow(browser2->win(), browser3->win())); |
| EXPECT_TRUE(TestWindowIsAboveOtherWindow(browser3->win(), browser4->win())); |
| EXPECT_TRUE(TestWindowIsAboveOtherWindow(browser2->win(), browser1->win())); |
| // The relative stacking of the first and fourth browsers isn't important. |
| |
| EXPECT_DOUBLE_EQ(kEdgeBrightness, browser1->brightness()); |
| EXPECT_DOUBLE_EQ(1.0, browser2->brightness()); |
| EXPECT_DOUBLE_EQ(kSecondaryBrightness, browser3->brightness()); |
| EXPECT_DOUBLE_EQ(kEdgeBrightness, browser4->brightness()); |
| |
| // Finally, switch to the first window. |
| // +-------+-----+-+ |
| // | | | | |
| // | 1* | 2 |3| |
| // | | | | |
| // +-------+-----+-+ |
| SendActiveWindowMessage(xid1); |
| EXPECT_EQ(xconn_->focused_xid(), xid1); |
| |
| left_bounds = |
| GetExpectedBoundsForBrowser( |
| kRootBounds, SIDE_LEFT, EDGE_HAS_NO_BROWSER, kInitialWidth); |
| right_bounds = |
| GetExpectedBoundsForBrowser( |
| kRootBounds, SIDE_RIGHT, EDGE_HAS_BROWSER, kInitialWidth); |
| right_offscreen_bounds = |
| GetExpectedBoundsForBrowser( |
| Rect(right_bounds.x - kGap, 0, |
| kRootBounds.width, kRootBounds.height), |
| SIDE_RIGHT, EDGE_HAS_BROWSER, kInitialWidth); |
| Rect far_right_offscreen_bounds = |
| GetExpectedBoundsForBrowser( |
| Rect(right_offscreen_bounds.x - kGap, 0, |
| kRootBounds.width, kRootBounds.height), |
| SIDE_RIGHT, EDGE_HAS_NO_BROWSER, kInitialWidth); |
| |
| EXPECT_EQ(left_bounds, info1->bounds); |
| EXPECT_EQ(right_bounds, info2->bounds); |
| EXPECT_EQ(right_offscreen_bounds, info3->bounds); |
| EXPECT_EQ(far_right_offscreen_bounds, info4->bounds); |
| |
| EXPECT_TRUE(TestWindowIsAboveOtherWindow(browser1->win(), browser2->win())); |
| EXPECT_TRUE(TestWindowIsAboveOtherWindow(browser2->win(), browser3->win())); |
| EXPECT_TRUE(TestWindowIsAboveOtherWindow(browser3->win(), browser4->win())); |
| |
| EXPECT_DOUBLE_EQ(1.0, browser1->brightness()); |
| EXPECT_DOUBLE_EQ(kSecondaryBrightness, browser2->brightness()); |
| EXPECT_DOUBLE_EQ(kEdgeBrightness, browser3->brightness()); |
| EXPECT_DOUBLE_EQ(kEdgeBrightness, browser4->brightness()); |
| } |
| |
| TEST_F(LayoutManager2Test, ClickToActivateBrowser) { |
| // Create and map three browser windows. |
| XWindow xid1 = CreateSimpleWindow(); |
| MockXConnection::WindowInfo* info1 = xconn_->GetWindowInfoOrDie(xid1); |
| SendInitialEventsForWindow(xid1); |
| |
| XWindow xid2 = CreateSimpleWindow(); |
| MockXConnection::WindowInfo* info2 = xconn_->GetWindowInfoOrDie(xid2); |
| SendInitialEventsForWindow(xid2); |
| |
| XWindow xid3 = CreateSimpleWindow(); |
| MockXConnection::WindowInfo* info3 = xconn_->GetWindowInfoOrDie(xid3); |
| SendInitialEventsForWindow(xid3); |
| |
| // The third window should be focused initially. |
| const int kInitialWidth = layout_manager()->GetInitialUnmaximizedWidth(); |
| const Rect kRootBounds = xconn_->root_bounds(); |
| EXPECT_EQ(xid3, xconn_->focused_xid()); |
| EXPECT_EQ(GetExpectedBoundsForBrowser( |
| kRootBounds, SIDE_RIGHT, EDGE_HAS_NO_BROWSER, kInitialWidth), |
| info3->bounds); |
| |
| // When we click on the input window at the left edge of the screen, we should |
| // activate the first browser window. |
| XEvent event; |
| xconn_->InitButtonPressEvent( |
| &event, layout_manager()->left_edge_input_xid_, Point(0, 0), 1); |
| wm_->HandleEvent(&event); |
| EXPECT_EQ(xid1, xconn_->focused_xid()); |
| EXPECT_EQ(GetExpectedBoundsForBrowser( |
| kRootBounds, SIDE_LEFT, EDGE_HAS_NO_BROWSER, kInitialWidth), |
| info1->bounds); |
| |
| // Clicking on the (onscreen) second browser window should activate it. |
| // The click should also be passed through. |
| int num_replay_ungrabs = xconn_->num_pointer_ungrabs_with_replayed_events(); |
| xconn_->set_pointer_grab_xid(xid2); |
| xconn_->InitButtonPressEvent(&event, xid2, Point(0, 0), 1); |
| wm_->HandleEvent(&event); |
| EXPECT_EQ(0, xconn_->pointer_grab_xid()); |
| EXPECT_EQ(num_replay_ungrabs + 1, |
| xconn_->num_pointer_ungrabs_with_replayed_events()); |
| EXPECT_EQ(xid2, xconn_->focused_xid()); |
| EXPECT_EQ(GetExpectedBoundsForBrowser( |
| kRootBounds, SIDE_RIGHT, EDGE_HAS_BROWSER, kInitialWidth), |
| info2->bounds); |
| } |
| |
| TEST_F(LayoutManager2Test, ResizeBrowser) { |
| XWindow xid1 = CreateSimpleWindow(); |
| MockXConnection::WindowInfo* info1 = xconn_->GetWindowInfoOrDie(xid1); |
| SendInitialEventsForWindow(xid1); |
| |
| XWindow xid2 = CreateSimpleWindow(); |
| MockXConnection::WindowInfo* info2 = xconn_->GetWindowInfoOrDie(xid2); |
| SendInitialEventsForWindow(xid2); |
| |
| const Rect kRootBounds = xconn_->root_bounds(); |
| |
| // Make the second browser window smaller by dragging the resize handle to the |
| // right. |
| int new_width = info2->bounds.width - 50; |
| DragResizeHandle(-1 * (new_width - info2->bounds.width)); |
| EXPECT_EQ(GetExpectedBoundsForBrowser( |
| kRootBounds, SIDE_RIGHT, EDGE_HAS_NO_BROWSER, new_width), |
| info2->bounds); |
| |
| // Now drag it back to the the left. |
| new_width = info2->bounds.width + 25; |
| DragResizeHandle(-1 * (new_width - info2->bounds.width)); |
| EXPECT_EQ(GetExpectedBoundsForBrowser( |
| kRootBounds, SIDE_RIGHT, EDGE_HAS_NO_BROWSER, new_width), |
| info2->bounds); |
| |
| // Do the same thing with the first browser on the left side of the screen. |
| SendActiveWindowMessage(xid1); |
| EXPECT_EQ(xconn_->focused_xid(), xid1); |
| new_width = info1->bounds.width - 60; |
| DragResizeHandle(new_width - info1->bounds.width); |
| EXPECT_EQ(GetExpectedBoundsForBrowser( |
| kRootBounds, SIDE_LEFT, EDGE_HAS_NO_BROWSER, new_width), |
| info1->bounds); |
| |
| new_width = info1->bounds.width + 30; |
| DragResizeHandle(new_width - info1->bounds.width); |
| EXPECT_EQ(GetExpectedBoundsForBrowser( |
| kRootBounds, SIDE_LEFT, EDGE_HAS_NO_BROWSER, new_width), |
| info1->bounds); |
| |
| // Try resizing via the keyboard as well. |
| const KeyBindings::KeyCombo kAltCommaKeyCombo( |
| XK_comma, KeyBindings::kAltMask); |
| const XTime kEventTime = wm_->GetCurrentTimeFromServer(); |
| int old_width = info1->bounds.width; |
| SendKey(xconn_->GetRootWindow(), kAltCommaKeyCombo, kEventTime, kEventTime); |
| EXPECT_LT(info1->bounds.width, old_width); |
| |
| const KeyBindings::KeyCombo kAltPeriodKeyCombo( |
| XK_period, KeyBindings::kAltMask); |
| old_width = info1->bounds.width; |
| SendKey(xconn_->GetRootWindow(), kAltPeriodKeyCombo, kEventTime, kEventTime); |
| EXPECT_GT(info1->bounds.width, old_width); |
| |
| // Check that we can switch to maximized mode by dragging the resize handle |
| // all the way over. |
| DragResizeHandle(kRootBounds.width); |
| EXPECT_TRUE(layout_manager()->maximized()); |
| EXPECT_EQ(kRootBounds, info1->bounds); |
| } |
| |
| TEST_F(LayoutManager2Test, ResizeHandleActor) { |
| // Create and map two browser windows. |
| XWindow xid1 = CreateSimpleWindow(); |
| MockXConnection::WindowInfo* info1 = xconn_->GetWindowInfoOrDie(xid1); |
| SendInitialEventsForWindow(xid1); |
| |
| XWindow xid2 = CreateSimpleWindow(); |
| MockXConnection::WindowInfo* info2 = xconn_->GetWindowInfoOrDie(xid2); |
| SendInitialEventsForWindow(xid2); |
| ASSERT_EQ(xid2, xconn_->focused_xid()); |
| |
| // The resize handle actor should initially be invisible. |
| Compositor::Actor* actor = layout_manager()->resize_handle_actor_.get(); |
| EXPECT_DOUBLE_EQ(0.0, actor->GetOpacity()); |
| |
| // When the pointer enters the resize handle input window, the actor should |
| // become visible, and its right edge should be aligned with the left edge of |
| // the second browser window. |
| XEvent event; |
| xconn_->InitEnterWindowEvent( |
| &event, layout_manager()->resize_handle_xid_, Point(0, 0)); |
| wm_->HandleEvent(&event); |
| EXPECT_GT(actor->GetOpacity(), 0.0); |
| EXPECT_EQ(info2->bounds.left(), actor->GetBounds().right()); |
| |
| // Drag the resize handle a pixel to the left, making the browser window |
| // bigger. The actor's position should be updated. |
| DragResizeHandle(-1); |
| EXPECT_GT(actor->GetOpacity(), 0.0); |
| EXPECT_EQ(info2->bounds.left(), actor->GetBounds().right()); |
| |
| // Check that dragging to the right works as well. |
| DragResizeHandle(2); |
| EXPECT_GT(actor->GetOpacity(), 0.0); |
| EXPECT_EQ(info2->bounds.left(), actor->GetBounds().right()); |
| |
| // When the pointer leaves the input window, the actor should be invisible. |
| xconn_->InitLeaveWindowEvent( |
| &event, layout_manager()->resize_handle_xid_, Point(0, 0)); |
| wm_->HandleEvent(&event); |
| EXPECT_DOUBLE_EQ(0.0, actor->GetOpacity()); |
| |
| // Focus the first window, move the pointer into the handle, and check that |
| // the actor's left edge lines up with the browser window's right edge. |
| SendActiveWindowMessage(xid1); |
| ASSERT_EQ(xid1, xconn_->focused_xid()); |
| xconn_->InitEnterWindowEvent( |
| &event, layout_manager()->resize_handle_xid_, Point(0, 0)); |
| wm_->HandleEvent(&event); |
| EXPECT_GT(actor->GetOpacity(), 0.0); |
| EXPECT_EQ(info1->bounds.right(), actor->GetBounds().x); |
| |
| // Drag the handle in both directions again. |
| DragResizeHandle(-1); |
| EXPECT_GT(actor->GetOpacity(), 0.0); |
| EXPECT_EQ(info1->bounds.right(), actor->GetBounds().x); |
| DragResizeHandle(2); |
| EXPECT_GT(actor->GetOpacity(), 0.0); |
| EXPECT_EQ(info1->bounds.right(), actor->GetBounds().x); |
| } |
| |
| TEST_F(LayoutManager2Test, SetLayoutMode) { |
| // We should start out in maximized mode. |
| EXPECT_TRUE(layout_manager()->maximized()); |
| EXPECT_EQ(chromeos::WM_IPC_LAYOUT_MAXIMIZED, GetLayoutModeProperty()); |
| |
| XWindow xid1 = CreateSimpleWindow(); |
| SendInitialEventsForWindow(xid1); |
| EXPECT_EQ(chromeos::WM_IPC_LAYOUT_MAXIMIZED, layout_manager()->layout_mode()); |
| EXPECT_EQ(chromeos::WM_IPC_LAYOUT_MAXIMIZED, GetLayoutModeProperty()); |
| |
| // F5 shouldn't do anything when there's only a single browser. |
| const KeyBindings::KeyCombo kF5KeyCombo(XK_F5, 0); |
| const XTime kEventTime = wm_->GetCurrentTimeFromServer(); |
| SendKey(xconn_->GetRootWindow(), kF5KeyCombo, kEventTime, kEventTime); |
| EXPECT_EQ(chromeos::WM_IPC_LAYOUT_MAXIMIZED, layout_manager()->layout_mode()); |
| EXPECT_EQ(chromeos::WM_IPC_LAYOUT_MAXIMIZED, GetLayoutModeProperty()); |
| |
| // After a second window is mapped, we should switch to overlapping mode. |
| XWindow xid2 = CreateSimpleWindow(); |
| SendInitialEventsForWindow(xid2); |
| EXPECT_EQ(chromeos::WM_IPC_LAYOUT_OVERLAPPING, |
| layout_manager()->layout_mode()); |
| EXPECT_EQ(chromeos::WM_IPC_LAYOUT_OVERLAPPING, GetLayoutModeProperty()); |
| |
| // Press F5 to switch to maximized mode. |
| SendKey(xconn_->GetRootWindow(), kF5KeyCombo, kEventTime, kEventTime); |
| EXPECT_EQ(chromeos::WM_IPC_LAYOUT_MAXIMIZED, layout_manager()->layout_mode()); |
| EXPECT_EQ(chromeos::WM_IPC_LAYOUT_MAXIMIZED, GetLayoutModeProperty()); |
| |
| // If we map another window while we already have multiple windows but aren't |
| // maximized (i.e. the user requested maximized mode), we should remain |
| // maximized. |
| XWindow xid3 = CreateSimpleWindow(); |
| SendInitialEventsForWindow(xid3); |
| EXPECT_EQ(chromeos::WM_IPC_LAYOUT_MAXIMIZED, layout_manager()->layout_mode()); |
| EXPECT_EQ(chromeos::WM_IPC_LAYOUT_MAXIMIZED, GetLayoutModeProperty()); |
| |
| // Press F5 to switch back to overlapping mode. |
| SendKey(xconn_->GetRootWindow(), kF5KeyCombo, kEventTime, kEventTime); |
| EXPECT_EQ(chromeos::WM_IPC_LAYOUT_OVERLAPPING, |
| layout_manager()->layout_mode()); |
| EXPECT_EQ(chromeos::WM_IPC_LAYOUT_OVERLAPPING, GetLayoutModeProperty()); |
| |
| // Send a message asking to switch to maximized mode. |
| WmIpc::Message msg(chromeos::WM_IPC_MESSAGE_WM_SET_LAYOUT_MODE); |
| msg.set_param(0, chromeos::WM_IPC_LAYOUT_MAXIMIZED); |
| SendWmIpcMessage(msg); |
| EXPECT_EQ(chromeos::WM_IPC_LAYOUT_MAXIMIZED, layout_manager()->layout_mode()); |
| EXPECT_EQ(chromeos::WM_IPC_LAYOUT_MAXIMIZED, GetLayoutModeProperty()); |
| |
| // Now switch back to overlapping. |
| msg.set_param(0, chromeos::WM_IPC_LAYOUT_OVERLAPPING); |
| SendWmIpcMessage(msg); |
| EXPECT_EQ(chromeos::WM_IPC_LAYOUT_OVERLAPPING, |
| layout_manager()->layout_mode()); |
| EXPECT_EQ(chromeos::WM_IPC_LAYOUT_OVERLAPPING, GetLayoutModeProperty()); |
| |
| // Sending a message asking for overlapping mode while we're already in it |
| // shouldn't do anything. |
| SendWmIpcMessage(msg); |
| EXPECT_EQ(chromeos::WM_IPC_LAYOUT_OVERLAPPING, |
| layout_manager()->layout_mode()); |
| EXPECT_EQ(chromeos::WM_IPC_LAYOUT_OVERLAPPING, GetLayoutModeProperty()); |
| |
| // Messages with bogus modes should be ignored too. |
| msg.set_param(0, -1); |
| SendWmIpcMessage(msg); |
| EXPECT_EQ(chromeos::WM_IPC_LAYOUT_OVERLAPPING, |
| layout_manager()->layout_mode()); |
| EXPECT_EQ(chromeos::WM_IPC_LAYOUT_OVERLAPPING, GetLayoutModeProperty()); |
| |
| // We should automatically go back to maximized mode if we only have a single |
| // window mapped. |
| XEvent event; |
| xconn_->InitUnmapEvent(&event, xid3); |
| wm_->HandleEvent(&event); |
| xconn_->InitUnmapEvent(&event, xid2); |
| wm_->HandleEvent(&event); |
| EXPECT_EQ(chromeos::WM_IPC_LAYOUT_MAXIMIZED, layout_manager()->layout_mode()); |
| EXPECT_EQ(chromeos::WM_IPC_LAYOUT_MAXIMIZED, GetLayoutModeProperty()); |
| } |
| |
| // Test that we maximize and unmaximize individual browser windows in the |
| // correct order when changing the layout mode. |
| TEST_F(LayoutManager2Test, AvoidJankOnLayoutModeChange) { |
| // Create and map two windows that support _NET_WM_SYNC_REQUEST. |
| XWindow xid1 = CreateSimpleWindow(); |
| MockXConnection::WindowInfo* info1 = xconn_->GetWindowInfoOrDie(xid1); |
| ConfigureWindowForSyncRequestProtocol(xid1); |
| SendInitialEventsForWindow(xid1); |
| SendSyncRequestProtocolAlarm(xid1); |
| |
| XWindow xid2 = CreateSimpleWindow(); |
| MockXConnection::WindowInfo* info2 = xconn_->GetWindowInfoOrDie(xid2); |
| ConfigureWindowForSyncRequestProtocol(xid2); |
| SendInitialEventsForWindow(xid2); |
| SendSyncRequestProtocolAlarm(xid2); |
| |
| ASSERT_FALSE(layout_manager()->maximized()); |
| |
| const int kInitialWidth = layout_manager()->GetInitialUnmaximizedWidth(); |
| |
| const Rect kRootBounds = xconn_->root_bounds(); |
| const Rect kLeftOffscreenBounds( |
| Point(0 - kRootBounds.width, 0), kRootBounds.size()); |
| const Rect kLeftUnmaximizedBounds = |
| GetExpectedBoundsForBrowser( |
| kRootBounds, SIDE_LEFT, EDGE_HAS_NO_BROWSER, kInitialWidth); |
| const Rect kRightUnmaximizedBounds = |
| GetExpectedBoundsForBrowser( |
| kRootBounds, SIDE_RIGHT, EDGE_HAS_NO_BROWSER, kInitialWidth); |
| |
| // Press F5 to switch to maximized mode. |
| const KeyBindings::KeyCombo kF5KeyCombo(XK_F5, 0); |
| const XTime kEventTime = wm_->GetCurrentTimeFromServer(); |
| SendKey(xconn_->GetRootWindow(), kF5KeyCombo, kEventTime, kEventTime); |
| ASSERT_TRUE(layout_manager()->maximized()); |
| |
| // We should resize the second, active browser window immediately, but the |
| // first should remain in its non-maximized position and size. |
| EXPECT_EQ(xid2, xconn_->focused_xid()); |
| EXPECT_EQ(kLeftUnmaximizedBounds, info1->bounds); |
| EXPECT_EQ(kRootBounds, info2->bounds); |
| |
| // After we're notified that the second window has been repainted, we should |
| // resize the first. |
| SendSyncRequestProtocolAlarm(xid2); |
| EXPECT_EQ(kLeftOffscreenBounds, info1->bounds); |
| EXPECT_EQ(kRootBounds, info2->bounds); |
| SendSyncRequestProtocolAlarm(xid1); |
| |
| // We should update the browsers in the opposite order when exiting maximized |
| // mode: the first browser is resized while the second, active browser is left |
| // covering the screen. |
| SendKey(xconn_->GetRootWindow(), kF5KeyCombo, kEventTime, kEventTime); |
| ASSERT_FALSE(layout_manager()->maximized()); |
| EXPECT_FALSE(layout_manager()->maximized()); |
| EXPECT_EQ(kLeftUnmaximizedBounds, info1->bounds); |
| EXPECT_EQ(kRootBounds, info2->bounds); |
| |
| // After the first browser has been redrawn, the second browser should be |
| // unmaximized. |
| SendSyncRequestProtocolAlarm(xid1); |
| EXPECT_EQ(kLeftUnmaximizedBounds, info1->bounds); |
| EXPECT_EQ(kRightUnmaximizedBounds, info2->bounds); |
| SendSyncRequestProtocolAlarm(xid2); |
| } |
| |
| TEST_F(LayoutManager2Test, DontFocusForClickInStatusArea) { |
| const XAtom kStatusBoundsAtom = xconn_->GetAtomOrDie("_CHROME_STATUS_BOUNDS"); |
| const XAtom kCardinalAtom = xconn_->GetAtomOrDie("CARDINAL"); |
| |
| // Create two browser windows and set a property describing the first's status |
| // area's bounds. |
| XWindow xid1 = CreateSimpleWindow(); |
| vector<int> values; |
| values.push_back(10); |
| values.push_back(10); |
| values.push_back(20); |
| values.push_back(20); |
| xconn_->SetIntArrayProperty(xid1, kStatusBoundsAtom, kCardinalAtom, values); |
| SendInitialEventsForWindow(xid1); |
| |
| XWindow xid2 = CreateSimpleWindow(); |
| SendInitialEventsForWindow(xid2); |
| ASSERT_EQ(xid2, xconn_->focused_xid()); |
| |
| // A click within the first browser's status area shouldn't focus it. |
| XEvent event; |
| xconn_->InitButtonPressEvent(&event, xid1, Point(15, 15), 1); |
| wm_->HandleEvent(&event); |
| EXPECT_EQ(xid2, xconn_->focused_xid()); |
| |
| // If we click outside of the status area, the browser should be focused. |
| xconn_->InitButtonPressEvent(&event, xid1, Point(5, 5), 1); |
| wm_->HandleEvent(&event); |
| EXPECT_EQ(xid1, xconn_->focused_xid()); |
| |
| // Set the property on the second browser and let WM know that it changed. |
| values.clear(); |
| values.push_back(50); |
| values.push_back(60); |
| values.push_back(20); |
| values.push_back(10); |
| xconn_->SetIntArrayProperty(xid2, kStatusBoundsAtom, kCardinalAtom, values); |
| xconn_->InitPropertyNotifyEvent(&event, xid2, kStatusBoundsAtom); |
| wm_->HandleEvent(&event); |
| |
| // Click inside of the status area. |
| xconn_->InitButtonPressEvent(&event, xid2, Point(60, 65), 1); |
| wm_->HandleEvent(&event); |
| EXPECT_EQ(xid1, xconn_->focused_xid()); |
| |
| // Click outside of the status area. |
| xconn_->InitButtonPressEvent(&event, xid2, Point(0, 0), 1); |
| wm_->HandleEvent(&event); |
| EXPECT_EQ(xid2, xconn_->focused_xid()); |
| } |
| |
| // Check that we ask Chrome windows on the left side of the screen to hide their |
| // status areas. |
| TEST_F(LayoutManager2Test, HideStatusArea) { |
| const XAtom kChromeStateAtom = xconn_->GetAtomOrDie("_CHROME_STATE"); |
| const XAtom kChromeStateStatusHiddenAtom = |
| xconn_->GetAtomOrDie("_CHROME_STATE_STATUS_HIDDEN"); |
| |
| // Map two Chrome browser windows. |
| XWindow chrome_xid1 = CreateSimpleWindow(); |
| ASSERT_TRUE(wm_->wm_ipc()->SetWindowType( |
| chrome_xid1, chromeos::WM_IPC_WINDOW_CHROME_TOPLEVEL, NULL)); |
| SendInitialEventsForWindow(chrome_xid1); |
| |
| XWindow chrome_xid2 = CreateSimpleWindow(); |
| ASSERT_TRUE(wm_->wm_ipc()->SetWindowType( |
| chrome_xid2, chromeos::WM_IPC_WINDOW_CHROME_TOPLEVEL, NULL)); |
| SendInitialEventsForWindow(chrome_xid2); |
| ASSERT_EQ(chrome_xid2, xconn_->focused_xid()); |
| |
| // We should be hiding the first (leftmost) Chrome window's status area but |
| // not the second's. |
| EXPECT_TRUE(IntArrayPropertyContains( |
| chrome_xid1, kChromeStateAtom, kChromeStateStatusHiddenAtom)); |
| EXPECT_FALSE(IntArrayPropertyContains( |
| chrome_xid2, kChromeStateAtom, kChromeStateStatusHiddenAtom)); |
| |
| // Now map a non-Chrome window. |
| XWindow non_chrome_xid = CreateSimpleWindow(); |
| SendInitialEventsForWindow(non_chrome_xid); |
| ASSERT_EQ(non_chrome_xid, xconn_->focused_xid()); |
| |
| // We should continue showing the second Chrome window's status area, since |
| // the focused non-Chrome window presumably doesn't have a status area. |
| EXPECT_TRUE(IntArrayPropertyContains( |
| chrome_xid1, kChromeStateAtom, kChromeStateStatusHiddenAtom)); |
| EXPECT_FALSE(IntArrayPropertyContains( |
| chrome_xid2, kChromeStateAtom, kChromeStateStatusHiddenAtom)); |
| EXPECT_FALSE(IntArrayPropertyContains( |
| non_chrome_xid, kChromeStateAtom, kChromeStateStatusHiddenAtom)); |
| |
| // Create a third Chrome window. |
| XWindow chrome_xid3 = CreateSimpleWindow(); |
| ASSERT_TRUE(wm_->wm_ipc()->SetWindowType( |
| chrome_xid3, chromeos::WM_IPC_WINDOW_CHROME_TOPLEVEL, NULL)); |
| SendInitialEventsForWindow(chrome_xid3); |
| ASSERT_EQ(chrome_xid3, xconn_->focused_xid()); |
| |
| // Now we should be hiding the first two Chrome window's status areas. |
| // There's no reason to ask the non-Chrome window to hide it. |
| EXPECT_TRUE(IntArrayPropertyContains( |
| chrome_xid1, kChromeStateAtom, kChromeStateStatusHiddenAtom)); |
| EXPECT_TRUE(IntArrayPropertyContains( |
| chrome_xid2, kChromeStateAtom, kChromeStateStatusHiddenAtom)); |
| EXPECT_FALSE(IntArrayPropertyContains( |
| non_chrome_xid, kChromeStateAtom, kChromeStateStatusHiddenAtom)); |
| EXPECT_FALSE(IntArrayPropertyContains( |
| chrome_xid3, kChromeStateAtom, kChromeStateStatusHiddenAtom)); |
| |
| // If we switch to the first Chrome window, we should show the second Chrome |
| // window's status area. |
| SendActiveWindowMessage(chrome_xid1); |
| ASSERT_EQ(chrome_xid1, xconn_->focused_xid()); |
| EXPECT_TRUE(IntArrayPropertyContains( |
| chrome_xid1, kChromeStateAtom, kChromeStateStatusHiddenAtom)); |
| EXPECT_FALSE(IntArrayPropertyContains( |
| chrome_xid2, kChromeStateAtom, kChromeStateStatusHiddenAtom)); |
| EXPECT_FALSE(IntArrayPropertyContains( |
| non_chrome_xid, kChromeStateAtom, kChromeStateStatusHiddenAtom)); |
| EXPECT_TRUE(IntArrayPropertyContains( |
| chrome_xid3, kChromeStateAtom, kChromeStateStatusHiddenAtom)); |
| } |
| |
| TEST_F(LayoutManager2Test, CloseNonChromeWindows) { |
| const XAtom kWmProtocolsAtom = xconn_->GetAtomOrDie("WM_PROTOCOLS"); |
| const XAtom kWmDeleteWindowAtom = xconn_->GetAtomOrDie("WM_DELETE_WINDOW"); |
| |
| // Map a Chrome window. |
| XWindow chrome_xid = CreateSimpleWindow(); |
| ASSERT_TRUE(wm_->wm_ipc()->SetWindowType( |
| chrome_xid, chromeos::WM_IPC_WINDOW_CHROME_TOPLEVEL, NULL)); |
| AppendAtomToProperty(chrome_xid, kWmProtocolsAtom, kWmDeleteWindowAtom); |
| SendInitialEventsForWindow(chrome_xid); |
| |
| // Ctrl-Shift-W shouldn't do anything. |
| const KeyBindings::KeyCombo kCtrlShiftWKeyCombo( |
| XK_w, KeyBindings::kControlMask | KeyBindings::kShiftMask); |
| const XTime kEventTime = wm_->GetCurrentTimeFromServer(); |
| SendKey(xconn_->GetRootWindow(), kCtrlShiftWKeyCombo, kEventTime, kEventTime); |
| EXPECT_EQ(0, GetNumDeleteWindowMessagesForWindow(chrome_xid)); |
| |
| // Now map a non-Chrome window. |
| XWindow non_chrome_xid = CreateSimpleWindow(); |
| AppendAtomToProperty(non_chrome_xid, kWmProtocolsAtom, kWmDeleteWindowAtom); |
| SendInitialEventsForWindow(non_chrome_xid); |
| ASSERT_EQ(non_chrome_xid, xconn_->focused_xid()); |
| |
| // We should send it a delete request on Ctrl-Shift-W. |
| ASSERT_EQ(0, GetNumDeleteWindowMessagesForWindow(non_chrome_xid)); |
| SendKey(xconn_->GetRootWindow(), kCtrlShiftWKeyCombo, kEventTime, kEventTime); |
| EXPECT_EQ(1, GetNumDeleteWindowMessagesForWindow(non_chrome_xid)); |
| |
| // After unmapping the non-Chrome window, the Ctrl-Shift-W binding should be |
| // disabled again. |
| XEvent event; |
| xconn_->InitUnmapEvent(&event, non_chrome_xid); |
| wm_->HandleEvent(&event); |
| ASSERT_EQ(chrome_xid, xconn_->focused_xid()); |
| SendKey(xconn_->GetRootWindow(), kCtrlShiftWKeyCombo, kEventTime, kEventTime); |
| EXPECT_EQ(0, GetNumDeleteWindowMessagesForWindow(chrome_xid)); |
| } |
| |
| // Check that we don't crash if we unmap a transient window after the window |
| // that owns it (http://crosbug.com/17377). |
| TEST_F(LayoutManager2Test, UnmapTransientAfterOwner) { |
| XWindow xid = CreateSimpleWindow(); |
| SendInitialEventsForWindow(xid); |
| |
| XWindow transient_xid = CreateSimpleWindow(); |
| MockXConnection::WindowInfo* transient_info = |
| xconn_->GetWindowInfoOrDie(transient_xid); |
| transient_info->transient_for = xid; |
| SendInitialEventsForWindow(transient_xid); |
| |
| XEvent event; |
| xconn_->InitUnmapEvent(&event, xid); |
| wm_->HandleEvent(&event); |
| xconn_->InitUnmapEvent(&event, transient_xid); |
| wm_->HandleEvent(&event); |
| } |
| |
| // Test the Alt-number hotkeys for switching windows. |
| TEST_F(LayoutManager2Test, SwitchWindowHotkeys) { |
| vector<XWindow> windows; |
| const int kNumWindows = 15; |
| for (int i = 0; i < kNumWindows; ++i) { |
| XWindow xid = CreateSimpleWindow(); |
| SendInitialEventsForWindow(xid); |
| windows.push_back(xid); |
| } |
| |
| // Initially, the last window should be focused. |
| EXPECT_EQ(windows[kNumWindows-1], xconn_->focused_xid()); |
| |
| // Alt-1 should focus the first window. |
| const XTime kEventTime = wm_->GetCurrentTimeFromServer(); |
| SendKey(xconn_->GetRootWindow(), |
| KeyBindings::KeyCombo(XK_1, KeyBindings::kAltMask), |
| kEventTime, kEventTime); |
| EXPECT_EQ(windows[0], xconn_->focused_xid()); |
| |
| // Alt-8 should focus the eighth window. |
| SendKey(xconn_->GetRootWindow(), |
| KeyBindings::KeyCombo(XK_8, KeyBindings::kAltMask), |
| kEventTime, kEventTime); |
| EXPECT_EQ(windows[7], xconn_->focused_xid()); |
| |
| // Alt-9 should focus the last window rather than the ninth. |
| SendKey(xconn_->GetRootWindow(), |
| KeyBindings::KeyCombo(XK_9, KeyBindings::kAltMask), |
| kEventTime, kEventTime); |
| EXPECT_EQ(windows[kNumWindows-1], xconn_->focused_xid()); |
| } |
| |
| // Test that --overlap_windows_by_default=false is honored. |
| TEST_F(LayoutManager2Test, DontOverlapWindowsByDefault) { |
| AutoReset<bool> overlap_windows_by_default_flag_resetter( |
| &FLAGS_overlap_windows_by_default, false); |
| CreateAndInitNewWm(); |
| |
| // Check that we're still in maximized mode after mapping two windows. |
| XWindow xid1 = CreateSimpleWindow(); |
| SendInitialEventsForWindow(xid1); |
| XWindow xid2 = CreateSimpleWindow(); |
| SendInitialEventsForWindow(xid2); |
| EXPECT_EQ(chromeos::WM_IPC_LAYOUT_MAXIMIZED, layout_manager()->layout_mode()); |
| |
| // We should still be able to switch to overlapping with F5. |
| const KeyBindings::KeyCombo kF5KeyCombo(XK_F5, 0); |
| const XTime kEventTime = wm_->GetCurrentTimeFromServer(); |
| SendKey(xconn_->GetRootWindow(), kF5KeyCombo, kEventTime, kEventTime); |
| EXPECT_EQ(chromeos::WM_IPC_LAYOUT_OVERLAPPING, |
| layout_manager()->layout_mode()); |
| } |
| |
| TEST_F(LayoutManager2Test, IgnoreScrollwheelButtons) { |
| // Create two windows. The second one should get the focus. |
| XWindow xid = CreateSimpleWindow(); |
| SendInitialEventsForWindow(xid); |
| XWindow xid2 = CreateSimpleWindow(); |
| SendInitialEventsForWindow(xid2); |
| ASSERT_EQ(xid2, xconn_->focused_xid()); |
| |
| // We should have grabbed buttons 1-3 but not 4 and 5 on the first window. |
| MockXConnection::WindowInfo* info = xconn_->GetWindowInfoOrDie(xid); |
| EXPECT_FALSE(info->button_is_grabbed(0)); // AnyButton |
| EXPECT_TRUE(info->button_is_grabbed(1)); |
| EXPECT_TRUE(info->button_is_grabbed(2)); |
| EXPECT_TRUE(info->button_is_grabbed(3)); |
| EXPECT_FALSE(info->button_is_grabbed(4)); // scroll up |
| EXPECT_FALSE(info->button_is_grabbed(5)); // scroll down |
| |
| // Open a third window. It should take the focus. |
| XWindow xid3 = CreateSimpleWindow(); |
| SendInitialEventsForWindow(xid3); |
| ASSERT_EQ(xid3, xconn_->focused_xid()); |
| |
| // We should ignore scrollwheel button presses on the input window covering |
| // the first browser window. |
| XEvent event; |
| xconn_->InitButtonPressEvent( |
| &event, layout_manager()->left_edge_input_xid_, Point(0, 0), 4); |
| wm_->HandleEvent(&event); |
| EXPECT_EQ(xid3, xconn_->focused_xid()); |
| xconn_->InitButtonPressEvent( |
| &event, layout_manager()->left_edge_input_xid_, Point(0, 0), 5); |
| wm_->HandleEvent(&event); |
| EXPECT_EQ(xid3, xconn_->focused_xid()); |
| } |
| |
| } // namespace window_manager |
| |
| int main(int argc, char** argv) { |
| return window_manager::InitAndRunTests(&argc, argv, &FLAGS_logtostderr); |
| } |