diff options
Diffstat (limited to 'chromium/components/mus/public/cpp/lib')
18 files changed, 3832 insertions, 0 deletions
diff --git a/chromium/components/mus/public/cpp/lib/DEPS b/chromium/components/mus/public/cpp/lib/DEPS new file mode 100644 index 00000000000..c635ea6f5e4 --- /dev/null +++ b/chromium/components/mus/public/cpp/lib/DEPS @@ -0,0 +1,3 @@ +include_rules = [ + "+mojo/gpu" +] diff --git a/chromium/components/mus/public/cpp/lib/command_buffer_client_impl.cc b/chromium/components/mus/public/cpp/lib/command_buffer_client_impl.cc new file mode 100644 index 00000000000..062c3f78477 --- /dev/null +++ b/chromium/components/mus/public/cpp/lib/command_buffer_client_impl.cc @@ -0,0 +1,370 @@ +// 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 "components/mus/public/cpp/lib/command_buffer_client_impl.h" + +#include <stddef.h> +#include <stdint.h> + +#include <limits> +#include <utility> + +#include "base/logging.h" +#include "base/process/process_handle.h" +#include "base/threading/thread_restrictions.h" +#include "components/mus/common/gpu_type_converters.h" +#include "components/mus/common/mojo_buffer_backing.h" +#include "components/mus/common/mojo_gpu_memory_buffer.h" +#include "gpu/command_buffer/client/gpu_control_client.h" +#include "gpu/command_buffer/common/command_buffer_id.h" +#include "gpu/command_buffer/common/gpu_memory_buffer_support.h" +#include "gpu/command_buffer/common/sync_token.h" +#include "mojo/public/cpp/system/platform_handle.h" + +namespace mus { + +namespace { + +bool CreateAndMapSharedBuffer(size_t size, + mojo::ScopedSharedBufferMapping* mapping, + mojo::ScopedSharedBufferHandle* handle) { + *handle = mojo::SharedBufferHandle::Create(size); + if (!handle->is_valid()) + return false; + + *mapping = (*handle)->Map(size); + if (!*mapping) + return false; + + return true; +} + +void MakeProgressCallback(gpu::CommandBuffer::State* output, + const gpu::CommandBuffer::State& input) { + *output = input; +} + +void InitializeCallback(mus::mojom::CommandBufferInitializeResultPtr* output, + mus::mojom::CommandBufferInitializeResultPtr input) { + *output = std::move(input); +} + +} // namespace + +CommandBufferClientImpl::CommandBufferClientImpl( + const std::vector<int32_t>& attribs, + mus::mojom::CommandBufferPtr command_buffer_ptr) + : gpu_control_client_(nullptr), + destroyed_(false), + attribs_(attribs), + client_binding_(this), + command_buffer_(std::move(command_buffer_ptr)), + command_buffer_id_(), + last_put_offset_(-1), + next_transfer_buffer_id_(0), + next_image_id_(0), + next_fence_sync_release_(1), + flushed_fence_sync_release_(0) { + command_buffer_.set_connection_error_handler( + base::Bind(&CommandBufferClientImpl::Destroyed, base::Unretained(this), + gpu::error::kUnknown, gpu::error::kLostContext)); +} + +CommandBufferClientImpl::~CommandBufferClientImpl() {} + +bool CommandBufferClientImpl::Initialize() { + const size_t kSharedStateSize = sizeof(gpu::CommandBufferSharedState); + mojo::ScopedSharedBufferHandle handle; + bool result = + CreateAndMapSharedBuffer(kSharedStateSize, &shared_state_, &handle); + if (!result) + return false; + + shared_state()->Initialize(); + + mus::mojom::CommandBufferClientPtr client_ptr; + client_binding_.Bind(GetProxy(&client_ptr)); + + mus::mojom::CommandBufferInitializeResultPtr initialize_result; + command_buffer_->Initialize( + std::move(client_ptr), std::move(handle), + mojo::Array<int32_t>::From(attribs_), + base::Bind(&InitializeCallback, &initialize_result)); + + base::ThreadRestrictions::ScopedAllowWait wait; + if (!command_buffer_.WaitForIncomingResponse()) { + VLOG(1) << "Channel encountered error while creating command buffer."; + return false; + } + + if (!initialize_result) { + VLOG(1) << "Command buffer cannot be initialized successfully."; + return false; + } + + DCHECK_EQ(gpu::CommandBufferNamespace::MOJO, + initialize_result->command_buffer_namespace); + command_buffer_id_ = gpu::CommandBufferId::FromUnsafeValue( + initialize_result->command_buffer_id); + capabilities_ = initialize_result->capabilities; + return true; +} + +gpu::CommandBuffer::State CommandBufferClientImpl::GetLastState() { + return last_state_; +} + +int32_t CommandBufferClientImpl::GetLastToken() { + TryUpdateState(); + return last_state_.token; +} + +void CommandBufferClientImpl::Flush(int32_t put_offset) { + if (last_put_offset_ == put_offset) + return; + + last_put_offset_ = put_offset; + command_buffer_->Flush(put_offset); + flushed_fence_sync_release_ = next_fence_sync_release_ - 1; +} + +void CommandBufferClientImpl::OrderingBarrier(int32_t put_offset) { + // TODO(jamesr): Implement this more efficiently. + Flush(put_offset); +} + +void CommandBufferClientImpl::WaitForTokenInRange(int32_t start, int32_t end) { + TryUpdateState(); + while (!InRange(start, end, last_state_.token) && + last_state_.error == gpu::error::kNoError) { + MakeProgressAndUpdateState(); + } +} + +void CommandBufferClientImpl::WaitForGetOffsetInRange(int32_t start, + int32_t end) { + TryUpdateState(); + while (!InRange(start, end, last_state_.get_offset) && + last_state_.error == gpu::error::kNoError) { + MakeProgressAndUpdateState(); + } +} + +void CommandBufferClientImpl::SetGetBuffer(int32_t shm_id) { + command_buffer_->SetGetBuffer(shm_id); + last_put_offset_ = -1; +} + +scoped_refptr<gpu::Buffer> CommandBufferClientImpl::CreateTransferBuffer( + size_t size, + int32_t* id) { + if (size >= std::numeric_limits<uint32_t>::max()) + return NULL; + + mojo::ScopedSharedBufferMapping mapping; + mojo::ScopedSharedBufferHandle handle; + if (!CreateAndMapSharedBuffer(size, &mapping, &handle)) { + if (last_state_.error == gpu::error::kNoError) + last_state_.error = gpu::error::kLostContext; + return NULL; + } + + *id = ++next_transfer_buffer_id_; + + command_buffer_->RegisterTransferBuffer(*id, std::move(handle), + static_cast<uint32_t>(size)); + + std::unique_ptr<gpu::BufferBacking> backing( + new mus::MojoBufferBacking(std::move(mapping), size)); + scoped_refptr<gpu::Buffer> buffer(new gpu::Buffer(std::move(backing))); + return buffer; +} + +void CommandBufferClientImpl::DestroyTransferBuffer(int32_t id) { + command_buffer_->DestroyTransferBuffer(id); +} + +void CommandBufferClientImpl::SetGpuControlClient(gpu::GpuControlClient* c) { + gpu_control_client_ = c; +} + +gpu::Capabilities CommandBufferClientImpl::GetCapabilities() { + return capabilities_; +} + +int32_t CommandBufferClientImpl::CreateImage(ClientBuffer buffer, + size_t width, + size_t height, + unsigned internalformat) { + int32_t new_id = ++next_image_id_; + + gfx::Size size(static_cast<int32_t>(width), static_cast<int32_t>(height)); + + mus::MojoGpuMemoryBufferImpl* gpu_memory_buffer = + mus::MojoGpuMemoryBufferImpl::FromClientBuffer(buffer); + gfx::GpuMemoryBufferHandle handle = gpu_memory_buffer->GetHandle(); + + bool requires_sync_point = false; + if (handle.type != gfx::SHARED_MEMORY_BUFFER) { + requires_sync_point = true; + NOTIMPLEMENTED(); + return -1; + } + + base::SharedMemoryHandle dupd_handle = + base::SharedMemory::DuplicateHandle(handle.handle); +#if defined(OS_WIN) + HANDLE platform_handle = dupd_handle.GetHandle(); +#else + int platform_handle = dupd_handle.fd; +#endif + + mojo::ScopedHandle scoped_handle = mojo::WrapPlatformFile(platform_handle); + command_buffer_->CreateImage( + new_id, std::move(scoped_handle), handle.type, std::move(size), + static_cast<int32_t>(gpu_memory_buffer->GetFormat()), internalformat); + if (requires_sync_point) { + NOTIMPLEMENTED(); + // TODO(jam): need to support this if we support types other than + // SHARED_MEMORY_BUFFER. + // gpu_memory_buffer_manager->SetDestructionSyncPoint(gpu_memory_buffer, + // InsertSyncPoint()); + } + + return new_id; +} + +void CommandBufferClientImpl::DestroyImage(int32_t id) { + command_buffer_->DestroyImage(id); +} + +int32_t CommandBufferClientImpl::CreateGpuMemoryBufferImage( + size_t width, + size_t height, + unsigned internalformat, + unsigned usage) { + std::unique_ptr<gfx::GpuMemoryBuffer> buffer( + mus::MojoGpuMemoryBufferImpl::Create( + gfx::Size(static_cast<int>(width), static_cast<int>(height)), + gpu::DefaultBufferFormatForImageFormat(internalformat), + gfx::BufferUsage::SCANOUT)); + if (!buffer) + return -1; + + return CreateImage(buffer->AsClientBuffer(), width, height, internalformat); +} + +int32_t CommandBufferClientImpl::GetImageGpuMemoryBufferId(unsigned image_id) { + // TODO(erikchen): Once this class supports IOSurface GpuMemoryBuffer backed + // images, it will also need to keep a local cache from image id to + // GpuMemoryBuffer id. + NOTIMPLEMENTED(); + return -1; +} + +void CommandBufferClientImpl::SignalQuery(uint32_t query, + const base::Closure& callback) { + // TODO(piman) + NOTIMPLEMENTED(); +} + +void CommandBufferClientImpl::Destroyed(int32_t lost_reason, int32_t error) { + if (destroyed_) + return; + last_state_.context_lost_reason = + static_cast<gpu::error::ContextLostReason>(lost_reason); + last_state_.error = static_cast<gpu::error::Error>(error); + if (gpu_control_client_) + gpu_control_client_->OnGpuControlLostContext(); + destroyed_ = true; +} + +void CommandBufferClientImpl::SignalAck(uint32_t id) {} + +void CommandBufferClientImpl::SwapBuffersCompleted(int32_t result) {} + +void CommandBufferClientImpl::UpdateState( + const gpu::CommandBuffer::State& state) {} + +void CommandBufferClientImpl::UpdateVSyncParameters(int64_t timebase, + int64_t interval) {} + +void CommandBufferClientImpl::TryUpdateState() { + if (last_state_.error == gpu::error::kNoError) + shared_state()->Read(&last_state_); +} + +void CommandBufferClientImpl::MakeProgressAndUpdateState() { + gpu::CommandBuffer::State state; + command_buffer_->MakeProgress(last_state_.get_offset, + base::Bind(&MakeProgressCallback, &state)); + + base::ThreadRestrictions::ScopedAllowWait wait; + if (!command_buffer_.WaitForIncomingResponse()) { + VLOG(1) << "Channel encountered error while waiting for command buffer."; + // TODO(piman): is it ok for this to re-enter? + Destroyed(gpu::error::kUnknown, gpu::error::kLostContext); + return; + } + + if (state.generation - last_state_.generation < 0x80000000U) + last_state_ = state; +} + +void CommandBufferClientImpl::SetLock(base::Lock* lock) {} + +void CommandBufferClientImpl::EnsureWorkVisible() { + // This is only relevant for out-of-process command buffers. +} + +gpu::CommandBufferNamespace CommandBufferClientImpl::GetNamespaceID() const { + return gpu::CommandBufferNamespace::MOJO; +} + +gpu::CommandBufferId CommandBufferClientImpl::GetCommandBufferID() const { + return command_buffer_id_; +} + +int32_t CommandBufferClientImpl::GetExtraCommandBufferData() const { + return 0; +} + +uint64_t CommandBufferClientImpl::GenerateFenceSyncRelease() { + return next_fence_sync_release_++; +} + +bool CommandBufferClientImpl::IsFenceSyncRelease(uint64_t release) { + return release != 0 && release < next_fence_sync_release_; +} + +bool CommandBufferClientImpl::IsFenceSyncFlushed(uint64_t release) { + return release != 0 && release <= flushed_fence_sync_release_; +} + +bool CommandBufferClientImpl::IsFenceSyncFlushReceived(uint64_t release) { + return IsFenceSyncFlushed(release); +} + +void CommandBufferClientImpl::SignalSyncToken(const gpu::SyncToken& sync_token, + const base::Closure& callback) { + // TODO(dyen) + NOTIMPLEMENTED(); +} + +bool CommandBufferClientImpl::CanWaitUnverifiedSyncToken( + const gpu::SyncToken* sync_token) { + // Right now, MOJO_LOCAL is only used by trusted code, so it is safe to wait + // on a sync token in MOJO_LOCAL command buffer. + if (sync_token->namespace_id() == gpu::CommandBufferNamespace::MOJO_LOCAL) + return true; + + // It is also safe to wait on the same context. + if (sync_token->namespace_id() == gpu::CommandBufferNamespace::MOJO && + sync_token->command_buffer_id() == GetCommandBufferID()) + return true; + + return false; +} + +} // namespace mus diff --git a/chromium/components/mus/public/cpp/lib/command_buffer_client_impl.h b/chromium/components/mus/public/cpp/lib/command_buffer_client_impl.h new file mode 100644 index 00000000000..80a46e93b7f --- /dev/null +++ b/chromium/components/mus/public/cpp/lib/command_buffer_client_impl.h @@ -0,0 +1,117 @@ +// 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. + +#ifndef COMPONENTS_MUS_PUBLIC_CPP_LIB_COMMAND_BUFFER_CLIENT_IMPL_H_ +#define COMPONENTS_MUS_PUBLIC_CPP_LIB_COMMAND_BUFFER_CLIENT_IMPL_H_ + +#include <stddef.h> +#include <stdint.h> + +#include <map> +#include <memory> +#include <vector> + +#include "base/macros.h" +#include "components/mus/public/interfaces/command_buffer.mojom.h" +#include "gpu/command_buffer/client/gpu_control.h" +#include "gpu/command_buffer/common/command_buffer.h" +#include "gpu/command_buffer/common/command_buffer_id.h" +#include "gpu/command_buffer/common/command_buffer_shared.h" +#include "mojo/public/cpp/bindings/binding.h" + +namespace base { +class RunLoop; +} + +namespace mus { +class CommandBufferClientImpl; + +class CommandBufferClientImpl : public mus::mojom::CommandBufferClient, + public gpu::CommandBuffer, + public gpu::GpuControl { + public: + explicit CommandBufferClientImpl( + const std::vector<int32_t>& attribs, + mus::mojom::CommandBufferPtr command_buffer_ptr); + ~CommandBufferClientImpl() override; + bool Initialize(); + + // CommandBuffer implementation: + State GetLastState() override; + int32_t GetLastToken() override; + void Flush(int32_t put_offset) override; + void OrderingBarrier(int32_t put_offset) override; + void WaitForTokenInRange(int32_t start, int32_t end) override; + void WaitForGetOffsetInRange(int32_t start, int32_t end) override; + void SetGetBuffer(int32_t shm_id) override; + scoped_refptr<gpu::Buffer> CreateTransferBuffer(size_t size, + int32_t* id) override; + void DestroyTransferBuffer(int32_t id) override; + + // gpu::GpuControl implementation: + void SetGpuControlClient(gpu::GpuControlClient*) override; + gpu::Capabilities GetCapabilities() override; + int32_t CreateImage(ClientBuffer buffer, + size_t width, + size_t height, + unsigned internalformat) override; + void DestroyImage(int32_t id) override; + int32_t CreateGpuMemoryBufferImage(size_t width, + size_t height, + unsigned internalformat, + unsigned usage) override; + int32_t GetImageGpuMemoryBufferId(unsigned image_id) override; + void SignalQuery(uint32_t query, const base::Closure& callback) override; + void SetLock(base::Lock*) override; + void EnsureWorkVisible() override; + gpu::CommandBufferNamespace GetNamespaceID() const override; + gpu::CommandBufferId GetCommandBufferID() const override; + int32_t GetExtraCommandBufferData() const override; + uint64_t GenerateFenceSyncRelease() override; + bool IsFenceSyncRelease(uint64_t release) override; + bool IsFenceSyncFlushed(uint64_t release) override; + bool IsFenceSyncFlushReceived(uint64_t release) override; + void SignalSyncToken(const gpu::SyncToken& sync_token, + const base::Closure& callback) override; + bool CanWaitUnverifiedSyncToken(const gpu::SyncToken* sync_token) override; + + private: + // mus::mojom::CommandBufferClient implementation: + void Destroyed(int32_t lost_reason, int32_t error) override; + void SignalAck(uint32_t id) override; + void SwapBuffersCompleted(int32_t result) override; + void UpdateState(const gpu::CommandBuffer::State& state) override; + void UpdateVSyncParameters(int64_t timebase, int64_t interval) override; + + void TryUpdateState(); + void MakeProgressAndUpdateState(); + + gpu::CommandBufferSharedState* shared_state() const { + return reinterpret_cast<gpu::CommandBufferSharedState*>( + shared_state_.get()); + } + + gpu::GpuControlClient* gpu_control_client_; + bool destroyed_; + std::vector<int32_t> attribs_; + mojo::Binding<mus::mojom::CommandBufferClient> client_binding_; + mus::mojom::CommandBufferPtr command_buffer_; + + gpu::CommandBufferId command_buffer_id_; + gpu::Capabilities capabilities_; + State last_state_; + mojo::ScopedSharedBufferMapping shared_state_; + int32_t last_put_offset_; + int32_t next_transfer_buffer_id_; + + // Image IDs are allocated in sequence. + int next_image_id_; + + uint64_t next_fence_sync_release_; + uint64_t flushed_fence_sync_release_; +}; + +} // mus + +#endif // COMPONENTS_MUS_PUBLIC_CPP_LIB_COMMAND_BUFFER_CLIENT_IMPL_H_ diff --git a/chromium/components/mus/public/cpp/lib/context_provider.cc b/chromium/components/mus/public/cpp/lib/context_provider.cc new file mode 100644 index 00000000000..02717112dd7 --- /dev/null +++ b/chromium/components/mus/public/cpp/lib/context_provider.cc @@ -0,0 +1,61 @@ +// 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 "components/mus/public/cpp/context_provider.h" + +#include <stdint.h> + +#include "base/logging.h" +#include "components/mus/public/cpp/gles2_context.h" +#include "services/shell/public/cpp/connector.h" + +namespace mus { + +ContextProvider::ContextProvider(shell::Connector* connector) + : connector_(connector->Clone()) {} + +bool ContextProvider::BindToCurrentThread() { + if (connector_) { + context_ = GLES2Context::CreateOffscreenContext(std::vector<int32_t>(), + connector_.get()); + // We don't need the connector anymore, so release it. + connector_.reset(); + } + return !!context_; +} + +gpu::gles2::GLES2Interface* ContextProvider::ContextGL() { + return context_->interface(); +} + +gpu::ContextSupport* ContextProvider::ContextSupport() { + if (!context_) + return NULL; + return context_->context_support(); +} + +class GrContext* ContextProvider::GrContext() { + return NULL; +} + +void ContextProvider::InvalidateGrContext(uint32_t state) {} + +gpu::Capabilities ContextProvider::ContextCapabilities() { + gpu::Capabilities capabilities; + // Enabled the CHROMIUM_image extension to use GpuMemoryBuffers. The + // implementation of which is used in CommandBufferDriver. + capabilities.image = true; + return capabilities; +} + +base::Lock* ContextProvider::GetLock() { + // This context provider is not used on multiple threads. + NOTREACHED(); + return nullptr; +} + +ContextProvider::~ContextProvider() { +} + +} // namespace mus diff --git a/chromium/components/mus/public/cpp/lib/gles2_context.cc b/chromium/components/mus/public/cpp/lib/gles2_context.cc new file mode 100644 index 00000000000..84363b15cfe --- /dev/null +++ b/chromium/components/mus/public/cpp/lib/gles2_context.cc @@ -0,0 +1,109 @@ +// 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 "components/mus/public/cpp/gles2_context.h" + +#include <stddef.h> +#include <stdint.h> + +#include <utility> + +#include "components/mus/common/gpu_service.h" +#include "components/mus/public/cpp/lib/command_buffer_client_impl.h" +#include "components/mus/public/interfaces/command_buffer.mojom.h" +#include "components/mus/public/interfaces/gpu_service.mojom.h" +#include "gpu/command_buffer/client/gles2_cmd_helper.h" +#include "gpu/command_buffer/client/shared_memory_limits.h" +#include "gpu/command_buffer/client/transfer_buffer.h" +#include "gpu/ipc/client/command_buffer_proxy_impl.h" +#include "mojo/public/cpp/system/core.h" +#include "services/shell/public/cpp/connector.h" +#include "url/gurl.h" + +namespace mus { + +GLES2Context::GLES2Context() {} + +GLES2Context::~GLES2Context() {} + +bool GLES2Context::Initialize(const std::vector<int32_t>& attribs, + shell::Connector* connector) { + gpu::CommandBuffer* command_buffer = nullptr; + gpu::GpuControl* gpu_control = nullptr; + // TODO(penghuang): Use type gpu::gles2::ContextCreationAttribHelper for + // attribs. + if (!mus::GpuService::UseChromeGpuCommandBuffer()) { + mojom::GpuPtr gpu; + connector->ConnectToInterface("mojo:mus", &gpu); + mojom::CommandBufferPtr command_buffer_ptr; + gpu->CreateOffscreenGLES2Context(GetProxy(&command_buffer_ptr)); + command_buffer_client_impl_.reset( + new CommandBufferClientImpl(attribs, std::move(command_buffer_ptr))); + if (!command_buffer_client_impl_->Initialize()) + return false; + command_buffer = command_buffer_client_impl_.get(); + gpu_control = command_buffer_client_impl_.get(); + } else { + scoped_refptr<gpu::GpuChannelHost> gpu_channel_host = + GpuService::GetInstance()->EstablishGpuChannelSync(); + if (!gpu_channel_host) + return false; + gpu::SurfaceHandle surface_handle = gfx::kNullAcceleratedWidget; + // TODO(penghuang): support shared group. + gpu::CommandBufferProxyImpl* shared_command_buffer = nullptr; + gpu::GpuStreamId stream_id = gpu::GpuStreamId::GPU_STREAM_DEFAULT; + gpu::GpuStreamPriority stream_priority = gpu::GpuStreamPriority::NORMAL; + gpu::gles2::ContextCreationAttribHelper attributes; + // TODO(penghuang): figure a useful active_url. + GURL active_url; + scoped_refptr<base::SingleThreadTaskRunner> task_runner = + base::ThreadTaskRunnerHandle::Get(); + if (!attributes.Parse(attribs)) + return false; + command_buffer_proxy_impl_ = gpu::CommandBufferProxyImpl::Create( + std::move(gpu_channel_host), surface_handle, shared_command_buffer, + stream_id, stream_priority, attributes, active_url, + std::move(task_runner)); + if (!command_buffer_proxy_impl_) + return false; + command_buffer = command_buffer_proxy_impl_.get(); + gpu_control = command_buffer_proxy_impl_.get(); + } + + constexpr gpu::SharedMemoryLimits default_limits; + gles2_helper_.reset(new gpu::gles2::GLES2CmdHelper(command_buffer)); + if (!gles2_helper_->Initialize(default_limits.command_buffer_size)) + return false; + gles2_helper_->SetAutomaticFlushes(false); + transfer_buffer_.reset(new gpu::TransferBuffer(gles2_helper_.get())); + gpu::Capabilities capabilities = gpu_control->GetCapabilities(); + bool bind_generates_resource = + !!capabilities.bind_generates_resource_chromium; + // TODO(piman): Some contexts (such as compositor) want this to be true, so + // this needs to be a public parameter. + bool lose_context_when_out_of_memory = false; + bool support_client_side_arrays = false; + implementation_.reset(new gpu::gles2::GLES2Implementation( + gles2_helper_.get(), NULL, transfer_buffer_.get(), + bind_generates_resource, lose_context_when_out_of_memory, + support_client_side_arrays, gpu_control)); + if (!implementation_->Initialize(default_limits.start_transfer_buffer_size, + default_limits.min_transfer_buffer_size, + default_limits.max_transfer_buffer_size, + default_limits.mapped_memory_reclaim_limit)) + return false; + return true; +} + +// static +std::unique_ptr<GLES2Context> GLES2Context::CreateOffscreenContext( + const std::vector<int32_t>& attribs, + shell::Connector* connector) { + std::unique_ptr<GLES2Context> gles2_context(new GLES2Context); + if (!gles2_context->Initialize(attribs, connector)) + gles2_context.reset(); + return gles2_context; +} + +} // namespace mus diff --git a/chromium/components/mus/public/cpp/lib/in_flight_change.cc b/chromium/components/mus/public/cpp/lib/in_flight_change.cc new file mode 100644 index 00000000000..04878742350 --- /dev/null +++ b/chromium/components/mus/public/cpp/lib/in_flight_change.cc @@ -0,0 +1,220 @@ +// 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/mus/public/cpp/lib/in_flight_change.h" + +#include "components/mus/public/cpp/lib/window_private.h" +#include "components/mus/public/cpp/window_tree_client.h" + +namespace mus { + +// InFlightChange ------------------------------------------------------------- + +InFlightChange::InFlightChange(Window* window, ChangeType type) + : window_(window), change_type_(type) {} + +InFlightChange::~InFlightChange() {} + +bool InFlightChange::Matches(const InFlightChange& change) const { + DCHECK(change.window_ == window_ && change.change_type_ == change_type_); + return true; +} + +void InFlightChange::ChangeFailed() {} + +// InFlightBoundsChange ------------------------------------------------------- + +InFlightBoundsChange::InFlightBoundsChange(Window* window, + const gfx::Rect& revert_bounds) + : InFlightChange(window, ChangeType::BOUNDS), + revert_bounds_(revert_bounds) {} + +void InFlightBoundsChange::SetRevertValueFrom(const InFlightChange& change) { + revert_bounds_ = + static_cast<const InFlightBoundsChange&>(change).revert_bounds_; +} + +void InFlightBoundsChange::Revert() { + WindowPrivate(window()).LocalSetBounds(window()->bounds(), revert_bounds_); +} + +// CrashInFlightChange -------------------------------------------------------- + +CrashInFlightChange::CrashInFlightChange(Window* window, ChangeType type) + : InFlightChange(window, type) {} + +CrashInFlightChange::~CrashInFlightChange() {} + +void CrashInFlightChange::SetRevertValueFrom(const InFlightChange& change) { + CHECK(false); +} + +void CrashInFlightChange::ChangeFailed() { + DLOG(ERROR) << "changed failed, type=" << static_cast<int>(change_type()); + CHECK(false); +} + +void CrashInFlightChange::Revert() { + CHECK(false); +} + +// InFlightWindowChange ------------------------------------------------------- + +InFlightWindowTreeClientChange::InFlightWindowTreeClientChange( + WindowTreeClient* client, + Window* revert_value, + ChangeType type) + : InFlightChange(nullptr, type), + client_(client), + revert_window_(nullptr) { + SetRevertWindow(revert_value); +} + +InFlightWindowTreeClientChange::~InFlightWindowTreeClientChange() { + SetRevertWindow(nullptr); +} + +void InFlightWindowTreeClientChange::SetRevertValueFrom( + const InFlightChange& change) { + SetRevertWindow(static_cast<const InFlightWindowTreeClientChange&>(change) + .revert_window_); +} + +void InFlightWindowTreeClientChange::SetRevertWindow(Window* window) { + if (revert_window_) + revert_window_->RemoveObserver(this); + revert_window_ = window; + if (revert_window_) + revert_window_->AddObserver(this); +} + +void InFlightWindowTreeClientChange::OnWindowDestroying(Window* window) { + SetRevertWindow(nullptr); +} + +// InFlightCaptureChange ------------------------------------------------------ + +InFlightCaptureChange::InFlightCaptureChange( + WindowTreeClient* client, Window* revert_value) + : InFlightWindowTreeClientChange(client, + revert_value, + ChangeType::CAPTURE) {} + +InFlightCaptureChange::~InFlightCaptureChange() {} + +void InFlightCaptureChange::Revert() { + client()->LocalSetCapture(revert_window()); +} + +// InFlightFocusChange -------------------------------------------------------- + +InFlightFocusChange::InFlightFocusChange( + WindowTreeClient* client, + Window* revert_value) + : InFlightWindowTreeClientChange(client, + revert_value, + ChangeType::FOCUS) {} + +InFlightFocusChange::~InFlightFocusChange() {} + +void InFlightFocusChange::Revert() { + client()->LocalSetFocus(revert_window()); +} + +// InFlightPropertyChange ----------------------------------------------------- + +InFlightPropertyChange::InFlightPropertyChange( + Window* window, + const std::string& property_name, + const mojo::Array<uint8_t>& revert_value) + : InFlightChange(window, ChangeType::PROPERTY), + property_name_(property_name), + revert_value_(revert_value.Clone()) {} + +InFlightPropertyChange::~InFlightPropertyChange() {} + +bool InFlightPropertyChange::Matches(const InFlightChange& change) const { + return static_cast<const InFlightPropertyChange&>(change).property_name_ == + property_name_; +} + +void InFlightPropertyChange::SetRevertValueFrom(const InFlightChange& change) { + revert_value_ = + static_cast<const InFlightPropertyChange&>(change).revert_value_.Clone(); +} + +void InFlightPropertyChange::Revert() { + WindowPrivate(window()) + .LocalSetSharedProperty(property_name_, std::move(revert_value_)); +} + +// InFlightPredefinedCursorChange --------------------------------------------- + +InFlightPredefinedCursorChange::InFlightPredefinedCursorChange( + Window* window, + mojom::Cursor revert_value) + : InFlightChange(window, ChangeType::PREDEFINED_CURSOR), + revert_cursor_(revert_value) {} + +InFlightPredefinedCursorChange::~InFlightPredefinedCursorChange() {} + +void InFlightPredefinedCursorChange::SetRevertValueFrom( + const InFlightChange& change) { + revert_cursor_ = + static_cast<const InFlightPredefinedCursorChange&>(change).revert_cursor_; +} + +void InFlightPredefinedCursorChange::Revert() { + WindowPrivate(window()).LocalSetPredefinedCursor(revert_cursor_); +} + +// InFlightVisibleChange ------------------------------------------------------- + +InFlightVisibleChange::InFlightVisibleChange(Window* window, + bool revert_value) + : InFlightChange(window, ChangeType::VISIBLE), + revert_visible_(revert_value) {} + +InFlightVisibleChange::~InFlightVisibleChange() {} + +void InFlightVisibleChange::SetRevertValueFrom(const InFlightChange& change) { + revert_visible_ = + static_cast<const InFlightVisibleChange&>(change).revert_visible_; +} + +void InFlightVisibleChange::Revert() { + WindowPrivate(window()).LocalSetVisible(revert_visible_); +} + +// InFlightOpacityChange ------------------------------------------------------- + +InFlightOpacityChange::InFlightOpacityChange(Window* window, float revert_value) + : InFlightChange(window, ChangeType::OPACITY), + revert_opacity_(revert_value) {} + +InFlightOpacityChange::~InFlightOpacityChange() {} + +void InFlightOpacityChange::SetRevertValueFrom(const InFlightChange& change) { + revert_opacity_ = + static_cast<const InFlightOpacityChange&>(change).revert_opacity_; +} + +void InFlightOpacityChange::Revert() { + WindowPrivate(window()).LocalSetOpacity(revert_opacity_); +} + +// InFlightSetModalChange ------------------------------------------------------ + +InFlightSetModalChange::InFlightSetModalChange(Window* window) + : InFlightChange(window, ChangeType::SET_MODAL) {} + +InFlightSetModalChange::~InFlightSetModalChange() {} + +void InFlightSetModalChange::SetRevertValueFrom(const InFlightChange& change) {} + +void InFlightSetModalChange::Revert() { + WindowPrivate(window()).LocalUnsetModal(); +} + +} // namespace mus diff --git a/chromium/components/mus/public/cpp/lib/in_flight_change.h b/chromium/components/mus/public/cpp/lib/in_flight_change.h new file mode 100644 index 00000000000..bf77fc6e6b1 --- /dev/null +++ b/chromium/components/mus/public/cpp/lib/in_flight_change.h @@ -0,0 +1,298 @@ +// 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_MUS_PUBLIC_CPP_LIB_IN_FLIGHT_CHANGE_H_ +#define COMPONENTS_MUS_PUBLIC_CPP_LIB_IN_FLIGHT_CHANGE_H_ + +#include <stdint.h> + +#include <memory> +#include <string> +#include <vector> + +#include "base/macros.h" +#include "base/memory/ptr_util.h" +#include "components/mus/public/cpp/window_observer.h" +#include "mojo/public/cpp/bindings/array.h" +#include "ui/gfx/geometry/rect.h" + +namespace mus { + +namespace mojom { +enum class Cursor : int32_t; +} + +class Window; +class WindowTreeClient; + +enum class ChangeType { + ADD_CHILD, + ADD_TRANSIENT_WINDOW, + BOUNDS, + CAPTURE, + DELETE_WINDOW, + FOCUS, + NEW_WINDOW, + NEW_TOP_LEVEL_WINDOW, + OPACITY, + PREDEFINED_CURSOR, + PROPERTY, + REMOVE_CHILD, + REMOVE_TRANSIENT_WINDOW_FROM_PARENT, + REORDER, + SET_MODAL, + VISIBLE, +}; + +// InFlightChange is used to track function calls to the server and take the +// appropriate action when the call fails, or the same property changes while +// waiting for the response. When a function is called on the server an +// InFlightChange is created. The function call is complete when +// OnChangeCompleted() is received from the server. The following may occur +// while waiting for a response: +// . A new value is encountered from the server. For example, the bounds of +// a window is locally changed and while waiting for the ack +// OnWindowBoundsChanged() is received. +// When this happens SetRevertValueFrom() is invoked on the InFlightChange. +// The expectation is SetRevertValueFrom() takes the value to revert from the +// supplied change. +// . While waiting for the ack the property is again modified locally. When +// this happens a new InFlightChange is created. Once the ack for the first +// call is received, and the server rejected the change ChangeFailed() is +// invoked on the first change followed by SetRevertValueFrom() on the second +// InFlightChange. This allows the new change to update the value to revert +// should the second call fail. +// . If the server responds that the call failed and there is not another +// InFlightChange for the same window outstanding, then ChangeFailed() is +// invoked followed by Revert(). The expectation is Revert() sets the +// appropriate value back on the Window. +// +// In general there are two classes of changes: +// 1. We are the only side allowed to make the change. +// 2. The change can also be applied by another client. For example, the +// window manager may change the bounds as well as the local client. +// +// For (1) use CrashInFlightChange. As the name implies this change CHECKs that +// the change succeeded. Use the following pattern for this. This code goes +// where the change is sent to the server (in WindowTreeClient): +// const uint32_t change_id = +// ScheduleInFlightChange(base::WrapUnique(new CrashInFlightChange( +// window, ChangeType::REORDER))); +// +// For (2) use the same pattern as (1), but in the on change callback from the +// server (e.g. OnWindowBoundsChanged()) add the following: +// // value_from_server is the value supplied from the server. It corresponds +// // to the value of the property at the time the server processed the +// // change. If the local change fails, this is the value reverted to. +// InFlightBoundsChange new_change(window, value_from_server); +// if (ApplyServerChangeToExistingInFlightChange(new_change)) { +// // There was an in flight change for the same property. The in flight +// // change takes the value to revert from |new_change|. +// return; +// } +// +// // else case is no flight in change and the new value can be applied +// // immediately. +// WindowPrivate(window).LocalSetValue(new_value_from_server); +// +class InFlightChange { + public: + InFlightChange(Window* window, ChangeType type); + virtual ~InFlightChange(); + + // NOTE: for properties not associated with any window window is null. + Window* window() { return window_; } + const Window* window() const { return window_; } + ChangeType change_type() const { return change_type_; } + + // Returns true if the two changes are considered the same. This is only + // invoked if the window id and ChangeType match. + virtual bool Matches(const InFlightChange& change) const; + + // Called in two cases: + // . When the corresponding property on the server changes. In this case + // |change| corresponds to the value from the server. + // . When |change| unsuccesfully completes. + virtual void SetRevertValueFrom(const InFlightChange& change) = 0; + + // The change failed. Normally you need not take any action here as Revert() + // is called as appropriate. + virtual void ChangeFailed(); + + // The change failed and there is no pending change of the same type + // outstanding, revert the value. + virtual void Revert() = 0; + + private: + Window* window_; + const ChangeType change_type_; +}; + +class InFlightBoundsChange : public InFlightChange { + public: + InFlightBoundsChange(Window* window, const gfx::Rect& revert_bounds); + + // InFlightChange: + void SetRevertValueFrom(const InFlightChange& change) override; + void Revert() override; + + private: + gfx::Rect revert_bounds_; + + DISALLOW_COPY_AND_ASSIGN(InFlightBoundsChange); +}; + +// Inflight change that crashes on failure. This is useful for changes that are +// expected to always complete. +class CrashInFlightChange : public InFlightChange { + public: + CrashInFlightChange(Window* window, ChangeType type); + ~CrashInFlightChange() override; + + // InFlightChange: + void SetRevertValueFrom(const InFlightChange& change) override; + void ChangeFailed() override; + void Revert() override; + + private: + DISALLOW_COPY_AND_ASSIGN(CrashInFlightChange); +}; + +// Use this class for properties that are specific to the client, and not a +// particular window. For example, only a single window can have focus, so focus +// is specific to the client. +// +// This does not implement InFlightChange::Revert, subclasses must implement +// that to update the WindowTreeClient. +class InFlightWindowTreeClientChange : public InFlightChange, + public WindowObserver { + public: + InFlightWindowTreeClientChange(WindowTreeClient* client, + Window* revert_value, + ChangeType type); + ~InFlightWindowTreeClientChange() override; + + // InFlightChange: + void SetRevertValueFrom(const InFlightChange& change) override; + + protected: + WindowTreeClient* client() { return client_; } + Window* revert_window() { return revert_window_; } + + private: + void SetRevertWindow(Window* window); + + // WindowObserver: + void OnWindowDestroying(Window* window) override; + + WindowTreeClient* client_; + Window* revert_window_; + + DISALLOW_COPY_AND_ASSIGN(InFlightWindowTreeClientChange); +}; + +class InFlightCaptureChange : public InFlightWindowTreeClientChange { + public: + InFlightCaptureChange(WindowTreeClient* client, Window* revert_value); + ~InFlightCaptureChange() override; + + // InFlightChange: + void Revert() override; + + private: + DISALLOW_COPY_AND_ASSIGN(InFlightCaptureChange); +}; + +class InFlightFocusChange : public InFlightWindowTreeClientChange { + public: + InFlightFocusChange(WindowTreeClient* client, Window* revert_value); + ~InFlightFocusChange() override; + + // InFlightChange: + void Revert() override; + + private: + DISALLOW_COPY_AND_ASSIGN(InFlightFocusChange); +}; + +class InFlightPropertyChange : public InFlightChange { + public: + InFlightPropertyChange(Window* window, + const std::string& property_name, + const mojo::Array<uint8_t>& revert_value); + ~InFlightPropertyChange() override; + + // InFlightChange: + bool Matches(const InFlightChange& change) const override; + void SetRevertValueFrom(const InFlightChange& change) override; + void Revert() override; + + private: + const std::string property_name_; + mojo::Array<uint8_t> revert_value_; + + DISALLOW_COPY_AND_ASSIGN(InFlightPropertyChange); +}; + +class InFlightPredefinedCursorChange : public InFlightChange { + public: + InFlightPredefinedCursorChange(Window* window, mojom::Cursor revert_value); + ~InFlightPredefinedCursorChange() override; + + // InFlightChange: + void SetRevertValueFrom(const InFlightChange& change) override; + void Revert() override; + + private: + mojom::Cursor revert_cursor_; + + DISALLOW_COPY_AND_ASSIGN(InFlightPredefinedCursorChange); +}; + +class InFlightVisibleChange : public InFlightChange { + public: + InFlightVisibleChange(Window* window, const bool revert_value); + ~InFlightVisibleChange() override; + + // InFlightChange: + void SetRevertValueFrom(const InFlightChange& change) override; + void Revert() override; + + private: + bool revert_visible_; + + DISALLOW_COPY_AND_ASSIGN(InFlightVisibleChange); +}; + +class InFlightOpacityChange : public InFlightChange { + public: + InFlightOpacityChange(Window* window, float revert_value); + ~InFlightOpacityChange() override; + + // InFlightChange: + void SetRevertValueFrom(const InFlightChange& change) override; + void Revert() override; + + private: + float revert_opacity_; + + DISALLOW_COPY_AND_ASSIGN(InFlightOpacityChange); +}; + +class InFlightSetModalChange : public InFlightChange { + public: + explicit InFlightSetModalChange(Window* window); + ~InFlightSetModalChange() override; + + // InFlightChange: + void SetRevertValueFrom(const InFlightChange& change) override; + void Revert() override; + + private: + DISALLOW_COPY_AND_ASSIGN(InFlightSetModalChange); +}; + +} // namespace mus + +#endif // COMPONENTS_MUS_PUBLIC_CPP_LIB_IN_FLIGHT_CHANGE_H_ diff --git a/chromium/components/mus/public/cpp/lib/output_surface.cc b/chromium/components/mus/public/cpp/lib/output_surface.cc new file mode 100644 index 00000000000..86462ec95f4 --- /dev/null +++ b/chromium/components/mus/public/cpp/lib/output_surface.cc @@ -0,0 +1,70 @@ +// 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 "components/mus/public/cpp/output_surface.h" + +#include "base/bind.h" +#include "cc/output/compositor_frame.h" +#include "cc/output/compositor_frame_ack.h" +#include "cc/output/output_surface_client.h" +#include "components/mus/public/cpp/window_surface.h" + +namespace mus { + +OutputSurface::OutputSurface( + const scoped_refptr<cc::ContextProvider>& context_provider, + std::unique_ptr<mus::WindowSurface> surface) + : cc::OutputSurface(context_provider, nullptr, nullptr), + surface_(std::move(surface)) { + capabilities_.delegated_rendering = true; +} + +OutputSurface::~OutputSurface() {} + +bool OutputSurface::BindToClient(cc::OutputSurfaceClient* client) { + surface_->BindToThread(); + surface_->set_client(this); + return cc::OutputSurface::BindToClient(client); +} + +void OutputSurface::DetachFromClient() { + surface_.reset(); + cc::OutputSurface::DetachFromClient(); +} + +void OutputSurface::BindFramebuffer() { + // This is a delegating output surface, no framebuffer/direct drawing support. + NOTREACHED(); +} + +uint32_t OutputSurface::GetFramebufferCopyTextureFormat() { + // This is a delegating output surface, no framebuffer/direct drawing support. + NOTREACHED(); + return 0; +} + +void OutputSurface::SwapBuffers(cc::CompositorFrame frame) { + // TODO(fsamuel, rjkroege): We should probably throttle compositor frames. + client_->DidSwapBuffers(); + // OutputSurface owns WindowSurface, and so if OutputSurface is + // destroyed then SubmitCompositorFrame's callback will never get called. + // Thus, base::Unretained is safe here. + surface_->SubmitCompositorFrame( + std::move(frame), + base::Bind(&OutputSurface::SwapBuffersComplete, base::Unretained(this))); +} + +void OutputSurface::OnResourcesReturned( + mus::WindowSurface* surface, + mojo::Array<cc::ReturnedResource> resources) { + cc::CompositorFrameAck cfa; + cfa.resources = resources.To<cc::ReturnedResourceArray>(); + ReclaimResources(&cfa); +} + +void OutputSurface::SwapBuffersComplete() { + client_->DidSwapBuffersComplete(); +} + +} // namespace mus diff --git a/chromium/components/mus/public/cpp/lib/property_type_converters.cc b/chromium/components/mus/public/cpp/lib/property_type_converters.cc new file mode 100644 index 00000000000..17cacf1f249 --- /dev/null +++ b/chromium/components/mus/public/cpp/lib/property_type_converters.cc @@ -0,0 +1,207 @@ +// 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/mus/public/cpp/property_type_converters.h" + +#include <stdint.h> + +#include "base/strings/utf_string_conversions.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/size.h" + +namespace { + +// Maximum allowed height or width of a bitmap, in pixels. This limit prevents +// malformed bitmap headers from causing arbitrarily large memory allocations +// for pixel data. +const int kMaxBitmapSize = 4096; + +} // namespace + +namespace mojo { + +// static +std::vector<uint8_t> TypeConverter<std::vector<uint8_t>, gfx::Rect>::Convert( + const gfx::Rect& input) { + std::vector<uint8_t> vec(16); + vec[0] = (input.x() >> 24) & 0xFF; + vec[1] = (input.x() >> 16) & 0xFF; + vec[2] = (input.x() >> 8) & 0xFF; + vec[3] = input.x() & 0xFF; + vec[4] = (input.y() >> 24) & 0xFF; + vec[5] = (input.y() >> 16) & 0xFF; + vec[6] = (input.y() >> 8) & 0xFF; + vec[7] = input.y() & 0xFF; + vec[8] = (input.width() >> 24) & 0xFF; + vec[9] = (input.width() >> 16) & 0xFF; + vec[10] = (input.width() >> 8) & 0xFF; + vec[11] = input.width() & 0xFF; + vec[12] = (input.height() >> 24) & 0xFF; + vec[13] = (input.height() >> 16) & 0xFF; + vec[14] = (input.height() >> 8) & 0xFF; + vec[15] = input.height() & 0xFF; + return vec; +} + +// static +gfx::Rect TypeConverter<gfx::Rect, std::vector<uint8_t>>::Convert( + const std::vector<uint8_t>& input) { + return gfx::Rect( + input[0] << 24 | input[1] << 16 | input[2] << 8 | input[3], + input[4] << 24 | input[5] << 16 | input[6] << 8 | input[7], + input[8] << 24 | input[9] << 16 | input[10] << 8 | input[11], + input[12] << 24 | input[13] << 16 | input[14] << 8 | input[15]); +} + +// static +std::vector<uint8_t> TypeConverter<std::vector<uint8_t>, gfx::Size>::Convert( + const gfx::Size& input) { + std::vector<uint8_t> vec(8); + vec[0] = (input.width() >> 24) & 0xFF; + vec[1] = (input.width() >> 16) & 0xFF; + vec[2] = (input.width() >> 8) & 0xFF; + vec[3] = input.width() & 0xFF; + vec[4] = (input.height() >> 24) & 0xFF; + vec[5] = (input.height() >> 16) & 0xFF; + vec[6] = (input.height() >> 8) & 0xFF; + vec[7] = input.height() & 0xFF; + return vec; +} + +// static +gfx::Size TypeConverter<gfx::Size, std::vector<uint8_t>>::Convert( + const std::vector<uint8_t>& input) { + return gfx::Size(input[0] << 24 | input[1] << 16 | input[2] << 8 | input[3], + input[4] << 24 | input[5] << 16 | input[6] << 8 | input[7]); +} + +// static +std::vector<uint8_t> TypeConverter<std::vector<uint8_t>, int32_t>::Convert( + const int32_t& input) { + std::vector<uint8_t> vec(4); + vec[0] = (input >> 24) & 0xFF; + vec[1] = (input >> 16) & 0xFF; + vec[2] = (input >> 8) & 0xFF; + vec[3] = input & 0xFF; + return vec; +} + +// static +int32_t TypeConverter<int32_t, std::vector<uint8_t>>::Convert( + const std::vector<uint8_t>& input) { + return input[0] << 24 | input[1] << 16 | input[2] << 8 | input[3]; +} + +// static +std::vector<uint8_t> +TypeConverter<std::vector<uint8_t>, base::string16>::Convert( + const base::string16& input) { + return ConvertTo<std::vector<uint8_t>>(base::UTF16ToUTF8(input)); +} + +// static +base::string16 TypeConverter<base::string16, std::vector<uint8_t>>::Convert( + const std::vector<uint8_t>& input) { + return base::UTF8ToUTF16(ConvertTo<std::string>(input)); +} + +// static +std::vector<uint8_t> TypeConverter<std::vector<uint8_t>, std::string>::Convert( + const std::string& input) { + return std::vector<uint8_t>(input.begin(), input.end()); +} + +// static +std::string TypeConverter<std::string, std::vector<uint8_t>>::Convert( + const std::vector<uint8_t>& input) { + return std::string(input.begin(), input.end()); +} + +// static +std::vector<uint8_t> TypeConverter<std::vector<uint8_t>, SkBitmap>::Convert( + const SkBitmap& input) { + // Empty images are valid to serialize and are represented by an empty vector. + if (input.isNull()) + return std::vector<uint8_t>(); + + // Only RGBA 8888 bitmaps with premultiplied alpha are supported. + if (input.colorType() != kBGRA_8888_SkColorType || + input.alphaType() != kPremul_SkAlphaType) { + NOTREACHED(); + return std::vector<uint8_t>(); + } + + // Sanity check the bitmap size. + int width = input.width(); + int height = input.height(); + if (width < 0 || width > kMaxBitmapSize || height < 0 || + height > kMaxBitmapSize) { + NOTREACHED(); + return std::vector<uint8_t>(); + } + + // Serialize the bitmap. The size is restricted so only 2 bytes are required + // per dimension. + std::vector<uint8_t> vec(4 + input.getSize()); + vec[0] = (width >> 8) & 0xFF; + vec[1] = width & 0xFF; + vec[2] = (height >> 8) & 0xFF; + vec[3] = height & 0xFF; + if (!input.copyPixelsTo(&vec[4], input.getSize())) + return std::vector<uint8_t>(); + return vec; +} + +// static +SkBitmap TypeConverter<SkBitmap, std::vector<uint8_t>>::Convert( + const std::vector<uint8_t>& input) { + // Empty images are represented by empty vectors. + if (input.empty()) + return SkBitmap(); + + // Read and sanity check size. + int width = input[0] << 8 | input[1]; + int height = input[2] << 8 | input[3]; + if (width < 0 || width > kMaxBitmapSize || height < 0 || + height > kMaxBitmapSize) { + NOTREACHED(); + return SkBitmap(); + } + + // Try to allocate a bitmap of the appropriate size. + SkBitmap bitmap; + if (!bitmap.tryAllocPixels(SkImageInfo::Make( + width, height, kBGRA_8888_SkColorType, kPremul_SkAlphaType))) { + return SkBitmap(); + } + + // Ensure the vector contains the right amount of data. + if (input.size() != bitmap.getSize() + 4) { + NOTREACHED(); + return SkBitmap(); + } + + // Read the pixel data. + SkAutoLockPixels lock(bitmap); + memcpy(bitmap.getPixels(), &input[4], bitmap.getSize()); + return bitmap; +} + +// static +std::vector<uint8_t> TypeConverter<std::vector<uint8_t>, bool>::Convert( + bool input) { + std::vector<uint8_t> vec(1); + vec[0] = input ? 1 : 0; + return vec; +} + +// static +bool TypeConverter<bool, std::vector<uint8_t>>::Convert( + const std::vector<uint8_t>& input) { + // Empty vectors are interpreted as false. + return !input.empty() && (input[0] == 1); +} + +} // namespace mojo diff --git a/chromium/components/mus/public/cpp/lib/scoped_window_ptr.cc b/chromium/components/mus/public/cpp/lib/scoped_window_ptr.cc new file mode 100644 index 00000000000..360da1ff4b5 --- /dev/null +++ b/chromium/components/mus/public/cpp/lib/scoped_window_ptr.cc @@ -0,0 +1,44 @@ +// 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/mus/public/cpp/scoped_window_ptr.h" + +#include "components/mus/public/cpp/window.h" +#include "components/mus/public/cpp/window_observer.h" +#include "components/mus/public/cpp/window_tree_client.h" + +namespace mus { + +ScopedWindowPtr::ScopedWindowPtr(Window* window) : window_(window) { + window_->AddObserver(this); +} + +ScopedWindowPtr::~ScopedWindowPtr() { + if (window_) + DeleteWindowOrWindowManager(window_); + DetachFromWindow(); +} + +// static +void ScopedWindowPtr::DeleteWindowOrWindowManager(Window* window) { + if (window->window_tree()->GetRoots().count(window) > 0) + delete window->window_tree(); + else + window->Destroy(); +} + +void ScopedWindowPtr::DetachFromWindow() { + if (!window_) + return; + + window_->RemoveObserver(this); + window_ = nullptr; +} + +void ScopedWindowPtr::OnWindowDestroying(Window* window) { + DCHECK_EQ(window_, window); + DetachFromWindow(); +} + +} // namespace mus diff --git a/chromium/components/mus/public/cpp/lib/window.cc b/chromium/components/mus/public/cpp/lib/window.cc new file mode 100644 index 00000000000..83785c4544e --- /dev/null +++ b/chromium/components/mus/public/cpp/lib/window.cc @@ -0,0 +1,891 @@ +// 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 "components/mus/public/cpp/window.h" + +#include <stddef.h> +#include <stdint.h> + +#include <set> +#include <string> + +#include "base/bind.h" +#include "base/macros.h" +#include "components/mus/common/transient_window_utils.h" +#include "components/mus/public/cpp/lib/window_private.h" +#include "components/mus/public/cpp/property_type_converters.h" +#include "components/mus/public/cpp/window_observer.h" +#include "components/mus/public/cpp/window_property.h" +#include "components/mus/public/cpp/window_surface.h" +#include "components/mus/public/cpp/window_tracker.h" +#include "components/mus/public/cpp/window_tree_client.h" +#include "components/mus/public/interfaces/window_manager.mojom.h" +#include "ui/display/display.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/size.h" + +namespace mus { + +namespace { + +void NotifyWindowTreeChangeAtReceiver( + Window* receiver, + const WindowObserver::TreeChangeParams& params, + bool change_applied) { + WindowObserver::TreeChangeParams local_params = params; + local_params.receiver = receiver; + if (change_applied) { + FOR_EACH_OBSERVER(WindowObserver, *WindowPrivate(receiver).observers(), + OnTreeChanged(local_params)); + } else { + FOR_EACH_OBSERVER(WindowObserver, *WindowPrivate(receiver).observers(), + OnTreeChanging(local_params)); + } +} + +void NotifyWindowTreeChangeUp(Window* start_at, + const WindowObserver::TreeChangeParams& params, + bool change_applied) { + for (Window* current = start_at; current; current = current->parent()) + NotifyWindowTreeChangeAtReceiver(current, params, change_applied); +} + +void NotifyWindowTreeChangeDown(Window* start_at, + const WindowObserver::TreeChangeParams& params, + bool change_applied) { + NotifyWindowTreeChangeAtReceiver(start_at, params, change_applied); + Window::Children::const_iterator it = start_at->children().begin(); + for (; it != start_at->children().end(); ++it) + NotifyWindowTreeChangeDown(*it, params, change_applied); +} + +void NotifyWindowTreeChange(const WindowObserver::TreeChangeParams& params, + bool change_applied) { + NotifyWindowTreeChangeDown(params.target, params, change_applied); + if (params.old_parent) + NotifyWindowTreeChangeUp(params.old_parent, params, change_applied); + if (params.new_parent) + NotifyWindowTreeChangeUp(params.new_parent, params, change_applied); +} + +class ScopedTreeNotifier { + public: + ScopedTreeNotifier(Window* target, Window* old_parent, Window* new_parent) { + params_.target = target; + params_.old_parent = old_parent; + params_.new_parent = new_parent; + NotifyWindowTreeChange(params_, false); + } + ~ScopedTreeNotifier() { NotifyWindowTreeChange(params_, true); } + + private: + WindowObserver::TreeChangeParams params_; + + DISALLOW_COPY_AND_ASSIGN(ScopedTreeNotifier); +}; + +void RemoveChildImpl(Window* child, Window::Children* children) { + Window::Children::iterator it = + std::find(children->begin(), children->end(), child); + if (it != children->end()) { + children->erase(it); + WindowPrivate(child).ClearParent(); + } +} + +class OrderChangedNotifier { + public: + OrderChangedNotifier(Window* window, + Window* relative_window, + mojom::OrderDirection direction) + : window_(window), + relative_window_(relative_window), + direction_(direction) {} + + ~OrderChangedNotifier() {} + + void NotifyWindowReordering() { + FOR_EACH_OBSERVER( + WindowObserver, *WindowPrivate(window_).observers(), + OnWindowReordering(window_, relative_window_, direction_)); + } + + void NotifyWindowReordered() { + FOR_EACH_OBSERVER(WindowObserver, *WindowPrivate(window_).observers(), + OnWindowReordered(window_, relative_window_, direction_)); + } + + private: + Window* window_; + Window* relative_window_; + mojom::OrderDirection direction_; + + DISALLOW_COPY_AND_ASSIGN(OrderChangedNotifier); +}; + +class ScopedSetBoundsNotifier { + public: + ScopedSetBoundsNotifier(Window* window, + const gfx::Rect& old_bounds, + const gfx::Rect& new_bounds) + : window_(window), old_bounds_(old_bounds), new_bounds_(new_bounds) { + FOR_EACH_OBSERVER( + WindowObserver, *WindowPrivate(window_).observers(), + OnWindowBoundsChanging(window_, old_bounds_, new_bounds_)); + } + ~ScopedSetBoundsNotifier() { + FOR_EACH_OBSERVER(WindowObserver, *WindowPrivate(window_).observers(), + OnWindowBoundsChanged(window_, old_bounds_, new_bounds_)); + } + + private: + Window* window_; + const gfx::Rect old_bounds_; + const gfx::Rect new_bounds_; + + DISALLOW_COPY_AND_ASSIGN(ScopedSetBoundsNotifier); +}; + +// Some operations are only permitted in the client that created the window. +bool OwnsWindow(WindowTreeClient* client, Window* window) { + return !client || client->OwnsWindow(window); +} + +bool IsClientRoot(Window* window) { + return window->window_tree() && + window->window_tree()->GetRoots().count(window) > 0; +} + +bool OwnsWindowOrIsRoot(Window* window) { + return OwnsWindow(window->window_tree(), window) || IsClientRoot(window); +} + +void EmptyEmbedCallback(bool result) {} + +} // namespace + +//////////////////////////////////////////////////////////////////////////////// +// Window, public: + +void Window::Destroy() { + if (!OwnsWindowOrIsRoot(this)) + return; + + if (client_) + client_->DestroyWindow(this); + while (!children_.empty()) { + Window* child = children_.front(); + if (!OwnsWindow(client_, child)) { + WindowPrivate(child).ClearParent(); + children_.erase(children_.begin()); + } else { + child->Destroy(); + DCHECK(std::find(children_.begin(), children_.end(), child) == + children_.end()); + } + } + LocalDestroy(); +} + +void Window::SetBounds(const gfx::Rect& bounds) { + if (!OwnsWindowOrIsRoot(this)) + return; + if (bounds_ == bounds) + return; + if (client_) + client_->SetBounds(this, bounds_, bounds); + LocalSetBounds(bounds_, bounds); +} + +gfx::Rect Window::GetBoundsInRoot() const { + gfx::Vector2d offset; + for (const Window* w = parent(); w != nullptr; w = w->parent()) + offset += w->bounds().OffsetFromOrigin(); + return bounds() + offset; +} + +void Window::SetClientArea( + const gfx::Insets& client_area, + const std::vector<gfx::Rect>& additional_client_areas) { + if (!OwnsWindowOrIsRoot(this)) + return; + + if (client_) + client_->SetClientArea(server_id_, client_area, + additional_client_areas); + LocalSetClientArea(client_area, additional_client_areas); +} + +void Window::SetHitTestMask(const gfx::Rect& mask) { + if (!OwnsWindowOrIsRoot(this)) + return; + + if (hit_test_mask_ && *hit_test_mask_ == mask) + return; + + if (client_) + client_->SetHitTestMask(server_id_, mask); + hit_test_mask_.reset(new gfx::Rect(mask)); +} + +void Window::ClearHitTestMask() { + if (!OwnsWindowOrIsRoot(this)) + return; + + if (!hit_test_mask_) + return; + + if (client_) + client_->ClearHitTestMask(server_id_); + hit_test_mask_.reset(); +} + +void Window::SetVisible(bool value) { + if (visible_ == value) + return; + + if (client_) + client_->SetVisible(this, value); + LocalSetVisible(value); +} + +void Window::SetOpacity(float opacity) { + if (client_) + client_->SetOpacity(this, opacity); + LocalSetOpacity(opacity); +} + +void Window::SetPredefinedCursor(mus::mojom::Cursor cursor_id) { + if (cursor_id_ == cursor_id) + return; + + if (client_) + client_->SetPredefinedCursor(server_id_, cursor_id); + LocalSetPredefinedCursor(cursor_id); +} + +bool Window::IsDrawn() const { + if (!visible_) + return false; + return parent_ ? parent_->IsDrawn() : parent_drawn_; +} + +std::unique_ptr<WindowSurface> Window::RequestSurface(mojom::SurfaceType type) { + std::unique_ptr<WindowSurfaceBinding> surface_binding; + std::unique_ptr<WindowSurface> surface = + WindowSurface::Create(&surface_binding); + AttachSurface(type, std::move(surface_binding)); + return surface; +} + +void Window::AttachSurface( + mojom::SurfaceType type, + std::unique_ptr<WindowSurfaceBinding> surface_binding) { + window_tree()->AttachSurface( + server_id_, type, std::move(surface_binding->surface_request_), + mojo::MakeProxy(std::move(surface_binding->surface_client_))); +} + +void Window::ClearSharedProperty(const std::string& name) { + SetSharedPropertyInternal(name, nullptr); +} + +bool Window::HasSharedProperty(const std::string& name) const { + return properties_.count(name) > 0; +} + +void Window::AddObserver(WindowObserver* observer) { + observers_.AddObserver(observer); +} + +void Window::RemoveObserver(WindowObserver* observer) { + observers_.RemoveObserver(observer); +} + +const Window* Window::GetRoot() const { + const Window* root = this; + for (const Window* parent = this; parent; parent = parent->parent()) + root = parent; + return root; +} + +void Window::AddChild(Window* child) { + // TODO(beng): not necessarily valid to all clients, but possibly to the + // embeddee in an embedder-embeddee relationship. + if (client_) + CHECK_EQ(child->client_, client_); + // Roots can not be added as children of other windows. + if (window_tree() && window_tree()->IsRoot(child)) + return; + LocalAddChild(child); + if (client_) + client_->AddChild(this, child->server_id()); +} + +void Window::RemoveChild(Window* child) { + // TODO(beng): not necessarily valid to all clients, but possibly to the + // embeddee in an embedder-embeddee relationship. + if (client_) + CHECK_EQ(child->client_, client_); + LocalRemoveChild(child); + if (client_) + client_->RemoveChild(this, child->server_id()); +} + +void Window::Reorder(Window* relative, mojom::OrderDirection direction) { + if (!LocalReorder(relative, direction)) + return; + if (client_) + client_->Reorder(this, relative->server_id(), direction); +} + +void Window::MoveToFront() { + if (!parent_ || parent_->children_.back() == this) + return; + Reorder(parent_->children_.back(), mojom::OrderDirection::ABOVE); +} + +void Window::MoveToBack() { + if (!parent_ || parent_->children_.front() == this) + return; + Reorder(parent_->children_.front(), mojom::OrderDirection::BELOW); +} + +bool Window::Contains(const Window* child) const { + if (!child) + return false; + if (child == this) + return true; + if (client_) + CHECK_EQ(child->client_, client_); + for (const Window* p = child->parent(); p; p = p->parent()) { + if (p == this) + return true; + } + return false; +} + +void Window::AddTransientWindow(Window* transient_window) { + // A system modal window cannot become a transient child. + DCHECK(!transient_window->is_modal() || transient_window->transient_parent()); + + if (client_) + CHECK_EQ(transient_window->client_, client_); + LocalAddTransientWindow(transient_window); + if (client_) + client_->AddTransientWindow(this, transient_window->server_id()); +} + +void Window::RemoveTransientWindow(Window* transient_window) { + if (client_) + CHECK_EQ(transient_window->window_tree(), client_); + LocalRemoveTransientWindow(transient_window); + if (client_) + client_->RemoveTransientWindowFromParent(transient_window); +} + +void Window::SetModal() { + if (is_modal_) + return; + + LocalSetModal(); + if (client_) + client_->SetModal(this); +} + +Window* Window::GetChildByLocalId(int id) { + if (id == local_id_) + return this; + // TODO(beng): this could be improved depending on how we decide to own + // windows. + for (Window* child : children_) { + Window* matching_child = child->GetChildByLocalId(id); + if (matching_child) + return matching_child; + } + return nullptr; +} + +void Window::SetTextInputState(mojo::TextInputStatePtr state) { + if (client_) + client_->SetWindowTextInputState(server_id_, std::move(state)); +} + +void Window::SetImeVisibility(bool visible, mojo::TextInputStatePtr state) { + // SetImeVisibility() shouldn't be used if the window is not editable. + DCHECK(state.is_null() || state->type != mojo::TextInputType::NONE); + if (client_) + client_->SetImeVisibility(server_id_, visible, std::move(state)); +} + +bool Window::HasCapture() const { + return client_ && client_->GetCaptureWindow() == this; +} + +void Window::SetCapture() { + if (client_) + client_->SetCapture(this); +} + +void Window::ReleaseCapture() { + if (client_) + client_->ReleaseCapture(this); +} + +void Window::SetFocus() { + if (client_ && IsDrawn()) + client_->SetFocus(this); +} + +bool Window::HasFocus() const { + return client_ && client_->GetFocusedWindow() == this; +} + +void Window::SetCanFocus(bool can_focus) { + if (client_) + client_->SetCanFocus(server_id_, can_focus); +} + +void Window::Embed(mus::mojom::WindowTreeClientPtr client, uint32_t flags) { + Embed(std::move(client), base::Bind(&EmptyEmbedCallback), flags); +} + +void Window::Embed(mus::mojom::WindowTreeClientPtr client, + const EmbedCallback& callback, + uint32_t flags) { + if (PrepareForEmbed()) + client_->Embed(server_id_, std::move(client), flags, callback); + else + callback.Run(false); +} + +void Window::RequestClose() { + if (client_) + client_->RequestClose(this); +} + +std::string Window::GetName() const { + if (HasSharedProperty(mojom::WindowManager::kName_Property)) + return GetSharedProperty<std::string>(mojom::WindowManager::kName_Property); + + return std::string(); +} + +//////////////////////////////////////////////////////////////////////////////// +// Window, protected: + +Window::Window() : Window(nullptr, static_cast<Id>(-1)) {} + +Window::~Window() { + FOR_EACH_OBSERVER(WindowObserver, observers_, OnWindowDestroying(this)); + if (client_) + client_->OnWindowDestroying(this); + + if (HasFocus()) { + // The focused window is being removed. When this happens the server + // advances focus. We don't want to randomly pick a Window to get focus, so + // we update local state only, and wait for the next focus change from the + // server. + client_->LocalSetFocus(nullptr); + } + + // Remove from transient parent. + if (transient_parent_) + transient_parent_->LocalRemoveTransientWindow(this); + + // Remove transient children. + while (!transient_children_.empty()) { + Window* transient_child = transient_children_.front(); + LocalRemoveTransientWindow(transient_child); + transient_child->LocalDestroy(); + DCHECK(transient_children_.empty() || + transient_children_.front() != transient_child); + } + + if (parent_) + parent_->LocalRemoveChild(this); + + // We may still have children. This can happen if the embedder destroys the + // root while we're still alive. + while (!children_.empty()) { + Window* child = children_.front(); + LocalRemoveChild(child); + DCHECK(children_.empty() || children_.front() != child); + } + + // Clear properties. + for (auto& pair : prop_map_) { + if (pair.second.deallocator) + (*pair.second.deallocator)(pair.second.value); + } + prop_map_.clear(); + + FOR_EACH_OBSERVER(WindowObserver, observers_, OnWindowDestroyed(this)); + + // Invoke after observers so that can clean up any internal state observers + // may have changed. + if (window_tree()) + window_tree()->OnWindowDestroyed(this); +} + +//////////////////////////////////////////////////////////////////////////////// +// Window, private: + +Window::Window(WindowTreeClient* client, Id id) + : client_(client), + server_id_(id), + parent_(nullptr), + stacking_target_(nullptr), + transient_parent_(nullptr), + is_modal_(false), + // Matches aura, see aura::Window for details. + observers_(base::ObserverList<WindowObserver>::NOTIFY_EXISTING_ONLY), + input_event_handler_(nullptr), + visible_(false), + opacity_(1.0f), + display_id_(display::Display::kInvalidDisplayID), + cursor_id_(mojom::Cursor::CURSOR_NULL), + parent_drawn_(false) {} + +void Window::SetSharedPropertyInternal(const std::string& name, + const std::vector<uint8_t>* value) { + if (!OwnsWindowOrIsRoot(this)) + return; + + if (client_) { + mojo::Array<uint8_t> transport_value(nullptr); + if (value) { + transport_value.resize(value->size()); + if (value->size()) + memcpy(&transport_value.front(), &(value->front()), value->size()); + } + // TODO: add test coverage of this (450303). + client_->SetProperty(this, name, std::move(transport_value)); + } + LocalSetSharedProperty(name, value); +} + +int64_t Window::SetLocalPropertyInternal(const void* key, + const char* name, + PropertyDeallocator deallocator, + int64_t value, + int64_t default_value) { + int64_t old = GetLocalPropertyInternal(key, default_value); + if (value == default_value) { + prop_map_.erase(key); + } else { + Value prop_value; + prop_value.name = name; + prop_value.value = value; + prop_value.deallocator = deallocator; + prop_map_[key] = prop_value; + } + FOR_EACH_OBSERVER(WindowObserver, observers_, + OnWindowLocalPropertyChanged(this, key, old)); + return old; +} + +int64_t Window::GetLocalPropertyInternal(const void* key, + int64_t default_value) const { + std::map<const void*, Value>::const_iterator iter = prop_map_.find(key); + if (iter == prop_map_.end()) + return default_value; + return iter->second.value; +} + +void Window::LocalDestroy() { + delete this; +} + +void Window::LocalAddChild(Window* child) { + ScopedTreeNotifier notifier(child, child->parent(), this); + if (child->parent()) + RemoveChildImpl(child, &child->parent_->children_); + children_.push_back(child); + child->parent_ = this; + child->display_id_ = display_id_; +} + +void Window::LocalRemoveChild(Window* child) { + DCHECK_EQ(this, child->parent()); + ScopedTreeNotifier notifier(child, this, nullptr); + RemoveChildImpl(child, &children_); +} + +void Window::LocalAddTransientWindow(Window* transient_window) { + if (transient_window->transient_parent()) + RemoveTransientWindowImpl(transient_window); + transient_children_.push_back(transient_window); + transient_window->transient_parent_ = this; + + // Restack |transient_window| properly above its transient parent, if they + // share the same parent. + if (transient_window->parent() == parent()) + RestackTransientDescendants(this, &GetStackingTarget, + &ReorderWithoutNotification); + + // TODO(fsamuel): We might want a notification here. +} + +void Window::LocalRemoveTransientWindow(Window* transient_window) { + DCHECK_EQ(this, transient_window->transient_parent()); + RemoveTransientWindowImpl(transient_window); + // TODO(fsamuel): We might want a notification here. +} + +void Window::LocalSetModal() { + is_modal_ = true; +} + +bool Window::LocalReorder(Window* relative, mojom::OrderDirection direction) { + OrderChangedNotifier notifier(this, relative, direction); + return ReorderImpl(this, relative, direction, ¬ifier); +} + +void Window::LocalSetBounds(const gfx::Rect& old_bounds, + const gfx::Rect& new_bounds) { + // If this client owns the window, then it should be the only one to change + // the bounds. + DCHECK(!OwnsWindow(client_, this) || old_bounds == bounds_); + ScopedSetBoundsNotifier notifier(this, old_bounds, new_bounds); + bounds_ = new_bounds; +} + +void Window::LocalSetClientArea( + const gfx::Insets& new_client_area, + const std::vector<gfx::Rect>& additional_client_areas) { + const std::vector<gfx::Rect> old_additional_client_areas = + additional_client_areas_; + const gfx::Insets old_client_area = client_area_; + client_area_ = new_client_area; + additional_client_areas_ = additional_client_areas; + FOR_EACH_OBSERVER(WindowObserver, observers_, + OnWindowClientAreaChanged(this, old_client_area, + old_additional_client_areas)); +} + +void Window::LocalSetDisplay(int64_t display_id) { + display_id_ = display_id; + // TODO(sad): Notify observers (of this window, and of the descendant windows) + // when a window moves from one display into another. https://crbug.com/614887 +} + +void Window::LocalSetParentDrawn(bool value) { + if (parent_drawn_ == value) + return; + + // As IsDrawn() is derived from |visible_| and |parent_drawn_|, only send + // drawn notification is the value of IsDrawn() is really changing. + if (IsDrawn() == value) { + parent_drawn_ = value; + return; + } + FOR_EACH_OBSERVER(WindowObserver, observers_, OnWindowDrawnChanging(this)); + parent_drawn_ = value; + FOR_EACH_OBSERVER(WindowObserver, observers_, OnWindowDrawnChanged(this)); +} + +void Window::LocalSetVisible(bool visible) { + if (visible_ == visible) + return; + + FOR_EACH_OBSERVER(WindowObserver, observers_, + OnWindowVisibilityChanging(this)); + visible_ = visible; + NotifyWindowVisibilityChanged(this); +} + +void Window::LocalSetOpacity(float opacity) { + if (opacity_ == opacity) + return; + + float old_opacity = opacity_; + opacity_ = opacity; + FOR_EACH_OBSERVER(WindowObserver, observers_, + OnWindowOpacityChanged(this, old_opacity, opacity_)); +} + +void Window::LocalSetPredefinedCursor(mojom::Cursor cursor_id) { + if (cursor_id_ == cursor_id) + return; + + cursor_id_ = cursor_id; + FOR_EACH_OBSERVER(WindowObserver, observers_, + OnWindowPredefinedCursorChanged(this, cursor_id)); +} + +void Window::LocalSetSharedProperty(const std::string& name, + const std::vector<uint8_t>* value) { + std::vector<uint8_t> old_value; + std::vector<uint8_t>* old_value_ptr = nullptr; + auto it = properties_.find(name); + if (it != properties_.end()) { + old_value = it->second; + old_value_ptr = &old_value; + + if (value && old_value == *value) + return; + } else if (!value) { + // This property isn't set in |properties_| and |value| is nullptr, so + // there's no change. + return; + } + + if (value) { + properties_[name] = *value; + } else if (it != properties_.end()) { + properties_.erase(it); + } + + FOR_EACH_OBSERVER( + WindowObserver, observers_, + OnWindowSharedPropertyChanged(this, name, old_value_ptr, value)); +} + +void Window::NotifyWindowStackingChanged() { + if (stacking_target_) { + Children::const_iterator window_i = std::find( + parent()->children().begin(), parent()->children().end(), this); + DCHECK(window_i != parent()->children().end()); + if (window_i != parent()->children().begin() && + (*(window_i - 1) == stacking_target_)) + return; + } + RestackTransientDescendants(this, &GetStackingTarget, + &ReorderWithoutNotification); +} + +void Window::NotifyWindowVisibilityChanged(Window* target) { + if (!NotifyWindowVisibilityChangedDown(target)) { + return; // |this| has been deleted. + } + NotifyWindowVisibilityChangedUp(target); +} + +bool Window::NotifyWindowVisibilityChangedAtReceiver(Window* target) { + // |this| may be deleted during a call to OnWindowVisibilityChanged() on one + // of the observers. We create an local observer for that. In that case we + // exit without further access to any members. + WindowTracker tracker; + tracker.Add(this); + FOR_EACH_OBSERVER(WindowObserver, observers_, + OnWindowVisibilityChanged(target)); + return tracker.Contains(this); +} + +bool Window::NotifyWindowVisibilityChangedDown(Window* target) { + if (!NotifyWindowVisibilityChangedAtReceiver(target)) + return false; // |this| was deleted. + std::set<const Window*> child_already_processed; + bool child_destroyed = false; + do { + child_destroyed = false; + for (Window::Children::const_iterator it = children_.begin(); + it != children_.end(); ++it) { + if (!child_already_processed.insert(*it).second) + continue; + if (!(*it)->NotifyWindowVisibilityChangedDown(target)) { + // |*it| was deleted, |it| is invalid and |children_| has changed. We + // exit the current for-loop and enter a new one. + child_destroyed = true; + break; + } + } + } while (child_destroyed); + return true; +} + +void Window::NotifyWindowVisibilityChangedUp(Window* target) { + // Start with the parent as we already notified |this| + // in NotifyWindowVisibilityChangedDown. + for (Window* window = parent(); window; window = window->parent()) { + bool ret = window->NotifyWindowVisibilityChangedAtReceiver(target); + DCHECK(ret); + } +} + +bool Window::PrepareForEmbed() { + if (!OwnsWindow(client_, this)) + return false; + + while (!children_.empty()) + RemoveChild(children_[0]); + return true; +} + +void Window::RemoveTransientWindowImpl(Window* transient_window) { + Window::Children::iterator it = std::find( + transient_children_.begin(), transient_children_.end(), transient_window); + if (it != transient_children_.end()) { + transient_children_.erase(it); + transient_window->transient_parent_ = nullptr; + } + // If |transient_window| and its former transient parent share the same + // parent, |transient_window| should be restacked properly so it is not among + // transient children of its former parent, anymore. + if (parent() == transient_window->parent()) + RestackTransientDescendants(this, &GetStackingTarget, + &ReorderWithoutNotification); + + // TOOD(fsamuel): We might want to notify observers here. +} + +// static +void Window::ReorderWithoutNotification(Window* window, + Window* relative, + mojom::OrderDirection direction) { + ReorderImpl(window, relative, direction, nullptr); +} + +// static +// Returns true if the order actually changed. +bool Window::ReorderImpl(Window* window, + Window* relative, + mojom::OrderDirection direction, + OrderChangedNotifier* notifier) { + DCHECK(relative); + DCHECK_NE(window, relative); + DCHECK_EQ(window->parent(), relative->parent()); + DCHECK(window->parent()); + + if (!AdjustStackingForTransientWindows(&window, &relative, &direction, + window->stacking_target_)) + return false; + + const size_t child_i = std::find(window->parent_->children_.begin(), + window->parent_->children_.end(), window) - + window->parent_->children_.begin(); + const size_t target_i = + std::find(window->parent_->children_.begin(), + window->parent_->children_.end(), relative) - + window->parent_->children_.begin(); + if ((direction == mojom::OrderDirection::ABOVE && child_i == target_i + 1) || + (direction == mojom::OrderDirection::BELOW && child_i + 1 == target_i)) { + return false; + } + + if (notifier) + notifier->NotifyWindowReordering(); + + const size_t dest_i = direction == mojom::OrderDirection::ABOVE + ? (child_i < target_i ? target_i : target_i + 1) + : (child_i < target_i ? target_i - 1 : target_i); + window->parent_->children_.erase(window->parent_->children_.begin() + + child_i); + window->parent_->children_.insert(window->parent_->children_.begin() + dest_i, + window); + + window->NotifyWindowStackingChanged(); + + if (notifier) + notifier->NotifyWindowReordered(); + + return true; +} + +// static +Window** Window::GetStackingTarget(Window* window) { + return &window->stacking_target_; +} +} // namespace mus diff --git a/chromium/components/mus/public/cpp/lib/window_observer.cc b/chromium/components/mus/public/cpp/lib/window_observer.cc new file mode 100644 index 00000000000..26e65078f77 --- /dev/null +++ b/chromium/components/mus/public/cpp/lib/window_observer.cc @@ -0,0 +1,18 @@ +// 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 "components/mus/public/cpp/window_observer.h" + +namespace mus { + +//////////////////////////////////////////////////////////////////////////////// +// WindowObserver, public: + +WindowObserver::TreeChangeParams::TreeChangeParams() + : target(nullptr), + old_parent(nullptr), + new_parent(nullptr), + receiver(nullptr) {} + +} // namespace mus diff --git a/chromium/components/mus/public/cpp/lib/window_private.cc b/chromium/components/mus/public/cpp/lib/window_private.cc new file mode 100644 index 00000000000..11c7ebeefae --- /dev/null +++ b/chromium/components/mus/public/cpp/lib/window_private.cc @@ -0,0 +1,31 @@ +// 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 "components/mus/public/cpp/lib/window_private.h" + +namespace mus { + +WindowPrivate::WindowPrivate(Window* window) : window_(window) { + CHECK(window); +} + +WindowPrivate::~WindowPrivate() {} + +// static +Window* WindowPrivate::LocalCreate() { + return new Window; +} + +void WindowPrivate::LocalSetSharedProperty(const std::string& name, + mojo::Array<uint8_t> new_data) { + std::vector<uint8_t> data; + std::vector<uint8_t>* data_ptr = nullptr; + if (!new_data.is_null()) { + data = new_data.To<std::vector<uint8_t>>(); + data_ptr = &data; + } + LocalSetSharedProperty(name, data_ptr); +} + +} // namespace mus diff --git a/chromium/components/mus/public/cpp/lib/window_private.h b/chromium/components/mus/public/cpp/lib/window_private.h new file mode 100644 index 00000000000..4b2640e8b37 --- /dev/null +++ b/chromium/components/mus/public/cpp/lib/window_private.h @@ -0,0 +1,100 @@ +// 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. + +#ifndef COMPONENTS_MUS_PUBLIC_CPP_LIB_WINDOW_PRIVATE_H_ +#define COMPONENTS_MUS_PUBLIC_CPP_LIB_WINDOW_PRIVATE_H_ + +#include <stdint.h> + +#include <vector> + +#include "base/macros.h" +#include "components/mus/public/cpp/window.h" +#include "mojo/public/cpp/bindings/array.h" + +namespace mus { + +// This class is a friend of a Window and contains functions to mutate internal +// state of Window. +class WindowPrivate { + public: + explicit WindowPrivate(Window* window); + ~WindowPrivate(); + + // Creates and returns a new Window. Caller owns the return value. + static Window* LocalCreate(); + + base::ObserverList<WindowObserver>* observers() { + return &window_->observers_; + } + + void ClearParent() { window_->parent_ = nullptr; } + + void ClearTransientParent() { window_->transient_parent_ = nullptr; } + + void set_visible(bool visible) { window_->visible_ = visible; } + + void set_parent_drawn(bool drawn) { window_->parent_drawn_ = drawn; } + bool parent_drawn() { return window_->parent_drawn_; } + + void set_server_id(Id id) { window_->server_id_ = id; } + Id server_id() { return window_->server_id_; } + + void set_client(WindowTreeClient* client) { + window_->client_ = client; + } + + void set_properties(const std::map<std::string, std::vector<uint8_t>>& data) { + window_->properties_ = data; + } + + void LocalSetDisplay(int64_t new_display) { + window_->LocalSetDisplay(new_display); + } + + void LocalDestroy() { window_->LocalDestroy(); } + void LocalAddChild(Window* child) { window_->LocalAddChild(child); } + void LocalRemoveChild(Window* child) { window_->LocalRemoveChild(child); } + void LocalAddTransientWindow(Window* child) { + window_->LocalAddTransientWindow(child); + } + void LocalRemoveTransientWindow(Window* child) { + window_->LocalRemoveTransientWindow(child); + } + void LocalUnsetModal() { window_->is_modal_ = false; } + void LocalReorder(Window* relative, mojom::OrderDirection direction) { + window_->LocalReorder(relative, direction); + } + void LocalSetBounds(const gfx::Rect& old_bounds, + const gfx::Rect& new_bounds) { + window_->LocalSetBounds(old_bounds, new_bounds); + } + void LocalSetClientArea( + const gfx::Insets& client_area, + const std::vector<gfx::Rect>& additional_client_areas) { + window_->LocalSetClientArea(client_area, additional_client_areas); + } + void LocalSetParentDrawn(bool drawn) { window_->LocalSetParentDrawn(drawn); } + void LocalSetVisible(bool visible) { window_->LocalSetVisible(visible); } + void LocalSetOpacity(float opacity) { window_->LocalSetOpacity(opacity); } + void LocalSetPredefinedCursor(mojom::Cursor cursor) { + window_->LocalSetPredefinedCursor(cursor); + } + void LocalSetSharedProperty(const std::string& name, + mojo::Array<uint8_t> new_data); + void LocalSetSharedProperty(const std::string& name, + const std::vector<uint8_t>* data) { + window_->LocalSetSharedProperty(name, data); + } + void NotifyWindowStackingChanged() { window_->NotifyWindowStackingChanged(); } + + private: + Window* window_; + + DISALLOW_COPY_AND_ASSIGN(WindowPrivate); +}; + +} // namespace mus + +#endif // COMPONENTS_MUS_PUBLIC_CPP_LIB_WINDOW_PRIVATE_H_ diff --git a/chromium/components/mus/public/cpp/lib/window_surface.cc b/chromium/components/mus/public/cpp/lib/window_surface.cc new file mode 100644 index 00000000000..1f0b840a102 --- /dev/null +++ b/chromium/components/mus/public/cpp/lib/window_surface.cc @@ -0,0 +1,69 @@ +// 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/mus/public/cpp/window_surface.h" + +#include "base/memory/ptr_util.h" +#include "components/mus/public/cpp/window_surface_client.h" + +namespace mus { + +// static +std::unique_ptr<WindowSurface> WindowSurface::Create( + std::unique_ptr<WindowSurfaceBinding>* surface_binding) { + mojom::SurfacePtr surface; + mojom::SurfaceClientPtr surface_client; + mojo::InterfaceRequest<mojom::SurfaceClient> surface_client_request = + GetProxy(&surface_client); + + surface_binding->reset(new WindowSurfaceBinding( + GetProxy(&surface), surface_client.PassInterface())); + return base::WrapUnique(new WindowSurface(surface.PassInterface(), + std::move(surface_client_request))); +} + +WindowSurface::~WindowSurface() {} + +void WindowSurface::BindToThread() { + DCHECK(!thread_checker_); + thread_checker_.reset(new base::ThreadChecker()); + surface_.Bind(std::move(surface_info_)); + client_binding_.reset(new mojo::Binding<mojom::SurfaceClient>( + this, std::move(client_request_))); +} + +void WindowSurface::SubmitCompositorFrame(cc::CompositorFrame frame, + const base::Closure& callback) { + DCHECK(thread_checker_); + DCHECK(thread_checker_->CalledOnValidThread()); + if (!surface_) + return; + surface_->SubmitCompositorFrame(std::move(frame), callback); +} + +WindowSurface::WindowSurface( + mojo::InterfacePtrInfo<mojom::Surface> surface_info, + mojo::InterfaceRequest<mojom::SurfaceClient> client_request) + : client_(nullptr), + surface_info_(std::move(surface_info)), + client_request_(std::move(client_request)) {} + +void WindowSurface::ReturnResources( + mojo::Array<cc::ReturnedResource> resources) { + DCHECK(thread_checker_); + DCHECK(thread_checker_->CalledOnValidThread()); + if (!client_) + return; + client_->OnResourcesReturned(this, std::move(resources)); +} + +WindowSurfaceBinding::~WindowSurfaceBinding() {} + +WindowSurfaceBinding::WindowSurfaceBinding( + mojo::InterfaceRequest<mojom::Surface> surface_request, + mojo::InterfacePtrInfo<mojom::SurfaceClient> surface_client) + : surface_request_(std::move(surface_request)), + surface_client_(std::move(surface_client)) {} + +} // namespace mus diff --git a/chromium/components/mus/public/cpp/lib/window_tree_client.cc b/chromium/components/mus/public/cpp/lib/window_tree_client.cc new file mode 100644 index 00000000000..7a43101ff6a --- /dev/null +++ b/chromium/components/mus/public/cpp/lib/window_tree_client.cc @@ -0,0 +1,1181 @@ +// 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 "components/mus/public/cpp/window_tree_client.h" + +#include <stddef.h> + +#include <string> +#include <utility> +#include <vector> + +#include "base/bind.h" +#include "base/memory/ptr_util.h" +#include "components/mus/common/util.h" +#include "components/mus/public/cpp/input_event_handler.h" +#include "components/mus/public/cpp/lib/in_flight_change.h" +#include "components/mus/public/cpp/lib/window_private.h" +#include "components/mus/public/cpp/window_manager_delegate.h" +#include "components/mus/public/cpp/window_observer.h" +#include "components/mus/public/cpp/window_tracker.h" +#include "components/mus/public/cpp/window_tree_client_delegate.h" +#include "components/mus/public/cpp/window_tree_client_observer.h" +#include "components/mus/public/interfaces/window_manager_window_tree_factory.mojom.h" +#include "services/shell/public/cpp/connector.h" +#include "ui/display/mojo/display_type_converters.h" +#include "ui/events/event.h" +#include "ui/gfx/geometry/insets.h" +#include "ui/gfx/geometry/size.h" + +namespace mus { + +void DeleteWindowTreeClient(WindowTreeClient* client) { delete client; } + +Id MakeTransportId(ClientSpecificId client_id, ClientSpecificId local_id) { + return (client_id << 16) | local_id; +} + +Id server_id(Window* window) { + return WindowPrivate(window).server_id(); +} + +// Helper called to construct a local window object from transport data. +Window* AddWindowToClient(WindowTreeClient* client, + Window* parent, + const mojom::WindowDataPtr& window_data) { + // We don't use the ctor that takes a WindowTreeClient here, since it will + // call back to the service and attempt to create a new window. + Window* window = WindowPrivate::LocalCreate(); + WindowPrivate private_window(window); + private_window.set_client(client); + private_window.set_server_id(window_data->window_id); + private_window.set_visible(window_data->visible); + private_window.set_properties( + window_data->properties + .To<std::map<std::string, std::vector<uint8_t>>>()); + client->AddWindow(window); + private_window.LocalSetBounds(gfx::Rect(), window_data->bounds); + if (parent) + WindowPrivate(parent).LocalAddChild(window); + return window; +} + +Window* BuildWindowTree(WindowTreeClient* client, + const mojo::Array<mojom::WindowDataPtr>& windows, + Window* initial_parent) { + std::vector<Window*> parents; + Window* root = NULL; + Window* last_window = NULL; + if (initial_parent) + parents.push_back(initial_parent); + for (size_t i = 0; i < windows.size(); ++i) { + if (last_window && windows[i]->parent_id == server_id(last_window)) { + parents.push_back(last_window); + } else if (!parents.empty()) { + while (server_id(parents.back()) != windows[i]->parent_id) + parents.pop_back(); + } + Window* window = AddWindowToClient( + client, !parents.empty() ? parents.back() : NULL, windows[i]); + if (!last_window) + root = window; + last_window = window; + } + return root; +} + +WindowTreeClient::WindowTreeClient( + WindowTreeClientDelegate* delegate, + WindowManagerDelegate* window_manager_delegate, + mojo::InterfaceRequest<mojom::WindowTreeClient> request) + : client_id_(0), + next_window_id_(1), + next_change_id_(1), + delegate_(delegate), + window_manager_delegate_(window_manager_delegate), + capture_window_(nullptr), + focused_window_(nullptr), + binding_(this), + tree_(nullptr), + delete_on_no_roots_(!window_manager_delegate), + in_destructor_(false), + weak_factory_(this) { + // Allow for a null request in tests. + if (request.is_pending()) + binding_.Bind(std::move(request)); + if (window_manager_delegate) + window_manager_delegate->SetWindowManagerClient(this); +} + +WindowTreeClient::~WindowTreeClient() { + in_destructor_ = true; + + std::vector<Window*> non_owned; + WindowTracker tracker; + while (!windows_.empty()) { + IdToWindowMap::iterator it = windows_.begin(); + if (OwnsWindow(it->second)) { + it->second->Destroy(); + } else { + tracker.Add(it->second); + windows_.erase(it); + } + } + + // Delete the non-owned windows last. In the typical case these are roots. The + // exception is the window manager and embed roots, which may know about + // other random windows that it doesn't own. + // NOTE: we manually delete as we're a friend. + while (!tracker.windows().empty()) + delete tracker.windows().front(); + + FOR_EACH_OBSERVER(WindowTreeClientObserver, observers_, + OnWillDestroyClient(this)); + + delegate_->OnWindowTreeClientDestroyed(this); +} + +void WindowTreeClient::ConnectViaWindowTreeFactory( + shell::Connector* connector) { + // Clients created with no root shouldn't delete automatically. + delete_on_no_roots_ = false; + + // The client id doesn't really matter, we use 101 purely for debugging. + client_id_ = 101; + + mojom::WindowTreeFactoryPtr factory; + connector->ConnectToInterface("mojo:mus", &factory); + mojom::WindowTreePtr window_tree; + factory->CreateWindowTree(GetProxy(&window_tree), + binding_.CreateInterfacePtrAndBind()); + SetWindowTree(std::move(window_tree)); +} + +void WindowTreeClient::ConnectAsWindowManager(shell::Connector* connector) { + DCHECK(window_manager_delegate_); + + mojom::WindowManagerWindowTreeFactoryPtr factory; + connector->ConnectToInterface("mojo:mus", &factory); + mojom::WindowTreePtr window_tree; + factory->CreateWindowTree(GetProxy(&window_tree), + binding_.CreateInterfacePtrAndBind()); + SetWindowTree(std::move(window_tree)); +} + +void WindowTreeClient::WaitForEmbed() { + DCHECK(roots_.empty()); + // OnEmbed() is the first function called. + binding_.WaitForIncomingMethodCall(); + // TODO(sky): deal with pipe being closed before we get OnEmbed(). +} + +void WindowTreeClient::DestroyWindow(Window* window) { + DCHECK(tree_); + const uint32_t change_id = ScheduleInFlightChange(base::WrapUnique( + new CrashInFlightChange(window, ChangeType::DELETE_WINDOW))); + tree_->DeleteWindow(change_id, server_id(window)); +} + +void WindowTreeClient::AddChild(Window* parent, Id child_id) { + DCHECK(tree_); + const uint32_t change_id = ScheduleInFlightChange( + base::WrapUnique(new CrashInFlightChange(parent, ChangeType::ADD_CHILD))); + tree_->AddWindow(change_id, parent->server_id(), child_id); +} + +void WindowTreeClient::RemoveChild(Window* parent, Id child_id) { + DCHECK(tree_); + const uint32_t change_id = ScheduleInFlightChange(base::WrapUnique( + new CrashInFlightChange(parent, ChangeType::REMOVE_CHILD))); + tree_->RemoveWindowFromParent(change_id, child_id); +} + +void WindowTreeClient::AddTransientWindow(Window* window, + Id transient_window_id) { + DCHECK(tree_); + const uint32_t change_id = ScheduleInFlightChange(base::WrapUnique( + new CrashInFlightChange(window, ChangeType::ADD_TRANSIENT_WINDOW))); + tree_->AddTransientWindow(change_id, server_id(window), transient_window_id); +} + +void WindowTreeClient::RemoveTransientWindowFromParent(Window* window) { + DCHECK(tree_); + const uint32_t change_id = + ScheduleInFlightChange(base::WrapUnique(new CrashInFlightChange( + window, ChangeType::REMOVE_TRANSIENT_WINDOW_FROM_PARENT))); + tree_->RemoveTransientWindowFromParent(change_id, server_id(window)); +} + +void WindowTreeClient::SetModal(Window* window) { + DCHECK(tree_); + const uint32_t change_id = ScheduleInFlightChange( + base::WrapUnique(new InFlightSetModalChange(window))); + tree_->SetModal(change_id, server_id(window)); +} + +void WindowTreeClient::Reorder(Window* window, + Id relative_window_id, + mojom::OrderDirection direction) { + DCHECK(tree_); + const uint32_t change_id = ScheduleInFlightChange( + base::WrapUnique(new CrashInFlightChange(window, ChangeType::REORDER))); + tree_->ReorderWindow(change_id, server_id(window), relative_window_id, + direction); +} + +bool WindowTreeClient::OwnsWindow(Window* window) const { + // Windows created via CreateTopLevelWindow() are not owned by us, but have + // our client id. + return HiWord(server_id(window)) == client_id_ && + roots_.count(window) == 0; +} + +void WindowTreeClient::SetBounds(Window* window, + const gfx::Rect& old_bounds, + const gfx::Rect& bounds) { + DCHECK(tree_); + const uint32_t change_id = ScheduleInFlightChange( + base::WrapUnique(new InFlightBoundsChange(window, old_bounds))); + tree_->SetWindowBounds(change_id, server_id(window), bounds); +} + +void WindowTreeClient::SetCapture(Window* window) { + // In order for us to get here we had to have exposed a window, which implies + // we got a client. + DCHECK(tree_); + if (capture_window_ == window) + return; + const uint32_t change_id = ScheduleInFlightChange( + base::WrapUnique(new InFlightCaptureChange(this, capture_window_))); + tree_->SetCapture(change_id, server_id(window)); + LocalSetCapture(window); +} + +void WindowTreeClient::ReleaseCapture(Window* window) { + // In order for us to get here we had to have exposed a window, which implies + // we got a client. + DCHECK(tree_); + if (capture_window_ != window) + return; + const uint32_t change_id = ScheduleInFlightChange( + base::WrapUnique(new InFlightCaptureChange(this, window))); + tree_->ReleaseCapture(change_id, server_id(window)); + LocalSetCapture(nullptr); +} + +void WindowTreeClient::SetClientArea( + Id window_id, + const gfx::Insets& client_area, + const std::vector<gfx::Rect>& additional_client_areas) { + DCHECK(tree_); + tree_->SetClientArea(window_id, client_area, additional_client_areas); +} + +void WindowTreeClient::SetHitTestMask(Id window_id, const gfx::Rect& mask) { + DCHECK(tree_); + tree_->SetHitTestMask(window_id, mask); +} + +void WindowTreeClient::ClearHitTestMask(Id window_id) { + DCHECK(tree_); + tree_->SetHitTestMask(window_id, {}); +} + +void WindowTreeClient::SetFocus(Window* window) { + // In order for us to get here we had to have exposed a window, which implies + // we got a client. + DCHECK(tree_); + const uint32_t change_id = ScheduleInFlightChange( + base::WrapUnique(new InFlightFocusChange(this, focused_window_))); + tree_->SetFocus(change_id, window ? server_id(window) : 0); + LocalSetFocus(window); +} + +void WindowTreeClient::SetCanFocus(Id window_id, bool can_focus) { + DCHECK(tree_); + tree_->SetCanFocus(window_id, can_focus); +} + +void WindowTreeClient::SetPredefinedCursor(Id window_id, + mus::mojom::Cursor cursor_id) { + DCHECK(tree_); + + Window* window = GetWindowByServerId(window_id); + if (!window) + return; + + // We make an inflight change thing here. + const uint32_t change_id = ScheduleInFlightChange(base::WrapUnique( + new InFlightPredefinedCursorChange(window, window->predefined_cursor()))); + tree_->SetPredefinedCursor(change_id, window_id, cursor_id); +} + +void WindowTreeClient::SetVisible(Window* window, bool visible) { + DCHECK(tree_); + const uint32_t change_id = ScheduleInFlightChange( + base::WrapUnique(new InFlightVisibleChange(window, !visible))); + tree_->SetWindowVisibility(change_id, server_id(window), visible); +} + +void WindowTreeClient::SetOpacity(Window* window, float opacity) { + DCHECK(tree_); + const uint32_t change_id = ScheduleInFlightChange( + base::WrapUnique(new InFlightOpacityChange(window, window->opacity()))); + tree_->SetWindowOpacity(change_id, server_id(window), opacity); +} + +void WindowTreeClient::SetProperty(Window* window, + const std::string& name, + mojo::Array<uint8_t> data) { + DCHECK(tree_); + + mojo::Array<uint8_t> old_value(nullptr); + if (window->HasSharedProperty(name)) + old_value = mojo::Array<uint8_t>::From(window->properties_[name]); + + const uint32_t change_id = ScheduleInFlightChange( + base::WrapUnique(new InFlightPropertyChange(window, name, old_value))); + tree_->SetWindowProperty(change_id, server_id(window), mojo::String(name), + std::move(data)); +} + +void WindowTreeClient::SetWindowTextInputState( + Id window_id, + mojo::TextInputStatePtr state) { + DCHECK(tree_); + tree_->SetWindowTextInputState(window_id, std::move(state)); +} + +void WindowTreeClient::SetImeVisibility(Id window_id, + bool visible, + mojo::TextInputStatePtr state) { + DCHECK(tree_); + tree_->SetImeVisibility(window_id, visible, std::move(state)); +} + +void WindowTreeClient::Embed(Id window_id, + mojom::WindowTreeClientPtr client, + uint32_t flags, + const mojom::WindowTree::EmbedCallback& callback) { + DCHECK(tree_); + tree_->Embed(window_id, std::move(client), flags, callback); +} + +void WindowTreeClient::RequestClose(Window* window) { + if (window_manager_internal_client_) + window_manager_internal_client_->WmRequestClose(server_id(window)); +} + +void WindowTreeClient::AttachSurface( + Id window_id, + mojom::SurfaceType type, + mojo::InterfaceRequest<mojom::Surface> surface, + mojom::SurfaceClientPtr client) { + DCHECK(tree_); + tree_->AttachSurface(window_id, type, std::move(surface), std::move(client)); +} + +void WindowTreeClient::LocalSetCapture(Window* window) { + if (capture_window_ == window) + return; + Window* lost_capture = capture_window_; + capture_window_ = window; + if (lost_capture) { + FOR_EACH_OBSERVER(WindowObserver, *WindowPrivate(lost_capture).observers(), + OnWindowLostCapture(lost_capture)); + } +} + +void WindowTreeClient::LocalSetFocus(Window* focused) { + Window* blurred = focused_window_; + // Update |focused_window_| before calling any of the observers, so that the + // observers get the correct result from calling |Window::HasFocus()|, + // |WindowTreeClient::GetFocusedWindow()| etc. + focused_window_ = focused; + if (blurred) { + FOR_EACH_OBSERVER(WindowObserver, *WindowPrivate(blurred).observers(), + OnWindowFocusChanged(focused, blurred)); + } + if (focused) { + FOR_EACH_OBSERVER(WindowObserver, *WindowPrivate(focused).observers(), + OnWindowFocusChanged(focused, blurred)); + } + FOR_EACH_OBSERVER(WindowTreeClientObserver, observers_, + OnWindowTreeFocusChanged(focused, blurred)); +} + +void WindowTreeClient::AddWindow(Window* window) { + DCHECK(windows_.find(server_id(window)) == windows_.end()); + windows_[server_id(window)] = window; +} + +void WindowTreeClient::OnWindowDestroying(Window* window) { + if (window == capture_window_) { + // Normally the queue updates itself upon window destruction. However since + // |window| is being destroyed, it will not be possible to notify its + // observers of the lost capture. Update local state now. + LocalSetCapture(nullptr); + } + // For |focused_window_| window destruction clears the entire focus state. +} + +void WindowTreeClient::OnWindowDestroyed(Window* window) { + windows_.erase(server_id(window)); + + for (auto& entry : embedded_windows_) { + auto it = entry.second.find(window); + if (it != entry.second.end()) { + entry.second.erase(it); + break; + } + } + + // Remove any InFlightChanges associated with the window. + std::set<uint32_t> in_flight_change_ids_to_remove; + for (const auto& pair : in_flight_map_) { + if (pair.second->window() == window) + in_flight_change_ids_to_remove.insert(pair.first); + } + for (auto change_id : in_flight_change_ids_to_remove) + in_flight_map_.erase(change_id); + + if (roots_.erase(window) > 0 && roots_.empty() && delete_on_no_roots_ && + !in_destructor_) { + delete this; + } +} + +Window* WindowTreeClient::GetWindowByServerId(Id id) { + IdToWindowMap::const_iterator it = windows_.find(id); + return it != windows_.end() ? it->second : NULL; +} + +InFlightChange* WindowTreeClient::GetOldestInFlightChangeMatching( + const InFlightChange& change) { + for (const auto& pair : in_flight_map_) { + if (pair.second->window() == change.window() && + pair.second->change_type() == change.change_type() && + pair.second->Matches(change)) { + return pair.second.get(); + } + } + return nullptr; +} + +uint32_t WindowTreeClient::ScheduleInFlightChange( + std::unique_ptr<InFlightChange> change) { + DCHECK(!change->window() || + windows_.count(change->window()->server_id()) > 0); + const uint32_t change_id = next_change_id_++; + in_flight_map_[change_id] = std::move(change); + return change_id; +} + +bool WindowTreeClient::ApplyServerChangeToExistingInFlightChange( + const InFlightChange& change) { + InFlightChange* existing_change = GetOldestInFlightChangeMatching(change); + if (!existing_change) + return false; + + existing_change->SetRevertValueFrom(change); + return true; +} + +Window* WindowTreeClient::NewWindowImpl( + NewWindowType type, + const Window::SharedProperties* properties) { + DCHECK(tree_); + Window* window = + new Window(this, MakeTransportId(client_id_, next_window_id_++)); + if (properties) + window->properties_ = *properties; + AddWindow(window); + + const uint32_t change_id = ScheduleInFlightChange(base::WrapUnique( + new CrashInFlightChange(window, type == NewWindowType::CHILD + ? ChangeType::NEW_WINDOW + : ChangeType::NEW_TOP_LEVEL_WINDOW))); + mojo::Map<mojo::String, mojo::Array<uint8_t>> transport_properties; + if (properties) { + transport_properties = + mojo::Map<mojo::String, mojo::Array<uint8_t>>::From(*properties); + } + if (type == NewWindowType::CHILD) { + tree_->NewWindow(change_id, server_id(window), + std::move(transport_properties)); + } else { + roots_.insert(window); + tree_->NewTopLevelWindow(change_id, server_id(window), + std::move(transport_properties)); + } + return window; +} + +void WindowTreeClient::SetWindowTree(mojom::WindowTreePtr window_tree_ptr) { + tree_ptr_ = std::move(window_tree_ptr); + tree_ = tree_ptr_.get(); + + tree_ptr_->GetCursorLocationMemory( + base::Bind(&WindowTreeClient::OnReceivedCursorLocationMemory, + weak_factory_.GetWeakPtr())); + + tree_ptr_.set_connection_error_handler(base::Bind( + &WindowTreeClient::OnConnectionLost, weak_factory_.GetWeakPtr())); + + if (window_manager_delegate_) { + tree_ptr_->GetWindowManagerClient(GetProxy(&window_manager_internal_client_, + tree_ptr_.associated_group())); + } +} + +void WindowTreeClient::OnConnectionLost() { + delete this; +} + +void WindowTreeClient::OnEmbedImpl(mojom::WindowTree* window_tree, + ClientSpecificId client_id, + mojom::WindowDataPtr root_data, + int64_t display_id, + Id focused_window_id, + bool drawn) { + // WARNING: this is only called if WindowTreeClient was created as the + // result of an embedding. + tree_ = window_tree; + client_id_ = client_id; + + DCHECK(roots_.empty()); + Window* root = AddWindowToClient(this, nullptr, root_data); + WindowPrivate(root).LocalSetDisplay(display_id); + roots_.insert(root); + + focused_window_ = GetWindowByServerId(focused_window_id); + + WindowPrivate(root).LocalSetParentDrawn(drawn); + + delegate_->OnEmbed(root); + + if (focused_window_) { + FOR_EACH_OBSERVER(WindowTreeClientObserver, observers_, + OnWindowTreeFocusChanged(focused_window_, nullptr)); + } +} + +void WindowTreeClient::WmNewDisplayAddedImpl(const display::Display& display, + mojom::WindowDataPtr root_data, + bool parent_drawn) { + DCHECK(window_manager_delegate_); + + Window* root = AddWindowToClient(this, nullptr, root_data); + WindowPrivate(root).LocalSetDisplay(display.id()); + WindowPrivate(root).LocalSetParentDrawn(parent_drawn); + roots_.insert(root); + + window_manager_delegate_->OnWmNewDisplay(root, display); +} + +void WindowTreeClient::OnReceivedCursorLocationMemory( + mojo::ScopedSharedBufferHandle handle) { + cursor_location_mapping_ = handle->Map(sizeof(base::subtle::Atomic32)); + DCHECK(cursor_location_mapping_); +} + +//////////////////////////////////////////////////////////////////////////////// +// WindowTreeClient, WindowTreeClient implementation: + +void WindowTreeClient::SetDeleteOnNoRoots(bool value) { + delete_on_no_roots_ = value; +} + +const std::set<Window*>& WindowTreeClient::GetRoots() { + return roots_; +} + +Window* WindowTreeClient::GetFocusedWindow() { + return focused_window_; +} + +void WindowTreeClient::ClearFocus() { + if (!focused_window_) + return; + + SetFocus(nullptr); +} + +gfx::Point WindowTreeClient::GetCursorScreenPoint() { + // We raced initialization. Return (0, 0). + if (!cursor_location_memory()) + return gfx::Point(); + + base::subtle::Atomic32 location = + base::subtle::NoBarrier_Load(cursor_location_memory()); + return gfx::Point(static_cast<int16_t>(location >> 16), + static_cast<int16_t>(location & 0xFFFF)); +} + +void WindowTreeClient::SetEventObserver(mojom::EventMatcherPtr matcher) { + if (matcher.is_null()) { + has_event_observer_ = false; + tree_->SetEventObserver(nullptr, 0u); + } else { + has_event_observer_ = true; + event_observer_id_++; + tree_->SetEventObserver(std::move(matcher), event_observer_id_); + } +} + +Window* WindowTreeClient::NewWindow( + const Window::SharedProperties* properties) { + return NewWindowImpl(NewWindowType::CHILD, properties); +} + +Window* WindowTreeClient::NewTopLevelWindow( + const Window::SharedProperties* properties) { + Window* window = NewWindowImpl(NewWindowType::TOP_LEVEL, properties); + // Assume newly created top level windows are drawn by default, otherwise + // requests to focus will fail. We will get the real value in + // OnTopLevelCreated(). + window->LocalSetParentDrawn(true); + return window; +} + +#if !defined(NDEBUG) +std::string WindowTreeClient::GetDebugWindowHierarchy() const { + std::string result; + for (Window* root : roots_) + BuildDebugInfo(std::string(), root, &result); + return result; +} + +void WindowTreeClient::BuildDebugInfo(const std::string& depth, + Window* window, + std::string* result) const { + std::string name = window->GetName(); + *result += base::StringPrintf( + "%sid=%d visible=%s bounds=%d,%d %dx%d %s\n", depth.c_str(), + window->server_id(), window->visible() ? "true" : "false", + window->bounds().x(), window->bounds().y(), window->bounds().width(), + window->bounds().height(), !name.empty() ? name.c_str() : "(no name)"); + for (Window* child : window->children()) + BuildDebugInfo(depth + " ", child, result); +} +#endif // !defined(NDEBUG) + +//////////////////////////////////////////////////////////////////////////////// +// WindowTreeClient, WindowTreeClient implementation: + +void WindowTreeClient::AddObserver(WindowTreeClientObserver* observer) { + observers_.AddObserver(observer); +} + +void WindowTreeClient::RemoveObserver(WindowTreeClientObserver* observer) { + observers_.RemoveObserver(observer); +} + +void WindowTreeClient::OnEmbed(ClientSpecificId client_id, + mojom::WindowDataPtr root_data, + mojom::WindowTreePtr tree, + int64_t display_id, + Id focused_window_id, + bool drawn) { + DCHECK(!tree_ptr_); + tree_ptr_ = std::move(tree); + tree_ptr_.set_connection_error_handler( + base::Bind(&DeleteWindowTreeClient, this)); + + if (window_manager_delegate_) { + tree_ptr_->GetWindowManagerClient(GetProxy(&window_manager_internal_client_, + tree_ptr_.associated_group())); + } + + OnEmbedImpl(tree_ptr_.get(), client_id, std::move(root_data), display_id, + focused_window_id, drawn); +} + +void WindowTreeClient::OnEmbeddedAppDisconnected(Id window_id) { + Window* window = GetWindowByServerId(window_id); + if (window) { + FOR_EACH_OBSERVER(WindowObserver, *WindowPrivate(window).observers(), + OnWindowEmbeddedAppDisconnected(window)); + } +} + +void WindowTreeClient::OnUnembed(Id window_id) { + Window* window = GetWindowByServerId(window_id); + if (!window) + return; + + delegate_->OnUnembed(window); + WindowPrivate(window).LocalDestroy(); +} + +void WindowTreeClient::OnLostCapture(Id window_id) { + Window* window = GetWindowByServerId(window_id); + if (!window) + return; + + InFlightCaptureChange reset_change(this, nullptr); + if (ApplyServerChangeToExistingInFlightChange(reset_change)) + return; + + LocalSetCapture(nullptr); +} + +void WindowTreeClient::OnTopLevelCreated(uint32_t change_id, + mojom::WindowDataPtr data, + int64_t display_id, + bool drawn) { + // The server ack'd the top level window we created and supplied the state + // of the window at the time the server created it. For properties we do not + // have changes in flight for we can update them immediately. For properties + // with changes in flight we set the revert value from the server. + + if (!in_flight_map_.count(change_id)) { + // The window may have been destroyed locally before the server could finish + // creating the window, and before the server received the notification that + // the window has been destroyed. + return; + } + std::unique_ptr<InFlightChange> change(std::move(in_flight_map_[change_id])); + in_flight_map_.erase(change_id); + + Window* window = change->window(); + WindowPrivate window_private(window); + + // Drawn state and display-id always come from the server (they can't be + // modified locally). + window_private.LocalSetParentDrawn(drawn); + window_private.LocalSetDisplay(display_id); + + // The default visibilty is false, we only need update visibility if it + // differs from that. + if (data->visible) { + InFlightVisibleChange visible_change(window, data->visible); + InFlightChange* current_change = + GetOldestInFlightChangeMatching(visible_change); + if (current_change) + current_change->SetRevertValueFrom(visible_change); + else + window_private.LocalSetVisible(true); + } + + const gfx::Rect bounds(data->bounds); + { + InFlightBoundsChange bounds_change(window, bounds); + InFlightChange* current_change = + GetOldestInFlightChangeMatching(bounds_change); + if (current_change) + current_change->SetRevertValueFrom(bounds_change); + else if (window->bounds() != bounds) + window_private.LocalSetBounds(window->bounds(), bounds); + } + + // There is currently no API to bulk set properties, so we iterate over each + // property individually. + Window::SharedProperties properties = + data->properties.To<std::map<std::string, std::vector<uint8_t>>>(); + for (const auto& pair : properties) { + InFlightPropertyChange property_change( + window, pair.first, mojo::Array<uint8_t>::From(pair.second)); + InFlightChange* current_change = + GetOldestInFlightChangeMatching(property_change); + if (current_change) + current_change->SetRevertValueFrom(property_change); + else + window_private.LocalSetSharedProperty(pair.first, &(pair.second)); + } + + // Top level windows should not have a parent. + DCHECK_EQ(0u, data->parent_id); +} + +void WindowTreeClient::OnWindowBoundsChanged(Id window_id, + const gfx::Rect& old_bounds, + const gfx::Rect& new_bounds) { + Window* window = GetWindowByServerId(window_id); + if (!window) + return; + + InFlightBoundsChange new_change(window, new_bounds); + if (ApplyServerChangeToExistingInFlightChange(new_change)) + return; + + WindowPrivate(window).LocalSetBounds(old_bounds, new_bounds); +} + +void WindowTreeClient::OnClientAreaChanged( + uint32_t window_id, + const gfx::Insets& new_client_area, + mojo::Array<gfx::Rect> new_additional_client_areas) { + Window* window = GetWindowByServerId(window_id); + if (window) { + WindowPrivate(window).LocalSetClientArea( + new_client_area, + new_additional_client_areas.To<std::vector<gfx::Rect>>()); + } +} + +void WindowTreeClient::OnTransientWindowAdded( + uint32_t window_id, + uint32_t transient_window_id) { + Window* window = GetWindowByServerId(window_id); + Window* transient_window = GetWindowByServerId(transient_window_id); + // window or transient_window or both may be null if a local delete occurs + // with an in flight add from the server. + if (window && transient_window) + WindowPrivate(window).LocalAddTransientWindow(transient_window); +} + +void WindowTreeClient::OnTransientWindowRemoved( + uint32_t window_id, + uint32_t transient_window_id) { + Window* window = GetWindowByServerId(window_id); + Window* transient_window = GetWindowByServerId(transient_window_id); + // window or transient_window or both may be null if a local delete occurs + // with an in flight delete from the server. + if (window && transient_window) + WindowPrivate(window).LocalRemoveTransientWindow(transient_window); +} + +void WindowTreeClient::OnWindowHierarchyChanged( + Id window_id, + Id old_parent_id, + Id new_parent_id, + mojo::Array<mojom::WindowDataPtr> windows) { + Window* initial_parent = + windows.size() ? GetWindowByServerId(windows[0]->parent_id) : NULL; + + const bool was_window_known = GetWindowByServerId(window_id) != nullptr; + + BuildWindowTree(this, windows, initial_parent); + + // If the window was not known, then BuildWindowTree() will have created it + // and parented the window. + if (!was_window_known) + return; + + Window* new_parent = GetWindowByServerId(new_parent_id); + Window* old_parent = GetWindowByServerId(old_parent_id); + Window* window = GetWindowByServerId(window_id); + if (new_parent) + WindowPrivate(new_parent).LocalAddChild(window); + else + WindowPrivate(old_parent).LocalRemoveChild(window); +} + +void WindowTreeClient::OnWindowReordered(Id window_id, + Id relative_window_id, + mojom::OrderDirection direction) { + Window* window = GetWindowByServerId(window_id); + Window* relative_window = GetWindowByServerId(relative_window_id); + if (window && relative_window) + WindowPrivate(window).LocalReorder(relative_window, direction); +} + +void WindowTreeClient::OnWindowDeleted(Id window_id) { + Window* window = GetWindowByServerId(window_id); + if (window) + WindowPrivate(window).LocalDestroy(); +} + +Window* WindowTreeClient::GetCaptureWindow() { + return capture_window_; +} + +void WindowTreeClient::OnWindowVisibilityChanged(Id window_id, + bool visible) { + Window* window = GetWindowByServerId(window_id); + if (!window) + return; + + InFlightVisibleChange new_change(window, visible); + if (ApplyServerChangeToExistingInFlightChange(new_change)) + return; + + WindowPrivate(window).LocalSetVisible(visible); +} + +void WindowTreeClient::OnWindowOpacityChanged(Id window_id, + float old_opacity, + float new_opacity) { + Window* window = GetWindowByServerId(window_id); + if (!window) + return; + + InFlightOpacityChange new_change(window, new_opacity); + if (ApplyServerChangeToExistingInFlightChange(new_change)) + return; + + WindowPrivate(window).LocalSetOpacity(new_opacity); +} + +void WindowTreeClient::OnWindowParentDrawnStateChanged(Id window_id, + bool drawn) { + Window* window = GetWindowByServerId(window_id); + if (window) + WindowPrivate(window).LocalSetParentDrawn(drawn); +} + +void WindowTreeClient::OnWindowSharedPropertyChanged( + Id window_id, + const mojo::String& name, + mojo::Array<uint8_t> new_data) { + Window* window = GetWindowByServerId(window_id); + if (!window) + return; + + InFlightPropertyChange new_change(window, name, new_data); + if (ApplyServerChangeToExistingInFlightChange(new_change)) + return; + + WindowPrivate(window).LocalSetSharedProperty(name, std::move(new_data)); +} + +void WindowTreeClient::OnWindowInputEvent(uint32_t event_id, + Id window_id, + std::unique_ptr<ui::Event> event, + uint32_t event_observer_id) { + DCHECK(event); + Window* window = GetWindowByServerId(window_id); // May be null. + + // Non-zero event_observer_id means it matched an event observer on the + // server. + if (event_observer_id != 0 && has_event_observer_ && + event_observer_id == event_observer_id_) + delegate_->OnEventObserved(*event.get(), window); + + if (!window || !window->input_event_handler_) { + tree_->OnWindowInputEventAck(event_id, mojom::EventResult::UNHANDLED); + return; + } + + std::unique_ptr<base::Callback<void(mojom::EventResult)>> ack_callback( + new base::Callback<void(mojom::EventResult)>( + base::Bind(&mojom::WindowTree::OnWindowInputEventAck, + base::Unretained(tree_), event_id))); + + // TODO(moshayedi): crbug.com/617222. No need to convert to ui::MouseEvent or + // ui::TouchEvent once we have proper support for pointer events. + if (event->IsMousePointerEvent()) { + window->input_event_handler_->OnWindowInputEvent( + window, ui::MouseEvent(*event->AsPointerEvent()), &ack_callback); + } else if (event->IsTouchPointerEvent()) { + window->input_event_handler_->OnWindowInputEvent( + window, ui::TouchEvent(*event->AsPointerEvent()), &ack_callback); + } else { + window->input_event_handler_->OnWindowInputEvent(window, *event.get(), + &ack_callback); + } + + // The handler did not take ownership of the callback, so we send the ack, + // marking the event as not consumed. + if (ack_callback) + ack_callback->Run(mojom::EventResult::UNHANDLED); +} + +void WindowTreeClient::OnEventObserved(std::unique_ptr<ui::Event> event, + uint32_t event_observer_id) { + DCHECK(event); + if (has_event_observer_ && event_observer_id == event_observer_id_) + delegate_->OnEventObserved(*event.get(), nullptr /* target */); +} + +void WindowTreeClient::OnWindowFocused(Id focused_window_id) { + Window* focused_window = GetWindowByServerId(focused_window_id); + InFlightFocusChange new_change(this, focused_window); + if (ApplyServerChangeToExistingInFlightChange(new_change)) + return; + + LocalSetFocus(focused_window); +} + +void WindowTreeClient::OnWindowPredefinedCursorChanged( + Id window_id, + mojom::Cursor cursor) { + Window* window = GetWindowByServerId(window_id); + if (!window) + return; + + InFlightPredefinedCursorChange new_change(window, cursor); + if (ApplyServerChangeToExistingInFlightChange(new_change)) + return; + + WindowPrivate(window).LocalSetPredefinedCursor(cursor); +} + +void WindowTreeClient::OnChangeCompleted(uint32_t change_id, bool success) { + std::unique_ptr<InFlightChange> change(std::move(in_flight_map_[change_id])); + in_flight_map_.erase(change_id); + if (!change) + return; + + if (!success) + change->ChangeFailed(); + + InFlightChange* next_change = GetOldestInFlightChangeMatching(*change); + if (next_change) { + if (!success) + next_change->SetRevertValueFrom(*change); + } else if (!success) { + change->Revert(); + } +} + +void WindowTreeClient::GetWindowManager( + mojo::AssociatedInterfaceRequest<WindowManager> internal) { + window_manager_internal_.reset( + new mojo::AssociatedBinding<mojom::WindowManager>(this, + std::move(internal))); +} + +void WindowTreeClient::RequestClose(uint32_t window_id) { + Window* window = GetWindowByServerId(window_id); + if (!window || !IsRoot(window)) + return; + + FOR_EACH_OBSERVER(WindowObserver, *WindowPrivate(window).observers(), + OnRequestClose(window)); +} + +void WindowTreeClient::OnConnect(ClientSpecificId client_id) { + client_id_ = client_id; +} + +void WindowTreeClient::WmNewDisplayAdded(mojom::DisplayPtr display, + mojom::WindowDataPtr root_data, + bool parent_drawn) { + WmNewDisplayAddedImpl(display.To<display::Display>(), std::move(root_data), + parent_drawn); +} + +void WindowTreeClient::WmSetBounds(uint32_t change_id, + Id window_id, + const gfx::Rect& transit_bounds) { + Window* window = GetWindowByServerId(window_id); + bool result = false; + if (window) { + DCHECK(window_manager_delegate_); + gfx::Rect bounds = transit_bounds; + result = window_manager_delegate_->OnWmSetBounds(window, &bounds); + if (result) { + // If the resulting bounds differ return false. Returning false ensures + // the client applies the bounds we set below. + result = bounds == transit_bounds; + window->SetBounds(bounds); + } + } + if (window_manager_internal_client_) + window_manager_internal_client_->WmResponse(change_id, result); +} + +void WindowTreeClient::WmSetProperty(uint32_t change_id, + Id window_id, + const mojo::String& name, + mojo::Array<uint8_t> transit_data) { + Window* window = GetWindowByServerId(window_id); + bool result = false; + if (window) { + DCHECK(window_manager_delegate_); + std::unique_ptr<std::vector<uint8_t>> data; + if (!transit_data.is_null()) { + data.reset( + new std::vector<uint8_t>(transit_data.To<std::vector<uint8_t>>())); + } + result = window_manager_delegate_->OnWmSetProperty(window, name, &data); + if (result) { + // If the resulting bounds differ return false. Returning false ensures + // the client applies the bounds we set below. + window->SetSharedPropertyInternal(name, data.get()); + } + } + if (window_manager_internal_client_) + window_manager_internal_client_->WmResponse(change_id, result); +} + +void WindowTreeClient::WmCreateTopLevelWindow( + uint32_t change_id, + ClientSpecificId requesting_client_id, + mojo::Map<mojo::String, mojo::Array<uint8_t>> transport_properties) { + std::map<std::string, std::vector<uint8_t>> properties = + transport_properties.To<std::map<std::string, std::vector<uint8_t>>>(); + Window* window = + window_manager_delegate_->OnWmCreateTopLevelWindow(&properties); + embedded_windows_[requesting_client_id].insert(window); + if (window_manager_internal_client_) { + window_manager_internal_client_->OnWmCreatedTopLevelWindow( + change_id, server_id(window)); + } +} + +void WindowTreeClient::WmClientJankinessChanged(ClientSpecificId client_id, + bool janky) { + if (window_manager_delegate_) { + auto it = embedded_windows_.find(client_id); + CHECK(it != embedded_windows_.end()); + window_manager_delegate_->OnWmClientJankinessChanged( + embedded_windows_[client_id], janky); + } +} + +void WindowTreeClient::OnAccelerator(uint32_t id, + std::unique_ptr<ui::Event> event) { + DCHECK(event); + window_manager_delegate_->OnAccelerator(id, *event.get()); +} + +void WindowTreeClient::SetFrameDecorationValues( + mojom::FrameDecorationValuesPtr values) { + if (window_manager_internal_client_) { + window_manager_internal_client_->WmSetFrameDecorationValues( + std::move(values)); + } +} + +void WindowTreeClient::SetNonClientCursor(Window* window, + mus::mojom::Cursor cursor_id) { + window_manager_internal_client_->WmSetNonClientCursor(server_id(window), + cursor_id); +} + +void WindowTreeClient::AddAccelerator( + uint32_t id, + mojom::EventMatcherPtr event_matcher, + const base::Callback<void(bool)>& callback) { + if (window_manager_internal_client_) { + window_manager_internal_client_->AddAccelerator( + id, std::move(event_matcher), callback); + } +} + +void WindowTreeClient::RemoveAccelerator(uint32_t id) { + if (window_manager_internal_client_) { + window_manager_internal_client_->RemoveAccelerator(id); + } +} + +void WindowTreeClient::AddActivationParent(Window* window) { + if (window_manager_internal_client_) + window_manager_internal_client_->AddActivationParent(server_id(window)); +} + +void WindowTreeClient::RemoveActivationParent(Window* window) { + if (window_manager_internal_client_) + window_manager_internal_client_->RemoveActivationParent(server_id(window)); +} + +void WindowTreeClient::ActivateNextWindow() { + if (window_manager_internal_client_) + window_manager_internal_client_->ActivateNextWindow(); +} + +void WindowTreeClient::SetUnderlaySurfaceOffsetAndExtendedHitArea( + Window* window, + const gfx::Vector2d& offset, + const gfx::Insets& hit_area) { + if (window_manager_internal_client_) { + window_manager_internal_client_->SetUnderlaySurfaceOffsetAndExtendedHitArea( + server_id(window), offset.x(), offset.y(), hit_area); + } +} + +} // namespace mus diff --git a/chromium/components/mus/public/cpp/lib/window_tree_client_delegate.cc b/chromium/components/mus/public/cpp/lib/window_tree_client_delegate.cc new file mode 100644 index 00000000000..ed53012b227 --- /dev/null +++ b/chromium/components/mus/public/cpp/lib/window_tree_client_delegate.cc @@ -0,0 +1,11 @@ +// 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/mus/public/cpp/window_tree_client_delegate.h" + +namespace mus { + +void WindowTreeClientDelegate::OnUnembed(Window* root) {} + +} // namespace mus diff --git a/chromium/components/mus/public/cpp/lib/window_tree_host_factory.cc b/chromium/components/mus/public/cpp/lib/window_tree_host_factory.cc new file mode 100644 index 00000000000..0d55bae13fe --- /dev/null +++ b/chromium/components/mus/public/cpp/lib/window_tree_host_factory.cc @@ -0,0 +1,32 @@ +// 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/mus/public/cpp/window_tree_host_factory.h" + +#include "components/mus/public/cpp/window_tree_client.h" +#include "components/mus/public/cpp/window_tree_client_delegate.h" +#include "services/shell/public/cpp/connector.h" + +namespace mus { + +void CreateWindowTreeHost(mojom::WindowTreeHostFactory* factory, + WindowTreeClientDelegate* delegate, + mojom::WindowTreeHostPtr* host, + WindowManagerDelegate* window_manager_delegate) { + mojom::WindowTreeClientPtr tree_client; + new WindowTreeClient(delegate, window_manager_delegate, + GetProxy(&tree_client)); + factory->CreateWindowTreeHost(GetProxy(host), std::move(tree_client)); +} + +void CreateWindowTreeHost(shell::Connector* connector, + WindowTreeClientDelegate* delegate, + mojom::WindowTreeHostPtr* host, + WindowManagerDelegate* window_manager_delegate) { + mojom::WindowTreeHostFactoryPtr factory; + connector->ConnectToInterface("mojo:mus", &factory); + CreateWindowTreeHost(factory.get(), delegate, host, window_manager_delegate); +} + +} // namespace mus |