blob: cd47a3c93e4beef8ec747100e7afcf5b04688a0c [file] [log] [blame]
// 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 "android_webview/browser/test/fake_window.h"
#include "android_webview/browser/browser_view_renderer.h"
#include "android_webview/browser/child_frame.h"
#include "android_webview/browser/render_thread_manager.h"
#include "base/location.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread.h"
#include "base/threading/thread_task_runner_handle.h"
#include "ui/gl/gl_bindings.h"
#include "ui/gl/init/gl_factory.h"
namespace android_webview {
class FakeWindow::ScopedMakeCurrent {
public:
ScopedMakeCurrent(FakeWindow* view_root) : view_root_(view_root) {
DCHECK(!view_root_->context_current_);
view_root_->context_current_ = true;
bool result = view_root_->context_->MakeCurrent(view_root_->surface_.get());
DCHECK(result);
};
~ScopedMakeCurrent() {
DCHECK(view_root_->context_current_);
view_root_->context_current_ = false;
// Release the underlying EGLContext. This is required because the real
// GLContextEGL may no longer be current here and to satisfy DCHECK in
// GLContextEGL::IsCurrent.
eglMakeCurrent(view_root_->surface_->GetDisplay(), EGL_NO_SURFACE,
EGL_NO_SURFACE, EGL_NO_CONTEXT);
view_root_->context_->ReleaseCurrent(view_root_->surface_.get());
}
private:
FakeWindow* view_root_;
};
FakeWindow::FakeWindow(BrowserViewRenderer* view,
WindowHooks* hooks,
gfx::Rect location)
: view_(view),
hooks_(hooks),
surface_size_(100, 100),
location_(location),
on_draw_hardware_pending_(false),
context_current_(false),
weak_ptr_factory_(this) {
CheckCurrentlyOnUIThread();
DCHECK(view_);
view_->OnAttachedToWindow(location_.width(), location_.height());
view_->SetWindowVisibility(true);
view_->SetViewVisibility(true);
}
FakeWindow::~FakeWindow() {
CheckCurrentlyOnUIThread();
if (render_thread_loop_) {
base::WaitableEvent completion(
base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
render_thread_loop_->PostTask(
FROM_HERE, base::Bind(&FakeWindow::DestroyOnRT, base::Unretained(this),
&completion));
completion.Wait();
}
render_thread_.reset();
}
void FakeWindow::Detach() {
CheckCurrentlyOnUIThread();
view_->OnDetachedFromWindow();
}
void FakeWindow::RequestInvokeGL(FakeFunctor* functor,
bool wait_for_completion) {
CheckCurrentlyOnUIThread();
base::WaitableEvent completion(
base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
render_thread_loop_->PostTask(
FROM_HERE,
base::Bind(&FakeWindow::InvokeFunctorOnRT, base::Unretained(this),
functor, wait_for_completion ? &completion : nullptr));
if (wait_for_completion)
completion.Wait();
}
void FakeWindow::InvokeFunctorOnRT(FakeFunctor* functor,
base::WaitableEvent* sync) {
CheckCurrentlyOnRT();
ScopedMakeCurrent make_current(this);
functor->Invoke(hooks_);
if (sync)
sync->Signal();
}
void FakeWindow::RequestDrawGL(FakeFunctor* functor) {
CheckCurrentlyOnUIThread();
render_thread_loop_->PostTask(FROM_HERE,
base::Bind(&FakeWindow::ProcessDrawOnRT,
base::Unretained(this), functor));
}
void FakeWindow::PostInvalidate() {
CheckCurrentlyOnUIThread();
if (on_draw_hardware_pending_)
return;
on_draw_hardware_pending_ = true;
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::Bind(&FakeWindow::OnDrawHardware, weak_ptr_factory_.GetWeakPtr()));
}
void FakeWindow::OnDrawHardware() {
CheckCurrentlyOnUIThread();
DCHECK(on_draw_hardware_pending_);
on_draw_hardware_pending_ = false;
view_->PrepareToDraw(gfx::Vector2d(), location_);
hooks_->WillOnDraw();
bool success = view_->OnDrawHardware();
hooks_->DidOnDraw(success);
FakeFunctor* functor = hooks_->GetFunctor();
if (success && functor) {
CreateRenderThreadIfNeeded();
base::WaitableEvent completion(
base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
render_thread_loop_->PostTask(
FROM_HERE, base::Bind(&FakeWindow::DrawFunctorOnRT,
base::Unretained(this), functor, &completion));
completion.Wait();
}
}
void FakeWindow::ProcessSyncOnRT(FakeFunctor* functor,
base::WaitableEvent* sync) {
CheckCurrentlyOnRT();
functor->Sync(location_, hooks_);
sync->Signal();
}
void FakeWindow::ProcessDrawOnRT(FakeFunctor* functor) {
CheckCurrentlyOnRT();
ScopedMakeCurrent make_current(this);
functor->Draw(hooks_);
}
void FakeWindow::DrawFunctorOnRT(FakeFunctor* functor,
base::WaitableEvent* sync) {
ProcessSyncOnRT(functor, sync);
ProcessDrawOnRT(functor);
}
void FakeWindow::CheckCurrentlyOnUIThread() {
DCHECK(ui_checker_.CalledOnValidSequence());
}
void FakeWindow::CreateRenderThreadIfNeeded() {
CheckCurrentlyOnUIThread();
if (render_thread_) {
DCHECK(render_thread_loop_);
return;
}
render_thread_.reset(new base::Thread("TestRenderThread"));
render_thread_->Start();
render_thread_loop_ = render_thread_->task_runner();
rt_checker_.DetachFromSequence();
base::WaitableEvent completion(
base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
render_thread_loop_->PostTask(
FROM_HERE, base::Bind(&FakeWindow::InitializeOnRT, base::Unretained(this),
&completion));
completion.Wait();
}
void FakeWindow::InitializeOnRT(base::WaitableEvent* sync) {
CheckCurrentlyOnRT();
surface_ = gl::init::CreateOffscreenGLSurface(surface_size_);
DCHECK(surface_);
DCHECK(surface_->GetHandle());
context_ =
gl::init::CreateGLContext(nullptr, surface_.get(), gl::PreferDiscreteGpu);
DCHECK(context_);
sync->Signal();
}
void FakeWindow::DestroyOnRT(base::WaitableEvent* sync) {
CheckCurrentlyOnRT();
if (context_) {
DCHECK(!context_->IsCurrent(surface_.get()));
context_ = nullptr;
surface_ = nullptr;
}
sync->Signal();
}
void FakeWindow::CheckCurrentlyOnRT() {
DCHECK(rt_checker_.CalledOnValidSequence());
}
FakeFunctor::FakeFunctor() : window_(nullptr) {}
FakeFunctor::~FakeFunctor() {
render_thread_manager_.reset();
}
void FakeFunctor::Init(
FakeWindow* window,
std::unique_ptr<RenderThreadManager> render_thread_manager) {
window_ = window;
render_thread_manager_ = std::move(render_thread_manager);
callback_ = base::Bind(&RenderThreadManager::DrawGL,
base::Unretained(render_thread_manager_.get()));
}
void FakeFunctor::Sync(const gfx::Rect& location,
WindowHooks* hooks) {
DCHECK(!callback_.is_null());
committed_location_ = location;
AwDrawGLInfo sync_info;
sync_info.version = kAwDrawGLInfoVersion;
sync_info.mode = AwDrawGLInfo::kModeSync;
hooks->WillSyncOnRT();
callback_.Run(&sync_info);
hooks->DidSyncOnRT();
}
void FakeFunctor::Draw(WindowHooks* hooks) {
DCHECK(!callback_.is_null());
AwDrawGLInfo draw_info;
draw_info.version = kAwDrawGLInfoVersion;
draw_info.mode = AwDrawGLInfo::kModeDraw;
draw_info.clip_left = committed_location_.x();
draw_info.clip_top = committed_location_.y();
draw_info.clip_right = committed_location_.x() + committed_location_.width();
draw_info.clip_bottom =
committed_location_.y() + committed_location_.height();
if (!hooks->WillDrawOnRT(&draw_info))
return;
callback_.Run(&draw_info);
hooks->DidDrawOnRT();
}
CompositorFrameConsumer* FakeFunctor::GetCompositorFrameConsumer() {
return render_thread_manager_.get();
}
void FakeFunctor::Invoke(WindowHooks* hooks) {
DCHECK(!callback_.is_null());
AwDrawGLInfo invoke_info;
invoke_info.version = kAwDrawGLInfoVersion;
invoke_info.mode = AwDrawGLInfo::kModeProcess;
hooks->WillProcessOnRT();
callback_.Run(&invoke_info);
hooks->DidProcessOnRT();
}
bool FakeFunctor::RequestInvokeGL(bool wait_for_completion) {
DCHECK(window_);
window_->RequestInvokeGL(this, wait_for_completion);
return true;
}
void FakeFunctor::DetachFunctorFromView() {}
} // namespace android_webview