| // 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 "services/sky/document_view.h" |
| |
| #include "base/bind.h" |
| #include "base/location.h" |
| #include "base/message_loop/message_loop.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/strings/string_util.h" |
| #include "base/thread_task_runner_handle.h" |
| #include "mojo/converters/geometry/geometry_type_converters.h" |
| #include "mojo/converters/input_events/input_events_type_converters.h" |
| #include "mojo/public/cpp/application/connect.h" |
| #include "mojo/public/cpp/system/data_pipe.h" |
| #include "mojo/public/interfaces/application/shell.mojom.h" |
| #include "mojo/services/view_manager/public/cpp/view.h" |
| #include "mojo/services/view_manager/public/cpp/view_manager.h" |
| #include "mojo/services/view_manager/public/interfaces/view_manager.mojom.h" |
| #include "services/asset_bundle/asset_unpacker_job.h" |
| #include "services/sky/compositor/layer.h" |
| #include "services/sky/compositor/layer_host.h" |
| #include "services/sky/compositor/rasterizer_bitmap.h" |
| #include "services/sky/compositor/rasterizer_ganesh.h" |
| #include "services/sky/converters/input_event_types.h" |
| #include "services/sky/dart_library_provider_impl.h" |
| #include "services/sky/internals.h" |
| #include "services/sky/runtime_flags.h" |
| #include "skia/ext/refptr.h" |
| #include "sky/engine/public/platform/Platform.h" |
| #include "sky/engine/public/platform/WebInputEvent.h" |
| #include "sky/engine/public/web/Sky.h" |
| #include "third_party/skia/include/core/SkCanvas.h" |
| #include "third_party/skia/include/core/SkColor.h" |
| #include "third_party/skia/include/core/SkDevice.h" |
| #include "ui/events/gestures/gesture_recognizer.h" |
| |
| using mojo::asset_bundle::AssetUnpackerJob; |
| |
| namespace sky { |
| namespace { |
| |
| const char kSnapshotKey[] = "snapshot_blob.bin"; |
| |
| ui::EventType ConvertEventTypeToUIEventType(blink::WebInputEvent::Type type) { |
| if (type == blink::WebInputEvent::PointerDown) |
| return ui::ET_TOUCH_PRESSED; |
| if (type == blink::WebInputEvent::PointerUp) |
| return ui::ET_TOUCH_RELEASED; |
| if (type == blink::WebInputEvent::PointerMove) |
| return ui::ET_TOUCH_MOVED; |
| DCHECK(type == blink::WebInputEvent::PointerCancel); |
| return ui::ET_TOUCH_CANCELLED; |
| } |
| |
| scoped_ptr<ui::TouchEvent> ConvertToUITouchEvent( |
| const blink::WebInputEvent& event, |
| float device_pixel_ratio) { |
| if (!blink::WebInputEvent::isPointerEventType(event.type)) |
| return nullptr; |
| const blink::WebPointerEvent& pointer_event = |
| static_cast<const blink::WebPointerEvent&>(event); |
| return make_scoped_ptr(new ui::TouchEvent( |
| ConvertEventTypeToUIEventType(event.type), |
| gfx::PointF(pointer_event.x * device_pixel_ratio, |
| pointer_event.y * device_pixel_ratio), |
| pointer_event.pointer, |
| base::TimeDelta::FromMillisecondsD(pointer_event.timeStampMS))); |
| } |
| |
| scoped_ptr<DartLibraryProviderImpl::PrefetchedLibrary> |
| CreatePrefetchedLibraryIfNeeded(const String& name, |
| mojo::URLResponsePtr response) { |
| scoped_ptr<DartLibraryProviderImpl::PrefetchedLibrary> prefetched; |
| if (response->status_code == 200) { |
| prefetched.reset(new DartLibraryProviderImpl::PrefetchedLibrary()); |
| prefetched->name = name.toUTF8(); |
| prefetched->pipe = response->body.Pass(); |
| } |
| return prefetched.Pass(); |
| } |
| |
| } // namespace |
| |
| DocumentView::DocumentView( |
| mojo::InterfaceRequest<mojo::ServiceProvider> services, |
| mojo::ServiceProviderPtr exported_services, |
| mojo::URLResponsePtr response, |
| mojo::Shell* shell) |
| : response_(response.Pass()), |
| exported_services_(services.Pass()), |
| imported_services_(exported_services.Pass()), |
| shell_(shell), |
| root_(nullptr), |
| view_manager_client_factory_(shell_, this), |
| bitmap_rasterizer_(nullptr), |
| weak_factory_(this) { |
| exported_services_.AddService(&view_manager_client_factory_); |
| InitServiceRegistry(); |
| mojo::ServiceProviderPtr network_service_provider; |
| shell->ConnectToApplication("mojo:authenticated_network_service", |
| mojo::GetProxy(&network_service_provider), |
| nullptr); |
| mojo::ConnectToService(network_service_provider.get(), &network_service_); |
| } |
| |
| DocumentView::~DocumentView() { |
| if (root_) |
| root_->RemoveObserver(this); |
| ui::GestureRecognizer::Get()->CleanupStateForConsumer(this); |
| } |
| |
| base::WeakPtr<DocumentView> DocumentView::GetWeakPtr() { |
| return weak_factory_.GetWeakPtr(); |
| } |
| |
| void DocumentView::OnEmbed( |
| mojo::View* root, |
| mojo::InterfaceRequest<mojo::ServiceProvider> services_provided_to_embedder, |
| mojo::ServiceProviderPtr services_provided_by_embedder) { |
| root_ = root; |
| |
| if (services_provided_by_embedder.get()) { |
| mojo::ConnectToService(services_provided_by_embedder.get(), |
| &navigator_host_); |
| } |
| |
| services_provided_to_embedder_ = services_provided_to_embedder.Pass(); |
| services_provided_by_embedder_ = services_provided_by_embedder.Pass(); |
| |
| Load(response_.Pass()); |
| |
| UpdateRootSizeAndViewportMetrics(root_->bounds()); |
| |
| root_->AddObserver(this); |
| } |
| |
| void DocumentView::OnViewManagerDisconnected(mojo::ViewManager* view_manager) { |
| // TODO(aa): Need to figure out how shutdown works. |
| } |
| |
| void DocumentView::LoadFromSnapshotStream( |
| String name, mojo::ScopedDataPipeConsumerHandle snapshot) { |
| if (sky_view_) |
| sky_view_->RunFromSnapshot(name, snapshot.Pass()); |
| } |
| |
| void DocumentView::Load(mojo::URLResponsePtr response) { |
| sky_view_ = blink::SkyView::Create(this); |
| layer_host_.reset(new LayerHost(this)); |
| root_layer_ = make_scoped_refptr(new Layer(this)); |
| root_layer_->set_rasterizer(CreateRasterizer()); |
| layer_host_->SetRootLayer(root_layer_); |
| |
| String name = String::fromUTF8(response->url); |
| if (name.endsWith(".skyx")) { |
| AssetUnpackerJob* unpacker = new AssetUnpackerJob( |
| mojo::GetProxy(&root_bundle_), |
| base::MessageLoop::current()->task_runner()); |
| unpacker->Unpack(response->body.Pass()); |
| root_bundle_->GetAsStream(kSnapshotKey, |
| base::Bind(&DocumentView::LoadFromSnapshotStream, |
| weak_factory_.GetWeakPtr(), name)); |
| return; |
| } |
| library_provider_.reset(new DartLibraryProviderImpl( |
| network_service_.get(), |
| CreatePrefetchedLibraryIfNeeded(name, response.Pass()))); |
| sky_view_->RunFromLibrary(name, library_provider_.get()); |
| } |
| |
| scoped_ptr<Rasterizer> DocumentView::CreateRasterizer() { |
| if (!RuntimeFlags::Get().testing()) |
| return make_scoped_ptr(new RasterizerGanesh(layer_host_.get())); |
| // TODO(abarth): If we have more than one layer, we'll need to re-think how |
| // we capture pixels for testing; |
| DCHECK(!bitmap_rasterizer_); |
| bitmap_rasterizer_ = new RasterizerBitmap(layer_host_.get()); |
| return make_scoped_ptr(bitmap_rasterizer_); |
| } |
| |
| void DocumentView::GetPixelsForTesting(std::vector<unsigned char>* pixels) { |
| DCHECK(RuntimeFlags::Get().testing()) << "Requires testing runtime flag"; |
| DCHECK(root_layer_) << "The root layer owns the rasterizer"; |
| return bitmap_rasterizer_->GetPixelsForTesting(pixels); |
| } |
| |
| mojo::ScopedMessagePipeHandle DocumentView::TakeRootBundleHandle() { |
| return root_bundle_.PassInterface().PassHandle(); |
| } |
| |
| mojo::ScopedMessagePipeHandle DocumentView::TakeServicesProvidedToEmbedder() { |
| return services_provided_to_embedder_.PassMessagePipe(); |
| } |
| |
| mojo::ScopedMessagePipeHandle DocumentView::TakeServicesProvidedByEmbedder() { |
| return services_provided_by_embedder_.PassInterface().PassHandle(); |
| } |
| |
| mojo::ScopedMessagePipeHandle DocumentView::TakeServiceRegistry() { |
| return service_registry_.PassInterface().PassHandle(); |
| } |
| |
| mojo::Shell* DocumentView::GetShell() { |
| return shell_; |
| } |
| |
| void DocumentView::BeginFrame(base::TimeTicks frame_time) { |
| if (sky_view_) { |
| sky_view_->BeginFrame(frame_time); |
| root_layer_->SetSize(sky_view_->display_metrics().physical_size); |
| } |
| } |
| |
| void DocumentView::OnSurfaceIdAvailable(mojo::SurfaceIdPtr surface_id) { |
| if (root_) |
| root_->SetSurfaceId(surface_id.Pass()); |
| } |
| |
| void DocumentView::PaintContents(SkCanvas* canvas, const gfx::Rect& clip) { |
| blink::WebRect rect(clip.x(), clip.y(), clip.width(), clip.height()); |
| |
| if (sky_view_) { |
| RefPtr<SkPicture> picture = sky_view_->Paint(); |
| if (picture) |
| canvas->drawPicture(picture.get()); |
| } |
| } |
| |
| float DocumentView::GetDevicePixelRatio() const { |
| if (root_) |
| return root_->viewport_metrics().device_pixel_ratio; |
| return 1.f; |
| } |
| |
| void DocumentView::DidCreateIsolate(Dart_Isolate isolate) { |
| Internals::Create(isolate, this); |
| } |
| |
| mojo::NavigatorHost* DocumentView::NavigatorHost() { |
| return navigator_host_.get(); |
| } |
| |
| void DocumentView::OnViewBoundsChanged(mojo::View* view, |
| const mojo::Rect& old_bounds, |
| const mojo::Rect& new_bounds) { |
| DCHECK_EQ(view, root_); |
| UpdateRootSizeAndViewportMetrics(new_bounds); |
| } |
| |
| void DocumentView::OnViewViewportMetricsChanged( |
| mojo::View* view, |
| const mojo::ViewportMetrics& old_metrics, |
| const mojo::ViewportMetrics& new_metrics) { |
| DCHECK_EQ(view, root_); |
| |
| UpdateRootSizeAndViewportMetrics(root_->bounds()); |
| } |
| |
| void DocumentView::UpdateRootSizeAndViewportMetrics( |
| const mojo::Rect& new_bounds) { |
| float device_pixel_ratio = GetDevicePixelRatio(); |
| |
| if (sky_view_) { |
| blink::SkyDisplayMetrics metrics; |
| mojo::Rect bounds = root_->bounds(); |
| metrics.physical_size = blink::WebSize(bounds.width, bounds.height); |
| metrics.device_pixel_ratio = device_pixel_ratio; |
| sky_view_->SetDisplayMetrics(metrics); |
| return; |
| } |
| } |
| |
| void DocumentView::OnViewFocusChanged(mojo::View* gained_focus, |
| mojo::View* lost_focus) { |
| } |
| |
| void DocumentView::OnViewDestroyed(mojo::View* view) { |
| DCHECK_EQ(view, root_); |
| |
| root_ = nullptr; |
| } |
| |
| void DocumentView::OnViewInputEvent( |
| mojo::View* view, const mojo::EventPtr& event) { |
| float device_pixel_ratio = GetDevicePixelRatio(); |
| scoped_ptr<blink::WebInputEvent> web_event = |
| ConvertEvent(event, device_pixel_ratio); |
| if (!web_event) |
| return; |
| |
| ui::GestureRecognizer* recognizer = ui::GestureRecognizer::Get(); |
| scoped_ptr<ui::TouchEvent> touch_event = |
| ConvertToUITouchEvent(*web_event, device_pixel_ratio); |
| if (touch_event) |
| recognizer->ProcessTouchEventPreDispatch(*touch_event, this); |
| |
| bool handled = false; |
| |
| if (sky_view_) |
| sky_view_->HandleInputEvent(*web_event); |
| |
| if (touch_event) { |
| ui::EventResult result = handled ? ui::ER_UNHANDLED : ui::ER_UNHANDLED; |
| if (auto gestures = recognizer->ProcessTouchEventPostDispatch( |
| *touch_event, result, this)) { |
| for (auto& gesture : *gestures) { |
| scoped_ptr<blink::WebInputEvent> gesture_event = |
| ConvertEvent(*gesture, device_pixel_ratio); |
| if (gesture_event) { |
| if (sky_view_) |
| sky_view_->HandleInputEvent(*gesture_event); |
| } |
| } |
| } |
| } |
| } |
| |
| void DocumentView::StartDebuggerInspectorBackend() { |
| // FIXME: Do we need this for dart? |
| } |
| |
| void DocumentView::InitServiceRegistry() { |
| mojo::ConnectToService(imported_services_.get(), &service_registry_); |
| mojo::Array<mojo::String> interface_names(1); |
| interface_names[0] = mojo::ViewManagerClient::Name_; |
| mojo::ServiceProviderImpl* sp_impl(new mojo::ServiceProviderImpl()); |
| sp_impl->AddService(&view_manager_client_factory_); |
| mojo::ServiceProviderPtr sp; |
| service_registry_service_provider_binding_.reset( |
| new mojo::StrongBinding<mojo::ServiceProvider>(sp_impl, &sp)); |
| service_registry_->AddServices(interface_names.Pass(), sp.Pass()); |
| } |
| |
| void DocumentView::ScheduleFrame() { |
| DCHECK(sky_view_); |
| layer_host_->SetNeedsAnimate(); |
| } |
| |
| } // namespace sky |