1 // Copyright (c) 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/devtools/devtools_frame_trace_recorder.h"
10 #include "base/atomicops.h"
11 #include "base/base64.h"
12 #include "base/bind.h"
13 #include "base/memory/ref_counted.h"
14 #include "base/trace_event/trace_event_impl.h"
15 #include "cc/output/compositor_frame_metadata.h"
16 #include "content/browser/frame_host/render_frame_host_impl.h"
17 #include "content/browser/renderer_host/render_widget_host_view_base.h"
18 #include "content/public/browser/readback_types.h"
19 #include "third_party/skia/include/core/SkBitmap.h"
20 #include "ui/gfx/codec/png_codec.h"
21 #include "ui/gfx/geometry/size.h"
22 #include "ui/gfx/geometry/size_conversions.h"
28 static base::subtle::Atomic32 frame_data_count
= 0;
29 static int kMaximumFrameDataCount
= 150;
30 static size_t kFrameAreaLimit
= 256000;
32 class TraceableDevToolsScreenshot
33 : public base::trace_event::ConvertableToTraceFormat
{
35 TraceableDevToolsScreenshot(const SkBitmap
& bitmap
) : frame_(bitmap
) {}
37 void AppendAsTraceFormat(std::string
* out
) const override
{
39 if (!frame_
.drawsNothing()) {
40 std::vector
<unsigned char> data
;
41 SkAutoLockPixels
lock_image(frame_
);
42 bool encoded
= gfx::PNGCodec::Encode(
43 reinterpret_cast<unsigned char*>(frame_
.getAddr32(0, 0)),
44 gfx::PNGCodec::FORMAT_SkBitmap
,
45 gfx::Size(frame_
.width(), frame_
.height()),
46 frame_
.width() * frame_
.bytesPerPixel(), false,
47 std::vector
<gfx::PNGCodec::Comment
>(), &data
);
49 std::string encoded_data
;
51 base::StringPiece(reinterpret_cast<char*>(&data
[0]), data
.size()),
53 out
->append(encoded_data
);
60 ~TraceableDevToolsScreenshot() override
{
61 base::subtle::NoBarrier_AtomicIncrement(&frame_data_count
, -1);
69 class DevToolsFrameTraceRecorderData
70 : public base::RefCounted
<DevToolsFrameTraceRecorderData
> {
72 DevToolsFrameTraceRecorderData(const cc::CompositorFrameMetadata
& metadata
)
73 : metadata_(metadata
), timestamp_(base::TraceTicks::Now()) {}
75 void FrameCaptured(const SkBitmap
& bitmap
, ReadbackResponse response
) {
76 if (response
!= READBACK_SUCCESS
)
78 int current_frame_count
= base::subtle::NoBarrier_Load(&frame_data_count
);
79 if (current_frame_count
>= kMaximumFrameDataCount
)
81 if (bitmap
.drawsNothing())
83 base::subtle::NoBarrier_AtomicIncrement(&frame_data_count
, 1);
84 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID_AND_TIMESTAMP(
85 TRACE_DISABLED_BY_DEFAULT("devtools.screenshot"), "Screenshot", 1,
86 timestamp_
.ToInternalValue(),
87 scoped_refptr
<base::trace_event::ConvertableToTraceFormat
>(
88 new TraceableDevToolsScreenshot(bitmap
)));
91 void CaptureFrame(RenderFrameHostImpl
* host
) {
92 RenderWidgetHostViewBase
* view
=
93 static_cast<RenderWidgetHostViewBase
*>(host
->GetView());
96 int current_frame_count
= base::subtle::NoBarrier_Load(&frame_data_count
);
97 if (current_frame_count
>= kMaximumFrameDataCount
)
99 float scale
= metadata_
.page_scale_factor
;
100 float area
= metadata_
.scrollable_viewport_size
.GetArea();
101 if (area
* scale
* scale
> kFrameAreaLimit
)
102 scale
= sqrt(kFrameAreaLimit
/ area
);
103 gfx::Size
snapshot_size(gfx::ToRoundedSize(gfx::ScaleSize(
104 metadata_
.scrollable_viewport_size
, scale
)));
105 view
->CopyFromCompositingSurface(
106 gfx::Rect(), snapshot_size
,
107 base::Bind(&DevToolsFrameTraceRecorderData::FrameCaptured
, this),
112 friend class base::RefCounted
<DevToolsFrameTraceRecorderData
>;
113 ~DevToolsFrameTraceRecorderData() {}
115 cc::CompositorFrameMetadata metadata_
;
116 base::TraceTicks timestamp_
;
118 DISALLOW_COPY_AND_ASSIGN(DevToolsFrameTraceRecorderData
);
121 DevToolsFrameTraceRecorder::DevToolsFrameTraceRecorder() { }
123 DevToolsFrameTraceRecorder::~DevToolsFrameTraceRecorder() { }
125 void DevToolsFrameTraceRecorder::OnSwapCompositorFrame(
126 RenderFrameHostImpl
* host
,
127 const cc::CompositorFrameMetadata
& frame_metadata
,
133 TRACE_EVENT_CATEGORY_GROUP_ENABLED(
134 TRACE_DISABLED_BY_DEFAULT("devtools.screenshot"), &enabled
);
135 if (!enabled
|| !do_capture
) {
136 pending_frame_data_
= nullptr;
141 TRACE_EVENT_IS_NEW_TRACE(&is_new_trace
);
142 if (!is_new_trace
&& pending_frame_data_
)
143 pending_frame_data_
->CaptureFrame(host
);
145 pending_frame_data_
= new DevToolsFrameTraceRecorderData(frame_metadata
);
148 } // namespace content