| // Copyright (c) 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 "content/browser/devtools/devtools_frame_trace_recorder.h" |
| |
| #include <stddef.h> |
| |
| #include <string> |
| #include <vector> |
| |
| #include "base/atomicops.h" |
| #include "base/base64.h" |
| #include "base/bind.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/trace_event/trace_event_impl.h" |
| #include "components/viz/common/quads/compositor_frame_metadata.h" |
| #include "content/browser/frame_host/render_frame_host_impl.h" |
| #include "content/browser/renderer_host/render_widget_host_view_base.h" |
| #include "content/public/browser/readback_types.h" |
| #include "third_party/skia/include/core/SkBitmap.h" |
| #include "ui/gfx/codec/jpeg_codec.h" |
| #include "ui/gfx/geometry/size.h" |
| #include "ui/gfx/geometry/size_conversions.h" |
| |
| namespace content { |
| |
| namespace { |
| |
| static base::subtle::Atomic32 frame_data_count = 0; |
| static int kMaximumFrameDataCount = 450; |
| static size_t kFrameAreaLimit = 256000; |
| |
| class TraceableDevToolsScreenshot |
| : public base::trace_event::ConvertableToTraceFormat { |
| public: |
| TraceableDevToolsScreenshot(const SkBitmap& bitmap) : frame_(bitmap) {} |
| |
| void AppendAsTraceFormat(std::string* out) const override { |
| out->append("\""); |
| if (!frame_.drawsNothing()) { |
| std::vector<unsigned char> data; |
| bool encoded = gfx::JPEGCodec::Encode(frame_, 80, &data); |
| if (encoded) { |
| std::string encoded_data; |
| base::Base64Encode( |
| base::StringPiece(reinterpret_cast<char*>(&data[0]), data.size()), |
| &encoded_data); |
| out->append(encoded_data); |
| } |
| } |
| out->append("\""); |
| } |
| |
| private: |
| ~TraceableDevToolsScreenshot() override { |
| base::subtle::NoBarrier_AtomicIncrement(&frame_data_count, -1); |
| } |
| |
| SkBitmap frame_; |
| }; |
| |
| void FrameCaptured(base::TimeTicks timestamp, const SkBitmap& bitmap, |
| ReadbackResponse response) { |
| if (response != READBACK_SUCCESS) |
| return; |
| int current_frame_count = base::subtle::NoBarrier_Load(&frame_data_count); |
| if (current_frame_count >= kMaximumFrameDataCount) |
| return; |
| if (bitmap.drawsNothing()) |
| return; |
| base::subtle::NoBarrier_AtomicIncrement(&frame_data_count, 1); |
| TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID_AND_TIMESTAMP( |
| TRACE_DISABLED_BY_DEFAULT("devtools.screenshot"), "Screenshot", 1, |
| timestamp, |
| std::unique_ptr<base::trace_event::ConvertableToTraceFormat>( |
| new TraceableDevToolsScreenshot(bitmap))); |
| } |
| |
| void CaptureFrame(RenderFrameHostImpl* host, |
| const viz::CompositorFrameMetadata& metadata) { |
| RenderWidgetHostViewBase* view = |
| static_cast<RenderWidgetHostViewBase*>(host->GetView()); |
| if (!view) |
| return; |
| int current_frame_count = base::subtle::NoBarrier_Load(&frame_data_count); |
| if (current_frame_count >= kMaximumFrameDataCount) |
| return; |
| |
| gfx::Size predicted_bitmap_size = gfx::ToCeiledSize(gfx::ScaleSize( |
| metadata.scrollable_viewport_size, metadata.page_scale_factor)); |
| gfx::Size snapshot_size; |
| float area = predicted_bitmap_size.GetArea(); |
| if (area <= kFrameAreaLimit) { |
| snapshot_size = predicted_bitmap_size; |
| } else { |
| double scale = sqrt(kFrameAreaLimit / area); |
| snapshot_size = gfx::ScaleToCeiledSize(predicted_bitmap_size, scale); |
| } |
| |
| view->CopyFromSurface(gfx::Rect(), snapshot_size, |
| base::Bind(FrameCaptured, base::TimeTicks::Now()), |
| kN32_SkColorType); |
| } |
| |
| bool ScreenshotCategoryEnabled() { |
| bool enabled; |
| TRACE_EVENT_CATEGORY_GROUP_ENABLED( |
| TRACE_DISABLED_BY_DEFAULT("devtools.screenshot"), &enabled); |
| return enabled; |
| } |
| |
| } // namespace |
| |
| DevToolsFrameTraceRecorder::DevToolsFrameTraceRecorder() { } |
| |
| DevToolsFrameTraceRecorder::~DevToolsFrameTraceRecorder() { } |
| |
| void DevToolsFrameTraceRecorder::OnSwapCompositorFrame( |
| RenderFrameHostImpl* host, |
| const viz::CompositorFrameMetadata& frame_metadata) { |
| if (!host || !ScreenshotCategoryEnabled()) |
| return; |
| CaptureFrame(host, frame_metadata); |
| } |
| |
| void DevToolsFrameTraceRecorder::OnSynchronousSwapCompositorFrame( |
| RenderFrameHostImpl* host, |
| const viz::CompositorFrameMetadata& frame_metadata) { |
| if (!host || !ScreenshotCategoryEnabled()) { |
| last_metadata_.reset(); |
| return; |
| } |
| |
| bool is_new_trace; |
| TRACE_EVENT_IS_NEW_TRACE(&is_new_trace); |
| if (!is_new_trace && last_metadata_) |
| CaptureFrame(host, *last_metadata_); |
| last_metadata_.reset(new viz::CompositorFrameMetadata); |
| *last_metadata_ = frame_metadata.Clone(); |
| } |
| |
| } // namespace content |