1 // Copyright 2018 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.
4
5 #include "headless/lib/browser/protocol/headless_handler.h"
6
7 #include "base/base_switches.h"
8 #include "base/bind.h"
9 #include "base/command_line.h"
10 #include "cc/base/switches.h"
11 #include "components/viz/common/frame_sinks/begin_frame_args.h"
12 #include "components/viz/common/switches.h"
13 #include "content/public/common/content_switches.h"
14 #include "headless/lib/browser/headless_browser_impl.h"
15 #include "headless/lib/browser/headless_web_contents_impl.h"
16 #include "third_party/skia/include/core/SkBitmap.h"
17 #include "ui/base/resource/resource_bundle.h"
18 #include "ui/gfx/codec/jpeg_codec.h"
19 #include "ui/gfx/codec/png_codec.h"
20 #include "ui/gfx/image/image.h"
21 #include "ui/gfx/image/image_util.h"
22
23 namespace headless {
24 namespace protocol {
25
26 using HeadlessExperimental::ScreenshotParams;
27
28 namespace {
29
30
31 enum class ImageEncoding { kPng, kJpeg };
32 constexpr int kDefaultScreenshotQuality = 80;
33
EncodeBitmap(const SkBitmap & bitmap,ImageEncoding encoding,int quality)34 protocol::Binary EncodeBitmap(const SkBitmap& bitmap,
35 ImageEncoding encoding,
36 int quality) {
37 gfx::Image image = gfx::Image::CreateFrom1xBitmap(bitmap);
38 DCHECK(!image.IsEmpty());
39
40 scoped_refptr<base::RefCountedMemory> data;
41 if (encoding == ImageEncoding::kPng) {
42 data = image.As1xPNGBytes();
43 } else if (encoding == ImageEncoding::kJpeg) {
44 scoped_refptr<base::RefCountedBytes> bytes(new base::RefCountedBytes());
45 if (gfx::JPEG1xEncodedDataFromImage(image, quality, &bytes->data()))
46 data = bytes;
47 }
48 if (!data || !data->front())
49 return protocol::Binary();
50 return protocol::Binary::fromRefCounted(data);
51 }
52
OnBeginFrameFinished(std::unique_ptr<HeadlessHandler::BeginFrameCallback> callback,ImageEncoding encoding,int quality,bool has_damage,std::unique_ptr<SkBitmap> bitmap,std::string error_message)53 void OnBeginFrameFinished(
54 std::unique_ptr<HeadlessHandler::BeginFrameCallback> callback,
55 ImageEncoding encoding,
56 int quality,
57 bool has_damage,
58 std::unique_ptr<SkBitmap> bitmap,
59 std::string error_message) {
60 if (!error_message.empty()) {
61 callback->sendFailure(Response::ServerError(std::move(error_message)));
62 return;
63 }
64 if (!bitmap || bitmap->drawsNothing()) {
65 callback->sendSuccess(has_damage, Maybe<protocol::Binary>());
66 return;
67 }
68 callback->sendSuccess(has_damage, EncodeBitmap(*bitmap, encoding, quality));
69 }
70
71 } // namespace
72
HeadlessHandler(HeadlessBrowserImpl * browser,content::WebContents * web_contents)73 HeadlessHandler::HeadlessHandler(HeadlessBrowserImpl* browser,
74 content::WebContents* web_contents)
75 : browser_(browser), web_contents_(web_contents) {}
76
~HeadlessHandler()77 HeadlessHandler::~HeadlessHandler() {}
78
Wire(UberDispatcher * dispatcher)79 void HeadlessHandler::Wire(UberDispatcher* dispatcher) {
80 frontend_.reset(new HeadlessExperimental::Frontend(dispatcher->channel()));
81 HeadlessExperimental::Dispatcher::wire(dispatcher, this);
82 }
83
Enable()84 Response HeadlessHandler::Enable() {
85 if (frontend_)
86 frontend_->NeedsBeginFramesChanged(true);
87 return Response::Success();
88 }
89
Disable()90 Response HeadlessHandler::Disable() {
91 return Response::Success();
92 }
93
BeginFrame(Maybe<double> in_frame_time_ticks,Maybe<double> in_interval,Maybe<bool> in_no_display_updates,Maybe<ScreenshotParams> screenshot,std::unique_ptr<BeginFrameCallback> callback)94 void HeadlessHandler::BeginFrame(Maybe<double> in_frame_time_ticks,
95 Maybe<double> in_interval,
96 Maybe<bool> in_no_display_updates,
97 Maybe<ScreenshotParams> screenshot,
98 std::unique_ptr<BeginFrameCallback> callback) {
99 HeadlessWebContentsImpl* headless_contents =
100 HeadlessWebContentsImpl::From(browser_, web_contents_);
101 if (!headless_contents->begin_frame_control_enabled()) {
102 callback->sendFailure(Response::ServerError(
103 "Command is only supported if BeginFrameControl is enabled."));
104 return;
105 }
106
107 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
108 ::switches::kRunAllCompositorStagesBeforeDraw)) {
109 callback->sendFailure(
110 Response::ServerError("Command is only supported with "
111 "--run-all-compositor-stages-before-draw, see "
112 "https://goo.gl/3zHXhB for more info."));
113 return;
114 }
115
116 base::TimeTicks frame_time_ticks;
117 base::TimeDelta interval;
118 bool no_display_updates = in_no_display_updates.fromMaybe(false);
119
120 if (in_frame_time_ticks.isJust()) {
121 frame_time_ticks = base::TimeTicks() + base::TimeDelta::FromMillisecondsD(
122 in_frame_time_ticks.fromJust());
123 } else {
124 frame_time_ticks = base::TimeTicks::Now();
125 }
126
127 if (in_interval.isJust()) {
128 double interval_double = in_interval.fromJust();
129 if (interval_double <= 0) {
130 callback->sendFailure(
131 Response::InvalidParams("interval has to be greater than 0"));
132 return;
133 }
134 interval = base::TimeDelta::FromMillisecondsD(interval_double);
135 } else {
136 interval = viz::BeginFrameArgs::DefaultInterval();
137 }
138
139 base::TimeTicks deadline = frame_time_ticks + interval;
140
141 bool capture_screenshot = false;
142 ImageEncoding encoding;
143 int quality;
144
145 if (screenshot.isJust()) {
146 capture_screenshot = true;
147 const std::string format =
148 screenshot.fromJust()->GetFormat(ScreenshotParams::FormatEnum::Png);
149 if (format != ScreenshotParams::FormatEnum::Png &&
150 format != ScreenshotParams::FormatEnum::Jpeg) {
151 callback->sendFailure(
152 Response::InvalidParams("Invalid screenshot.format"));
153 return;
154 }
155 encoding = format == ScreenshotParams::FormatEnum::Png
156 ? ImageEncoding::kPng
157 : ImageEncoding::kJpeg;
158 quality = screenshot.fromJust()->GetQuality(kDefaultScreenshotQuality);
159 if (quality < 0 || quality > 100) {
160 callback->sendFailure(Response::InvalidParams(
161 "screenshot.quality has to be in range 0..100"));
162 return;
163 }
164 }
165
166 headless_contents->BeginFrame(
167 frame_time_ticks, deadline, interval, no_display_updates,
168 capture_screenshot,
169 base::BindOnce(&OnBeginFrameFinished, std::move(callback), encoding,
170 quality));
171 }
172
173 } // namespace protocol
174 } // namespace headless
175