Index: components/html_viewer/html_frame_apptest.cc |
diff --git a/components/html_viewer/html_frame_apptest.cc b/components/html_viewer/html_frame_apptest.cc |
index 85109f584773c7d088d5ff365c1c6a652c8160eb..0931d528f2b933a2629f1e00506c9b3fb97f5845 100644 |
--- a/components/html_viewer/html_frame_apptest.cc |
+++ b/components/html_viewer/html_frame_apptest.cc |
@@ -4,9 +4,11 @@ |
#include "base/bind.h" |
#include "base/command_line.h" |
+#include "base/json/json_reader.h" |
#include "base/run_loop.h" |
#include "base/strings/stringprintf.h" |
#include "base/test/test_timeouts.h" |
+#include "base/values.h" |
#include "components/html_viewer/public/interfaces/test_html_viewer.mojom.h" |
#include "components/view_manager/public/cpp/tests/view_manager_test_base.h" |
#include "components/view_manager/public/cpp/view.h" |
@@ -31,20 +33,48 @@ namespace { |
// Switch to enable out of process iframes. |
const char kOOPIF[] = "oopifs"; |
+const char kAddFrameWithEmptyPageScript[] = |
+ "var iframe = document.createElement(\"iframe\");" |
+ "iframe.src = \"http://127.0.0.1:%u/files/empty_page.html\";" |
+ "document.body.appendChild(iframe);"; |
+ |
bool EnableOOPIFs() { |
return base::CommandLine::ForCurrentProcess()->HasSwitch(kOOPIF); |
} |
+mojo::ApplicationConnection* ApplicationConnectionForFrame(Frame* frame) { |
+ return static_cast<FrameConnection*>(frame->user_data()) |
+ ->application_connection(); |
+} |
+ |
std::string GetFrameText(ApplicationConnection* connection) { |
html_viewer::TestHTMLViewerPtr test_html_viewer; |
connection->ConnectToService(&test_html_viewer); |
std::string result; |
- test_html_viewer->GetContentAsText( |
- [&result](const String& mojo_string) { result = mojo_string; }); |
- test_html_viewer.WaitForIncomingResponse(); |
+ test_html_viewer->GetContentAsText([&result](const String& mojo_string) { |
+ result = mojo_string; |
+ ASSERT_TRUE(ViewManagerTestBase::QuitRunLoop()); |
+ }); |
+ if (!ViewManagerTestBase::DoRunLoopWithTimeout()) |
+ ADD_FAILURE() << "Timed out waiting for execute to complete"; |
+ // test_html_viewer.WaitForIncomingResponse(); |
return result; |
} |
+scoped_ptr<base::Value> ExecuteScript(ApplicationConnection* connection, |
+ const std::string& script) { |
+ html_viewer::TestHTMLViewerPtr test_html_viewer; |
+ connection->ConnectToService(&test_html_viewer); |
+ scoped_ptr<base::Value> result; |
+ test_html_viewer->ExecuteScript(script, [&result](const String& json_string) { |
+ result = base::JSONReader::Read(json_string.To<std::string>()); |
+ ASSERT_TRUE(ViewManagerTestBase::QuitRunLoop()); |
+ }); |
+ if (!ViewManagerTestBase::DoRunLoopWithTimeout()) |
+ ADD_FAILURE() << "Timed out waiting for execute to complete"; |
+ return result.Pass(); |
+} |
+ |
} // namespace |
class HTMLFrameTest : public ViewManagerTestBase { |
@@ -53,11 +83,44 @@ class HTMLFrameTest : public ViewManagerTestBase { |
~HTMLFrameTest() override {} |
protected: |
- mojo::URLRequestPtr BuildRequestForURL(const std::string& url_string) { |
+ Frame* LoadEmptyPageAndCreateFrame() { |
+ View* embed_view = window_manager()->CreateView(); |
+ FrameConnection* root_connection = |
+ InitFrameTree(embed_view, "http://127.0.0.1:%u/files/empty_page.html"); |
+ const std::string frame_text = |
+ GetFrameText(root_connection->application_connection()); |
+ if (frame_text != "child") { |
+ ADD_FAILURE() << "unexpected text " << frame_text; |
+ return nullptr; |
+ } |
+ |
+ // Dynamically add a new frame. |
+ ExecuteScript(root_connection->application_connection(), |
+ AddPortToString(kAddFrameWithEmptyPageScript)); |
+ |
+ // Wait for the frame to appear. |
+ if (frame_tree_->root()->children().empty() && |
+ !WaitForEmbedForDescendant()) { |
+ ADD_FAILURE() << "timed out waiting for child"; |
+ return nullptr; |
+ } |
+ |
+ if (embed_view->children().size() != 1u) { |
+ ADD_FAILURE() << "unexpected number of children " |
+ << embed_view->children().size(); |
+ return nullptr; |
+ } |
+ return frame_tree_->root()->FindFrame(embed_view->children()[0]->id()); |
+ } |
+ |
+ std::string AddPortToString(const std::string& string) { |
const uint16_t assigned_port = http_server_->host_port_pair().port(); |
+ return base::StringPrintf(string.c_str(), assigned_port); |
+ } |
+ |
+ mojo::URLRequestPtr BuildRequestForURL(const std::string& url_string) { |
mojo::URLRequestPtr request(mojo::URLRequest::New()); |
- request->url = mojo::String::From( |
- base::StringPrintf(url_string.c_str(), assigned_port)); |
+ request->url = mojo::String::From(AddPortToString(url_string)); |
return request.Pass(); |
} |
@@ -126,7 +189,7 @@ class HTMLFrameTest : public ViewManagerTestBase { |
DISALLOW_COPY_AND_ASSIGN(HTMLFrameTest); |
}; |
-TEST_F(HTMLFrameTest, HelloWorld) { |
+TEST_F(HTMLFrameTest, PageWithSingleFrame) { |
if (!EnableOOPIFs()) |
return; |
@@ -139,9 +202,8 @@ TEST_F(HTMLFrameTest, HelloWorld) { |
GetFrameText(root_connection->application_connection())); |
// page_with_single_frame contains a child frame. The child frame should |
- // create |
- // a new View and Frame. |
- if (embed_view->children().empty()) |
+ // create a new View and Frame. |
+ if (frame_tree_->root()->children().empty()) |
ASSERT_TRUE(WaitForEmbedForDescendant()); |
ASSERT_EQ(1u, embed_view->children().size()); |
@@ -154,4 +216,67 @@ TEST_F(HTMLFrameTest, HelloWorld) { |
->application_connection())); |
} |
+namespace {} // namespace |
+ |
+TEST_F(HTMLFrameTest, DynamicallyAddFrameAndVerifyParent) { |
+ if (!EnableOOPIFs()) |
+ return; |
+ |
+ Frame* child_frame = LoadEmptyPageAndCreateFrame(); |
+ ASSERT_TRUE(child_frame); |
+ |
+ mojo::ApplicationConnection* child_frame_connection = |
+ ApplicationConnectionForFrame(child_frame); |
+ |
+ ASSERT_EQ("child", GetFrameText(child_frame_connection)); |
+ // The child's parent should not be itself: |
+ const char kGetWindowParentNameScript[] = |
+ "window.parent == window ? 'parent is self' : 'parent not self';"; |
+ scoped_ptr<base::Value> parent_value( |
+ ExecuteScript(child_frame_connection, kGetWindowParentNameScript)); |
+ ASSERT_TRUE(parent_value->IsType(base::Value::TYPE_LIST)); |
+ base::ListValue* parent_list; |
+ ASSERT_TRUE(parent_value->GetAsList(&parent_list)); |
+ ASSERT_EQ(1u, parent_list->GetSize()); |
+ std::string parent_name; |
+ ASSERT_TRUE(parent_list->GetString(0u, &parent_name)); |
+ EXPECT_EQ("parent not self", parent_name); |
+} |
+ |
+TEST_F(HTMLFrameTest, DynamicallyAddFrameAndSeeNameChange) { |
+ if (!EnableOOPIFs()) |
+ return; |
+ |
+ Frame* child_frame = LoadEmptyPageAndCreateFrame(); |
+ ASSERT_TRUE(child_frame); |
+ |
+ mojo::ApplicationConnection* child_frame_connection = |
+ ApplicationConnectionForFrame(child_frame); |
+ |
+ // Change the name of the child's window. |
+ ExecuteScript(child_frame_connection, "window.name = 'new_child';"); |
+ |
+ // Eventually the parent should see the change. There is no convenient way |
+ // to observer this change, so we repeatedly ask for it and timeout if we |
+ // never get the right value. |
+ const base::TimeTicks start_time(base::TimeTicks::Now()); |
+ std::string find_window_result; |
+ do { |
+ scoped_ptr<base::Value> script_value( |
+ ExecuteScript(ApplicationConnectionForFrame(frame_tree_->root()), |
+ "window.frames['new_child'] != null ? 'found frame' : " |
+ "'unable to find frame';")); |
+ if (script_value->IsType(base::Value::TYPE_LIST)) { |
+ base::ListValue* script_value_as_list; |
+ if (script_value->GetAsList(&script_value_as_list) && |
+ script_value_as_list->GetSize() == 1) { |
+ script_value_as_list->GetString(0u, &find_window_result); |
+ } |
+ } |
+ } while (find_window_result != "found frame" && |
+ base::TimeTicks::Now() - start_time < |
+ TestTimeouts::action_timeout()); |
+ EXPECT_EQ("found frame", find_window_result); |
+} |
+ |
} // namespace mojo |