// Copyright (c) 2016 The Chromium Embedded Framework 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 "include/views/cef_box_layout.h"
#include "include/views/cef_fill_layout.h"
#include "include/views/cef_layout.h"
#include "include/views/cef_panel.h"
#include "include/views/cef_panel_delegate.h"
#include "include/views/cef_window.h"
#include "tests/ceftests/thread_helper.h"
#include "tests/gtest/include/gtest/gtest.h"

#define PANEL_TEST(name) UI_THREAD_TEST(ViewsPanelTest, name)

namespace {

class EmptyPanelDelegate : public CefPanelDelegate {
 public:
  EmptyPanelDelegate() {}

 private:
  IMPLEMENT_REFCOUNTING(EmptyPanelDelegate);
  DISALLOW_COPY_AND_ASSIGN(EmptyPanelDelegate);
};

void CreatePanel(CefRefPtr<CefPanelDelegate> delegate) {
  CefRefPtr<CefPanel> panel = CefPanel::CreatePanel(delegate);
  EXPECT_TRUE(panel.get());

  // Verify the derived View relationship.
  EXPECT_TRUE(panel->AsPanel().get());
  EXPECT_FALSE(panel->AsWindow().get());
  EXPECT_TRUE(panel->IsSame(panel));

  // Verify default View state.
  EXPECT_STREQ("Panel", panel->GetTypeString().ToString().c_str());
  EXPECT_TRUE(panel->IsValid());
  EXPECT_FALSE(panel->IsAttached());
  if (delegate)
    EXPECT_EQ(delegate.get(), panel->GetDelegate().get());
  else
    EXPECT_FALSE(panel->GetDelegate().get());
  EXPECT_EQ(0, panel->GetID());
  EXPECT_FALSE(panel->GetParentView().get());
  EXPECT_EQ(CefRect(0, 0, 0, 0), panel->GetBounds());
  EXPECT_EQ(CefRect(0, 0, 0, 0), panel->GetBoundsInScreen());
  EXPECT_EQ(CefSize(0, 0), panel->GetPreferredSize());
  EXPECT_EQ(CefSize(0, 0), panel->GetMinimumSize());
  EXPECT_EQ(CefSize(0, 0), panel->GetMaximumSize());
  EXPECT_EQ(CefSize(0, 0), panel->GetMaximumSize());
  EXPECT_EQ(0, panel->GetHeightForWidth(100));
  EXPECT_TRUE(panel->IsVisible());
  EXPECT_FALSE(panel->IsDrawn());
  EXPECT_TRUE(panel->IsEnabled());
  EXPECT_FALSE(panel->IsFocusable());
  EXPECT_FALSE(panel->IsAccessibilityFocusable());
  EXPECT_EQ(CefColorSetARGB(255, 255, 255, 255), panel->GetBackgroundColor());

  // Verify default Panel state.
  EXPECT_TRUE(panel->GetLayout().get());
  EXPECT_EQ(0U, panel->GetChildViewCount());

  // Destroy the Panel.
  panel = nullptr;

  if (delegate) {
    // Verify that nothing is keeping a reference to the delegate.
    EXPECT_TRUE(delegate->HasOneRef());
  }
}

void CreatePanelNoDelegateImpl() {
  CreatePanel(nullptr);
}

void CreatePanelWithDelegateImpl() {
  CreatePanel(new EmptyPanelDelegate);
}

}  // namespace

// Test creation.
PANEL_TEST(CreatePanelNoDelegate)
PANEL_TEST(CreatePanelWithDelegate)

namespace {

class ParentPanelDelegate : public CefPanelDelegate {
 public:
  ParentPanelDelegate() {}

  void OnParentViewChanged(CefRefPtr<CefView> view,
                           bool added,
                           CefRefPtr<CefView> parent) override {
    EXPECT_FALSE(true);  // Not reached.
  }

  void OnChildViewChanged(CefRefPtr<CefView> view,
                          bool added,
                          CefRefPtr<CefView> child) override {
    Changed changed;
    changed.view_ = view;
    changed.added_ = added;
    changed.child_ = child;
    changed_.push_back(changed);
  }

  void Verify(int callback_index,
              CefRefPtr<CefView> view,
              bool added,
              CefRefPtr<CefView> child) {
    EXPECT_LT(callback_index, static_cast<int>(changed_.size()));
    EXPECT_TRUE(view->IsSame(changed_[callback_index].view_))
        << "callback_index " << callback_index;
    EXPECT_EQ(added, changed_[callback_index].added_)
        << "callback_index " << callback_index;
    EXPECT_TRUE(child->IsSame(changed_[callback_index].child_))
        << "callback_index " << callback_index;
  }

  void Reset() { changed_.clear(); }

  bool IsReset() const { return changed_.empty(); }

  struct Changed {
    CefRefPtr<CefView> view_;
    bool added_ = false;
    CefRefPtr<CefView> child_;
  };
  std::vector<Changed> changed_;

 private:
  IMPLEMENT_REFCOUNTING(ParentPanelDelegate);
  DISALLOW_COPY_AND_ASSIGN(ParentPanelDelegate);
};

class ChildPanelDelegate : public CefPanelDelegate {
 public:
  ChildPanelDelegate() {}

  void OnParentViewChanged(CefRefPtr<CefView> view,
                           bool added,
                           CefRefPtr<CefView> parent) override {
    EXPECT_FALSE(on_parent_view_changed_);
    on_parent_view_changed_ = true;
    view_ = view;
    added_ = added;
    parent_ = parent;
  }

  void OnChildViewChanged(CefRefPtr<CefView> view,
                          bool added,
                          CefRefPtr<CefView> child) override {
    EXPECT_FALSE(true);  // Not reached.
  }

  void Verify(CefRefPtr<CefView> view, bool added, CefRefPtr<CefView> parent) {
    EXPECT_TRUE(on_parent_view_changed_);
    EXPECT_TRUE(view->IsSame(view_));
    EXPECT_EQ(added, added_);
    EXPECT_TRUE(parent->IsSame(parent_));
  }

  void Reset() {
    on_parent_view_changed_ = false;
    view_ = nullptr;
    added_ = false;
    parent_ = nullptr;
  }

  bool IsReset() const { return !on_parent_view_changed_; }

  bool on_parent_view_changed_ = false;
  CefRefPtr<CefView> view_;
  bool added_ = false;
  CefRefPtr<CefView> parent_;

