text: // Copyright 2015 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "services/ui/ws/focus_controller.h"
  
  #include <stddef.h>
  #include <stdint.h>
  
  #include "base/macros.h"
  #include "services/ui/ws/focus_controller_delegate.h"
  #include "services/ui/ws/focus_controller_observer.h"
  #include "services/ui/ws/server_window.h"
  #include "services/ui/ws/test_server_window_delegate.h"
  #include "testing/gtest/include/gtest/gtest.h"
  
  namespace ui {
  
  namespace ws {
  namespace {
  
  const char kDisallowActiveChildren[] = "disallow-active-children";
  
  class TestFocusControllerObserver : public FocusControllerObserver,
                                      public FocusControllerDelegate {
   public:
    TestFocusControllerObserver()
        : ignore_explicit_(true),
          focus_change_count_(0u),
          old_focused_window_(nullptr),
          new_focused_window_(nullptr),
          old_active_window_(nullptr),
          new_active_window_(nullptr) {}
  
    void ClearAll() {
      focus_change_count_ = 0u;
      old_focused_window_ = nullptr;
      new_focused_window_ = nullptr;
      old_active_window_ = nullptr;
      new_active_window_ = nullptr;
    }
    size_t focus_change_count() const { return focus_change_count_; }
    ServerWindow* old_focused_window() { return old_focused_window_; }
    ServerWindow* new_focused_window() { return new_focused_window_; }
  
    ServerWindow* old_active_window() { return old_active_window_; }
    ServerWindow* new_active_window() { return new_active_window_; }
  
    void set_ignore_explicit(bool ignore) { ignore_explicit_ = ignore; }
  
   private:
    // FocusControllerDelegate:
    bool CanHaveActiveChildren(ServerWindow* window) const override {
      return !window || window->properties().count(kDisallowActiveChildren) == 0;
    }
    // FocusControllerObserver:
    void OnActivationChanged(ServerWindow* old_active_window,
                             ServerWindow* new_active_window) override {
      old_active_window_ = old_active_window;
      new_active_window_ = new_active_window;
    }
    void OnFocusChanged(FocusControllerChangeSource source,
                        ServerWindow* old_focused_window,
                        ServerWindow* new_focused_window) override {
      if (ignore_explicit_ && source == FocusControllerChangeSource::EXPLICIT)
        return;
  
      focus_change_count_++;
      old_focused_window_ = old_focused_window;
      new_focused_window_ = new_focused_window;
    }
  
    bool ignore_explicit_;
    size_t focus_change_count_;
    ServerWindow* old_focused_window_;
    ServerWindow* new_focused_window_;
    ServerWindow* old_active_window_;
    ServerWindow* new_active_window_;
  
    DISALLOW_COPY_AND_ASSIGN(TestFocusControllerObserver);
  };
  
  }  // namespace
  
  TEST(FocusControllerTest, Basic) {
    TestServerWindowDelegate server_window_delegate;
    ServerWindow root(&server_window_delegate, WindowId());
    server_window_delegate.set_root_window(&root);
    root.SetVisible(true);
    ServerWindow child(&server_window_delegate, WindowId());
    child.SetVisible(true);
    root.Add(&child);
    ServerWindow child_child(&server_window_delegate, WindowId());
    child_child.SetVisible(true);
    child.Add(&child_child);
  
    TestFocusControllerObserver focus_observer;
    FocusController focus_controller(&focus_observer, &root);
    focus_controller.AddObserver(&focus_observer);
  
    focus_controller.SetFocusedWindow(&child_child);
    EXPECT_EQ(0u, focus_observer.focus_change_count());
  
    // Remove the ancestor of the focused window, focus should go to the |root|.
    root.Remove(&child);
    EXPECT_EQ(1u, focus_observer.focus_change_count());
    EXPECT_EQ(&root, focus_observer.new_focused_window());
    EXPECT_EQ(&child_child, focus_observer.old_focused_window());
    focus_observer.ClearAll();
  
    // Make the focused window invisible. Focus is lost in this case (as no one
    // to give focus to).
    root.SetVisible(false);
    EXPECT_EQ(1u, focus_observer.focus_change_count());
    EXPECT_EQ(nullptr, focus_observer.new_focused_window());
    EXPECT_EQ(&root, focus_observer.old_focused_window());
    focus_observer.ClearAll();
  
    // Go back to initial state and focus |child_child|.
    root.SetVisible(true);
    root.Add(&child);
    focus_controller.SetFocusedWindow(&child_child);
    EXPECT_EQ(0u, focus_observer.focus_change_count());
  
    // Hide the focused window, focus should go to parent.
    child_child.SetVisible(false);
    EXPECT_EQ(1u, focus_observer.focus_change_count());
    EXPECT_EQ(&child, focus_observer.new_focused_window());
    EXPECT_EQ(&child_child, focus_observer.old_focused_window());
    focus_observer.ClearAll();
  
    child_child.SetVisible(true);
    focus_controller.SetFocusedWindow(&child_child);
    EXPECT_EQ(0u, focus_observer.focus_change_count());
  
    // Hide the parent of the focused window.
    child.SetVisible(false);
    EXPECT_EQ(1u, focus_observer.focus_change_count());
    EXPECT_EQ(&root, focus_observer.new_focused_window());
    EXPECT_EQ(&child_child, focus_observer.old_focused_window());
    focus_observer.ClearAll();
    focus_controller.RemoveObserver(&focus_observer);
  }
  
  // Tests that focus shifts correctly if the focused window is destroyed.
  TEST(FocusControllerTest, FocusShiftsOnDestroy) {
    TestServerWindowDelegate server_window_delegate;
    ServerWindow parent(&server_window_delegate, WindowId());
    server_window_delegate.set_root_window(&parent);
    parent.SetVisible(true);
    ServerWindow child_first(&server_window_delegate, WindowId());
    child_first.SetVisible(true);
    parent.Add(&child_first);
    std::unique_ptr<ServerWindow> child_second(
        new ServerWindow(&server_window_delegate, WindowId()));
    child_second->SetVisible(true);
    parent.Add(child_second.get());
    std::vector<uint8_t> dummy;
    // Allow only |parent| to be activated.
    parent.SetProperty(kDisallowActiveChildren, &dummy);
  
    TestFocusControllerObserver focus_observer;
    focus_observer.set_ignore_explicit(false);
    FocusController focus_controller(&focus_observer, &parent);
    focus_controller.AddObserver(&focus_observer);
  
    focus_controller.ActivateNextWindow();
    EXPECT_EQ(nullptr, focus_observer.old_active_window());
    EXPECT_EQ(&parent, focus_observer.new_active_window());
    EXPECT_EQ(nullptr, focus_observer.old_focused_window());
    EXPECT_EQ(child_second.get(), focus_observer.new_focused_window());
    focus_observer.ClearAll();
  
    // Destroying |child_second| should move focus to |child_first|.
    child_second.reset();
    EXPECT_NE(nullptr, focus_observer.old_focused_window());
    EXPECT_EQ(&child_first, focus_observer.new_focused_window());
  
    focus_controller.RemoveObserver(&focus_observer);
  }
  
  TEST(FocusControllerTest, ActivationSkipsOverHiddenWindow) {
    TestServerWindowDelegate server_window_delegate;
    ServerWindow parent(&server_window_delegate, WindowId());
    server_window_delegate.set_root_window(&parent);
    parent.SetVisible(true);
  
    ServerWindow child_first(&server_window_delegate, WindowId());
    ServerWindow child_second(&server_window_delegate, WindowId());
    ServerWindow child_third(&server_window_delegate, WindowId());
  
    parent.Add(&child_first);
    parent.Add(&child_second);
    parent.Add(&child_third);
  
    child_first.SetVisible(true);
    child_second.SetVisible(false);
    child_third.SetVisible(true);
  
    TestFocusControllerObserver focus_observer;
    focus_observer.set_ignore_explicit(false);
    FocusController focus_controller(&focus_observer, &parent);
    focus_controller.AddObserver(&focus_observer);
  
    // Since |child_second| is invisible, activation should cycle from
    // |child_third|, to |child_first|, to |parent|, back to |child_third|.
    focus_controller.ActivateNextWindow();
    EXPECT_EQ(nullptr, focus_observer.old_active_window());
    EXPECT_EQ(&child_third, focus_observer.new_active_window());
    focus_observer.ClearAll();
  
    focus_controller.ActivateNextWindow();
    EXPECT_EQ(&child_third, focus_observer.old_active_window());
    EXPECT_EQ(&child_first, focus_observer.new_active_window());
    focus_observer.ClearAll();
  
    focus_controller.ActivateNextWindow();
    EXPECT_EQ(&child_first, focus_observer.old_active_window());
    EXPECT_EQ(&parent, focus_observer.new_active_window());
    focus_observer.ClearAll();
  
    focus_controller.ActivateNextWindow();
    EXPECT_EQ(&parent, focus_observer.old_active_window());
    EXPECT_EQ(&child_third, focus_observer.new_active_window());
    focus_observer.ClearAll();
  
    // Once |child_second| is made visible, activation should go from
    // |child_third| to |child_second|.
    child_second.SetVisible(true);
    focus_controller.ActivateNextWindow();
    EXPECT_EQ(&child_third, focus_observer.old_active_window());
    EXPECT_EQ(&child_second, focus_observer.new_active_window());
  }
  
  TEST(FocusControllerTest, ActivationSkipsOverHiddenContainers) {
    TestServerWindowDelegate server_window_delegate;
    ServerWindow parent(&server_window_delegate, WindowId());
    server_window_delegate.set_root_window(&parent);
    parent.SetVisible(true);
  
    ServerWindow child1(&server_window_delegate, WindowId());
    ServerWindow child2(&server_window_delegate, WindowId());
  
    parent.Add(&child1);
    parent.Add(&child2);
  
    child1.SetVisible(true);
    child2.SetVisible(true);
  
    ServerWindow child11(&server_window_delegate, WindowId());
    ServerWindow child12(&server_window_delegate, WindowId());
    ServerWindow child21(&server_window_delegate, WindowId());
    ServerWindow child22(&server_window_delegate, WindowId());
  
    child1.Add(&child11);
    child1.Add(&child12);
    child2.Add(&child21);
    child2.Add(&child22);
  
    child11.SetVisible(true);
    child12.SetVisible(true);
    child21.SetVisible(true);
    child22.SetVisible(true);
  
    TestFocusControllerObserver focus_observer;
    FocusController focus_controller(&focus_observer, &parent);
    focus_controller.AddObserver(&focus_observer);
  
    focus_controller.ActivateNextWindow();
    EXPECT_EQ(nullptr, focus_observer.old_active_window());
    EXPECT_EQ(&child22, focus_observer.new_active_window());
    focus_observer.ClearAll();
  
    child2.SetVisible(false);
    EXPECT_EQ(&child22, focus_observer.old_active_window());
    EXPECT_EQ(&child12, focus_observer.new_active_window());
  }
  
  TEST(FocusControllerTest, NonFocusableWindowNotActivated) {
    TestServerWindowDelegate server_window_delegate;
    ServerWindow parent(&server_window_delegate, WindowId());
    server_window_delegate.set_root_window(&parent);
    parent.SetVisible(true);
  
    ServerWindow child_first(&server_window_delegate, WindowId());
    ServerWindow child_second(&server_window_delegate, WindowId());
    parent.Add(&child_first);
    parent.Add(&child_second);
    child_first.SetVisible(true);
    child_second.SetVisible(true);
  
    TestFocusControllerObserver focus_observer;
    focus_observer.set_ignore_explicit(false);
    FocusController focus_controller(&focus_observer, &parent);
    focus_controller.AddObserver(&focus_observer);
  
    child_first.set_can_focus(false);
  
    // |child_second| is activated first, but then activation skips over
    // |child_first| and goes to |parent| instead.
    focus_controller.ActivateNextWindow();
    EXPECT_EQ(nullptr, focus_observer.old_active_window());
    EXPECT_EQ(&child_second, focus_observer.new_active_window());
    focus_observer.ClearAll();
  
    focus_controller.ActivateNextWindow();
    EXPECT_EQ(&child_second, focus_observer.old_active_window());
    EXPECT_EQ(&parent, focus_observer.new_active_window());
  }
  
  }  // namespace ws
  }  // namespace ui
  