Revert of Mandoline UI Process: Update namespaces and file names (patchset #9 id...
[chromium-blink-merge.git] / components / web_view / frame_apptest.cc
blob2526bd8c7c97689ec2c13a4b8eaece154d301118
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/web_view/frame.h"
7 #include "base/bind.h"
8 #include "base/location.h"
9 #include "base/logging.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/run_loop.h"
12 #include "base/test/test_timeouts.h"
13 #include "components/mus/public/cpp/view_observer.h"
14 #include "components/mus/public/cpp/view_tree_connection.h"
15 #include "components/mus/public/cpp/view_tree_delegate.h"
16 #include "components/mus/public/cpp/view_tree_host_factory.h"
17 #include "components/web_view/frame.h"
18 #include "components/web_view/frame_connection.h"
19 #include "components/web_view/frame_tree.h"
20 #include "components/web_view/frame_tree_delegate.h"
21 #include "components/web_view/frame_user_data.h"
22 #include "components/web_view/test_frame_tree_delegate.h"
23 #include "mojo/application/public/cpp/application_connection.h"
24 #include "mojo/application/public/cpp/application_delegate.h"
25 #include "mojo/application/public/cpp/application_impl.h"
26 #include "mojo/application/public/cpp/application_test_base.h"
27 #include "mojo/application/public/cpp/service_provider_impl.h"
29 using mojo::View;
30 using mojo::ViewTreeConnection;
32 namespace web_view {
34 namespace {
36 base::RunLoop* current_run_loop = nullptr;
38 void TimeoutRunLoop(const base::Closure& timeout_task, bool* timeout) {
39 CHECK(current_run_loop);
40 *timeout = true;
41 timeout_task.Run();
44 bool DoRunLoopWithTimeout() {
45 if (current_run_loop != nullptr)
46 return false;
48 bool timeout = false;
49 base::RunLoop run_loop;
50 base::MessageLoop::current()->PostDelayedTask(
51 FROM_HERE, base::Bind(&TimeoutRunLoop, run_loop.QuitClosure(), &timeout),
52 TestTimeouts::action_timeout());
54 current_run_loop = &run_loop;
55 current_run_loop->Run();
56 current_run_loop = nullptr;
57 return !timeout;
60 void QuitRunLoop() {
61 current_run_loop->Quit();
62 current_run_loop = nullptr;
65 } // namespace
67 void OnGotIdCallback(base::RunLoop* run_loop) {
68 run_loop->Quit();
71 // Creates a new FrameConnection. This runs a nested message loop until the
72 // content handler id is obtained.
73 scoped_ptr<FrameConnection> CreateFrameConnection(mojo::ApplicationImpl* app) {
74 scoped_ptr<FrameConnection> frame_connection(new FrameConnection);
75 mojo::URLRequestPtr request(mojo::URLRequest::New());
76 request->url = mojo::String::From(app->url());
77 base::RunLoop run_loop;
78 frame_connection->Init(app, request.Pass(),
79 base::Bind(&OnGotIdCallback, &run_loop));
80 run_loop.Run();
81 return frame_connection;
84 class TestFrameTreeClient : public FrameTreeClient {
85 public:
86 TestFrameTreeClient() : connect_count_(0) {}
87 ~TestFrameTreeClient() override {}
89 int connect_count() const { return connect_count_; }
91 mojo::Array<FrameDataPtr> connect_frames() { return connect_frames_.Pass(); }
93 mojo::Array<FrameDataPtr> adds() { return adds_.Pass(); }
95 // Sets a callback to run once OnConnect() is received.
96 void set_on_connect_callback(const base::Closure& closure) {
97 on_connect_callback_ = closure;
100 FrameTreeServer* server() { return server_.get(); }
102 // TestFrameTreeClient:
103 void OnConnect(FrameTreeServerPtr server,
104 uint32_t change_id,
105 uint32_t view_id,
106 ViewConnectType view_connect_type,
107 mojo::Array<FrameDataPtr> frames,
108 const OnConnectCallback& callback) override {
109 connect_count_++;
110 connect_frames_ = frames.Pass();
111 server_ = server.Pass();
112 callback.Run();
113 if (!on_connect_callback_.is_null())
114 on_connect_callback_.Run();
116 void OnFrameAdded(uint32_t change_id, FrameDataPtr frame) override {
117 adds_.push_back(frame.Pass());
119 void OnFrameRemoved(uint32_t change_id, uint32_t frame_id) override {}
120 void OnFrameClientPropertyChanged(uint32_t frame_id,
121 const mojo::String& name,
122 mojo::Array<uint8_t> new_data) override {}
123 void OnPostMessageEvent(uint32_t source_frame_id,
124 uint32_t target_frame_id,
125 HTMLMessageEventPtr event) override {}
126 void OnWillNavigate(uint32_t frame_id) override {}
128 private:
129 int connect_count_;
130 mojo::Array<FrameDataPtr> connect_frames_;
131 FrameTreeServerPtr server_;
132 mojo::Array<FrameDataPtr> adds_;
133 base::Closure on_connect_callback_;
135 DISALLOW_COPY_AND_ASSIGN(TestFrameTreeClient);
138 class FrameTest;
140 // ViewAndFrame maintains the View and TestFrameTreeClient associated with
141 // a single FrameTreeClient. In other words this maintains the data structures
142 // needed to represent a client side frame. To obtain one use
143 // FrameTest::WaitForViewAndFrame().
144 class ViewAndFrame : public mojo::ViewTreeDelegate {
145 public:
146 ~ViewAndFrame() override {
147 if (view_)
148 delete view_->connection();
151 // The View associated with the frame.
152 mojo::View* view() { return view_; }
153 TestFrameTreeClient* test_frame_tree_client() {
154 return &test_frame_tree_client_;
156 FrameTreeServer* frame_tree_server() {
157 return test_frame_tree_client_.server();
160 private:
161 friend class FrameTest;
163 ViewAndFrame()
164 : view_(nullptr), frame_tree_binding_(&test_frame_tree_client_) {}
166 // Runs a message loop until the view and frame data have been received.
167 void WaitForViewAndFrame() { run_loop_.Run(); }
169 void Bind(mojo::InterfaceRequest<FrameTreeClient> request) {
170 ASSERT_FALSE(frame_tree_binding_.is_bound());
171 test_frame_tree_client_.set_on_connect_callback(
172 base::Bind(&ViewAndFrame::OnGotConnect, base::Unretained(this)));
173 frame_tree_binding_.Bind(request.Pass());
176 void OnGotConnect() { QuitRunLoopIfNecessary(); }
178 void QuitRunLoopIfNecessary() {
179 if (view_ && test_frame_tree_client_.connect_count())
180 run_loop_.Quit();
183 // Overridden from ViewTreeDelegate:
184 void OnEmbed(View* root) override {
185 view_ = root;
186 QuitRunLoopIfNecessary();
188 void OnConnectionLost(ViewTreeConnection* connection) override {
189 view_ = nullptr;
192 mojo::View* view_;
193 base::RunLoop run_loop_;
194 TestFrameTreeClient test_frame_tree_client_;
195 mojo::Binding<FrameTreeClient> frame_tree_binding_;
197 DISALLOW_COPY_AND_ASSIGN(ViewAndFrame);
200 class FrameTest : public mojo::test::ApplicationTestBase,
201 public mojo::ApplicationDelegate,
202 public mojo::ViewTreeDelegate,
203 public mojo::InterfaceFactory<mojo::ViewTreeClient>,
204 public mojo::InterfaceFactory<FrameTreeClient> {
205 public:
206 FrameTest() : most_recent_connection_(nullptr), window_manager_(nullptr) {}
208 ViewTreeConnection* most_recent_connection() {
209 return most_recent_connection_;
212 protected:
213 ViewTreeConnection* window_manager() { return window_manager_; }
214 TestFrameTreeDelegate* frame_tree_delegate() {
215 return frame_tree_delegate_.get();
217 FrameTree* frame_tree() { return frame_tree_.get(); }
218 ViewAndFrame* root_view_and_frame() { return root_view_and_frame_.get(); }
220 mojo::Binding<FrameTreeServer>* frame_tree_server_binding(Frame* frame) {
221 return frame->frame_tree_server_binding_.get();
224 // Creates a new shared frame as a child of |parent|.
225 Frame* CreateSharedFrame(ViewAndFrame* parent) {
226 mojo::View* child_frame_view = parent->view()->connection()->CreateView();
227 parent->view()->AddChild(child_frame_view);
228 mojo::Map<mojo::String, mojo::Array<uint8_t>> client_properties;
229 client_properties.mark_non_null();
230 parent->frame_tree_server()->OnCreatedFrame(
231 child_frame_view->parent()->id(), child_frame_view->id(),
232 client_properties.Pass());
233 Frame* frame = frame_tree_delegate()->WaitForCreateFrame();
234 return HasFatalFailure() ? nullptr : frame;
237 scoped_ptr<ViewAndFrame> CreateChildViewAndFrame(ViewAndFrame* parent) {
238 // All frames start out initially shared.
239 Frame* child_frame = CreateSharedFrame(parent);
240 if (!child_frame)
241 return nullptr;
243 // Navigate the child frame, which should trigger a new ViewAndFrame.
244 mojo::URLRequestPtr request(mojo::URLRequest::New());
245 request->url = mojo::String::From(application_impl()->url());
246 parent->frame_tree_server()->RequestNavigate(
247 NAVIGATION_TARGET_TYPE_EXISTING_FRAME, child_frame->id(),
248 request.Pass());
249 return WaitForViewAndFrame();
252 // Runs a message loop until the data necessary to represent to a client side
253 // frame has been obtained.
254 scoped_ptr<ViewAndFrame> WaitForViewAndFrame() {
255 DCHECK(!view_and_frame_);
256 view_and_frame_.reset(new ViewAndFrame);
257 view_and_frame_->WaitForViewAndFrame();
258 return view_and_frame_.Pass();
261 private:
262 // ApplicationTestBase:
263 ApplicationDelegate* GetApplicationDelegate() override { return this; }
265 // ApplicationDelegate implementation.
266 bool ConfigureIncomingConnection(
267 mojo::ApplicationConnection* connection) override {
268 connection->AddService<mojo::ViewTreeClient>(this);
269 connection->AddService<FrameTreeClient>(this);
270 return true;
273 // Overridden from ViewTreeDelegate:
274 void OnEmbed(View* root) override {
275 most_recent_connection_ = root->connection();
276 QuitRunLoop();
278 void OnConnectionLost(ViewTreeConnection* connection) override {}
280 // Overridden from testing::Test:
281 void SetUp() override {
282 ApplicationTestBase::SetUp();
284 mojo::CreateSingleViewTreeHost(application_impl(), this, &host_);
286 ASSERT_TRUE(DoRunLoopWithTimeout());
287 std::swap(window_manager_, most_recent_connection_);
289 // Creates a FrameTree, which creates a single frame. Wait for the
290 // FrameTreeClient to be connected to.
291 frame_tree_delegate_.reset(new TestFrameTreeDelegate(application_impl()));
292 scoped_ptr<FrameConnection> frame_connection =
293 CreateFrameConnection(application_impl());
294 FrameTreeClient* frame_tree_client = frame_connection->frame_tree_client();
295 mojo::ViewTreeClientPtr view_tree_client =
296 frame_connection->GetViewTreeClient();
297 mojo::View* frame_root_view = window_manager()->CreateView();
298 window_manager()->GetRoot()->AddChild(frame_root_view);
299 frame_tree_.reset(
300 new FrameTree(0u, frame_root_view, view_tree_client.Pass(),
301 frame_tree_delegate_.get(), frame_tree_client,
302 frame_connection.Pass(), Frame::ClientPropertyMap()));
303 root_view_and_frame_ = WaitForViewAndFrame();
306 // Overridden from testing::Test:
307 void TearDown() override {
308 root_view_and_frame_.reset();
309 frame_tree_.reset();
310 frame_tree_delegate_.reset();
311 ApplicationTestBase::TearDown();
314 // Overridden from mojo::InterfaceFactory<mojo::ViewTreeClient>:
315 void Create(
316 mojo::ApplicationConnection* connection,
317 mojo::InterfaceRequest<mojo::ViewTreeClient> request) override {
318 if (view_and_frame_) {
319 mojo::ViewTreeConnection::Create(view_and_frame_.get(), request.Pass());
320 } else {
321 mojo::ViewTreeConnection::Create(this, request.Pass());
325 // Overridden from mojo::InterfaceFactory<FrameTreeClient>:
326 void Create(mojo::ApplicationConnection* connection,
327 mojo::InterfaceRequest<FrameTreeClient> request) override {
328 ASSERT_TRUE(view_and_frame_);
329 view_and_frame_->Bind(request.Pass());
332 scoped_ptr<TestFrameTreeDelegate> frame_tree_delegate_;
333 scoped_ptr<FrameTree> frame_tree_;
334 scoped_ptr<ViewAndFrame> root_view_and_frame_;
336 mojo::ViewTreeHostPtr host_;
338 // Used to receive the most recent view manager loaded by an embed action.
339 ViewTreeConnection* most_recent_connection_;
340 // The View Manager connection held by the window manager (app running at the
341 // root view).
342 ViewTreeConnection* window_manager_;
344 scoped_ptr<ViewAndFrame> view_and_frame_;
346 MOJO_DISALLOW_COPY_AND_ASSIGN(FrameTest);
349 // Verifies the FrameData supplied to the root FrameTreeClient::OnConnect().
350 TEST_F(FrameTest, RootFrameClientConnectData) {
351 mojo::Array<FrameDataPtr> frames =
352 root_view_and_frame()->test_frame_tree_client()->connect_frames();
353 ASSERT_EQ(1u, frames.size());
354 EXPECT_EQ(root_view_and_frame()->view()->id(), frames[0]->frame_id);
355 EXPECT_EQ(0u, frames[0]->parent_id);
358 // Verifies the FrameData supplied to a child FrameTreeClient::OnConnect().
359 TEST_F(FrameTest, ChildFrameClientConnectData) {
360 scoped_ptr<ViewAndFrame> child_view_and_frame(
361 CreateChildViewAndFrame(root_view_and_frame()));
362 ASSERT_TRUE(child_view_and_frame);
363 mojo::Array<FrameDataPtr> frames_in_child =
364 child_view_and_frame->test_frame_tree_client()->connect_frames();
365 // We expect 2 frames. One for the root, one for the child.
366 ASSERT_EQ(2u, frames_in_child.size());
367 EXPECT_EQ(frame_tree()->root()->id(), frames_in_child[0]->frame_id);
368 EXPECT_EQ(0u, frames_in_child[0]->parent_id);
369 EXPECT_EQ(child_view_and_frame->view()->id(), frames_in_child[1]->frame_id);
370 EXPECT_EQ(frame_tree()->root()->id(), frames_in_child[1]->parent_id);
373 TEST_F(FrameTest, OnViewEmbeddedInFrameDisconnected) {
374 scoped_ptr<ViewAndFrame> child_view_and_frame(
375 CreateChildViewAndFrame(root_view_and_frame()));
376 ASSERT_TRUE(child_view_and_frame);
378 // Delete the ViewTreeConnection for the child, which should trigger
379 // notification.
380 delete child_view_and_frame->view()->connection();
381 ASSERT_EQ(1u, frame_tree()->root()->children().size());
382 ASSERT_NO_FATAL_FAILURE(frame_tree_delegate()->WaitForFrameDisconnected(
383 frame_tree()->root()->children()[0]));
384 ASSERT_EQ(1u, frame_tree()->root()->children().size());
387 TEST_F(FrameTest, CantSendProgressChangeTargettingWrongApp) {
388 ASSERT_FALSE(frame_tree()->root()->IsLoading());
390 scoped_ptr<ViewAndFrame> child_view_and_frame(
391 CreateChildViewAndFrame(root_view_and_frame()));
392 ASSERT_TRUE(child_view_and_frame);
394 Frame* shared_frame = CreateSharedFrame(child_view_and_frame.get());
396 // Send LoadingStarted() from the root targetting a frame from another
397 // connection. It should be ignored (not update the loading status).
398 root_view_and_frame()->frame_tree_server()->LoadingStarted(
399 shared_frame->id());
400 frame_tree_server_binding(frame_tree()->root())->WaitForIncomingMethodCall();
401 EXPECT_FALSE(frame_tree()->root()->IsLoading());
404 } // namespace web_view