 private:
  IMPLEMENT_REFCOUNTING(ChildPanelDelegate);
  DISALLOW_COPY_AND_ASSIGN(ChildPanelDelegate);
};

void ChildVerifyRemovedState(CefRefPtr<ParentPanelDelegate> parent_delegate,
                             CefRefPtr<CefPanel> parent_panel,
                             CefRefPtr<ChildPanelDelegate> child_delegate,
                             CefRefPtr<CefPanel> child_panel) {
  EXPECT_FALSE(parent_panel->IsSame(child_panel));
  EXPECT_FALSE(child_panel->IsSame(parent_panel));
  EXPECT_FALSE(parent_panel->IsAttached());
  EXPECT_FALSE(child_panel->IsAttached());
  EXPECT_FALSE(parent_panel->GetParentView().get());
  EXPECT_FALSE(child_panel->GetParentView().get());
}

void ChildVerifyAddedState(CefRefPtr<ParentPanelDelegate> parent_delegate,
                           CefRefPtr<CefPanel> parent_panel,
                           CefRefPtr<ChildPanelDelegate> child_delegate,
                           CefRefPtr<CefPanel> child_panel,
                           int expected_child_index) {
  EXPECT_FALSE(parent_panel->IsSame(child_panel));
  EXPECT_FALSE(child_panel->IsSame(parent_panel));
  EXPECT_FALSE(parent_panel->IsAttached());
  EXPECT_TRUE(child_panel->IsAttached());
  EXPECT_TRUE(
      child_panel->IsSame(parent_panel->GetChildViewAt(expected_child_index)));
  EXPECT_TRUE(child_panel->GetParentView()->IsSame(parent_panel));
}

void ChildVerifyFinalCallbackState(
    CefRefPtr<ParentPanelDelegate> parent_delegate,
    CefRefPtr<CefPanel> parent_panel,
    CefRefPtr<ChildPanelDelegate> child_delegate,
    CefRefPtr<CefPanel> child_panel,
    int expected_parent_callback_index,
    bool added) {
  parent_delegate->Verify(expected_parent_callback_index, parent_panel, added,
                          child_panel);
  child_delegate->Verify(child_panel, added, parent_panel);
}

void ChildAdd(CefRefPtr<ParentPanelDelegate> parent_delegate,
              CefRefPtr<CefPanel> parent_panel,
              CefRefPtr<ChildPanelDelegate> child_delegate,
              CefRefPtr<CefPanel> child_panel,
              int expected_child_index = 0,
              int expected_parent_callback_index = 0) {
  // Verify initial parent/child state.
  ChildVerifyRemovedState(parent_delegate, parent_panel, child_delegate,
                          child_panel);

  // Verify initial child callback state.
  EXPECT_TRUE(child_delegate->IsReset());

  // Add the child view.
  parent_panel->AddChildView(child_panel);

  // Verify final callback state.
  ChildVerifyFinalCallbackState(parent_delegate, parent_panel, child_delegate,
                                child_panel, expected_parent_callback_index,
                                true);

  // Reset child callback state.
  child_delegate->Reset();

  // Verify final parent/child state.
  ChildVerifyAddedState(parent_delegate, parent_panel, child_delegate,
                        child_panel, expected_child_index);
}

void ChildAddAt(CefRefPtr<ParentPanelDelegate> parent_delegate,
                CefRefPtr<CefPanel> parent_panel,
                CefRefPtr<ChildPanelDelegate> child_delegate,
                CefRefPtr<CefPanel> child_panel,
                int child_index,
                int expected_parent_callback_index) {
  // Verify initial parent/child state.
  ChildVerifyRemovedState(parent_delegate, parent_panel, child_delegate,
                          child_panel);

  // Verify initial child callback state.
  EXPECT_TRUE(child_delegate->IsReset());

  // Add the child view.
  parent_panel->AddChildViewAt(child_panel, child_index);

  // Verify final callback state.
  ChildVerifyFinalCallbackState(parent_delegate, parent_panel, child_delegate,
                                child_panel, expected_parent_callback_index,
                                true);

  // Reset child callback state.
  child_delegate->Reset();

  // Verify final parent/child state.
  ChildVerifyAddedState(parent_delegate, parent_panel, child_delegate,
                        child_panel, child_index);
}

void ChildRemove(CefRefPtr<ParentPanelDelegate> parent_delegate,
                 CefRefPtr<CefPanel> parent_panel,
                 CefRefPtr<ChildPanelDelegate> child_delegate,
                 CefRefPtr<CefPanel> child_panel,
                 bool remove_all,
                 int expected_child_index = 0,
                 int expected_parent_callback_index = 0) {
  // Verify initial parent/child state.
  ChildVerifyAddedState(parent_delegate, parent_panel, child_delegate,
                        child_panel, expected_child_index);

  // Verify initial child callback state.
  EXPECT_TRUE(child_delegate->IsReset());

  // Remove the child view.
  if (remove_all)
    parent_panel->RemoveAllChildViews();
  else
    parent_panel->RemoveChildView(child_panel);

  // Verify final callback state.
  ChildVerifyFinalCallbackState(parent_delegate, parent_panel, child_delegate,
                                child_panel, expected_parent_callback_index,
                                false);

  // Reset child callback state.
  child_delegate->Reset();

  // Verify final parent/child state.
  ChildVerifyRemovedState(parent_delegate, parent_panel, child_delegate,
                          child_panel);
}

void ChildAddRemoveSingleImpl() {
  CefRefPtr<ParentPanelDelegate> parent_delegate = new ParentPanelDelegate();
  CefRefPtr<CefPanel> parent_panel = CefPanel::CreatePanel(parent_delegate);

  CefRefPtr<ChildPanelDelegate> child_delegate = new ChildPanelDelegate();
  CefRefPtr<CefPanel> child_panel = CefPanel::CreatePanel(child_delegate);

  // Add and explicitly remove the child view.
  EXPECT_TRUE(parent_delegate->IsReset());
  ChildAdd(parent_delegate, parent_panel, child_delegate, child_panel);
  parent_delegate->Reset();

  ChildRemove(parent_delegate, parent_panel, child_delegate, child_panel,
              false);
  parent_delegate->Reset();

  // Add and implicitly remove the child view.
  ChildAdd(parent_delegate, parent_panel, child_delegate, child_panel);
  parent_delegate->Reset();

  ChildRemove(parent_delegate, parent_panel, child_delegate, child_panel,
              false);
  parent_delegate->Reset();
}

void ChildAddRemoveMultipleImpl() {
  CefRefPtr<ParentPanelDelegate> parent_delegate = new ParentPanelDelegate();
  CefRefPtr<CefPanel> parent_panel = CefPanel::CreatePanel(parent_delegate);

  CefRefPtr<ChildPanelDelegate> child_delegate1 = new ChildPanelDelegate();
  CefRefPtr<CefPanel> child_panel1 = CefPanel::CreatePanel(child_delegate1);

  CefRefPtr<ChildPanelDelegate> child_delegate2 = new ChildPanelDelegate();
  CefRefPtr<CefPanel> child_panel2 = CefPanel::CreatePanel(child_delegate2);

  // Add multiple child views.
  EXPECT_TRUE(parent_delegate->IsReset());
  ChildAdd(parent_delegate, parent_panel, child_delegate1, child_panel1, 0, 0);
  EXPECT_TRUE(child_delegate2->IsReset());  // child2 not called.
  ChildAdd(parent_delegate, parent_panel, child_delegate2, child_panel2, 1, 1);
  EXPECT_TRUE(child_delegate1->IsReset());  // child1 not called.
  parent_delegate->Reset();

  EXPECT_EQ(2U, parent_panel->GetChildViewCount());

  // Explicitly remove specific child views.
  ChildRemove(parent_delegate, parent_panel, child_delegate1, child_panel1,
              false, 0, 0);
  EXPECT_TRUE(child_delegate2->IsReset());  // child2 not called.
  ChildRemove(parent_delegate, parent_panel, child_delegate2, child_panel2,
              false, 0, 1);
  EXPECT_TRUE(child_delegate1->IsReset());  // child1 not called.
  parent_delegate->Reset();

  EXPECT_EQ(0U, parent_panel->GetChildViewCount());

  // Add multiple child views.
  ChildAdd(parent_delegate, parent_panel, child_delegate1, child_panel1, 0, 0);
  EXPECT_TRUE(child_delegate2->IsReset());  // child2 not called.
  ChildAdd(parent_delegate, parent_panel, child_delegate2, child_panel2, 1, 1);
  EXPECT_TRUE(child_delegate1->IsReset());  // child1 not called.
  parent_delegate->Reset();

  EXPECT_EQ(2U, parent_panel->GetChildViewCount());

  EXPECT_TRUE(child_delegate1->IsReset());
  EXPECT_TRUE(child_delegate2->IsReset());

  // Implicitly remove all child views.
  parent_panel->RemoveAllChildViews();

  // Verify final callback state.
  ChildVerifyFinalCallbackState(parent_delegate, parent_panel, child_delegate1,
                                child_panel1, 0, false);
  ChildVerifyFinalCallbackState(parent_delegate, parent_panel, child_delegate2,
                                child_panel2, 1, false);

  EXPECT_EQ(0U, parent_panel->GetChildViewCount());

  // Reset callback state.
  parent_delegate->Reset();
  child_delegate1->Reset();
  child_delegate2->Reset();

  // Verify final parent/child state.
  ChildVerifyRemovedState(parent_delegate, parent_panel, child_delegate1,
                          child_panel1);
  ChildVerifyRemovedState(parent_delegate, parent_panel, child_delegate2,
                          child_panel2);
}

void ChildOrderImpl() {
  CefRefPtr<ParentPanelDelegate> parent_delegate = new ParentPanelDelegate();
  CefRefPtr<CefPanel> parent_panel = CefPanel::CreatePanel(parent_delegate);

  CefRefPtr<ChildPanelDelegate> child_delegate1 = new ChildPanelDelegate();
  CefRefPtr<CefPanel> child_panel1 = CefPanel::CreatePanel(child_delegate1);

  CefRefPtr<ChildPanelDelegate> child_delegate2 = new ChildPanelDelegate();
  CefRefPtr<CefPanel> child_panel2 = CefPanel::CreatePanel(child_delegate2);

  CefRefPtr<ChildPanelDelegate> child_delegate3 = new ChildPanelDelegate();
  CefRefPtr<CefPanel> child_panel3 = CefPanel::CreatePanel(child_delegate3);

  // Add child views at specific indexes.
  ChildAddAt(parent_delegate, parent_panel, child_delegate2, child_panel2, 0,
             0);
  ChildAddAt(parent_delegate, parent_panel, child_delegate3, child_panel3, 0,
             1);
  ChildAddAt(parent_delegate, parent_panel, child_delegate1, child_panel1, 1,
             2);
  parent_delegate->Reset();

  EXPECT_EQ(3U, parent_panel->GetChildViewCount());

  // ChildAddAt() will verify these results but let's check again just to make
  // sure.
  EXPECT_TRUE(child_panel3->IsSame(parent_panel->GetChildViewAt(0)));
  EXPECT_TRUE(child_panel1->IsSame(parent_panel->GetChildViewAt(1)));
  EXPECT_TRUE(child_panel2->IsSame(parent_panel->GetChildViewAt(2)));

  // Move panel2 to the front.
  parent_panel->ReorderChildView(child_panel2, 0);

  EXPECT_TRUE(child_panel2->IsSame(parent_panel->GetChildViewAt(0)));
  EXPECT_TRUE(child_panel3->IsSame(parent_panel->GetChildViewAt(1)));
  EXPECT_TRUE(child_panel1->IsSame(parent_panel->GetChildViewAt(2)));

  // Move panel3 to the end.
  parent_panel->ReorderChildView(child_panel3, -1);

  EXPECT_TRUE(child_panel2->IsSame(parent_panel->GetChildViewAt(0)));
  EXPECT_TRUE(child_panel1->IsSame(parent_panel->GetChildViewAt(1)));
  EXPECT_TRUE(child_panel3->IsSame(parent_panel->GetChildViewAt(2)));
}

void ChildVisibleImpl() {
  CefRefPtr<CefPanel> parent_panel = CefPanel::CreatePanel(nullptr);
  CefRefPtr<CefPanel> child_panel1 = CefPanel::CreatePanel(nullptr);
  CefRefPtr<CefPanel> child_panel2 = CefPanel::CreatePanel(nullptr);

  // Nothing drawn by default.
  EXPECT_FALSE(parent_panel->IsDrawn());
  EXPECT_FALSE(child_panel1->IsDrawn());
  EXPECT_FALSE(child_panel2->IsDrawn());

  // Everything visible by default.
  EXPECT_TRUE(parent_panel->IsVisible());
  EXPECT_TRUE(child_panel1->IsVisible());
  EXPECT_TRUE(child_panel2->IsVisible());

  parent_panel->AddChildView(child_panel1);
  parent_panel->AddChildView(child_panel2);

  // Still the same.
  EXPECT_FALSE(parent_panel->IsDrawn());
  EXPECT_FALSE(child_panel1->IsDrawn());
  EXPECT_FALSE(child_panel2->IsDrawn());
  EXPECT_TRUE(parent_panel->IsVisible());
  EXPECT_TRUE(child_panel1->IsVisible());
  EXPECT_TRUE(child_panel2->IsVisible());

  child_panel1->SetVisible(false);

  // Child1 not visible.
  EXPECT_TRUE(parent_panel->IsVisible());
  EXPECT_FALSE(child_panel1->IsVisible());
  EXPECT_TRUE(child_panel2->IsVisible());

  child_panel1->SetVisible(true);

  // Everything visible.
  EXPECT_TRUE(parent_panel->IsVisible());
  EXPECT_TRUE(child_panel1->IsVisible());
  EXPECT_TRUE(child_panel2->IsVisible());

  parent_panel->SetVisible(false);

  // Children visible.
  EXPECT_FALSE(parent_panel->IsVisible());
  EXPECT_TRUE(child_panel1->IsVisible());
  EXPECT_TRUE(child_panel2->IsVisible());

  parent_panel->SetVisible(true);

  // Everything visible.
  EXPECT_TRUE(parent_panel->IsVisible());
  EXPECT_TRUE(child_panel1->IsVisible());
  EXPECT_TRUE(child_panel2->IsVisible());
}

void ChildDrawnImpl() {
  CefRefPtr<CefPanel> parent_panel = CefPanel::CreatePanel(nullptr);
  CefRefPtr<CefPanel> child_panel1 = CefPanel::CreatePanel(nullptr);
  CefRefPtr<CefPanel> child_panel2 = CefPanel::CreatePanel(nullptr);

  // Nothing drawn by default.
  EXPECT_FALSE(parent_panel->IsDrawn());
  EXPECT_FALSE(child_panel1->IsDrawn());
  EXPECT_FALSE(child_panel2->IsDrawn());

  // Everything visible by default.
  EXPECT_TRUE(parent_panel->IsVisible());
  EXPECT_TRUE(child_panel1->IsVisible());
  EXPECT_TRUE(child_panel2->IsVisible());

  parent_panel->AddChildView(child_panel1);
  parent_panel->AddChildView(child_panel2);

  // Create and show a Window.
  CefRefPtr<CefWindow> window = CefWindow::CreateTopLevelWindow(nullptr);
  window->AddChildView(parent_panel);
  window->CenterWindow(CefSize(400, 400));
  window->Show();

  // Everything visible and drawn now.
  EXPECT_TRUE(parent_panel->IsVisible());
  EXPECT_TRUE(parent_panel->IsDrawn());
  EXPECT_TRUE(child_panel1->IsVisible());
  EXPECT_TRUE(child_panel1->IsDrawn());
  EXPECT_TRUE(child_panel2->IsVisible());
  EXPECT_TRUE(child_panel2->IsDrawn());

  child_panel1->SetVisible(false);

  // Child1 not visible or drawn.
  EXPECT_TRUE(parent_panel->IsVisible());
  EXPECT_TRUE(parent_panel->IsDrawn());
  EXPECT_FALSE(child_panel1->IsVisible());
  EXPECT_FALSE(child_panel1->IsDrawn());
  EXPECT_TRUE(child_panel2->IsVisible());
  EXPECT_TRUE(child_panel2->IsDrawn());

  child_panel1->SetVisible(true);

  // Everything visible and drawn.
  EXPECT_TRUE(parent_panel->IsVisible());
  EXPECT_TRUE(parent_panel->IsDrawn());
  EXPECT_TRUE(child_panel1->IsVisible());
  EXPECT_TRUE(child_panel1->IsDrawn());
  EXPECT_TRUE(child_panel2->IsVisible());
  EXPECT_TRUE(child_panel2->IsDrawn());

  parent_panel->SetVisible(false);

  // Children visible, but nothing drawn.
  EXPECT_FALSE(parent_panel->IsVisible());
  EXPECT_FALSE(parent_panel->IsDrawn());
  EXPECT_TRUE(child_panel1->IsVisible());
  EXPECT_FALSE(child_panel1->IsDrawn());
  EXPECT_TRUE(child_panel2->IsVisible());
  EXPECT_FALSE(child_panel2->IsDrawn());

  parent_panel->SetVisible(true);

  // Everything visible and drawn.
  EXPECT_TRUE(parent_panel->IsVisible());
  EXPECT_TRUE(parent_panel->IsDrawn());
  EXPECT_TRUE(child_panel1->IsVisible());
  EXPECT_TRUE(child_panel1->IsDrawn());
  EXPECT_TRUE(child_panel2->IsVisible());
  EXPECT_TRUE(child_panel2->IsDrawn());

  // Close the window.
  window->Close();
}

}  // namespace

