Commit 627fd583 authored by Wez's avatar Wez Committed by Commit Bot

[Fuchsia] Split navigation control and events out of FrameImpl.

NavigationControllerImpl holds all top-level frame navigation logic:
- Implementation of the fuchsia.web.NavigationController API.
- Logic to deliver updates to an attached NavigationEventListener.

Change-Id: I7f19de43cd17de0ad634c9218c7abe1dc6855053
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1672263Reviewed-by: default avatarKevin Marshall <kmarshall@chromium.org>
Commit-Queue: Wez <wez@chromium.org>
Cr-Commit-Position: refs/heads/master@{#671951}
parent dcb6ebb1
......@@ -105,6 +105,8 @@ component("web_engine_core") {
"browser/discarding_event_filter.h",
"browser/frame_impl.cc",
"browser/frame_impl.h",
"browser/navigation_controller_impl.cc",
"browser/navigation_controller_impl.h",
"browser/web_engine_browser_context.cc",
"browser/web_engine_browser_context.h",
"browser/web_engine_browser_main.cc",
......
This diff is collapsed.
......@@ -17,12 +17,11 @@
#include "base/macros.h"
#include "base/memory/platform_shared_memory_region.h"
#include "base/memory/weak_ptr.h"
#include "content/public/browser/web_contents_delegate.h"
#include "content/public/browser/web_contents_observer.h"
#include "fuchsia/engine/browser/discarding_event_filter.h"
#include "fuchsia/engine/browser/navigation_controller_impl.h"
#include "fuchsia/engine/on_load_script_injector.mojom.h"
#include "fuchsia/engine/web_engine_export.h"
#include "ui/aura/window_tree_host.h"
#include "ui/wm/core/focus_controller.h"
#include "url/gurl.h"
......@@ -39,7 +38,6 @@ class ContextImpl;
// Implementation of fuchsia.web.Frame based on content::WebContents.
class FrameImpl : public fuchsia::web::Frame,
public fuchsia::web::NavigationController,
public content::WebContentsObserver,
public content::WebContentsDelegate {
public:
......@@ -63,30 +61,6 @@ class FrameImpl : public fuchsia::web::Frame,
FRIEND_TEST_ALL_PREFIXES(FrameImplTest, ReloadFrame);
FRIEND_TEST_ALL_PREFIXES(FrameImplTest, Stop);
// fuchsia::web::Frame implementation.
void CreateView(fuchsia::ui::views::ViewToken view_token) override;
void GetNavigationController(
fidl::InterfaceRequest<fuchsia::web::NavigationController> controller)
override;
void ExecuteJavaScriptNoResult(
std::vector<std::string> origins,
fuchsia::mem::Buffer script,
ExecuteJavaScriptNoResultCallback callback) override;
void AddBeforeLoadJavaScript(
uint64_t id,
std::vector<std::string> origins,
fuchsia::mem::Buffer script,
AddBeforeLoadJavaScriptCallback callback) override;
void RemoveBeforeLoadJavaScript(uint64_t id) override;
void PostMessage(std::string origin,
fuchsia::web::WebMessage message,
fuchsia::web::Frame::PostMessageCallback callback) override;
void SetNavigationEventListener(
fidl::InterfaceHandle<fuchsia::web::NavigationEventListener> listener)
override;
void SetJavaScriptLogLevel(fuchsia::web::ConsoleLogLevel level) override;
void SetEnableInput(bool enable_input) override;
class OriginScopedScript {
public:
OriginScopedScript();
......@@ -112,23 +86,29 @@ class FrameImpl : public fuchsia::web::Frame,
// Release the resources associated with the View, if one is active.
void TearDownView();
// Processes the most recent changes to the browser's navigation state and
// triggers the publishing of change events.
void OnNavigationEntryChanged();
// Sends |pending_navigation_event_| to the observer if there are any changes
// to be reported.
void MaybeSendNavigationEvent();
// fuchsia::web::NavigationController implementation.
void LoadUrl(std::string url,
fuchsia::web::LoadUrlParams params,
LoadUrlCallback callback) override;
void GoBack() override;
void GoForward() override;
void Stop() override;
void Reload(fuchsia::web::ReloadType type) override;
void GetVisibleEntry(GetVisibleEntryCallback callback) override;
// fuchsia::web::Frame implementation.
void CreateView(fuchsia::ui::views::ViewToken view_token) override;
void GetNavigationController(
fidl::InterfaceRequest<fuchsia::web::NavigationController> controller)
override;
void ExecuteJavaScriptNoResult(
std::vector<std::string> origins,
fuchsia::mem::Buffer script,
ExecuteJavaScriptNoResultCallback callback) override;
void AddBeforeLoadJavaScript(
uint64_t id,
std::vector<std::string> origins,
fuchsia::mem::Buffer script,
AddBeforeLoadJavaScriptCallback callback) override;
void RemoveBeforeLoadJavaScript(uint64_t id) override;
void PostMessage(std::string origin,
fuchsia::web::WebMessage message,
fuchsia::web::Frame::PostMessageCallback callback) override;
void SetNavigationEventListener(
fidl::InterfaceHandle<fuchsia::web::NavigationEventListener> listener)
override;
void SetJavaScriptLogLevel(fuchsia::web::ConsoleLogLevel level) override;
void SetEnableInput(bool enable_input) override;
// content::WebContentsDelegate implementation.
void CloseContents(content::WebContents* source) override;
......@@ -154,12 +134,8 @@ class FrameImpl : public fuchsia::web::Frame,
// content::WebContentsObserver implementation.
void ReadyToCommitNavigation(
content::NavigationHandle* navigation_handle) override;
void TitleWasSet(content::NavigationEntry*) override;
void DocumentAvailableInMainFrame() override;
void DidFinishLoad(content::RenderFrameHost* render_frame_host,
const GURL& validated_url) override;
void DidStartNavigation(
content::NavigationHandle* navigation_handle) override;
std::unique_ptr<aura::WindowTreeHost> window_tree_host_;
const std::unique_ptr<content::WebContents> web_contents_;
......@@ -167,29 +143,16 @@ class FrameImpl : public fuchsia::web::Frame,
ContextImpl* const context_;
DiscardingEventFilter discarding_event_filter_;
fuchsia::web::NavigationEventListenerPtr navigation_listener_;
fuchsia::web::NavigationState previous_navigation_state_;
fuchsia::web::NavigationState pending_navigation_event_;
bool waiting_for_navigation_event_ack_;
NavigationControllerImpl navigation_controller_;
logging::LogSeverity log_level_;
std::map<uint64_t, OriginScopedScript> before_load_scripts_;
std::vector<uint64_t> before_load_scripts_order_;
base::RepeatingCallback<void(base::StringPiece)> console_log_message_hook_;
bool is_main_document_loaded_ = false;
fidl::Binding<fuchsia::web::Frame> binding_;
fidl::BindingSet<fuchsia::web::NavigationController> controller_bindings_;
base::WeakPtrFactory<FrameImpl> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(FrameImpl);
};
// Computes the differences from old_entry to new_entry and stores the result in
// |difference|.
WEB_ENGINE_EXPORT void DiffNavigationEntries(
const fuchsia::web::NavigationState& old_entry,
const fuchsia::web::NavigationState& new_entry,
fuchsia::web::NavigationState* difference);
#endif // FUCHSIA_ENGINE_BROWSER_FRAME_IMPL_H_
// Copyright 2019 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 "fuchsia/engine/browser/navigation_controller_impl.h"
#include "base/strings/strcat.h"
#include "base/strings/utf_string_conversions.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/was_activated_option.h"
#include "ui/base/page_transition_types.h"
namespace {
void UpdateNavigationStateFromNavigationEntry(
content::NavigationEntry* entry,
content::WebContents* web_contents,
fuchsia::web::NavigationState* navigation_state) {
DCHECK(entry);
DCHECK(web_contents);
DCHECK(navigation_state);
navigation_state->set_title(base::UTF16ToUTF8(entry->GetTitleForDisplay()));
navigation_state->set_url(entry->GetURL().spec());
switch (entry->GetPageType()) {
case content::PageType::PAGE_TYPE_NORMAL:
case content::PageType::PAGE_TYPE_INTERSTITIAL:
navigation_state->set_page_type(fuchsia::web::PageType::NORMAL);
break;
case content::PageType::PAGE_TYPE_ERROR:
navigation_state->set_page_type(fuchsia::web::PageType::ERROR);
break;
}
navigation_state->set_can_go_back(web_contents->GetController().CanGoBack());
navigation_state->set_can_go_forward(
web_contents->GetController().CanGoForward());
}
} // namespace
NavigationControllerImpl::NavigationControllerImpl(
content::WebContents* web_contents)
: web_contents_(web_contents), weak_factory_(this) {
Observe(web_contents_);
}
NavigationControllerImpl::~NavigationControllerImpl() = default;
void NavigationControllerImpl::AddBinding(
fidl::InterfaceRequest<fuchsia::web::NavigationController> controller) {
controller_bindings_.AddBinding(this, std::move(controller));
}
void NavigationControllerImpl::SetEventListener(
fidl::InterfaceHandle<fuchsia::web::NavigationEventListener> listener) {
// Reset the event buffer state.
waiting_for_navigation_event_ack_ = false;
previous_navigation_state_ = {};
pending_navigation_event_ = {};
if (listener) {
navigation_listener_.Bind(std::move(listener));
navigation_listener_.set_error_handler(
[this](zx_status_t status) { SetEventListener(nullptr); });
} else {
navigation_listener_.Unbind();
}
}
void NavigationControllerImpl::OnNavigationEntryChanged() {
fuchsia::web::NavigationState new_state;
new_state.set_is_main_document_loaded(is_main_document_loaded_);
UpdateNavigationStateFromNavigationEntry(
web_contents_->GetController().GetVisibleEntry(), web_contents_,
&new_state);
DiffNavigationEntries(previous_navigation_state_, new_state,
&pending_navigation_event_);
previous_navigation_state_ = std::move(new_state);
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&NavigationControllerImpl::MaybeSendNavigationEvent,
weak_factory_.GetWeakPtr()));
}
void NavigationControllerImpl::MaybeSendNavigationEvent() {
if (!navigation_listener_)
return;
if (pending_navigation_event_.IsEmpty() ||
waiting_for_navigation_event_ack_) {
return;
}
waiting_for_navigation_event_ack_ = true;
// Send the event to the observer and, upon acknowledgement, revisit this
// function to send another update.
navigation_listener_->OnNavigationStateChanged(
std::move(pending_navigation_event_), [this]() {
waiting_for_navigation_event_ack_ = false;
MaybeSendNavigationEvent();
});
pending_navigation_event_ = {};
}
void NavigationControllerImpl::LoadUrl(std::string url,
fuchsia::web::LoadUrlParams params,
LoadUrlCallback callback) {
fuchsia::web::NavigationController_LoadUrl_Result result;
GURL validated_url(url);
if (!validated_url.is_valid()) {
result.set_err(fuchsia::web::NavigationControllerError::INVALID_URL);
callback(std::move(result));
return;
}
content::NavigationController::LoadURLParams params_converted(validated_url);
if (params.has_headers()) {
std::vector<std::string> extra_headers;
extra_headers.reserve(params.headers().size());
for (const auto& header : params.headers()) {
// TODO(crbug.com/964732): Check there is no colon in |header_name|.
base::StringPiece header_name(
reinterpret_cast<const char*>(header.name.data()),
header.name.size());
base::StringPiece header_value(
reinterpret_cast<const char*>(header.value.data()),
header.value.size());
extra_headers.emplace_back(
base::StrCat({header_name, ": ", header_value}));
}
params_converted.extra_headers = base::JoinString(extra_headers, "\n");
}
if (validated_url.scheme() == url::kDataScheme)
params_converted.load_type = content::NavigationController::LOAD_TYPE_DATA;
params_converted.transition_type = ui::PageTransitionFromInt(
ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR);
if (params.has_was_user_activated() && params.was_user_activated()) {
params_converted.was_activated = content::WasActivatedOption::kYes;
} else {
params_converted.was_activated = content::WasActivatedOption::kNo;
}
web_contents_->GetController().LoadURLWithParams(params_converted);
result.set_response(fuchsia::web::NavigationController_LoadUrl_Response());
callback(std::move(result));
}
void NavigationControllerImpl::GoBack() {
if (web_contents_->GetController().CanGoBack())
web_contents_->GetController().GoBack();
}
void NavigationControllerImpl::GoForward() {
if (web_contents_->GetController().CanGoForward())
web_contents_->GetController().GoForward();
}
void NavigationControllerImpl::Stop() {
web_contents_->Stop();
}
void NavigationControllerImpl::Reload(fuchsia::web::ReloadType type) {
content::ReloadType internal_reload_type;
switch (type) {
case fuchsia::web::ReloadType::PARTIAL_CACHE:
internal_reload_type = content::ReloadType::NORMAL;
break;
case fuchsia::web::ReloadType::NO_CACHE:
internal_reload_type = content::ReloadType::BYPASSING_CACHE;
break;
}
web_contents_->GetController().Reload(internal_reload_type, false);
}
void NavigationControllerImpl::GetVisibleEntry(
fuchsia::web::NavigationController::GetVisibleEntryCallback callback) {
content::NavigationEntry* entry =
web_contents_->GetController().GetVisibleEntry();
if (!entry) {
callback({});
return;
}
fuchsia::web::NavigationState state;
state.set_is_main_document_loaded(is_main_document_loaded_);
UpdateNavigationStateFromNavigationEntry(entry, web_contents_, &state);
callback(std::move(state));
}
void NavigationControllerImpl::TitleWasSet(content::NavigationEntry* entry) {
// The title was changed after the document was loaded.
OnNavigationEntryChanged();
}
void NavigationControllerImpl::DocumentAvailableInMainFrame() {
// The main document is loaded, but not necessarily all the subresources. Some
// fields like "title" will change here.
OnNavigationEntryChanged();
}
void NavigationControllerImpl::DidFinishLoad(
content::RenderFrameHost* render_frame_host,
const GURL& validated_url) {
// The document and its statically-declared subresources are loaded.
is_main_document_loaded_ = true;
OnNavigationEntryChanged();
}
void NavigationControllerImpl::DidStartNavigation(
content::NavigationHandle* navigation_handle) {
if (navigation_handle->IsSameDocument())
return;
is_main_document_loaded_ = false;
OnNavigationEntryChanged();
}
void DiffNavigationEntries(const fuchsia::web::NavigationState& old_entry,
const fuchsia::web::NavigationState& new_entry,
fuchsia::web::NavigationState* difference) {
DCHECK(difference);
DCHECK(new_entry.has_title());
if (!old_entry.has_title() || (new_entry.title() != old_entry.title())) {
difference->set_title(new_entry.title());
}
DCHECK(new_entry.has_url());
if (!old_entry.has_url() || (new_entry.url() != old_entry.url())) {
difference->set_url(new_entry.url());
}
DCHECK(new_entry.has_page_type());
if (!old_entry.has_page_type() ||
(new_entry.page_type() != old_entry.page_type())) {
difference->set_page_type(new_entry.page_type());
}
DCHECK(new_entry.has_can_go_back());
if (!old_entry.has_can_go_back() ||
old_entry.can_go_back() != new_entry.can_go_back()) {
difference->set_can_go_back(new_entry.can_go_back());
}
DCHECK(new_entry.has_can_go_forward());
if (!old_entry.has_can_go_forward() ||
old_entry.can_go_forward() != new_entry.can_go_forward()) {
difference->set_can_go_forward(new_entry.can_go_forward());
}
DCHECK(new_entry.has_is_main_document_loaded());
if (!old_entry.has_is_main_document_loaded() ||
old_entry.is_main_document_loaded() !=
new_entry.is_main_document_loaded()) {
difference->set_is_main_document_loaded(
new_entry.is_main_document_loaded());
}
}
// Copyright 2019 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 FUCHSIA_ENGINE_BROWSER_NAVIGATION_CONTROLLER_IMPL_H_
#define FUCHSIA_ENGINE_BROWSER_NAVIGATION_CONTROLLER_IMPL_H_
#include <fuchsia/web/cpp/fidl.h>
#include <lib/fidl/cpp/binding_set.h>
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "content/public/browser/web_contents_observer.h"
#include "fuchsia/engine/web_engine_export.h"
namespace content {
class NavigationEntry;
class NavigationHandle;
class WebContents;
} // namespace content
// Implementation of fuchsia.web.NavigationController for content::WebContents.
class NavigationControllerImpl : public fuchsia::web::NavigationController,
public content::WebContentsObserver {
public:
explicit NavigationControllerImpl(content::WebContents* web_contents);
~NavigationControllerImpl() final;
void AddBinding(
fidl::InterfaceRequest<fuchsia::web::NavigationController> controller);
void SetEventListener(
fidl::InterfaceHandle<fuchsia::web::NavigationEventListener> listener);
private:
// Processes the most recent changes to the browser's navigation state and
// triggers the publishing of change events.
void OnNavigationEntryChanged();
// Sends |pending_navigation_event_| to the observer if there are any changes
// to be reported.
void MaybeSendNavigationEvent();
// fuchsia::web::NavigationController implementation.
void LoadUrl(std::string url,
fuchsia::web::LoadUrlParams params,
LoadUrlCallback callback) final;
void GoBack() final;
void GoForward() final;
void Stop() final;
void Reload(fuchsia::web::ReloadType type) final;
void GetVisibleEntry(GetVisibleEntryCallback callback) final;
// content::WebContentsObserver implementation.
void TitleWasSet(content::NavigationEntry*) override;
void DocumentAvailableInMainFrame() override;
void DidFinishLoad(content::RenderFrameHost* render_frame_host,
const GURL& validated_url) override;
void DidStartNavigation(
content::NavigationHandle* navigation_handle) override;
content::WebContents* const web_contents_;
// NavigationController client bindings.
fidl::BindingSet<fuchsia::web::NavigationController> controller_bindings_;
// Fields used to dispatch events to the NavigationEventListener.
fuchsia::web::NavigationEventListenerPtr navigation_listener_;
fuchsia::web::NavigationState previous_navigation_state_;
fuchsia::web::NavigationState pending_navigation_event_;
bool waiting_for_navigation_event_ack_ = false;
// True once the main document finishes loading.
bool is_main_document_loaded_ = false;
base::WeakPtrFactory<NavigationControllerImpl> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(NavigationControllerImpl);
};
// Computes the differences from old_entry to new_entry and stores the result in
// |difference|.
WEB_ENGINE_EXPORT void DiffNavigationEntries(
const fuchsia::web::NavigationState& old_entry,
const fuchsia::web::NavigationState& new_entry,
fuchsia::web::NavigationState* difference);
#endif // FUCHSIA_ENGINE_BROWSER_NAVIGATION_CONTROLLER_IMPL_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment