summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsky <sky@chromium.org>2015-06-29 15:50:16 -0700
committerCommit bot <commit-bot@chromium.org>2015-06-29 22:50:46 +0000
commit89d6be01582ba5dacbcb3c7fdaa7dd54829d757b (patch)
tree7ea49d0405def9261afc3d3e0c620d3faa25acb8
parent69475131d1707afa5935dbccd98092f464459bdb (diff)
downloadchromium_src-89d6be01582ba5dacbcb3c7fdaa7dd54829d757b.zip
chromium_src-89d6be01582ba5dacbcb3c7fdaa7dd54829d757b.tar.gz
chromium_src-89d6be01582ba5dacbcb3c7fdaa7dd54829d757b.tar.bz2
Adds a frame based test for html_viewer
I'm making the test code use mandoline/tab as that dramatically simplifies things, and lets me test that code too. I'm also creating a testing interface for HTMLViewer. It's only available if a command line flag is supplied. For now it's simple, but I'm going to add more to it (such as injecting JS). BUG=479172,490221 TEST=mostly test change R=ben@chromium.org Review URL: https://codereview.chromium.org/1210323002 Cr-Commit-Position: refs/heads/master@{#336668}
-rw-r--r--components/html_viewer/BUILD.gn6
-rw-r--r--components/html_viewer/DEPS4
-rw-r--r--components/html_viewer/ax_provider_impl.h3
-rw-r--r--components/html_viewer/frame_tree_manager.cc4
-rw-r--r--components/html_viewer/frame_tree_manager.h2
-rw-r--r--components/html_viewer/html_document_oopif.cc68
-rw-r--r--components/html_viewer/html_document_oopif.h31
-rw-r--r--components/html_viewer/html_frame_apptest.cc157
-rw-r--r--components/html_viewer/public/interfaces/BUILD.gn11
-rw-r--r--components/html_viewer/public/interfaces/test_html_viewer.mojom9
-rw-r--r--components/html_viewer/test_html_viewer_impl.cc30
-rw-r--r--components/html_viewer/test_html_viewer_impl.h37
-rw-r--r--components/test/data/html_viewer/empty_page.html5
-rw-r--r--components/test/data/html_viewer/page_with_single_frame.html7
-rw-r--r--mandoline/tab/frame.cc9
-rw-r--r--mandoline/tab/frame.h8
-rw-r--r--mandoline/tab/frame_tree.cc20
-rw-r--r--mandoline/tab/frame_tree.h9
-rw-r--r--mandoline/tab/frame_tree_delegate.h1
-rw-r--r--mandoline/ui/browser/browser.cc15
-rw-r--r--mojo/tools/data/apptests3
21 files changed, 410 insertions, 29 deletions
diff --git a/components/html_viewer/BUILD.gn b/components/html_viewer/BUILD.gn
index cbcc4c1..17757ffc64 100644
--- a/components/html_viewer/BUILD.gn
+++ b/components/html_viewer/BUILD.gn
@@ -88,6 +88,8 @@ source_set("lib") {
"media_factory.h",
"mock_web_blob_registry_impl.cc",
"mock_web_blob_registry_impl.h",
+ "test_html_viewer_impl.cc",
+ "test_html_viewer_impl.h",
"touch_handler.cc",
"touch_handler.h",
"web_clipboard_impl.cc",
@@ -121,6 +123,7 @@ source_set("lib") {
"//components/clipboard/public/interfaces",
"//components/devtools_service/public/cpp",
"//components/devtools_service/public/interfaces",
+ "//components/html_viewer/public/interfaces",
"//components/message_port",
"//components/mime_util",
"//components/resource_provider/public/cpp",
@@ -223,11 +226,14 @@ mojo_native_application("apptests") {
sources = [
"ax_provider_apptest.cc",
+ "html_frame_apptest.cc",
]
deps = [
"//base",
+ "//components/html_viewer/public/interfaces",
"//components/view_manager/public/cpp/tests:test_support",
+ "//mandoline/tab",
"//mandoline/tab/public/interfaces",
"//mojo/application/public/cpp:test_support",
"//net:test_support",
diff --git a/components/html_viewer/DEPS b/components/html_viewer/DEPS
index 6a70ad0..64e3c45 100644
--- a/components/html_viewer/DEPS
+++ b/components/html_viewer/DEPS
@@ -45,4 +45,8 @@ specific_include_rules = {
"run_all_unittests\.cc": [
"+third_party/mojo/src/mojo/edk/embedder/test_embedder.h",
],
+ "html_frame_apptest\.cc": [
+ "+mandoline/tab",
+ "+third_party/mojo/src/mojo/edk/embedder/test_embedder.h",
+ ],
}
diff --git a/components/html_viewer/ax_provider_impl.h b/components/html_viewer/ax_provider_impl.h
index 45582f2..18634cc 100644
--- a/components/html_viewer/ax_provider_impl.h
+++ b/components/html_viewer/ax_provider_impl.h
@@ -5,6 +5,7 @@
#ifndef COMPONENTS_HTML_VIEWER_AX_PROVIDER_IMPL_H_
#define COMPONENTS_HTML_VIEWER_AX_PROVIDER_IMPL_H_
+#include "base/basictypes.h"
#include "third_party/mojo/src/mojo/public/cpp/bindings/binding.h"
#include "third_party/mojo_services/src/accessibility/public/interfaces/accessibility.mojom.h"
@@ -35,6 +36,8 @@ class AxProviderImpl : public mojo::AxProvider {
blink::WebView* web_view_;
mojo::Binding<mojo::AxProvider> binding_;
+
+ DISALLOW_COPY_AND_ASSIGN(AxProviderImpl);
};
} // namespace html_viewer
diff --git a/components/html_viewer/frame_tree_manager.cc b/components/html_viewer/frame_tree_manager.cc
index 170bd5b..98d8947 100644
--- a/components/html_viewer/frame_tree_manager.cc
+++ b/components/html_viewer/frame_tree_manager.cc
@@ -120,6 +120,10 @@ blink::WebLocalFrame* FrameTreeManager::GetLocalWebFrame() {
return GetLocalFrame()->web_frame()->toWebLocalFrame();
}
+blink::WebView* FrameTreeManager::GetWebView() {
+ return root_->web_view();
+}
+
void FrameTreeManager::InitFrames(mojo::View* local_view, Frame* frame) {
frame->Init(local_view);
diff --git a/components/html_viewer/frame_tree_manager.h b/components/html_viewer/frame_tree_manager.h
index 0a613cc..ef13e7da 100644
--- a/components/html_viewer/frame_tree_manager.h
+++ b/components/html_viewer/frame_tree_manager.h
@@ -66,6 +66,8 @@ class FrameTreeManager : public mandoline::FrameTreeClient {
Frame* GetLocalFrame();
blink::WebLocalFrame* GetLocalWebFrame();
+ blink::WebView* GetWebView();
+
private:
friend class Frame;
diff --git a/components/html_viewer/html_document_oopif.cc b/components/html_viewer/html_document_oopif.cc
index 9bd2ab6..1dfc61b 100644
--- a/components/html_viewer/html_document_oopif.cc
+++ b/components/html_viewer/html_document_oopif.cc
@@ -17,6 +17,7 @@
#include "components/html_viewer/frame.h"
#include "components/html_viewer/frame_tree_manager.h"
#include "components/html_viewer/global_state.h"
+#include "components/html_viewer/test_html_viewer_impl.h"
#include "components/html_viewer/web_url_loader_impl.h"
#include "components/view_manager/ids.h"
#include "components/view_manager/public/cpp/view.h"
@@ -36,6 +37,13 @@ using mojo::View;
namespace html_viewer {
namespace {
+const char kEnableTestInterface[] = "enable-html-viewer-test-interface";
+
+bool IsTestInterfaceEnabled() {
+ return base::CommandLine::ForCurrentProcess()->HasSwitch(
+ kEnableTestInterface);
+}
+
bool EnableRemoteDebugging() {
return base::CommandLine::ForCurrentProcess()->HasSwitch(
devtools_service::kRemoteDebuggingPort);
@@ -43,6 +51,14 @@ bool EnableRemoteDebugging() {
} // namespace
+HTMLDocumentOOPIF::BeforeLoadCache::BeforeLoadCache() {
+}
+
+HTMLDocumentOOPIF::BeforeLoadCache::~BeforeLoadCache() {
+ STLDeleteElements(&ax_provider_requests);
+ STLDeleteElements(&test_interface_requests);
+}
+
HTMLDocumentOOPIF::HTMLDocumentOOPIF(mojo::ApplicationImpl* html_document_app,
mojo::ApplicationConnection* connection,
mojo::URLResponsePtr response,
@@ -62,6 +78,10 @@ HTMLDocumentOOPIF::HTMLDocumentOOPIF(mojo::ApplicationImpl* html_document_app,
static_cast<mojo::InterfaceFactory<mandoline::FrameTreeClient>*>(this));
connection->AddService(static_cast<InterfaceFactory<AxProvider>*>(this));
connection->AddService(&view_manager_client_factory_);
+ if (IsTestInterfaceEnabled()) {
+ connection->AddService(
+ static_cast<mojo::InterfaceFactory<TestHTMLViewer>*>(this));
+ }
resource_waiter_.reset(
new DocumentResourceWaiter(global_state_, response.Pass(), this));
@@ -93,7 +113,6 @@ HTMLDocumentOOPIF::~HTMLDocumentOOPIF() {
delete_callback_.Run(this);
STLDeleteElements(&ax_providers_);
- STLDeleteElements(&ax_provider_requests_);
}
void HTMLDocumentOOPIF::LoadIfNecessary() {
@@ -153,6 +172,13 @@ void HTMLDocumentOOPIF::Load() {
frame_tree_manager_->GetLocalWebFrame()->loadRequest(web_request);
}
+HTMLDocumentOOPIF::BeforeLoadCache* HTMLDocumentOOPIF::GetBeforeLoadCache() {
+ CHECK(!did_finish_local_frame_load_);
+ if (!before_load_cache_.get())
+ before_load_cache_.reset(new BeforeLoadCache);
+ return before_load_cache_.get();
+}
+
void HTMLDocumentOOPIF::OnEmbed(View* root) {
// We're an observer until the document is loaded.
root->AddObserver(this);
@@ -184,30 +210,54 @@ bool HTMLDocumentOOPIF::ShouldNavigateLocallyInMainFrame() {
void HTMLDocumentOOPIF::OnFrameDidFinishLoad(Frame* frame) {
// TODO(msw): Notify AxProvider clients of updates on child frame loads.
- if (frame != frame_tree_manager_->GetLocalFrame())
+ if (frame_tree_manager_ &&
+ frame != frame_tree_manager_->GetLocalFrame()) {
return;
+ }
- did_finish_main_frame_load_ = true;
+ did_finish_local_frame_load_ = true;
+ scoped_ptr<BeforeLoadCache> before_load_cache = before_load_cache_.Pass();
+ if (!before_load_cache)
+ return;
- // Bind any pending AxProviderImpl interface requests.
- for (auto it : ax_provider_requests_)
- ax_providers_.insert(new AxProviderImpl(frame->web_view(), it->Pass()));
- STLDeleteElements(&ax_provider_requests_);
+ // Bind any pending AxProvider and TestHTMLViewer interface requests.
+ for (auto it : before_load_cache->ax_provider_requests) {
+ ax_providers_.insert(
+ new AxProviderImpl(frame_tree_manager_->GetWebView(), it->Pass()));
+ }
+ for (auto it : before_load_cache->test_interface_requests) {
+ CHECK(IsTestInterfaceEnabled());
+ test_html_viewers_.push_back(new TestHTMLViewerImpl(
+ frame_tree_manager_->GetLocalWebFrame(), it->Pass()));
+ }
}
void HTMLDocumentOOPIF::Create(mojo::ApplicationConnection* connection,
mojo::InterfaceRequest<AxProvider> request) {
- if (!did_finish_main_frame_load_) {
+ if (!did_finish_local_frame_load_) {
// Cache AxProvider interface requests until the document finishes loading.
auto cached_request = new mojo::InterfaceRequest<AxProvider>();
*cached_request = request.Pass();
- ax_provider_requests_.insert(cached_request);
+ GetBeforeLoadCache()->ax_provider_requests.insert(cached_request);
} else {
ax_providers_.insert(new AxProviderImpl(
frame_tree_manager_->GetLocalFrame()->web_view(), request.Pass()));
}
}
+void HTMLDocumentOOPIF::Create(mojo::ApplicationConnection* connection,
+ mojo::InterfaceRequest<TestHTMLViewer> request) {
+ CHECK(IsTestInterfaceEnabled());
+ if (!did_finish_local_frame_load_) {
+ auto cached_request = new mojo::InterfaceRequest<TestHTMLViewer>();
+ *cached_request = request.Pass();
+ GetBeforeLoadCache()->test_interface_requests.insert(cached_request);
+ } else {
+ test_html_viewers_.push_back(new TestHTMLViewerImpl(
+ frame_tree_manager_->GetLocalWebFrame(), request.Pass()));
+ }
+}
+
void HTMLDocumentOOPIF::Create(
mojo::ApplicationConnection* connection,
mojo::InterfaceRequest<mandoline::FrameTreeClient> request) {
diff --git a/components/html_viewer/html_document_oopif.h b/components/html_viewer/html_document_oopif.h
index 5e9db3f..176317e 100644
--- a/components/html_viewer/html_document_oopif.h
+++ b/components/html_viewer/html_document_oopif.h
@@ -10,8 +10,10 @@
#include "base/callback.h"
#include "base/macros.h"
#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
#include "components/html_viewer/ax_provider_impl.h"
#include "components/html_viewer/frame_tree_manager_delegate.h"
+#include "components/html_viewer/public/interfaces/test_html_viewer.mojom.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_observer.h"
@@ -39,6 +41,7 @@ class DocumentResourceWaiter;
class Frame;
class FrameTreeManager;
class GlobalState;
+class TestHTMLViewerImpl;
class WebLayerTreeViewImpl;
// A view for a single HTML document.
@@ -51,7 +54,8 @@ class HTMLDocumentOOPIF
public mojo::ViewObserver,
public FrameTreeManagerDelegate,
public mojo::InterfaceFactory<mojo::AxProvider>,
- public mojo::InterfaceFactory<mandoline::FrameTreeClient> {
+ public mojo::InterfaceFactory<mandoline::FrameTreeClient>,
+ public mojo::InterfaceFactory<TestHTMLViewer> {
public:
using DeleteCallback = base::Callback<void(HTMLDocumentOOPIF*)>;
@@ -71,11 +75,23 @@ class HTMLDocumentOOPIF
private:
friend class DocumentResourceWaiter; // So it can call LoadIfNecessary().
+ // Requests for interfaces before the document is loaded go here. Once
+ // loaded the requests are bound and BeforeLoadCache is deleted.
+ struct BeforeLoadCache {
+ BeforeLoadCache();
+ ~BeforeLoadCache();
+
+ std::set<mojo::InterfaceRequest<mojo::AxProvider>*> ax_provider_requests;
+ std::set<mojo::InterfaceRequest<TestHTMLViewer>*> test_interface_requests;
+ };
+
~HTMLDocumentOOPIF() override;
void LoadIfNecessary();
void Load();
+ BeforeLoadCache* GetBeforeLoadCache();
+
// ViewManagerDelegate:
void OnEmbed(mojo::View* root) override;
void OnViewManagerDestroyed(mojo::ViewManager* view_manager) override;
@@ -100,17 +116,22 @@ class HTMLDocumentOOPIF
mojo::ApplicationConnection* connection,
mojo::InterfaceRequest<mandoline::FrameTreeClient> request) override;
+ // mojo::InterfaceFactory<TestHTMLViewer>:
+ void Create(mojo::ApplicationConnection* connection,
+ mojo::InterfaceRequest<TestHTMLViewer> request) override;
+
scoped_ptr<mojo::AppRefCount> app_refcount_;
mojo::ApplicationImpl* html_document_app_;
mojo::ApplicationConnection* connection_;
mojo::ViewManagerClientFactory view_manager_client_factory_;
// HTMLDocument owns these pointers; binding requests after document load.
- std::set<mojo::InterfaceRequest<mojo::AxProvider>*> ax_provider_requests_;
std::set<AxProviderImpl*> ax_providers_;
- // A flag set on didFinishLoad.
- bool did_finish_main_frame_load_ = false;
+ ScopedVector<TestHTMLViewerImpl> test_html_viewers_;
+
+ // Set to true when the local frame has finished loading.
+ bool did_finish_local_frame_load_ = false;
GlobalState* global_state_;
@@ -122,6 +143,8 @@ class HTMLDocumentOOPIF
scoped_ptr<DocumentResourceWaiter> resource_waiter_;
+ scoped_ptr<BeforeLoadCache> before_load_cache_;
+
DeleteCallback delete_callback_;
DISALLOW_COPY_AND_ASSIGN(HTMLDocumentOOPIF);
diff --git a/components/html_viewer/html_frame_apptest.cc b/components/html_viewer/html_frame_apptest.cc
new file mode 100644
index 0000000..85109f58
--- /dev/null
+++ b/components/html_viewer/html_frame_apptest.cc
@@ -0,0 +1,157 @@
+// 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 "base/bind.h"
+#include "base/command_line.h"
+#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/test_timeouts.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"
+#include "components/view_manager/public/cpp/view_manager.h"
+#include "mandoline/tab/frame.h"
+#include "mandoline/tab/frame_connection.h"
+#include "mandoline/tab/frame_tree.h"
+#include "mandoline/tab/public/interfaces/frame_tree.mojom.h"
+#include "mojo/application/public/cpp/application_impl.h"
+#include "net/test/spawned_test_server/spawned_test_server.h"
+#include "third_party/mojo_services/src/accessibility/public/interfaces/accessibility.mojom.h"
+
+using mandoline::Frame;
+using mandoline::FrameConnection;
+using mandoline::FrameTree;
+using mandoline::FrameTreeClient;
+
+namespace mojo {
+
+namespace {
+
+// Switch to enable out of process iframes.
+const char kOOPIF[] = "oopifs";
+
+bool EnableOOPIFs() {
+ return base::CommandLine::ForCurrentProcess()->HasSwitch(kOOPIF);
+}
+
+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();
+ return result;
+}
+
+} // namespace
+
+class HTMLFrameTest : public ViewManagerTestBase {
+ public:
+ HTMLFrameTest() {}
+ ~HTMLFrameTest() override {}
+
+ protected:
+ mojo::URLRequestPtr BuildRequestForURL(const std::string& url_string) {
+ const uint16_t assigned_port = http_server_->host_port_pair().port();
+ mojo::URLRequestPtr request(mojo::URLRequest::New());
+ request->url = mojo::String::From(
+ base::StringPrintf(url_string.c_str(), assigned_port));
+ return request.Pass();
+ }
+
+ FrameConnection* InitFrameTree(View* view, const std::string& url_string) {
+ scoped_ptr<FrameConnection> frame_connection(new FrameConnection);
+ FrameConnection* result = frame_connection.get();
+ ViewManagerClientPtr view_manager_client;
+ frame_connection->Init(application_impl(), BuildRequestForURL(url_string),
+ &view_manager_client);
+ FrameTreeClient* frame_tree_client = frame_connection->frame_tree_client();
+ frame_tree_.reset(new FrameTree(view, nullptr, frame_tree_client,
+ frame_connection.Pass()));
+ view->Embed(view_manager_client.Pass());
+ return result;
+ }
+
+ bool WaitForEmbedForDescendant() {
+ if (embed_run_loop_)
+ return false;
+ embed_run_loop_.reset(new base::RunLoop);
+ embed_run_loop_->Run();
+ embed_run_loop_.reset();
+ return true;
+ }
+
+ // ViewManagerTest:
+ void SetUp() override {
+ ViewManagerTestBase::SetUp();
+
+ // Make it so we get OnEmbedForDescendant().
+ window_manager()->SetEmbedRoot();
+
+ // Start a test server.
+ http_server_.reset(new net::SpawnedTestServer(
+ net::SpawnedTestServer::TYPE_HTTP, net::SpawnedTestServer::kLocalhost,
+ base::FilePath(FILE_PATH_LITERAL("components/test/data/html_viewer"))));
+ ASSERT_TRUE(http_server_->Start());
+ }
+ void TearDown() override {
+ frame_tree_.reset();
+ http_server_.reset();
+ ViewManagerTestBase::TearDown();
+ }
+ void OnEmbedForDescendant(View* view,
+ URLRequestPtr request,
+ ViewManagerClientPtr* client) override {
+ Frame* frame = Frame::FindFirstFrameAncestor(view);
+ scoped_ptr<FrameConnection> frame_connection(new FrameConnection);
+ frame_connection->Init(application_impl(), request.Pass(), client);
+ FrameTreeClient* frame_tree_client = frame_connection->frame_tree_client();
+ frame_tree_->CreateOrReplaceFrame(frame, view, frame_tree_client,
+ frame_connection.Pass());
+ if (embed_run_loop_)
+ embed_run_loop_->Quit();
+ }
+
+ scoped_ptr<net::SpawnedTestServer> http_server_;
+ scoped_ptr<FrameTree> frame_tree_;
+
+ private:
+ // A runloop specifically for OnEmbedForDescendant(). We use a separate
+ // runloop here as it's possible at the time OnEmbedForDescendant() is invoked
+ // a separate RunLoop is already running that we shouldn't quit.
+ scoped_ptr<base::RunLoop> embed_run_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(HTMLFrameTest);
+};
+
+TEST_F(HTMLFrameTest, HelloWorld) {
+ if (!EnableOOPIFs())
+ return;
+
+ View* embed_view = window_manager()->CreateView();
+
+ FrameConnection* root_connection = InitFrameTree(
+ embed_view, "http://127.0.0.1:%u/files/page_with_single_frame.html");
+
+ ASSERT_EQ("Page with single frame",
+ 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())
+ ASSERT_TRUE(WaitForEmbedForDescendant());
+
+ ASSERT_EQ(1u, embed_view->children().size());
+ Frame* child_frame =
+ frame_tree_->root()->FindFrame(embed_view->children()[0]->id());
+ ASSERT_TRUE(child_frame);
+
+ ASSERT_EQ("child",
+ GetFrameText(static_cast<FrameConnection*>(child_frame->user_data())
+ ->application_connection()));
+}
+
+} // namespace mojo
diff --git a/components/html_viewer/public/interfaces/BUILD.gn b/components/html_viewer/public/interfaces/BUILD.gn
new file mode 100644
index 0000000..504c103
--- /dev/null
+++ b/components/html_viewer/public/interfaces/BUILD.gn
@@ -0,0 +1,11 @@
+# Copyright 2014 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.
+
+import("//third_party/mojo/src/mojo/public/tools/bindings/mojom.gni")
+
+mojom("interfaces") {
+ sources = [
+ "test_html_viewer.mojom",
+ ]
+}
diff --git a/components/html_viewer/public/interfaces/test_html_viewer.mojom b/components/html_viewer/public/interfaces/test_html_viewer.mojom
new file mode 100644
index 0000000..f2a6b8d
--- /dev/null
+++ b/components/html_viewer/public/interfaces/test_html_viewer.mojom
@@ -0,0 +1,9 @@
+// Copyright 2014 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.
+
+module html_viewer;
+
+interface TestHTMLViewer {
+ GetContentAsText() => (string contents);
+};
diff --git a/components/html_viewer/test_html_viewer_impl.cc b/components/html_viewer/test_html_viewer_impl.cc
new file mode 100644
index 0000000..d403959
--- /dev/null
+++ b/components/html_viewer/test_html_viewer_impl.cc
@@ -0,0 +1,30 @@
+// Copyright 2014 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 "components/html_viewer/test_html_viewer_impl.h"
+
+#include "third_party/WebKit/public/platform/WebString.h"
+#include "third_party/WebKit/public/web/WebDocument.h"
+#include "third_party/WebKit/public/web/WebFrame.h"
+
+using blink::WebDocument;
+using blink::WebFrame;
+
+namespace html_viewer {
+
+TestHTMLViewerImpl::TestHTMLViewerImpl(
+ blink::WebFrame* web_frame,
+ mojo::InterfaceRequest<TestHTMLViewer> request)
+ : web_frame_(web_frame), binding_(this, request.Pass()) {
+}
+
+TestHTMLViewerImpl::~TestHTMLViewerImpl() {
+}
+
+void TestHTMLViewerImpl::GetContentAsText(
+ const GetContentAsTextCallback& callback) {
+ callback.Run(web_frame_->document().contentAsTextForTesting().utf8());
+}
+
+} // namespace html_viewer
diff --git a/components/html_viewer/test_html_viewer_impl.h b/components/html_viewer/test_html_viewer_impl.h
new file mode 100644
index 0000000..05cc17d
--- /dev/null
+++ b/components/html_viewer/test_html_viewer_impl.h
@@ -0,0 +1,37 @@
+// 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.
+
+#ifndef COMPONENTS_HTML_VIEWER_TEST_HTML_VIEWER_IMPL_H_
+#define COMPONENTS_HTML_VIEWER_TEST_HTML_VIEWER_IMPL_H_
+
+#include "base/basictypes.h"
+#include "components/html_viewer/public/interfaces/test_html_viewer.mojom.h"
+#include "third_party/mojo/src/mojo/public/cpp/bindings/binding.h"
+
+namespace blink {
+class WebFrame;
+}
+
+namespace html_viewer {
+
+// Caller must ensure that |web_frame| outlives TestHTMLViewerImpl.
+class TestHTMLViewerImpl : public TestHTMLViewer {
+ public:
+ TestHTMLViewerImpl(blink::WebFrame* web_frame,
+ mojo::InterfaceRequest<TestHTMLViewer> request);
+ ~TestHTMLViewerImpl() override;
+
+ private:
+ // TestHTMLViewer:
+ void GetContentAsText(const GetContentAsTextCallback& callback) override;
+
+ blink::WebFrame* web_frame_;
+ mojo::Binding<TestHTMLViewer> binding_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestHTMLViewerImpl);
+};
+
+} // namespace html_viewer
+
+#endif // COMPONENTS_HTML_VIEWER_TEST_HTML_VIEWER_IMPL_H_
diff --git a/components/test/data/html_viewer/empty_page.html b/components/test/data/html_viewer/empty_page.html
new file mode 100644
index 0000000..a5786ef
--- /dev/null
+++ b/components/test/data/html_viewer/empty_page.html
@@ -0,0 +1,5 @@
+<html>
+ <body style="background-color: red">
+ child
+ </body>
+</html>
diff --git a/components/test/data/html_viewer/page_with_single_frame.html b/components/test/data/html_viewer/page_with_single_frame.html
new file mode 100644
index 0000000..cbca1f9
--- /dev/null
+++ b/components/test/data/html_viewer/page_with_single_frame.html
@@ -0,0 +1,7 @@
+<html>
+ <body style="background-color: blue">
+ <iframe width="50%" height="50%"
+ src="empty_page.html"></iframe>
+ Page with single frame
+ </body>
+</html>
diff --git a/mandoline/tab/frame.cc b/mandoline/tab/frame.cc
index e987697..78a781e 100644
--- a/mandoline/tab/frame.cc
+++ b/mandoline/tab/frame.cc
@@ -45,7 +45,7 @@ Frame::Frame(FrameTree* tree,
view_(view),
parent_(nullptr),
view_ownership_(view_ownership),
- frame_user_data_(user_data.Pass()),
+ user_data_(user_data.Pass()),
frame_tree_client_(frame_tree_client),
frame_tree_server_binding_(this) {
view_->SetLocalProperty(kFrame, this);
@@ -101,6 +101,13 @@ const Frame* Frame::FindFrame(uint32_t id) const {
return nullptr;
}
+bool Frame::HasAncestor(const Frame* frame) const {
+ const Frame* current = this;
+ while (current && current != frame)
+ current = current->parent_;
+ return current == frame;
+}
+
void Frame::BuildFrameTree(std::vector<const Frame*>* frames) const {
frames->push_back(this);
for (const Frame* frame : children_)
diff --git a/mandoline/tab/frame.h b/mandoline/tab/frame.h
index cd81d20..7b97aaf 100644
--- a/mandoline/tab/frame.h
+++ b/mandoline/tab/frame.h
@@ -39,7 +39,7 @@ class Frame : public mojo::ViewObserver, public FrameTreeServer {
void Init(Frame* parent);
// Walks the View tree starting at |view| going up returning the first
- // Frame that is associated with a view. For example, if |view|
+ // Frame that is associated with |view|. For example, if |view|
// has a Frame associated with it, then that is returned. Otherwise
// this checks view->parent() and so on.
static Frame* FindFirstFrameAncestor(mojo::View* view);
@@ -58,7 +58,9 @@ class Frame : public mojo::ViewObserver, public FrameTreeServer {
}
const Frame* FindFrame(uint32_t id) const;
- FrameUserData* user_data();
+ bool HasAncestor(const Frame* frame) const;
+
+ FrameUserData* user_data() { return user_data_.get(); }
private:
friend class FrameTree;
@@ -88,7 +90,7 @@ class Frame : public mojo::ViewObserver, public FrameTreeServer {
Frame* parent_;
ViewOwnership view_ownership_;
std::vector<Frame*> children_;
- scoped_ptr<FrameUserData> frame_user_data_;
+ scoped_ptr<FrameUserData> user_data_;
FrameTreeClient* frame_tree_client_;
diff --git a/mandoline/tab/frame_tree.cc b/mandoline/tab/frame_tree.cc
index b833a590..cba1757 100644
--- a/mandoline/tab/frame_tree.cc
+++ b/mandoline/tab/frame_tree.cc
@@ -35,4 +35,24 @@ Frame* FrameTree::CreateAndAddFrame(mojo::View* view,
return frame;
}
+Frame* FrameTree::CreateOrReplaceFrame(Frame* frame,
+ mojo::View* view,
+ FrameTreeClient* frame_tree_client,
+ scoped_ptr<FrameUserData> user_data) {
+ DCHECK(frame && frame->HasAncestor(&root_));
+
+ Frame* parent = frame;
+ if (frame->view() == view) {
+ DCHECK(frame != &root_);
+ // Rembed in existing frame.
+ parent = frame->parent();
+ delete frame;
+ frame = nullptr;
+ } // else case is a view is becoming associated with a Frame, eg a new frame.
+
+ DCHECK(parent);
+
+ return CreateAndAddFrame(view, parent, frame_tree_client, user_data.Pass());
+}
+
} // namespace mandoline
diff --git a/mandoline/tab/frame_tree.h b/mandoline/tab/frame_tree.h
index 6d21698..3dc491e6 100644
--- a/mandoline/tab/frame_tree.h
+++ b/mandoline/tab/frame_tree.h
@@ -35,6 +35,15 @@ class FrameTree {
FrameTreeClient* client,
scoped_ptr<FrameUserData> user_data);
+ // If frame->view() == |view|, then |frame| is deleted and a new Frame created
+ // to replace it. Otherwise a new Frame is created as a child of |frame|.
+ // It is expected this is called from
+ // ViewManagerDelegate::OnEmbedForDescendant().
+ Frame* CreateOrReplaceFrame(Frame* frame,
+ mojo::View* view,
+ FrameTreeClient* frame_tree_client,
+ scoped_ptr<FrameUserData> user_data);
+
private:
friend class Frame;
diff --git a/mandoline/tab/frame_tree_delegate.h b/mandoline/tab/frame_tree_delegate.h
index f35a89e..9b12aa3 100644
--- a/mandoline/tab/frame_tree_delegate.h
+++ b/mandoline/tab/frame_tree_delegate.h
@@ -7,6 +7,7 @@
namespace mandoline {
+class Frame;
class MessageEvent;
class FrameTreeDelegate {
diff --git a/mandoline/ui/browser/browser.cc b/mandoline/ui/browser/browser.cc
index 96424fd..3cdd52d 100644
--- a/mandoline/ui/browser/browser.cc
+++ b/mandoline/ui/browser/browser.cc
@@ -115,7 +115,8 @@ void Browser::OnEmbedForDescendant(mojo::View* view,
mojo::ViewManagerClientPtr* client) {
// TODO(sky): move this to Frame/FrameTree.
Frame* frame = Frame::FindFirstFrameAncestor(view);
- if (!frame) {
+ if (!frame || !frame->HasAncestor(frame_tree_->root()) ||
+ frame == frame_tree_->root()) {
// TODO(sky): add requestor url so that we can return false if it's not
// an app we expect.
mojo::ApplicationConnection* connection =
@@ -124,19 +125,11 @@ void Browser::OnEmbedForDescendant(mojo::View* view,
return;
}
- Frame* parent = frame;
- if (frame->view() == view) {
- parent = frame->parent();
- // This is a reembed.
- delete frame;
- frame = nullptr;
- }
-
scoped_ptr<FrameConnection> frame_connection(new FrameConnection);
frame_connection->Init(app_, request.Pass(), client);
FrameTreeClient* frame_tree_client = frame_connection->frame_tree_client();
- frame_tree_->CreateAndAddFrame(view, parent, frame_tree_client,
- frame_connection.Pass());
+ frame_tree_->CreateOrReplaceFrame(frame, view, frame_tree_client,
+ frame_connection.Pass());
}
void Browser::OnViewManagerDestroyed(mojo::ViewManager* view_manager) {
diff --git a/mojo/tools/data/apptests b/mojo/tools/data/apptests
index a664220..5043d5d 100644
--- a/mojo/tools/data/apptests
+++ b/mojo/tools/data/apptests
@@ -57,7 +57,8 @@ if config.target_os != config.OS_ANDROID:
{
"test": "mojo:html_viewer_apptests",
"type": "gtest_isolated",
- "args": ["--use-test-config", "--oopifs"]
+ "args": ["--use-test-config", "--oopifs",
+ "--enable-html-viewer-test-interface"]
},
{
"test": "mojo:mandoline_frame_apptests",