OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "base/bind.h" | 5 #include "base/bind.h" |
6 #include "base/command_line.h" | 6 #include "base/command_line.h" |
| 7 #include "base/json/json_reader.h" |
7 #include "base/run_loop.h" | 8 #include "base/run_loop.h" |
8 #include "base/strings/stringprintf.h" | 9 #include "base/strings/stringprintf.h" |
9 #include "base/test/test_timeouts.h" | 10 #include "base/test/test_timeouts.h" |
| 11 #include "base/values.h" |
10 #include "components/html_viewer/public/interfaces/test_html_viewer.mojom.h" | 12 #include "components/html_viewer/public/interfaces/test_html_viewer.mojom.h" |
11 #include "components/view_manager/public/cpp/tests/view_manager_test_base.h" | 13 #include "components/view_manager/public/cpp/tests/view_manager_test_base.h" |
12 #include "components/view_manager/public/cpp/view.h" | 14 #include "components/view_manager/public/cpp/view.h" |
13 #include "components/view_manager/public/cpp/view_manager.h" | 15 #include "components/view_manager/public/cpp/view_manager.h" |
14 #include "mandoline/tab/frame.h" | 16 #include "mandoline/tab/frame.h" |
15 #include "mandoline/tab/frame_connection.h" | 17 #include "mandoline/tab/frame_connection.h" |
16 #include "mandoline/tab/frame_tree.h" | 18 #include "mandoline/tab/frame_tree.h" |
17 #include "mandoline/tab/public/interfaces/frame_tree.mojom.h" | 19 #include "mandoline/tab/public/interfaces/frame_tree.mojom.h" |
18 #include "mojo/application/public/cpp/application_impl.h" | 20 #include "mojo/application/public/cpp/application_impl.h" |
19 #include "net/test/spawned_test_server/spawned_test_server.h" | 21 #include "net/test/spawned_test_server/spawned_test_server.h" |
20 #include "third_party/mojo_services/src/accessibility/public/interfaces/accessib
ility.mojom.h" | 22 #include "third_party/mojo_services/src/accessibility/public/interfaces/accessib
ility.mojom.h" |
21 | 23 |
22 using mandoline::Frame; | 24 using mandoline::Frame; |
23 using mandoline::FrameConnection; | 25 using mandoline::FrameConnection; |
24 using mandoline::FrameTree; | 26 using mandoline::FrameTree; |
25 using mandoline::FrameTreeClient; | 27 using mandoline::FrameTreeClient; |
26 | 28 |
27 namespace mojo { | 29 namespace mojo { |
28 | 30 |
29 namespace { | 31 namespace { |
30 | 32 |
31 // Switch to enable out of process iframes. | 33 // Switch to enable out of process iframes. |
32 const char kOOPIF[] = "oopifs"; | 34 const char kOOPIF[] = "oopifs"; |
33 | 35 |
| 36 const char kAddFrameWithEmptyPageScript[] = |
| 37 "var iframe = document.createElement(\"iframe\");" |
| 38 "iframe.src = \"http://127.0.0.1:%u/files/empty_page.html\";" |
| 39 "document.body.appendChild(iframe);"; |
| 40 |
34 bool EnableOOPIFs() { | 41 bool EnableOOPIFs() { |
35 return base::CommandLine::ForCurrentProcess()->HasSwitch(kOOPIF); | 42 return base::CommandLine::ForCurrentProcess()->HasSwitch(kOOPIF); |
36 } | 43 } |
37 | 44 |
| 45 mojo::ApplicationConnection* ApplicationConnectionForFrame(Frame* frame) { |
| 46 return static_cast<FrameConnection*>(frame->user_data()) |
| 47 ->application_connection(); |
| 48 } |
| 49 |
38 std::string GetFrameText(ApplicationConnection* connection) { | 50 std::string GetFrameText(ApplicationConnection* connection) { |
39 html_viewer::TestHTMLViewerPtr test_html_viewer; | 51 html_viewer::TestHTMLViewerPtr test_html_viewer; |
40 connection->ConnectToService(&test_html_viewer); | 52 connection->ConnectToService(&test_html_viewer); |
41 std::string result; | 53 std::string result; |
42 test_html_viewer->GetContentAsText( | 54 test_html_viewer->GetContentAsText([&result](const String& mojo_string) { |
43 [&result](const String& mojo_string) { result = mojo_string; }); | 55 result = mojo_string; |
44 test_html_viewer.WaitForIncomingResponse(); | 56 ASSERT_TRUE(ViewManagerTestBase::QuitRunLoop()); |
| 57 }); |
| 58 if (!ViewManagerTestBase::DoRunLoopWithTimeout()) |
| 59 ADD_FAILURE() << "Timed out waiting for execute to complete"; |
| 60 // test_html_viewer.WaitForIncomingResponse(); |
45 return result; | 61 return result; |
46 } | 62 } |
47 | 63 |
| 64 scoped_ptr<base::Value> ExecuteScript(ApplicationConnection* connection, |
| 65 const std::string& script) { |
| 66 html_viewer::TestHTMLViewerPtr test_html_viewer; |
| 67 connection->ConnectToService(&test_html_viewer); |
| 68 scoped_ptr<base::Value> result; |
| 69 test_html_viewer->ExecuteScript(script, [&result](const String& json_string) { |
| 70 result = base::JSONReader::Read(json_string.To<std::string>()); |
| 71 ASSERT_TRUE(ViewManagerTestBase::QuitRunLoop()); |
| 72 }); |
| 73 if (!ViewManagerTestBase::DoRunLoopWithTimeout()) |
| 74 ADD_FAILURE() << "Timed out waiting for execute to complete"; |
| 75 return result.Pass(); |
| 76 } |
| 77 |
48 } // namespace | 78 } // namespace |
49 | 79 |
50 class HTMLFrameTest : public ViewManagerTestBase { | 80 class HTMLFrameTest : public ViewManagerTestBase { |
51 public: | 81 public: |
52 HTMLFrameTest() {} | 82 HTMLFrameTest() {} |
53 ~HTMLFrameTest() override {} | 83 ~HTMLFrameTest() override {} |
54 | 84 |
55 protected: | 85 protected: |
| 86 Frame* LoadEmptyPageAndCreateFrame() { |
| 87 View* embed_view = window_manager()->CreateView(); |
| 88 FrameConnection* root_connection = |
| 89 InitFrameTree(embed_view, "http://127.0.0.1:%u/files/empty_page.html"); |
| 90 const std::string frame_text = |
| 91 GetFrameText(root_connection->application_connection()); |
| 92 if (frame_text != "child") { |
| 93 ADD_FAILURE() << "unexpected text " << frame_text; |
| 94 return nullptr; |
| 95 } |
| 96 |
| 97 // Dynamically add a new frame. |
| 98 ExecuteScript(root_connection->application_connection(), |
| 99 AddPortToString(kAddFrameWithEmptyPageScript)); |
| 100 |
| 101 // Wait for the frame to appear. |
| 102 if (frame_tree_->root()->children().empty() && |
| 103 !WaitForEmbedForDescendant()) { |
| 104 ADD_FAILURE() << "timed out waiting for child"; |
| 105 return nullptr; |
| 106 } |
| 107 |
| 108 if (embed_view->children().size() != 1u) { |
| 109 ADD_FAILURE() << "unexpected number of children " |
| 110 << embed_view->children().size(); |
| 111 return nullptr; |
| 112 } |
| 113 return frame_tree_->root()->FindFrame(embed_view->children()[0]->id()); |
| 114 } |
| 115 |
| 116 std::string AddPortToString(const std::string& string) { |
| 117 const uint16_t assigned_port = http_server_->host_port_pair().port(); |
| 118 return base::StringPrintf(string.c_str(), assigned_port); |
| 119 } |
| 120 |
56 mojo::URLRequestPtr BuildRequestForURL(const std::string& url_string) { | 121 mojo::URLRequestPtr BuildRequestForURL(const std::string& url_string) { |
57 const uint16_t assigned_port = http_server_->host_port_pair().port(); | |
58 mojo::URLRequestPtr request(mojo::URLRequest::New()); | 122 mojo::URLRequestPtr request(mojo::URLRequest::New()); |
59 request->url = mojo::String::From( | 123 request->url = mojo::String::From(AddPortToString(url_string)); |
60 base::StringPrintf(url_string.c_str(), assigned_port)); | |
61 return request.Pass(); | 124 return request.Pass(); |
62 } | 125 } |
63 | 126 |
64 FrameConnection* InitFrameTree(View* view, const std::string& url_string) { | 127 FrameConnection* InitFrameTree(View* view, const std::string& url_string) { |
65 scoped_ptr<FrameConnection> frame_connection(new FrameConnection); | 128 scoped_ptr<FrameConnection> frame_connection(new FrameConnection); |
66 FrameConnection* result = frame_connection.get(); | 129 FrameConnection* result = frame_connection.get(); |
67 ViewManagerClientPtr view_manager_client; | 130 ViewManagerClientPtr view_manager_client; |
68 frame_connection->Init(application_impl(), BuildRequestForURL(url_string), | 131 frame_connection->Init(application_impl(), BuildRequestForURL(url_string), |
69 &view_manager_client); | 132 &view_manager_client); |
70 FrameTreeClient* frame_tree_client = frame_connection->frame_tree_client(); | 133 FrameTreeClient* frame_tree_client = frame_connection->frame_tree_client(); |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
119 | 182 |
120 private: | 183 private: |
121 // A runloop specifically for OnEmbedForDescendant(). We use a separate | 184 // A runloop specifically for OnEmbedForDescendant(). We use a separate |
122 // runloop here as it's possible at the time OnEmbedForDescendant() is invoked | 185 // runloop here as it's possible at the time OnEmbedForDescendant() is invoked |
123 // a separate RunLoop is already running that we shouldn't quit. | 186 // a separate RunLoop is already running that we shouldn't quit. |
124 scoped_ptr<base::RunLoop> embed_run_loop_; | 187 scoped_ptr<base::RunLoop> embed_run_loop_; |
125 | 188 |
126 DISALLOW_COPY_AND_ASSIGN(HTMLFrameTest); | 189 DISALLOW_COPY_AND_ASSIGN(HTMLFrameTest); |
127 }; | 190 }; |
128 | 191 |
129 TEST_F(HTMLFrameTest, HelloWorld) { | 192 TEST_F(HTMLFrameTest, PageWithSingleFrame) { |
130 if (!EnableOOPIFs()) | 193 if (!EnableOOPIFs()) |
131 return; | 194 return; |
132 | 195 |
133 View* embed_view = window_manager()->CreateView(); | 196 View* embed_view = window_manager()->CreateView(); |
134 | 197 |
135 FrameConnection* root_connection = InitFrameTree( | 198 FrameConnection* root_connection = InitFrameTree( |
136 embed_view, "http://127.0.0.1:%u/files/page_with_single_frame.html"); | 199 embed_view, "http://127.0.0.1:%u/files/page_with_single_frame.html"); |
137 | 200 |
138 ASSERT_EQ("Page with single frame", | 201 ASSERT_EQ("Page with single frame", |
139 GetFrameText(root_connection->application_connection())); | 202 GetFrameText(root_connection->application_connection())); |
140 | 203 |
141 // page_with_single_frame contains a child frame. The child frame should | 204 // page_with_single_frame contains a child frame. The child frame should |
142 // create | 205 // create a new View and Frame. |
143 // a new View and Frame. | 206 if (frame_tree_->root()->children().empty()) |
144 if (embed_view->children().empty()) | |
145 ASSERT_TRUE(WaitForEmbedForDescendant()); | 207 ASSERT_TRUE(WaitForEmbedForDescendant()); |
146 | 208 |
147 ASSERT_EQ(1u, embed_view->children().size()); | 209 ASSERT_EQ(1u, embed_view->children().size()); |
148 Frame* child_frame = | 210 Frame* child_frame = |
149 frame_tree_->root()->FindFrame(embed_view->children()[0]->id()); | 211 frame_tree_->root()->FindFrame(embed_view->children()[0]->id()); |
150 ASSERT_TRUE(child_frame); | 212 ASSERT_TRUE(child_frame); |
151 | 213 |
152 ASSERT_EQ("child", | 214 ASSERT_EQ("child", |
153 GetFrameText(static_cast<FrameConnection*>(child_frame->user_data()) | 215 GetFrameText(static_cast<FrameConnection*>(child_frame->user_data()) |
154 ->application_connection())); | 216 ->application_connection())); |
155 } | 217 } |
156 | 218 |
| 219 namespace {} // namespace |
| 220 |
| 221 TEST_F(HTMLFrameTest, DynamicallyAddFrameAndVerifyParent) { |
| 222 if (!EnableOOPIFs()) |
| 223 return; |
| 224 |
| 225 Frame* child_frame = LoadEmptyPageAndCreateFrame(); |
| 226 ASSERT_TRUE(child_frame); |
| 227 |
| 228 mojo::ApplicationConnection* child_frame_connection = |
| 229 ApplicationConnectionForFrame(child_frame); |
| 230 |
| 231 ASSERT_EQ("child", GetFrameText(child_frame_connection)); |
| 232 // The child's parent should not be itself: |
| 233 const char kGetWindowParentNameScript[] = |
| 234 "window.parent == window ? 'parent is self' : 'parent not self';"; |
| 235 scoped_ptr<base::Value> parent_value( |
| 236 ExecuteScript(child_frame_connection, kGetWindowParentNameScript)); |
| 237 ASSERT_TRUE(parent_value->IsType(base::Value::TYPE_LIST)); |
| 238 base::ListValue* parent_list; |
| 239 ASSERT_TRUE(parent_value->GetAsList(&parent_list)); |
| 240 ASSERT_EQ(1u, parent_list->GetSize()); |
| 241 std::string parent_name; |
| 242 ASSERT_TRUE(parent_list->GetString(0u, &parent_name)); |
| 243 EXPECT_EQ("parent not self", parent_name); |
| 244 } |
| 245 |
| 246 TEST_F(HTMLFrameTest, DynamicallyAddFrameAndSeeNameChange) { |
| 247 if (!EnableOOPIFs()) |
| 248 return; |
| 249 |
| 250 Frame* child_frame = LoadEmptyPageAndCreateFrame(); |
| 251 ASSERT_TRUE(child_frame); |
| 252 |
| 253 mojo::ApplicationConnection* child_frame_connection = |
| 254 ApplicationConnectionForFrame(child_frame); |
| 255 |
| 256 // Change the name of the child's window. |
| 257 ExecuteScript(child_frame_connection, "window.name = 'new_child';"); |
| 258 |
| 259 // Eventually the parent should see the change. There is no convenient way |
| 260 // to observer this change, so we repeatedly ask for it and timeout if we |
| 261 // never get the right value. |
| 262 const base::TimeTicks start_time(base::TimeTicks::Now()); |
| 263 std::string find_window_result; |
| 264 do { |
| 265 scoped_ptr<base::Value> script_value( |
| 266 ExecuteScript(ApplicationConnectionForFrame(frame_tree_->root()), |
| 267 "window.frames['new_child'] != null ? 'found frame' : " |
| 268 "'unable to find frame';")); |
| 269 if (script_value->IsType(base::Value::TYPE_LIST)) { |
| 270 base::ListValue* script_value_as_list; |
| 271 if (script_value->GetAsList(&script_value_as_list) && |
| 272 script_value_as_list->GetSize() == 1) { |
| 273 script_value_as_list->GetString(0u, &find_window_result); |
| 274 } |
| 275 } |
| 276 } while (find_window_result != "found frame" && |
| 277 base::TimeTicks::Now() - start_time < |
| 278 TestTimeouts::action_timeout()); |
| 279 EXPECT_EQ("found frame", find_window_result); |
| 280 } |
| 281 |
157 } // namespace mojo | 282 } // namespace mojo |
OLD | NEW |