// Test child behaviors.
PANEL_TEST(ChildAddRemoveSingle)
PANEL_TEST(ChildAddRemoveMultiple)
PANEL_TEST(ChildOrder)
PANEL_TEST(ChildVisible)
PANEL_TEST(ChildDrawn)

namespace {

class SizingPanelDelegate : public CefPanelDelegate {
 public:
  SizingPanelDelegate() {}

  CefSize GetPreferredSize(CefRefPtr<CefView> view) override {
    got_get_preferred_size_ = true;
    view_ = view;
    return preferred_size_;
  }

  CefSize GetMinimumSize(CefRefPtr<CefView> view) override {
    got_get_minimum_size_ = true;
    view_ = view;
    return minimum_size_;
  }

  CefSize GetMaximumSize(CefRefPtr<CefView> view) override {
    got_get_maximum_size_ = true;
    view_ = view;
    return maximum_size_;
  }

  int GetHeightForWidth(CefRefPtr<CefView> view, int width) override {
    got_get_height_for_width_ = true;
    view_ = view;
    width_ = width;
    return height_for_width_;
  }

  void Reset() {
    preferred_size_ = CefSize(0, 0);
    minimum_size_ = CefSize(0, 0);
    maximum_size_ = CefSize(0, 0);
    height_for_width_ = 0;
    got_get_preferred_size_ = false;
    got_get_minimum_size_ = false;
    got_get_maximum_size_ = false;
    got_get_height_for_width_ = false;
    view_ = nullptr;
    width_ = 0;
  }

