blob: 97af6a1c2caeabab1ea91ecaf17e37c00c2cc1e8 [file] [log] [blame]
// 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 "blimp/engine/browser/blimp_engine_session.h"
#include "base/strings/utf_string_conversions.h"
#include "blimp/common/create_blimp_message.h"
#include "blimp/common/proto/tab_control.pb.h"
#include "blimp/engine/browser/blimp_browser_context.h"
#include "blimp/engine/ui/blimp_layout_manager.h"
#include "blimp/engine/ui/blimp_screen.h"
#include "blimp/engine/ui/blimp_ui_context_factory.h"
#include "blimp/net/blimp_connection.h"
#include "blimp/net/blimp_message_multiplexer.h"
#include "blimp/net/blimp_message_thread_pipe.h"
#include "blimp/net/browser_connection_handler.h"
#include "blimp/net/common.h"
#include "blimp/net/engine_authentication_handler.h"
#include "blimp/net/engine_connection_manager.h"
#include "blimp/net/null_blimp_message_processor.h"
#include "blimp/net/tcp_engine_transport.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host.h"
#include "content/public/browser/web_contents.h"
#include "net/base/net_errors.h"
#include "ui/aura/client/default_capture_client.h"
#include "ui/aura/env.h"
#include "ui/aura/window.h"
#include "ui/aura/window_tree_host.h"
#include "ui/gfx/geometry/size.h"
#include "ui/wm/core/base_focus_rules.h"
#include "ui/wm/core/default_activation_client.h"
#include "ui/wm/core/focus_controller.h"
#if !defined(USE_X11)
#include "blimp/engine/ui/blimp_window_tree_host.h"
#endif
namespace blimp {
namespace engine {
namespace {
const int kDummyTabId = 0;
const float kDefaultScaleFactor = 1.f;
const int kDefaultDisplayWidth = 800;
const int kDefaultDisplayHeight = 600;
const uint16_t kDefaultPort = 25467;
// Focus rules that support activating an child window.
class FocusRulesImpl : public wm::BaseFocusRules {
public:
FocusRulesImpl() {}
~FocusRulesImpl() override {}
bool SupportsChildActivation(aura::Window* window) const override {
return true;
}
private:
DISALLOW_COPY_AND_ASSIGN(FocusRulesImpl);
};
net::IPAddressNumber GetIPv4AnyAddress() {
net::IPAddressNumber output;
output.push_back(0);
output.push_back(0);
output.push_back(0);
output.push_back(0);
return output;
}
} // namespace
// EngineNetworkComponents is created by the BlimpEngineSession on the UI
// thread, and then used and destroyed on the IO thread.
class EngineNetworkComponents {
public:
explicit EngineNetworkComponents(net::NetLog* net_log);
~EngineNetworkComponents();
// Sets up network components and starts listening for incoming connection.
// This should be called after all features have been registered so that
// received messages can be properly handled.
void Initialize();
// Connects message pipes between the specified feature and the network layer,
// using |incoming_proxy| as the incoming message processor, and connecting
// |outgoing_pipe| to the actual message sender.
void RegisterFeature(BlimpMessage::Type type,
scoped_ptr<BlimpMessageThreadPipe> outgoing_pipe,
scoped_ptr<BlimpMessageProcessor> incoming_proxy);
private:
net::NetLog* net_log_;
scoped_ptr<BrowserConnectionHandler> connection_handler_;
scoped_ptr<EngineAuthenticationHandler> authentication_handler_;
scoped_ptr<EngineConnectionManager> connection_manager_;
// Container for the feature-specific MessageProcessors.
std::vector<scoped_ptr<BlimpMessageProcessor>> incoming_proxies_;
// Containers for the MessageProcessors used to write feature-specific
// messages to the network, and the thread-pipe endpoints through which
// they are used from the UI thread.
std::vector<scoped_ptr<BlimpMessageProcessor>> outgoing_message_processors_;
std::vector<scoped_ptr<BlimpMessageThreadPipe>> outgoing_pipes_;
DISALLOW_COPY_AND_ASSIGN(EngineNetworkComponents);
};
EngineNetworkComponents::EngineNetworkComponents(net::NetLog* net_log)
: net_log_(net_log) {}
EngineNetworkComponents::~EngineNetworkComponents() {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
}
void EngineNetworkComponents::Initialize() {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
DCHECK(connection_handler_);
DCHECK(!authentication_handler_);
// Creates and connects net components.
// A BlimpConnection flows from
// connection_manager_ --> authentication_handler_ --> connection_handler_
authentication_handler_ = make_scoped_ptr(
new EngineAuthenticationHandler(connection_handler_.get()));
connection_manager_ = make_scoped_ptr(
new EngineConnectionManager(authentication_handler_.get()));
// Adds BlimpTransports to connection_manager_.
net::IPEndPoint address(GetIPv4AnyAddress(), kDefaultPort);
connection_manager_->AddTransport(
make_scoped_ptr(new TCPEngineTransport(address, net_log_)));
}
void EngineNetworkComponents::RegisterFeature(
BlimpMessage::Type type,
scoped_ptr<BlimpMessageThreadPipe> outgoing_pipe,
scoped_ptr<BlimpMessageProcessor> incoming_proxy) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
if (!connection_handler_) {
connection_handler_ = make_scoped_ptr(new BrowserConnectionHandler);
}
// Registers |incoming_proxy| as the message processor for incoming
// messages with |type|. Sets the returned outgoing message processor as the
// target of the |outgoing_pipe|.
scoped_ptr<BlimpMessageProcessor> outgoing_message_processor =
connection_handler_->RegisterFeature(type, incoming_proxy.get());
outgoing_pipe->set_target_processor(outgoing_message_processor.get());
// This object manages the lifetimes of the pipe, proxy and target processor.
incoming_proxies_.push_back(std::move(incoming_proxy));
outgoing_pipes_.push_back(std::move(outgoing_pipe));
outgoing_message_processors_.push_back(std::move(outgoing_message_processor));
}
BlimpEngineSession::BlimpEngineSession(
scoped_ptr<BlimpBrowserContext> browser_context,
net::NetLog* net_log)
: browser_context_(std::move(browser_context)),
screen_(new BlimpScreen),
net_components_(new EngineNetworkComponents(net_log)) {
screen_->UpdateDisplayScaleAndSize(kDefaultScaleFactor,
gfx::Size(kDefaultDisplayWidth,
kDefaultDisplayHeight));
render_widget_feature_.SetDelegate(kDummyTabId, this);
}
BlimpEngineSession::~BlimpEngineSession() {
render_widget_feature_.RemoveDelegate(kDummyTabId);
// Safely delete network components on the IO thread.
content::BrowserThread::DeleteSoon(content::BrowserThread::IO, FROM_HERE,
net_components_.release());
}
void BlimpEngineSession::Initialize() {
DCHECK(!gfx::Screen::GetScreen());
gfx::Screen::SetScreenInstance(screen_.get());
#if defined(USE_X11)
window_tree_host_.reset(aura::WindowTreeHost::Create(
gfx::Rect(screen_->GetPrimaryDisplay().size())));
#else
context_factory_.reset(new BlimpUiContextFactory());
aura::Env::GetInstance()->set_context_factory(context_factory_.get());
window_tree_host_.reset(new BlimpWindowTreeHost());
#endif
window_tree_host_->InitHost();
window_tree_host_->window()->SetLayoutManager(
new BlimpLayoutManager(window_tree_host_->window()));
focus_client_.reset(new wm::FocusController(new FocusRulesImpl));
aura::client::SetFocusClient(window_tree_host_->window(),
focus_client_.get());
aura::client::SetActivationClient(window_tree_host_->window(),
focus_client_.get());
capture_client_.reset(
new aura::client::DefaultCaptureClient(window_tree_host_->window()));
#if defined(USE_X11)
window_tree_host_->Show();
#endif
window_tree_host_->SetBounds(gfx::Rect(screen_->GetPrimaryDisplay().size()));
// Register features' message senders and receivers.
tab_control_message_sender_ =
RegisterFeature(BlimpMessage::TAB_CONTROL, this);
navigation_message_sender_ = RegisterFeature(BlimpMessage::NAVIGATION, this);
render_widget_feature_.set_render_widget_message_sender(
RegisterFeature(BlimpMessage::RENDER_WIDGET, &render_widget_feature_));
render_widget_feature_.set_input_message_sender(
RegisterFeature(BlimpMessage::INPUT, &render_widget_feature_));
render_widget_feature_.set_compositor_message_sender(
RegisterFeature(BlimpMessage::COMPOSITOR, &render_widget_feature_));
// Initialize must only be posted after the RegisterFeature calls have
// completed.
content::BrowserThread::PostTask(
content::BrowserThread::IO, FROM_HERE,
base::Bind(&EngineNetworkComponents::Initialize,
base::Unretained(net_components_.get())));
}
scoped_ptr<BlimpMessageProcessor> BlimpEngineSession::RegisterFeature(
BlimpMessage::Type type,
BlimpMessageProcessor* incoming_processor) {
// Creates an outgoing pipe and a proxy for forwarding messages
// from features on the UI thread to network components on the IO thread.
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner =
content::BrowserThread::GetMessageLoopProxyForThread(
content::BrowserThread::IO);
scoped_ptr<BlimpMessageThreadPipe> outgoing_pipe(
new BlimpMessageThreadPipe(io_task_runner));
scoped_ptr<BlimpMessageProcessor> outgoing_proxy =
outgoing_pipe->CreateProxy();
// Creates an incoming pipe and a proxy for receiving messages
// from network components on the IO thread.
scoped_ptr<BlimpMessageThreadPipe> incoming_pipe(new BlimpMessageThreadPipe(
content::BrowserThread::GetMessageLoopProxyForThread(
content::BrowserThread::UI)));
incoming_pipe->set_target_processor(incoming_processor);
scoped_ptr<BlimpMessageProcessor> incoming_proxy =
incoming_pipe->CreateProxy();
// Finishes registration on IO thread.
io_task_runner->PostTask(
FROM_HERE, base::Bind(&EngineNetworkComponents::RegisterFeature,
base::Unretained(net_components_.get()), type,
base::Passed(std::move(outgoing_pipe)),
base::Passed(std::move(incoming_proxy))));
incoming_pipes_.push_back(std::move(incoming_pipe));
return outgoing_proxy;
}
bool BlimpEngineSession::CreateWebContents(const int target_tab_id) {
DVLOG(1) << "Create tab " << target_tab_id;
// TODO(haibinlu): Support more than one active WebContents (crbug/547231).
if (web_contents_) {
DLOG(WARNING) << "Tab " << target_tab_id << " already existed";
return false;
}
content::WebContents::CreateParams create_params(browser_context_.get(),
nullptr);
scoped_ptr<content::WebContents> new_contents =
make_scoped_ptr(content::WebContents::Create(create_params));
PlatformSetContents(std::move(new_contents));
return true;
}
void BlimpEngineSession::CloseWebContents(const int target_tab_id) {
DVLOG(1) << "Close tab " << target_tab_id;
DCHECK(web_contents_);
web_contents_->Close();
}
void BlimpEngineSession::HandleResize(float device_pixel_ratio,
const gfx::Size& size) {
DVLOG(1) << "Resize to " << size.ToString() << ", " << device_pixel_ratio;
screen_->UpdateDisplayScaleAndSize(device_pixel_ratio, size);
if (web_contents_ && web_contents_->GetRenderViewHost() &&
web_contents_->GetRenderViewHost()->GetWidget()) {
web_contents_->GetRenderViewHost()->GetWidget()->WasResized();
}
}
void BlimpEngineSession::LoadUrl(const int target_tab_id, const GURL& url) {
DVLOG(1) << "Load URL " << url << " in tab " << target_tab_id;
if (url.is_empty()) {
return;
}
// TODO(dtrainor, haibinlu): Fix up the URL with url_fixer.h. If that doesn't
// produce a valid spec() then try to build a search query?
content::NavigationController::LoadURLParams params(url);
params.transition_type = ui::PageTransitionFromInt(
ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR);
web_contents_->GetController().LoadURLWithParams(params);
web_contents_->Focus();
}
void BlimpEngineSession::GoBack(const int target_tab_id) {
DVLOG(1) << "Back in tab " << target_tab_id;
web_contents_->GetController().GoBack();
}
void BlimpEngineSession::GoForward(const int target_tab_id) {
DVLOG(1) << "Forward in tab " << target_tab_id;
web_contents_->GetController().GoForward();
}
void BlimpEngineSession::Reload(const int target_tab_id) {
DVLOG(1) << "Reload in tab " << target_tab_id;
web_contents_->GetController().Reload(true);
}
void BlimpEngineSession::OnWebInputEvent(
scoped_ptr<blink::WebInputEvent> event) {
if (!web_contents_ || !web_contents_->GetRenderViewHost())
return;
content::RenderWidgetHost* host =
web_contents_->GetRenderViewHost()->GetWidget();
if (!host)
return;
if (blink::WebInputEvent::isGestureEventType(event->type)) {
const blink::WebGestureEvent& gesture_event =
*static_cast<const blink::WebGestureEvent*>(event.get());
host->ForwardGestureEvent(gesture_event);
} else {
NOTIMPLEMENTED() << "Dropping event of type " << event->type;
}
}
void BlimpEngineSession::OnCompositorMessageReceived(
const std::vector<uint8_t>& message) {
// Make sure that We actually have a valid WebContents and RenderViewHost.
if (!web_contents_ || !web_contents_->GetRenderViewHost())
return;
content::RenderWidgetHost* host =
web_contents_->GetRenderViewHost()->GetWidget();
// Make sure we actually have a valid RenderWidgetHost.
if (!host)
return;
host->HandleCompositorProto(message);
}
void BlimpEngineSession::ProcessMessage(
scoped_ptr<BlimpMessage> message,
const net::CompletionCallback& callback) {
DCHECK(!callback.is_null());
DCHECK(message->type() == BlimpMessage::TAB_CONTROL ||
message->type() == BlimpMessage::NAVIGATION);
net::Error result = net::OK;
if (message->type() == BlimpMessage::TAB_CONTROL) {
switch (message->tab_control().type()) {
case TabControlMessage::CREATE_TAB:
if (!CreateWebContents(message->target_tab_id()))
result = net::ERR_FAILED;
break;
case TabControlMessage::CLOSE_TAB:
CloseWebContents(message->target_tab_id());
case TabControlMessage::SIZE:
HandleResize(message->tab_control().size().device_pixel_ratio(),
gfx::Size(message->tab_control().size().width(),
message->tab_control().size().height()));
break;
default:
NOTIMPLEMENTED();
result = net::ERR_NOT_IMPLEMENTED;
}
} else if (message->type() == BlimpMessage::NAVIGATION && web_contents_) {
switch (message->navigation().type()) {
case NavigationMessage::LOAD_URL:
LoadUrl(message->target_tab_id(),
GURL(message->navigation().load_url().url()));
break;
case NavigationMessage::GO_BACK:
GoBack(message->target_tab_id());
break;
case NavigationMessage::GO_FORWARD:
GoForward(message->target_tab_id());
break;
case NavigationMessage::RELOAD:
Reload(message->target_tab_id());
break;
default:
NOTIMPLEMENTED();
result = net::ERR_NOT_IMPLEMENTED;
}
} else {
DVLOG(1) << "No WebContents for navigation control";
result = net::ERR_FAILED;
}
callback.Run(result);
}
void BlimpEngineSession::AddNewContents(content::WebContents* source,
content::WebContents* new_contents,
WindowOpenDisposition disposition,
const gfx::Rect& initial_rect,
bool user_gesture,
bool* was_blocked) {
NOTIMPLEMENTED();
}
content::WebContents* BlimpEngineSession::OpenURLFromTab(
content::WebContents* source,
const content::OpenURLParams& params) {
// CURRENT_TAB is the only one we implement for now.
if (params.disposition != CURRENT_TAB) {
NOTIMPLEMENTED();
return nullptr;
}
// TODO(haibinlu): Add helper method to get LoadURLParams from OpenURLParams.
content::NavigationController::LoadURLParams load_url_params(params.url);
load_url_params.source_site_instance = params.source_site_instance;
load_url_params.referrer = params.referrer;
load_url_params.frame_tree_node_id = params.frame_tree_node_id;
load_url_params.transition_type = params.transition;
load_url_params.extra_headers = params.extra_headers;
load_url_params.should_replace_current_entry =
params.should_replace_current_entry;
load_url_params.is_renderer_initiated = params.is_renderer_initiated;
source->GetController().LoadURLWithParams(load_url_params);
return source;
}
void BlimpEngineSession::RequestToLockMouse(content::WebContents* web_contents,
bool user_gesture,
bool last_unlocked_by_target) {
web_contents->GotResponseToLockMouseRequest(true);
}
void BlimpEngineSession::CloseContents(content::WebContents* source) {
if (source == web_contents_.get()) {
Observe(nullptr);
web_contents_.reset();
}
}
void BlimpEngineSession::ActivateContents(content::WebContents* contents) {
contents->GetRenderViewHost()->GetWidget()->Focus();
}
void BlimpEngineSession::ForwardCompositorProto(
const std::vector<uint8_t>& proto) {
render_widget_feature_.SendCompositorMessage(kDummyTabId, proto);
}
void BlimpEngineSession::NavigationStateChanged(
content::WebContents* source,
content::InvalidateTypes changed_flags) {
if (source != web_contents_.get() || !changed_flags)
return;
NavigationMessage* navigation_message;
scoped_ptr<BlimpMessage> message =
CreateBlimpMessage(&navigation_message, kDummyTabId);
navigation_message->set_type(NavigationMessage::NAVIGATION_STATE_CHANGED);
NavigationStateChangeMessage* details =
navigation_message->mutable_navigation_state_change();
if (changed_flags & content::InvalidateTypes::INVALIDATE_TYPE_URL)
details->set_url(source->GetURL().spec());
if (changed_flags & content::InvalidateTypes::INVALIDATE_TYPE_TAB) {
// TODO(dtrainor): Serialize the favicon?
NOTIMPLEMENTED();
}
if (changed_flags & content::InvalidateTypes::INVALIDATE_TYPE_TITLE)
details->set_title(base::UTF16ToUTF8(source->GetTitle()));
if (changed_flags & content::InvalidateTypes::INVALIDATE_TYPE_LOAD)
details->set_loading(source->IsLoading());
navigation_message_sender_->ProcessMessage(std::move(message),
net::CompletionCallback());
}
void BlimpEngineSession::RenderViewHostChanged(
content::RenderViewHost* old_host,
content::RenderViewHost* new_host) {
// Informs client that WebContents swaps its visible RenderViewHost with
// another one.
render_widget_feature_.OnRenderWidgetInitialized(kDummyTabId);
}
void BlimpEngineSession::PlatformSetContents(
scoped_ptr<content::WebContents> new_contents) {
new_contents->SetDelegate(this);
Observe(new_contents.get());
web_contents_ = std::move(new_contents);
aura::Window* parent = window_tree_host_->window();
aura::Window* content = web_contents_->GetNativeView();
if (!parent->Contains(content))
parent->AddChild(content);
content->Show();
}
} // namespace engine
} // namespace blimp