diff options
author | sky <sky@chromium.org> | 2015-06-29 15:50:16 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-06-29 22:50:46 +0000 |
commit | 89d6be01582ba5dacbcb3c7fdaa7dd54829d757b (patch) | |
tree | 7ea49d0405def9261afc3d3e0c620d3faa25acb8 | |
parent | 69475131d1707afa5935dbccd98092f464459bdb (diff) | |
download | chromium_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}
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", |