  bool IsReset() const {
    return !got_get_preferred_size_ && !got_get_minimum_size_ &&
           !got_get_maximum_size_ && !got_get_height_for_width_;
  }

  CefSize preferred_size_;
  CefSize minimum_size_;
  CefSize maximum_size_;
  int height_for_width_ = 0;

  bool got_get_preferred_size_ = false;
  bool got_get_minimum_size_ = false;
  bool got_get_maximum_size_ = false;
  bool got_get_height_for_width_ = false;

  CefRefPtr<CefView> view_;
  int width_ = 0;

 private:
  IMPLEMENT_REFCOUNTING(SizingPanelDelegate);
  DISALLOW_COPY_AND_ASSIGN(SizingPanelDelegate);
};

void SizeNoDelegateImpl() {
  CefRefPtr<SizingPanelDelegate> delegate = new SizingPanelDelegate();
  CefRefPtr<CefPanel> panel = CefPanel::CreatePanel(delegate);

  // Default bounds are empty.
  EXPECT_EQ(CefRect(0, 0, 0, 0), panel->GetBounds());

  // Set and get the bounds.
  panel->SetBounds(CefRect(100, 100, 200, 200));
  EXPECT_EQ(CefRect(100, 100, 200, 200), panel->GetBounds());
  EXPECT_EQ(CefSize(200, 200), panel->GetSize());
  EXPECT_EQ(CefPoint(100, 100), panel->GetPosition());

  // GetBoundsInScreen() drops the position because there is no Window.
  EXPECT_EQ(CefRect(0, 0, 200, 200), panel->GetBoundsInScreen());

  // Adjust the position but keep the size the same.
  panel->SetPosition(CefPoint(50, 50));
  EXPECT_EQ(CefRect(50, 50, 200, 200), panel->GetBounds());
  EXPECT_EQ(CefSize(200, 200), panel->GetSize());
  EXPECT_EQ(CefPoint(50, 50), panel->GetPosition());

  // Adjust the size but keep the position the same.
  panel->SetSize(CefSize(400, 400));
  EXPECT_EQ(CefRect(50, 50, 400, 400), panel->GetBounds());
  EXPECT_EQ(CefSize(400, 400), panel->GetSize());
  EXPECT_EQ(CefPoint(50, 50), panel->GetPosition());

  // No delegate methods were called during this test.
  EXPECT_TRUE(delegate->IsReset());
}

void SizeWithDelegateImpl() {
  CefRefPtr<SizingPanelDelegate> delegate = new SizingPanelDelegate();
  CefRefPtr<CefPanel> panel = CefPanel::CreatePanel(delegate);

  // Default bounds are empty.
  EXPECT_EQ(CefRect(0, 0, 0, 0), panel->GetBounds());

  CefSize expected_size(100, 100);

  // Test GetPreferredSize().
  delegate->preferred_size_ = expected_size;
  EXPECT_EQ(expected_size, panel->GetPreferredSize());
  EXPECT_TRUE(delegate->got_get_preferred_size_);
  EXPECT_FALSE(delegate->got_get_minimum_size_);
  EXPECT_FALSE(delegate->got_get_maximum_size_);
  EXPECT_FALSE(delegate->got_get_height_for_width_);
  EXPECT_TRUE(panel->IsSame(delegate->view_));
  delegate->Reset();

  // Test GetMinimumSize().
  delegate->minimum_size_ = expected_size;
  EXPECT_EQ(expected_size, panel->GetMinimumSize());
  EXPECT_FALSE(delegate->got_get_preferred_size_);
  EXPECT_TRUE(delegate->got_get_minimum_size_);
  EXPECT_FALSE(delegate->got_get_maximum_size_);
  EXPECT_FALSE(delegate->got_get_height_for_width_);
  EXPECT_TRUE(panel->IsSame(delegate->view_));
  delegate->Reset();

  // Test GetMaximumSize().
  delegate->maximum_size_ = expected_size;
  EXPECT_EQ(expected_size, panel->GetMaximumSize());
  EXPECT_FALSE(delegate->got_get_preferred_size_);
  EXPECT_FALSE(delegate->got_get_minimum_size_);
  EXPECT_TRUE(delegate->got_get_maximum_size_);
  EXPECT_FALSE(delegate->got_get_height_for_width_);
  EXPECT_TRUE(panel->IsSame(delegate->view_));
  delegate->Reset();

  int expected_width = 200;
  int expected_height = 100;

  // Test GetHeightForWidth().
  delegate->height_for_width_ = expected_height;
  EXPECT_EQ(expected_height, panel->GetHeightForWidth(expected_width));
  EXPECT_FALSE(delegate->got_get_preferred_size_);
  EXPECT_FALSE(delegate->got_get_minimum_size_);
  EXPECT_FALSE(delegate->got_get_maximum_size_);
  EXPECT_TRUE(delegate->got_get_height_for_width_);
  EXPECT_EQ(expected_width, delegate->width_);
  EXPECT_TRUE(panel->IsSame(delegate->view_));
  delegate->Reset();
}

}  // namespace

