blob: 3e4cac498b6503c68695a72977d173f6798a865d [file]
// 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 "mandoline/tab/frame.h"
#include "base/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/test/test_timeouts.h"
#include "components/view_manager/public/cpp/lib/view_manager_client_impl.h"
#include "components/view_manager/public/cpp/view_manager_client_factory.h"
#include "components/view_manager/public/cpp/view_manager_delegate.h"
#include "components/view_manager/public/cpp/view_manager_init.h"
#include "components/view_manager/public/cpp/view_observer.h"
#include "mandoline/tab/frame.h"
#include "mandoline/tab/frame_tree.h"
#include "mandoline/tab/frame_tree_delegate.h"
#include "mandoline/tab/frame_user_data.h"
#include "mandoline/tab/test_frame_tree_delegate.h"
#include "mojo/application/public/cpp/application_connection.h"
#include "mojo/application/public/cpp/application_delegate.h"
#include "mojo/application/public/cpp/application_impl.h"
#include "mojo/application/public/cpp/application_test_base.h"
#include "mojo/application/public/cpp/service_provider_impl.h"
using mojo::View;
using mojo::ViewManager;
namespace mandoline {
namespace {
base::RunLoop* current_run_loop = nullptr;
void TimeoutRunLoop(const base::Closure& timeout_task, bool* timeout) {
CHECK(current_run_loop);
*timeout = true;
timeout_task.Run();
}
bool DoRunLoopWithTimeout() {
if (current_run_loop != nullptr)
return false;
bool timeout = false;
base::RunLoop run_loop;
base::MessageLoop::current()->PostDelayedTask(
FROM_HERE, base::Bind(&TimeoutRunLoop, run_loop.QuitClosure(), &timeout),
TestTimeouts::action_timeout());
current_run_loop = &run_loop;
current_run_loop->Run();
current_run_loop = nullptr;
return !timeout;
}
void QuitRunLoop() {
current_run_loop->Quit();
current_run_loop = nullptr;
}
} // namespace
class FrameTest : public mojo::test::ApplicationTestBase,
public mojo::ApplicationDelegate,
public mojo::ViewManagerDelegate {
public:
FrameTest() : most_recent_view_manager_(nullptr), window_manager_(nullptr) {}
ViewManager* most_recent_view_manager() { return most_recent_view_manager_; }
// Overridden from ApplicationDelegate:
void Initialize(mojo::ApplicationImpl* app) override {
view_manager_client_factory_.reset(
new mojo::ViewManagerClientFactory(app->shell(), this));
}
// ApplicationDelegate implementation.
bool ConfigureIncomingConnection(
mojo::ApplicationConnection* connection) override {
connection->AddService(view_manager_client_factory_.get());
return true;
}
ViewManager* window_manager() { return window_manager_; }
// ApplicationTestBase:
ApplicationDelegate* GetApplicationDelegate() override { return this; }
// Overridden from ViewManagerDelegate:
void OnEmbed(View* root) override {
most_recent_view_manager_ = root->view_manager();
QuitRunLoop();
}
void OnViewManagerDestroyed(ViewManager* view_manager) override {}
private:
// Overridden from testing::Test:
void SetUp() override {
ApplicationTestBase::SetUp();
view_manager_init_.reset(
new mojo::ViewManagerInit(application_impl(), this, nullptr));
ASSERT_TRUE(DoRunLoopWithTimeout());
std::swap(window_manager_, most_recent_view_manager_);
}
// Overridden from testing::Test:
void TearDown() override {
view_manager_init_.reset(); // Uses application_impl() from base class.
ApplicationTestBase::TearDown();
}
scoped_ptr<mojo::ViewManagerInit> view_manager_init_;
scoped_ptr<mojo::ViewManagerClientFactory> view_manager_client_factory_;
// Used to receive the most recent view manager loaded by an embed action.
ViewManager* most_recent_view_manager_;
// The View Manager connection held by the window manager (app running at the
// root view).
ViewManager* window_manager_;
MOJO_DISALLOW_COPY_AND_ASSIGN(FrameTest);
};
class TestFrameTreeClient : public FrameTreeClient {
public:
TestFrameTreeClient() : connect_count_(0) {}
~TestFrameTreeClient() override {}
int connect_count() const { return connect_count_; }
mojo::Array<FrameDataPtr> connect_frames() { return connect_frames_.Pass(); }
mojo::Array<FrameDataPtr> adds() { return adds_.Pass(); }
// TestFrameTreeClient:
void OnConnect(FrameTreeServerPtr server,
uint32_t change_id,
mojo::Array<FrameDataPtr> frames) override {
connect_count_++;
connect_frames_ = frames.Pass();
server_ = server.Pass();
}
void OnFrameAdded(uint32_t change_id, FrameDataPtr frame) override {
adds_.push_back(frame.Pass());
}
void OnFrameRemoved(uint32_t change_id, uint32_t frame_id) override {}
void OnFrameClientPropertyChanged(uint32_t frame_id,
const mojo::String& name,
mojo::Array<uint8_t> new_data) override {}
void OnPostMessageEvent(uint32_t source_frame_id,
uint32_t target_frame_id,
HTMLMessageEventPtr event) override {}
void OnWillNavigate(uint32_t frame_id,
const OnWillNavigateCallback& callback) override {
callback.Run();
}
private:
int connect_count_;
mojo::Array<FrameDataPtr> connect_frames_;
FrameTreeServerPtr server_;
mojo::Array<FrameDataPtr> adds_;
DISALLOW_COPY_AND_ASSIGN(TestFrameTreeClient);
};
// Verifies the root gets a connect.
TEST_F(FrameTest, RootGetsConnect) {
TestFrameTreeDelegate tree_delegate;
TestFrameTreeClient root_client;
FrameTree tree(window_manager()->GetRoot(), &tree_delegate, &root_client,
nullptr);
ASSERT_EQ(1, root_client.connect_count());
mojo::Array<FrameDataPtr> frames = root_client.connect_frames();
ASSERT_EQ(1u, frames.size());
EXPECT_EQ(tree.root()->view()->id(), frames[0]->frame_id);
EXPECT_EQ(0u, frames[0]->parent_id);
}
// Verifies adding a child to the root.
TEST_F(FrameTest, SingleChild) {
TestFrameTreeDelegate tree_delegate;
TestFrameTreeClient root_client;
FrameTree tree(window_manager()->GetRoot(), &tree_delegate, &root_client,
nullptr);
View* child = window_manager()->CreateView();
EXPECT_EQ(nullptr, Frame::FindFirstFrameAncestor(child));
window_manager()->GetRoot()->AddChild(child);
EXPECT_EQ(tree.root(), Frame::FindFirstFrameAncestor(child));
TestFrameTreeClient child_client;
Frame* child_frame =
tree.CreateAndAddFrame(child, tree.root(), &child_client, nullptr);
EXPECT_EQ(tree.root(), child_frame->parent());
ASSERT_EQ(1, child_client.connect_count());
mojo::Array<FrameDataPtr> frames_in_child = child_client.connect_frames();
// We expect 2 frames. One for the root, one for the child.
ASSERT_EQ(2u, frames_in_child.size());
EXPECT_EQ(tree.root()->view()->id(), frames_in_child[0]->frame_id);
EXPECT_EQ(0u, frames_in_child[0]->parent_id);
EXPECT_EQ(child_frame->view()->id(), frames_in_child[1]->frame_id);
EXPECT_EQ(tree.root()->view()->id(), frames_in_child[1]->parent_id);
// We should have gotten notification of the add.
EXPECT_EQ(1u, root_client.adds().size());
}
} // namespace mandoline