summaryrefslogtreecommitdiffstats
path: root/components/web_view
diff options
context:
space:
mode:
authorerg <erg@chromium.org>2015-09-14 12:02:09 -0700
committerCommit bot <commit-bot@chromium.org>2015-09-14 19:02:42 +0000
commitf72ad72272047f3a1471de9f37e0cba7174ac0b0 (patch)
treedf579b7ed9c4f9a270631b1ee881acc55f8a5d0e /components/web_view
parentec1a9cfea91141cb9b4c0ebe18208afc08caac81 (diff)
downloadchromium_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.gn2
-rw-r--r--components/web_view/public/interfaces/web_view.mojom10
-rw-r--r--components/web_view/test_runner/test_runner_application_delegate.cc3
-rw-r--r--components/web_view/test_runner/test_runner_application_delegate.h2
-rw-r--r--components/web_view/url_request_cloneable.cc60
-rw-r--r--components/web_view/url_request_cloneable.h53
-rw-r--r--components/web_view/web_view_apptest.cc111
-rw-r--r--components/web_view/web_view_impl.cc54
-rw-r--r--components/web_view/web_view_impl.h20
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);
};