// Test sizing.
PANEL_TEST(SizeNoDelegate)
PANEL_TEST(SizeWithDelegate)

namespace {

void FillLayoutCreateImpl() {
  CefRefPtr<CefPanel> panel = CefPanel::CreatePanel(nullptr);

  // Explicitly set to FillLayout.
  panel->SetToFillLayout();

  CefRefPtr<CefLayout> layout = panel->GetLayout();
  EXPECT_TRUE(layout.get());
  EXPECT_TRUE(layout->AsFillLayout().get());
}

void FillLayoutSizeToPreferredSizeImpl() {
  CefRefPtr<SizingPanelDelegate> delegate = new SizingPanelDelegate();
  CefRefPtr<CefPanel> panel = CefPanel::CreatePanel(delegate);

  // Default Layout is FillLayout.
  CefRefPtr<CefLayout> layout = panel->GetLayout();
  EXPECT_TRUE(layout.get());
  EXPECT_TRUE(layout->AsFillLayout().get());

  // Default bounds are empty.
  EXPECT_EQ(CefRect(0, 0, 0, 0), panel->GetBounds());

  CefSize expected_size(100, 100);

  delegate->preferred_size_ = expected_size;

  // Trigger use of the preferred size.
  panel->Layout();

  EXPECT_TRUE(delegate->got_get_preferred_size_);
  EXPECT_FALSE(delegate->got_get_minimum_size_);
  EXPECT_FALSE(delegate->got_get_maximum_size_);
  EXPECT_FALSE(delegate->got_get_height_for_width_);
  EXPECT_TRUE(panel->IsSame(delegate->view_));
  delegate->Reset();

  // Size is now the preferred size.
  EXPECT_EQ(expected_size, panel->GetSize());

  // No additional delegate methods were called.
  EXPECT_TRUE(delegate->IsReset());
}

void FillLayoutSizeHierarchyImpl() {
  CefRefPtr<CefPanel> panel_parent = CefPanel::CreatePanel(nullptr);
  CefRefPtr<CefPanel> panel_child = CefPanel::CreatePanel(nullptr);

  CefSize expected_size(100, 100);

  // Default Layout is FillLayout.
  CefRefPtr<CefLayout> layout1 = panel_parent->GetLayout();
  EXPECT_TRUE(layout1.get());
  EXPECT_TRUE(layout1->AsFillLayout().get());

  // Default bounds are empty.
  EXPECT_EQ(CefRect(0, 0, 0, 0), panel_parent->GetBounds());
  EXPECT_EQ(CefRect(0, 0, 0, 0), panel_child->GetBounds());

  // Without delegates the size must be set on the parent.
  panel_parent->SetSize(expected_size);

  // FillLayout is the default Layout. Both panels should end up with the same
  // size.
  panel_parent->AddChildView(panel_child);

  // Force layout.
  panel_parent->Layout();

  // Panels are now the same size.
  EXPECT_EQ(expected_size, panel_parent->GetSize());
  EXPECT_EQ(expected_size, panel_child->GetSize());

  // Resize the parent panel to a larger size.
  CefSize expected_size2(200, 200);
  panel_parent->SetSize(expected_size2);

  // Force layout.
  panel_parent->Layout();

  // Panels are now the larger size.
  EXPECT_EQ(expected_size2, panel_parent->GetSize());
  EXPECT_EQ(expected_size2, panel_child->GetSize());
}

void FillLayoutSizeHierarchyWithDelegate(bool size_from_parent) {
  CefRefPtr<SizingPanelDelegate> delegate_parent = new SizingPanelDelegate();
  CefRefPtr<CefPanel> panel_parent = CefPanel::CreatePanel(delegate_parent);
  CefRefPtr<SizingPanelDelegate> delegate_child = new SizingPanelDelegate();
  CefRefPtr<CefPanel> panel_child = CefPanel::CreatePanel(delegate_child);

  CefSize expected_size(100, 100);

  // The default layout is FillLayout, but explicitly set it anyways just for
  // some testing variety.
  panel_parent->SetToFillLayout();
  panel_child->SetToFillLayout();

  // Default bounds are empty.
  EXPECT_EQ(CefRect(0, 0, 0, 0), panel_parent->GetBounds());
  EXPECT_EQ(CefRect(0, 0, 0, 0), panel_child->GetBounds());

  // With delegates the size can come from either the parent or child.
  if (size_from_parent)
    delegate_parent->preferred_size_ = expected_size;
  else
    delegate_child->preferred_size_ = expected_size;

  // FillLayout is the default Layout. Both panels should end up with the same
  // size.
  panel_parent->AddChildView(panel_child);

  // No delegate methods were called yet.
  EXPECT_TRUE(delegate_parent->IsReset());
  EXPECT_TRUE(delegate_child->IsReset());

  // Force layout.
  panel_parent->Layout();

  // delegate_parent will be called to get the preferred size for panel_parent.
  EXPECT_TRUE(delegate_parent->got_get_preferred_size_);
  EXPECT_FALSE(delegate_parent->got_get_minimum_size_);
  EXPECT_FALSE(delegate_parent->got_get_maximum_size_);
  EXPECT_FALSE(delegate_parent->got_get_height_for_width_);
  EXPECT_TRUE(panel_parent->IsSame(delegate_parent->view_));
  delegate_parent->Reset();

  // delegate_child will be called to get the preferred size for panel_child.
  EXPECT_TRUE(delegate_child->got_get_preferred_size_);
  EXPECT_FALSE(delegate_child->got_get_minimum_size_);
  EXPECT_FALSE(delegate_child->got_get_maximum_size_);
  EXPECT_FALSE(delegate_child->got_get_height_for_width_);
  EXPECT_TRUE(panel_child->IsSame(delegate_child->view_));
  delegate_child->Reset();

  // Panels are now the same size.
  EXPECT_EQ(expected_size, panel_parent->GetSize());
  EXPECT_EQ(expected_size, panel_child->GetSize());

  // Resize the parent panel to a larger size.
  CefSize expected_size2(200, 200);
  panel_parent->SetSize(expected_size2);

  // Force layout.
  panel_parent->Layout();

  // Panels are now the larger size.
  EXPECT_EQ(expected_size2, panel_parent->GetSize());
  EXPECT_EQ(expected_size2, panel_child->GetSize());

  // No additional delegate methods were called.
  EXPECT_TRUE(delegate_parent->IsReset());
  EXPECT_TRUE(delegate_child->IsReset());
}

void FillLayoutSizeHierarchyFromParentWithDelegateImpl() {
  FillLayoutSizeHierarchyWithDelegate(true);
}

void FillLayoutSizeHierarchyFromChildWithDelegateImpl() {
  FillLayoutSizeHierarchyWithDelegate(false);
}

}  // namespace

