diff options
author | erg <erg@chromium.org> | 2015-09-14 12:02:09 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-09-14 19:02:42 +0000 |
commit | f72ad72272047f3a1471de9f37e0cba7174ac0b0 (patch) | |
tree | df579b7ed9c4f9a270631b1ee881acc55f8a5d0e /components/web_view | |
parent | ec1a9cfea91141cb9b4c0ebe18208afc08caac81 (diff) | |
download | chromium_src-f72ad72272047f3a1471de9f37e0cba7174ac0b0.zip chromium_src-f72ad72272047f3a1471de9f37e0cba7174ac0b0.tar.gz chromium_src-f72ad72272047f3a1471de9f37e0cba7174ac0b0.tar.bz2 |
mandoline: Add back/forward support and UI.
BUG=526268
Review URL: https://codereview.chromium.org/1326443006
Cr-Commit-Position: refs/heads/master@{#348673}
Diffstat (limited to 'components/web_view')
-rw-r--r-- | components/web_view/BUILD.gn | 2 | ||||
-rw-r--r-- | components/web_view/public/interfaces/web_view.mojom | 10 | ||||
-rw-r--r-- | components/web_view/test_runner/test_runner_application_delegate.cc | 3 | ||||
-rw-r--r-- | components/web_view/test_runner/test_runner_application_delegate.h | 2 | ||||
-rw-r--r-- | components/web_view/url_request_cloneable.cc | 60 | ||||
-rw-r--r-- | components/web_view/url_request_cloneable.h | 53 | ||||
-rw-r--r-- | components/web_view/web_view_apptest.cc | 111 | ||||
-rw-r--r-- | components/web_view/web_view_impl.cc | 54 | ||||
-rw-r--r-- | components/web_view/web_view_impl.h | 20 |
9 files changed, 296 insertions, 19 deletions
diff --git a/components/web_view/BUILD.gn b/components/web_view/BUILD.gn index f7a8c6e..92eb85f 100644 --- a/components/web_view/BUILD.gn +++ b/components/web_view/BUILD.gn @@ -29,6 +29,8 @@ source_set("lib") { "frame_utils.h", "pending_web_view_load.cc", "pending_web_view_load.h", + "url_request_cloneable.cc", + "url_request_cloneable.h", "web_view_application_delegate.cc", "web_view_application_delegate.h", "web_view_impl.cc", diff --git a/components/web_view/public/interfaces/web_view.mojom b/components/web_view/public/interfaces/web_view.mojom index 3eb490e..8eff5c4 100644 --- a/components/web_view/public/interfaces/web_view.mojom +++ b/components/web_view/public/interfaces/web_view.mojom @@ -7,6 +7,11 @@ module web_view.mojom; import "network/public/interfaces/url_loader.mojom"; import "components/view_manager/public/interfaces/view_tree.mojom"; +enum ButtonState { + ENABLED, + DISABLED, +}; + interface WebViewClient { // Page-generated request for a top level frame navigation. TopLevelNavigate(mojo.URLRequest request); @@ -14,6 +19,7 @@ interface WebViewClient { // Loading and progress notifications. LoadingStateChanged(bool is_loading); ProgressChanged(double progress); + BackForwardChanged(ButtonState back_button, ButtonState forward_button); // TODO(beng): also forward text direction. TitleChanged(string? title); @@ -25,6 +31,10 @@ interface WebView { // Provide a ViewTreeClient for this specific WebView. GetViewTreeClient(mojo.ViewTreeClient& view_tree_client); + + // Moves forward and backward. + GoBack(); + GoForward(); }; interface WebViewFactory { diff --git a/components/web_view/test_runner/test_runner_application_delegate.cc b/components/web_view/test_runner/test_runner_application_delegate.cc index 3844d74..82e1044 100644 --- a/components/web_view/test_runner/test_runner_application_delegate.cc +++ b/components/web_view/test_runner/test_runner_application_delegate.cc @@ -123,6 +123,9 @@ void TestRunnerApplicationDelegate::TopLevelNavigate( void TestRunnerApplicationDelegate::LoadingStateChanged(bool is_loading) {} void TestRunnerApplicationDelegate::ProgressChanged(double progress) {} +void TestRunnerApplicationDelegate::BackForwardChanged( + mojom::ButtonState back_button, + mojom::ButtonState forward_button) {} void TestRunnerApplicationDelegate::TitleChanged(const mojo::String& title) {} //////////////////////////////////////////////////////////////////////////////// diff --git a/components/web_view/test_runner/test_runner_application_delegate.h b/components/web_view/test_runner/test_runner_application_delegate.h index 86ba932..1cdb652 100644 --- a/components/web_view/test_runner/test_runner_application_delegate.h +++ b/components/web_view/test_runner/test_runner_application_delegate.h @@ -52,6 +52,8 @@ class TestRunnerApplicationDelegate void TopLevelNavigate(mojo::URLRequestPtr request) override; void LoadingStateChanged(bool is_loading) override; void ProgressChanged(double progress) override; + void BackForwardChanged(mojom::ButtonState back_button, + mojom::ButtonState forward_button) override; void TitleChanged(const mojo::String& title) override; // LayoutTestRunner: diff --git a/components/web_view/url_request_cloneable.cc b/components/web_view/url_request_cloneable.cc new file mode 100644 index 0000000..3a56cb3 --- /dev/null +++ b/components/web_view/url_request_cloneable.cc @@ -0,0 +1,60 @@ +// 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 "components/web_view/url_request_cloneable.h" + +#include "base/logging.h" +#include "mojo/common/data_pipe_utils.h" + +namespace web_view { + +URLRequestCloneable::URLRequestCloneable(mojo::URLRequestPtr original_request) + : url_(original_request->url), + method_(original_request->method), + headers_(original_request->headers.Pass()), + response_body_buffer_size_(original_request->response_body_buffer_size), + auto_follow_redirects_(original_request->auto_follow_redirects), + bypass_cache_(original_request->bypass_cache), + original_body_null_(original_request->body.is_null()), + body_(original_request->body.size()) { + // TODO(erg): Maybe we can do some sort of async copy here? + for (size_t i = 0; i < original_request->body.size(); ++i) { + mojo::common::BlockingCopyToString(original_request->body[i].Pass(), + &body_[i]); + } +} + +URLRequestCloneable::~URLRequestCloneable() {} + +mojo::URLRequestPtr URLRequestCloneable::Clone() const { + mojo::URLRequestPtr request = mojo::URLRequest::New(); + request->url = url_; + request->method = method_; + request->headers = headers_.Clone(); + request->response_body_buffer_size = response_body_buffer_size_; + request->auto_follow_redirects = auto_follow_redirects_; + request->bypass_cache = bypass_cache_; + + if (!original_body_null_) { + request->body = + mojo::Array<mojo::ScopedDataPipeConsumerHandle>(body_.size()); + for (size_t i = 0; i < body_.size(); ++i) { + uint32_t num_bytes = body_[i].size(); + MojoCreateDataPipeOptions options; + options.struct_size = sizeof(MojoCreateDataPipeOptions); + options.flags = MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE; + options.element_num_bytes = 1; + options.capacity_num_bytes = num_bytes; + mojo::DataPipe data_pipe(options); + request->body[i] = data_pipe.consumer_handle.Pass(); + WriteDataRaw(data_pipe.producer_handle.get(), body_[i].data(), &num_bytes, + MOJO_WRITE_DATA_FLAG_ALL_OR_NONE); + DCHECK_EQ(num_bytes, body_[i].size()); + } + } + + return request.Pass(); +} + +} // namespace web_view diff --git a/components/web_view/url_request_cloneable.h b/components/web_view/url_request_cloneable.h new file mode 100644 index 0000000..f9f6530 --- /dev/null +++ b/components/web_view/url_request_cloneable.h @@ -0,0 +1,53 @@ +// 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_WEB_VIEW_URL_REQUEST_CLONEABLE_H_ +#define COMPONENTS_WEB_VIEW_URL_REQUEST_CLONEABLE_H_ + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "mojo/services/network/public/interfaces/url_loader.mojom.h" + +namespace web_view { + +// This class caches data associated with a mojo::URLRequest and vends copies +// of the request for the WebView system. Copies are used for repeated +// navigations, but URLRequest structs are not copyable, as they contain +// handles to transfer large amounts of data between processes, so we +// immediately read the data from pipes and keep a copy here. +class URLRequestCloneable { + public: + explicit URLRequestCloneable(mojo::URLRequestPtr original_request); + ~URLRequestCloneable(); + + // Creates a new URLRequest. + mojo::URLRequestPtr Clone() const; + + private: + // All of these are straight from mojo::URLRequest. + mojo::String url_; + mojo::String method_; + mojo::Array<mojo::HttpHeaderPtr> headers_; + uint32_t response_body_buffer_size_; + bool auto_follow_redirects_; + bool bypass_cache_; + + // Whether the body mojo array in the |original_request| was null. We keep + // track of this so we can differentiate between null arrays and empty + // arrays. + bool original_body_null_; + + // This is a serialized version of the data from mojo::URLRequest. We copy + // this data straight out of the data pipes, and then serve it any time that + // AsURLRequest() is called. + std::vector<std::string> body_; + + DISALLOW_COPY_AND_ASSIGN(URLRequestCloneable); +}; + +} // namespace web_view + +#endif // COMPONENTS_WEB_VIEW_URL_REQUEST_CLONEABLE_H_ diff --git a/components/web_view/web_view_apptest.cc b/components/web_view/web_view_apptest.cc index 2286539..0fffd4c 100644 --- a/components/web_view/web_view_apptest.cc +++ b/components/web_view/web_view_apptest.cc @@ -19,6 +19,15 @@ namespace web_view { +namespace { +const char kTestOneFile[] = "test_one.html"; +const char kTestOneTitle[] = "Test Title One"; +const char kTestTwoFile[] = "test_two.html"; +const char kTestTwoTitle[] = "Test Title Two"; +const char kTestThreeFile[] = "test_three.html"; +const char kTestThreeTitle[] = "Test Title Three"; +} + class WebViewTest : public mojo::ViewManagerTestBase, public mojom::WebViewClient { public: @@ -28,12 +37,30 @@ class WebViewTest : public mojo::ViewManagerTestBase, mojom::WebView* web_view() { return web_view_.web_view(); } const std::string& last_title() { return last_title_; } + mojom::ButtonState last_back_button_state() { + return last_back_button_state_; + } + mojom::ButtonState last_forward_button_state() { + return last_forward_button_state_; + } void StartNestedRunLoopUntilLoadingDone() { run_loop_.reset(new base::RunLoop); run_loop_->Run(); } + void NavigateTo(const std::string& file) { + base::FilePath data_file; + ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &data_file)); + data_file = data_file.AppendASCII("components/test/data/web_view") + .AppendASCII(file).NormalizePathSeparators(); + ASSERT_TRUE(base::PathExists(data_file)); + mojo::URLRequestPtr request(mojo::URLRequest::New()); + request->url = mojo::util::FilePathToFileURL(data_file).spec(); + web_view()->LoadRequest(request.Pass()); + StartNestedRunLoopUntilLoadingDone(); + } + private: void QuitNestedRunLoop() { if (run_loop_) { @@ -70,6 +97,11 @@ class WebViewTest : public mojo::ViewManagerTestBase, QuitNestedRunLoop(); } void ProgressChanged(double progress) override {} + void BackForwardChanged(mojom::ButtonState back_button, + mojom::ButtonState forward_button) override { + last_back_button_state_ = back_button; + last_forward_button_state_ = forward_button; + } void TitleChanged(const mojo::String& title) override { last_title_ = title.get(); } @@ -83,29 +115,76 @@ class WebViewTest : public mojo::ViewManagerTestBase, scoped_ptr<base::RunLoop> run_loop_; std::string last_title_; + mojom::ButtonState last_back_button_state_; + mojom::ButtonState last_forward_button_state_; DISALLOW_COPY_AND_ASSIGN(WebViewTest); }; TEST_F(WebViewTest, TestTitleChanged) { - base::FilePath data_file; - ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &data_file)); - data_file = data_file.AppendASCII("components"). - AppendASCII("test"). - AppendASCII("data"). - AppendASCII("web_view"). - AppendASCII("test_title_changed.html"); - ASSERT_TRUE(base::PathExists(data_file)); - - mojo::URLRequestPtr request(mojo::URLRequest::New()); - request->url = mojo::util::FilePathToFileURL(data_file).spec(); - web_view()->LoadRequest(request.Pass()); - - // Build a nested run loop. + ASSERT_NO_FATAL_FAILURE(NavigateTo(kTestOneFile)); + + // Our title should have been set on the navigation. + EXPECT_EQ(kTestOneTitle, last_title()); +} + +TEST_F(WebViewTest, CanGoBackAndForward) { + ASSERT_NO_FATAL_FAILURE(NavigateTo(kTestOneFile)); + + // We can't go back on first navigation since there's nothing previously on + // the stack. + EXPECT_EQ(kTestOneTitle, last_title()); + EXPECT_EQ(mojom::ButtonState::BUTTON_STATE_DISABLED, + last_back_button_state()); + EXPECT_EQ(mojom::ButtonState::BUTTON_STATE_DISABLED, + last_forward_button_state()); + + ASSERT_NO_FATAL_FAILURE(NavigateTo(kTestTwoFile)); + + EXPECT_EQ(kTestTwoTitle, last_title()); + EXPECT_EQ(mojom::ButtonState::BUTTON_STATE_ENABLED, last_back_button_state()); + EXPECT_EQ(mojom::ButtonState::BUTTON_STATE_DISABLED, + last_forward_button_state()); + + web_view()->GoBack(); + StartNestedRunLoopUntilLoadingDone(); + + EXPECT_EQ(kTestOneTitle, last_title()); + EXPECT_EQ(mojom::ButtonState::BUTTON_STATE_DISABLED, + last_back_button_state()); + EXPECT_EQ(mojom::ButtonState::BUTTON_STATE_ENABLED, + last_forward_button_state()); + + web_view()->GoForward(); StartNestedRunLoopUntilLoadingDone(); + EXPECT_EQ(kTestTwoTitle, last_title()); + EXPECT_EQ(mojom::ButtonState::BUTTON_STATE_ENABLED, last_back_button_state()); + EXPECT_EQ(mojom::ButtonState::BUTTON_STATE_DISABLED, + last_forward_button_state()); +} + +TEST_F(WebViewTest, NavigationClearsForward) { + // First navigate somewhere, navigate somewhere else, and go back so we have + // one item in the forward stack. + ASSERT_NO_FATAL_FAILURE(NavigateTo(kTestOneFile)); + ASSERT_NO_FATAL_FAILURE(NavigateTo(kTestTwoFile)); + + web_view()->GoBack(); + StartNestedRunLoopUntilLoadingDone(); + + EXPECT_EQ(kTestOneTitle, last_title()); + EXPECT_EQ(mojom::ButtonState::BUTTON_STATE_DISABLED, + last_back_button_state()); + EXPECT_EQ(mojom::ButtonState::BUTTON_STATE_ENABLED, + last_forward_button_state()); + + // Now navigate to a third file. This should clear the forward stack. + ASSERT_NO_FATAL_FAILURE(NavigateTo(kTestThreeFile)); - // Our title should have been set on the final. - EXPECT_EQ("Test Title Changed", last_title()); + EXPECT_EQ(kTestThreeTitle, last_title()); + EXPECT_EQ(mojom::ButtonState::BUTTON_STATE_ENABLED, last_back_button_state()); + EXPECT_EQ(mojom::ButtonState::BUTTON_STATE_DISABLED, + last_forward_button_state()); } } // namespace web_view diff --git a/components/web_view/web_view_impl.cc b/components/web_view/web_view_impl.cc index 9d04651..eab771be1a6 100644 --- a/components/web_view/web_view_impl.cc +++ b/components/web_view/web_view_impl.cc @@ -14,6 +14,7 @@ #include "components/web_view/frame_devtools_agent.h" #include "components/web_view/frame_tree.h" #include "components/web_view/pending_web_view_load.h" +#include "components/web_view/url_request_cloneable.h" #include "mojo/application/public/cpp/application_impl.h" #include "mojo/converters/geometry/geometry_type_converters.h" #include "url/gurl.h" @@ -28,6 +29,8 @@ bool EnableRemoteDebugging() { } // namespace +using web_view::mojom::ButtonState; + //////////////////////////////////////////////////////////////////////////////// // WebViewImpl, public: @@ -74,12 +77,34 @@ void WebViewImpl::OnLoad() { frame_tree_client, frame_connection.Pass(), client_properties)); } +void WebViewImpl::LoadRequestImpl(mojo::URLRequestPtr request) { + client_->BackForwardChanged( + back_list_.empty() ? ButtonState::BUTTON_STATE_DISABLED + : ButtonState::BUTTON_STATE_ENABLED, + forward_list_.empty() ? ButtonState::BUTTON_STATE_DISABLED + : ButtonState::BUTTON_STATE_ENABLED); + + current_page_request_.reset(new URLRequestCloneable(request.Pass())); + pending_load_.reset(new PendingWebViewLoad(this)); + pending_load_->Init(current_page_request_->Clone()); +} + //////////////////////////////////////////////////////////////////////////////// // WebViewImpl, WebView implementation: void WebViewImpl::LoadRequest(mojo::URLRequestPtr request) { - pending_load_.reset(new PendingWebViewLoad(this)); - pending_load_->Init(request.Pass()); + // Clear the forward list when performing a top level load request. + forward_list_.clear(); + + if (current_page_request_) { + // TODO(erg): This doesn't deal with redirect chains. If you navigate to a + // site, and it 300s, we put both the url which caused the 300 and the + // target url here, when we should not add the redirect url to the back + // list. + back_list_.push_back(current_page_request_.Pass()); + } + + LoadRequestImpl(request.Pass()); } void WebViewImpl::GetViewTreeClient( @@ -87,6 +112,31 @@ void WebViewImpl::GetViewTreeClient( mojo::ViewTreeConnection::Create(this, view_tree_client.Pass()); } +void WebViewImpl::GoBack() { + if (back_list_.empty()) + return; + + // Take the current page request and put it in the forward list. + forward_list_.push_back(current_page_request_.Pass()); + + mojo::URLRequestPtr new_request = back_list_.back()->Clone(); + back_list_.resize(back_list_.size() - 1); + + LoadRequestImpl(new_request.Pass()); +} + +void WebViewImpl::GoForward() { + if (forward_list_.empty()) + return; + + back_list_.push_back(current_page_request_.Pass()); + + mojo::URLRequestPtr new_request = forward_list_.back()->Clone(); + forward_list_.resize(forward_list_.size() - 1); + + LoadRequestImpl(new_request.Pass()); +} + //////////////////////////////////////////////////////////////////////////////// // WebViewImpl, mojo::ViewTreeDelegate implementation: diff --git a/components/web_view/web_view_impl.h b/components/web_view/web_view_impl.h index c10a7be..3c6301c 100644 --- a/components/web_view/web_view_impl.h +++ b/components/web_view/web_view_impl.h @@ -7,6 +7,7 @@ #include "base/macros.h" #include "base/memory/scoped_ptr.h" +#include "base/memory/scoped_vector.h" #include "components/view_manager/public/cpp/view_observer.h" #include "components/view_manager/public/cpp/view_tree_delegate.h" #include "components/web_view/frame_devtools_agent_delegate.h" @@ -21,10 +22,11 @@ class ApplicationImpl; namespace web_view { class Frame; -class FrameTree; class FrameDevToolsAgent; +class FrameTree; class HTMLMessageEvent; class PendingWebViewLoad; +class URLRequestCloneable; class WebViewImpl : public mojom::WebView, public mojo::ViewTreeDelegate, @@ -43,11 +45,18 @@ class WebViewImpl : public mojom::WebView, // See description above |pending_load_| for details. void OnLoad(); + // Actually performs the load and tells our delegate the state of the + // forward/back list is. This is the shared implementation between + // LoadRequest() and Go{Back,Forward}(). + void LoadRequestImpl(mojo::URLRequestPtr request); + // Overridden from WebView: void LoadRequest(mojo::URLRequestPtr request) override; void GetViewTreeClient( mojo::InterfaceRequest<mojo::ViewTreeClient> view_tree_client) override; + void GoBack() override; + void GoForward() override; // Overridden from mojo::ViewTreeDelegate: void OnEmbed(mojo::View* root) override; @@ -89,6 +98,15 @@ class WebViewImpl : public mojom::WebView, scoped_ptr<FrameDevToolsAgent> devtools_agent_; + // It would be nice if mojo::URLRequest were cloneable; however, its use of + // ScopedDataPipieConsumerHandle means that it isn't. + ScopedVector<URLRequestCloneable> back_list_; + ScopedVector<URLRequestCloneable> forward_list_; + + // The request which is either currently loading or completed. Added to + // |back_list_| or |forward_list_| on further navigation. + scoped_ptr<URLRequestCloneable> current_page_request_; + DISALLOW_COPY_AND_ASSIGN(WebViewImpl); }; |