// Test FillLayout.
PANEL_TEST(FillLayoutCreate)
PANEL_TEST(FillLayoutSizeToPreferredSize)
PANEL_TEST(FillLayoutSizeHierarchy)
PANEL_TEST(FillLayoutSizeHierarchyFromParentWithDelegate)
PANEL_TEST(FillLayoutSizeHierarchyFromChildWithDelegate)

namespace {

void BoxLayoutCreateImpl() {
  CefRefPtr<CefPanel> panel = CefPanel::CreatePanel(nullptr);

  CefBoxLayoutSettings settings;

  // Explicitly set to BoxLayout.
  panel->SetToBoxLayout(settings);

  CefRefPtr<CefLayout> layout = panel->GetLayout();
  EXPECT_TRUE(layout.get());
  EXPECT_TRUE(layout->AsBoxLayout().get());
}

const int kBLParentSize = 100;
const int kBLChildSize = 10;

void BoxLayoutSizeHierarchy(bool with_delegate,
                            const CefBoxLayoutSettings& settings,
                            const CefRect& expected_child1_bounds,
                            const CefRect& expected_child2_bounds,
                            int child1_flex = 0,
                            int child2_flex = 0) {
  CefRefPtr<SizingPanelDelegate> delegate_parent;
  if (with_delegate)
    delegate_parent = new SizingPanelDelegate();
  CefRefPtr<CefPanel> panel_parent = CefPanel::CreatePanel(delegate_parent);

  CefRefPtr<SizingPanelDelegate> delegate_child1, delegate_child2;
  if (with_delegate) {
    delegate_child1 = new SizingPanelDelegate();
    delegate_child2 = new SizingPanelDelegate();
  }
  CefRefPtr<CefPanel> panel_child1 = CefPanel::CreatePanel(delegate_child1);
  CefRefPtr<CefPanel> panel_child2 = CefPanel::CreatePanel(delegate_child2);

  // Default bounds are empty.
  EXPECT_EQ(CefRect(0, 0, 0, 0), panel_parent->GetBounds());
  EXPECT_EQ(CefRect(0, 0, 0, 0), panel_child1->GetBounds());
  EXPECT_EQ(CefRect(0, 0, 0, 0), panel_child2->GetBounds());

  // Give the parent a size.
  CefSize initial_parent_size(kBLParentSize, kBLParentSize);
  if (with_delegate)
    delegate_parent->preferred_size_ = initial_parent_size;
  else
    panel_parent->SetSize(initial_parent_size);

  // Give the children a size smaller than the parent.
  CefSize initial_child_size(kBLChildSize, kBLChildSize);
  if (with_delegate) {
    delegate_child1->preferred_size_ = initial_child_size;
    delegate_child2->preferred_size_ = initial_child_size;
  } else {
    panel_child1->SetSize(initial_child_size);
    panel_child2->SetSize(initial_child_size);
  }

  // Set to BoxLayout with |settings|.
  panel_parent->SetToBoxLayout(settings);

  panel_parent->AddChildView(panel_child1);
  panel_parent->AddChildView(panel_child2);

  if (child1_flex > 0 || child2_flex > 0) {
    // Flex will apply relative stretch in the main axis direction.
    CefRefPtr<CefBoxLayout> layout = panel_parent->GetLayout()->AsBoxLayout();
    if (child1_flex > 0)
      layout->SetFlexForView(panel_child1, child1_flex);
    if (child2_flex > 0)
      layout->SetFlexForView(panel_child2, child2_flex);
  }

  if (with_delegate) {
    // No delegate methods were called yet.
    EXPECT_TRUE(delegate_parent->IsReset());
    EXPECT_TRUE(delegate_child1->IsReset());
    EXPECT_TRUE(delegate_child2->IsReset());
  }

  // Force layout.
  panel_parent->Layout();

  if (with_delegate) {
    // delegate_parent will be called to get the preferred size for
    // panel_parent.
    EXPECT_TRUE(delegate_parent->got_get_preferred_size_);
    EXPECT_FALSE(delegate_parent->got_get_minimum_size_);
    EXPECT_FALSE(delegate_parent->got_get_maximum_size_);
    EXPECT_FALSE(delegate_parent->got_get_height_for_width_);
    EXPECT_TRUE(panel_parent->IsSame(delegate_parent->view_));
    delegate_parent->Reset();

    // delegate_child1 will be called to get the preferred size for
    // panel_child1.
    // GetHeightForWidth may also be called depending on the settings.
    EXPECT_TRUE(delegate_child1->got_get_preferred_size_);
    EXPECT_FALSE(delegate_child1->got_get_minimum_size_);
    EXPECT_FALSE(delegate_child1->got_get_maximum_size_);
    EXPECT_TRUE(panel_child1->IsSame(delegate_child1->view_));
    delegate_child1->Reset();

    // delegate_child2 will be called to get the preferred size for
    // panel_child2.
    // GetHeightForWidth may also be called depending on the settings.
    EXPECT_TRUE(delegate_child2->got_get_preferred_size_);
    EXPECT_FALSE(delegate_child2->got_get_minimum_size_);
    EXPECT_FALSE(delegate_child2->got_get_maximum_size_);
    EXPECT_TRUE(panel_child2->IsSame(delegate_child2->view_));
    delegate_child2->Reset();
  }

  // The parent should be the same size.
  EXPECT_EQ(initial_parent_size, panel_parent->GetSize());

  // Children should have the expected bounds.
  EXPECT_EQ(expected_child1_bounds, panel_child1->GetBounds());
  EXPECT_EQ(expected_child2_bounds, panel_child2->GetBounds());

  if (with_delegate) {
    // No additional delegate methods were called.
    EXPECT_TRUE(delegate_parent->IsReset());
    EXPECT_TRUE(delegate_child1->IsReset());
    EXPECT_TRUE(delegate_child2->IsReset());
  }
}

void BoxLayoutSizeHierarchyVerticalStretch(bool with_delegate) {
  // Vertical layout with children stretched along the horizontal axis.
  //
  // -----------
  // |111111111|
  // |222222222|
  // |         |
  // |         |
  // |         |
  // -----------
  //
  CefBoxLayoutSettings settings;

  CefRect expected_child1_bounds(0, 0, kBLParentSize, kBLChildSize);
  CefRect expected_child2_bounds(0, kBLChildSize, kBLParentSize, kBLChildSize);

  BoxLayoutSizeHierarchy(with_delegate, settings, expected_child1_bounds,
                         expected_child2_bounds);
}

void BoxLayoutSizeHierarchyVerticalStretchImpl() {
  BoxLayoutSizeHierarchyVerticalStretch(false);
}

void BoxLayoutSizeHierarchyVerticalStretchWithDelegateImpl() {
  BoxLayoutSizeHierarchyVerticalStretch(true);
}

void BoxLayoutSizeHierarchyHorizontalStretch(bool with_delegate) {
  // Horizontal layout with children stretched along the vertical axis.
  //
  // -----------
  // |12       |
  // |12       |
  // |12       |
  // |12       |
  // |12       |
  // -----------
  //
  CefBoxLayoutSettings settings;
  settings.horizontal = true;

  CefRect expected_child1_bounds(0, 0, kBLChildSize, kBLParentSize);
  CefRect expected_child2_bounds(kBLChildSize, 0, kBLChildSize, kBLParentSize);

  BoxLayoutSizeHierarchy(with_delegate, settings, expected_child1_bounds,
                         expected_child2_bounds);
}

void BoxLayoutSizeHierarchyHorizontalStretchImpl() {
  BoxLayoutSizeHierarchyHorizontalStretch(false);
}

void BoxLayoutSizeHierarchyHorizontalStretchWithDelegateImpl() {
  BoxLayoutSizeHierarchyHorizontalStretch(true);
}

void BoxLayoutSizeHierarchyVerticalCenter(bool with_delegate) {
  // Vertical layout with children centered along the horizontal axis.
  //
  // -----------
  // |    1    |
  // |    2    |
  // |         |
  // |         |
  // |         |
  // -----------
  //
  CefBoxLayoutSettings settings;
  settings.cross_axis_alignment = CEF_CROSS_AXIS_ALIGNMENT_CENTER;

  int xoffset = (kBLParentSize - kBLChildSize) / 2;
  CefRect expected_child1_bounds(xoffset, 0, kBLChildSize, kBLChildSize);
  CefRect expected_child2_bounds(xoffset, kBLChildSize, kBLChildSize,
                                 kBLChildSize);

  BoxLayoutSizeHierarchy(with_delegate, settings, expected_child1_bounds,
                         expected_child2_bounds);
}

void BoxLayoutSizeHierarchyVerticalCenterImpl() {
  BoxLayoutSizeHierarchyVerticalCenter(false);
}

void BoxLayoutSizeHierarchyVerticalCenterWithDelegateImpl() {
  BoxLayoutSizeHierarchyVerticalCenter(true);
}

void BoxLayoutSizeHierarchyHorizontalCenter(bool with_delegate) {
  // Horizontal layout with children centered along the vertical axis.
  //
  // -----------
  // |         |
  // |         |
  // |12       |
  // |         |
  // |         |
  // -----------
  //
  CefBoxLayoutSettings settings;
  settings.horizontal = true;
  settings.cross_axis_alignment = CEF_CROSS_AXIS_ALIGNMENT_CENTER;

  int yoffset = (kBLParentSize - kBLChildSize) / 2;
  CefRect expected_child1_bounds(0, yoffset, kBLChildSize, kBLChildSize);
  CefRect expected_child2_bounds(kBLChildSize, yoffset, kBLChildSize,
                                 kBLChildSize);

  BoxLayoutSizeHierarchy(with_delegate, settings, expected_child1_bounds,
                         expected_child2_bounds);
}

void BoxLayoutSizeHierarchyHorizontalCenterImpl() {
  BoxLayoutSizeHierarchyHorizontalCenter(false);
}

void BoxLayoutSizeHierarchyHorizontalCenterWithDelegateImpl() {
  BoxLayoutSizeHierarchyHorizontalCenter(true);
}

void BoxLayoutSizeHierarchyVerticalCenterCenter(bool with_delegate) {
  // Vertical layout with children centered along the horizontal and vertical
  // axis.
  //
  // -----------
  // |         |
  // |    1    |
  // |    2    |
  // |         |
  // -----------
  //
  CefBoxLayoutSettings settings;
  settings.main_axis_alignment = CEF_MAIN_AXIS_ALIGNMENT_CENTER;
  settings.cross_axis_alignment = CEF_CROSS_AXIS_ALIGNMENT_CENTER;

  int xoffset = (kBLParentSize - kBLChildSize) / 2;
  int yoffset = (kBLParentSize - (kBLChildSize * 2)) / 2;
  CefRect expected_child1_bounds(xoffset, yoffset, kBLChildSize, kBLChildSize);
  CefRect expected_child2_bounds(xoffset, yoffset + kBLChildSize, kBLChildSize,
                                 kBLChildSize);

  BoxLayoutSizeHierarchy(with_delegate, settings, expected_child1_bounds,
                         expected_child2_bounds);
}

void BoxLayoutSizeHierarchyVerticalCenterCenterImpl() {
  BoxLayoutSizeHierarchyVerticalCenterCenter(false);
}

void BoxLayoutSizeHierarchyVerticalCenterCenterWithDelegateImpl() {
  BoxLayoutSizeHierarchyVerticalCenterCenter(true);
}

void BoxLayoutSizeHierarchyHorizontalCenterCenter(bool with_delegate) {
  // Horizontal layout with children centered along the vertical and horizontal
  // axis.
  //
  // -----------
  // |         |
  // |         |
  // |   12    |
  // |         |
  // |         |
  // -----------
  //
  CefBoxLayoutSettings settings;
  settings.horizontal = true;
  settings.main_axis_alignment = CEF_MAIN_AXIS_ALIGNMENT_CENTER;
  settings.cross_axis_alignment = CEF_CROSS_AXIS_ALIGNMENT_CENTER;

  int xoffset = (kBLParentSize - (kBLChildSize * 2)) / 2;
  int yoffset = (kBLParentSize - kBLChildSize) / 2;
  CefRect expected_child1_bounds(xoffset, yoffset, kBLChildSize, kBLChildSize);
  CefRect expected_child2_bounds(xoffset + kBLChildSize, yoffset, kBLChildSize,
                                 kBLChildSize);

  BoxLayoutSizeHierarchy(with_delegate, settings, expected_child1_bounds,
                         expected_child2_bounds);
}

void BoxLayoutSizeHierarchyHorizontalCenterCenterImpl() {
  BoxLayoutSizeHierarchyHorizontalCenterCenter(false);
}

void BoxLayoutSizeHierarchyHorizontalCenterCenterWithDelegateImpl() {
  BoxLayoutSizeHierarchyHorizontalCenterCenter(true);
}

void BoxLayoutSizeHierarchyVerticalStretchFlexOne(bool with_delegate) {
  // Vertical layout with child1 stretched along the horizontal and vertical
  // axis and child2 stretched along the horizontal axis only (unequal flex).
  //
  // -----------
  // |111111111|
  // |111111111|
  // |111111111|
  // |111111111|
  // |222222222|
  // -----------
  //
  CefBoxLayoutSettings settings;

  CefRect expected_child1_bounds(0, 0, kBLParentSize,
                                 kBLParentSize - kBLChildSize);
  CefRect expected_child2_bounds(0, kBLParentSize - kBLChildSize, kBLParentSize,
                                 kBLChildSize);

  BoxLayoutSizeHierarchy(with_delegate, settings, expected_child1_bounds,
                         expected_child2_bounds, 1, 0);
}

void BoxLayoutSizeHierarchyVerticalStretchFlexOneImpl() {
  BoxLayoutSizeHierarchyVerticalStretchFlexOne(false);
}

void BoxLayoutSizeHierarchyVerticalStretchFlexOneWithDelegateImpl() {
  BoxLayoutSizeHierarchyVerticalStretchFlexOne(true);
}

void BoxLayoutSizeHierarchyHorizontalStretchFlexOne(bool with_delegate) {
  // Horizontal layout with child1 stretched along the vertical and horizontal
  // axis and child2 stretched along the vertical axis only (unequal flex).
  //
  // -----------
  // |111111112|
  // |111111112|
  // |111111112|
  // |111111112|
  // |111111112|
  // -----------
  //
  CefBoxLayoutSettings settings;
  settings.horizontal = true;

  CefRect expected_child1_bounds(0, 0, kBLParentSize - kBLChildSize,
                                 kBLParentSize);
  CefRect expected_child2_bounds(kBLParentSize - kBLChildSize, 0, kBLChildSize,
                                 kBLParentSize);

  BoxLayoutSizeHierarchy(with_delegate, settings, expected_child1_bounds,
                         expected_child2_bounds, 1, 0);
}

void BoxLayoutSizeHierarchyHorizontalStretchFlexOneImpl() {
  BoxLayoutSizeHierarchyHorizontalStretchFlexOne(false);
}

void BoxLayoutSizeHierarchyHorizontalStretchFlexOneWithDelegateImpl() {
  BoxLayoutSizeHierarchyHorizontalStretchFlexOne(true);
}

void BoxLayoutSizeHierarchyVerticalStretchFlexBoth(bool with_delegate) {
  // Vertical layout with children stretched along the horizontal and vertical
  // axis (equal flex).
  //
  // -----------
  // |111111111|
  // |111111111|
  // |111111111|
  // |222222222|
  // |222222222|
  // |222222222|
  // -----------
  //
  CefBoxLayoutSettings settings;

  CefRect expected_child1_bounds(0, 0, kBLParentSize, kBLParentSize / 2);
  CefRect expected_child2_bounds(0, kBLParentSize / 2, kBLParentSize,
                                 kBLParentSize / 2);

  BoxLayoutSizeHierarchy(with_delegate, settings, expected_child1_bounds,
                         expected_child2_bounds, 1, 1);
}

void BoxLayoutSizeHierarchyVerticalStretchFlexBothImpl() {
  BoxLayoutSizeHierarchyVerticalStretchFlexBoth(false);
}

void BoxLayoutSizeHierarchyVerticalStretchFlexBothWithDelegateImpl() {
  BoxLayoutSizeHierarchyVerticalStretchFlexBoth(true);
}

void BoxLayoutSizeHierarchyHorizontalStretchFlexBoth(bool with_delegate) {
  // Horizontal layout with children stretched along the vertical and horizontal
  // axis (equal flex).
  //
  // -----------
  // |111122222|
  // |111122222|
  // |111122222|
  // |111122222|
  // |111122222|
  // -----------
  //
  CefBoxLayoutSettings settings;
  settings.horizontal = true;

  CefRect expected_child1_bounds(0, 0, kBLParentSize / 2, kBLParentSize);
  CefRect expected_child2_bounds(kBLParentSize / 2, 0, kBLParentSize / 2,
                                 kBLParentSize);

  BoxLayoutSizeHierarchy(with_delegate, settings, expected_child1_bounds,
                         expected_child2_bounds, 1, 1);
}

void BoxLayoutSizeHierarchyHorizontalStretchFlexBothImpl() {
  BoxLayoutSizeHierarchyHorizontalStretchFlexBoth(false);
}

void BoxLayoutSizeHierarchyHorizontalStretchFlexBothWithDelegateImpl() {
  BoxLayoutSizeHierarchyHorizontalStretchFlexBoth(true);
}

}  // namespace

// Test BoxLayout. The BoxLayoutSizeHierarchy* tests are representative but not
// comprehensive (e.g. not all possible configurations are tested).
PANEL_TEST(BoxLayoutCreate)
PANEL_TEST(BoxLayoutSizeHierarchyVerticalStretch)
PANEL_TEST(BoxLayoutSizeHierarchyVerticalStretchWithDelegate)
PANEL_TEST(BoxLayoutSizeHierarchyHorizontalStretch)
PANEL_TEST(BoxLayoutSizeHierarchyHorizontalStretchWithDelegate)
PANEL_TEST(BoxLayoutSizeHierarchyVerticalCenter)
PANEL_TEST(BoxLayoutSizeHierarchyVerticalCenterWithDelegate)
PANEL_TEST(BoxLayoutSizeHierarchyHorizontalCenter)
PANEL_TEST(BoxLayoutSizeHierarchyHorizontalCenterWithDelegate)
PANEL_TEST(BoxLayoutSizeHierarchyVerticalCenterCenter)
PANEL_TEST(BoxLayoutSizeHierarchyVerticalCenterCenterWithDelegate)
PANEL_TEST(BoxLayoutSizeHierarchyHorizontalCenterCenter)
PANEL_TEST(BoxLayoutSizeHierarchyHorizontalCenterCenterWithDelegate)
PANEL_TEST(BoxLayoutSizeHierarchyVerticalStretchFlexOne)
PANEL_TEST(BoxLayoutSizeHierarchyVerticalStretchFlexOneWithDelegate)
PANEL_TEST(BoxLayoutSizeHierarchyHorizontalStretchFlexOne)
PANEL_TEST(BoxLayoutSizeHierarchyHorizontalStretchFlexOneWithDelegate)
PANEL_TEST(BoxLayoutSizeHierarchyVerticalStretchFlexBoth)
PANEL_TEST(BoxLayoutSizeHierarchyVerticalStretchFlexBothWithDelegate)
PANEL_TEST(BoxLayoutSizeHierarchyHorizontalStretchFlexBoth)
PANEL_TEST(BoxLayoutSizeHierarchyHorizontalStretchFlexBothWithDelegate)
