1 // Copyright 2013 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 "extensions/browser/api/web_contents_capture_client.h"
6
7 #include "base/base64.h"
8 #include "base/strings/stringprintf.h"
9 #include "base/syslog_logging.h"
10 #include "content/public/browser/render_widget_host.h"
11 #include "content/public/browser/render_widget_host_view.h"
12 #include "content/public/browser/web_contents.h"
13 #include "extensions/browser/extension_function.h"
14 #include "extensions/common/constants.h"
15 #include "third_party/skia/include/core/SkBitmap.h"
16 #include "ui/gfx/codec/jpeg_codec.h"
17 #include "ui/gfx/codec/png_codec.h"
18
19 using content::RenderWidgetHost;
20 using content::RenderWidgetHostView;
21 using content::WebContents;
22
23 namespace extensions {
24
25 using api::extension_types::ImageDetails;
26
CaptureAsync(WebContents * web_contents,const ImageDetails * image_details,base::OnceCallback<void (const SkBitmap &)> callback)27 WebContentsCaptureClient::CaptureResult WebContentsCaptureClient::CaptureAsync(
28 WebContents* web_contents,
29 const ImageDetails* image_details,
30 base::OnceCallback<void(const SkBitmap&)> callback) {
31 // TODO(crbug/419878): Account for fullscreen render widget?
32 RenderWidgetHostView* const view =
33 web_contents ? web_contents->GetRenderWidgetHostView() : nullptr;
34 if (!view)
35 return FAILURE_REASON_VIEW_INVISIBLE;
36
37 if (!IsScreenshotEnabled())
38 return FAILURE_REASON_SCREEN_SHOTS_DISABLED;
39
40 // The default format and quality setting used when encoding jpegs.
41 const api::extension_types::ImageFormat kDefaultFormat =
42 api::extension_types::IMAGE_FORMAT_JPEG;
43 const int kDefaultQuality = 90;
44
45 image_format_ = kDefaultFormat;
46 image_quality_ = kDefaultQuality;
47
48 if (image_details) {
49 if (image_details->format != api::extension_types::IMAGE_FORMAT_NONE)
50 image_format_ = image_details->format;
51 if (image_details->quality.get())
52 image_quality_ = *image_details->quality;
53 }
54
55 view->CopyFromSurface(gfx::Rect(), // Copy entire surface area.
56 gfx::Size(), // Result contains device-level detail.
57 std::move(callback));
58
59 #if defined(OS_CHROMEOS)
60 SYSLOG(INFO) << "Screenshot taken";
61 #endif
62
63 return OK;
64 }
65
CopyFromSurfaceComplete(const SkBitmap & bitmap)66 void WebContentsCaptureClient::CopyFromSurfaceComplete(const SkBitmap& bitmap) {
67 if (bitmap.drawsNothing()) {
68 OnCaptureFailure(FAILURE_REASON_READBACK_FAILED);
69 } else {
70 OnCaptureSuccess(bitmap);
71 }
72 }
73
74 // TODO(wjmaclean) can this be static?
EncodeBitmap(const SkBitmap & bitmap,std::string * base64_result)75 bool WebContentsCaptureClient::EncodeBitmap(const SkBitmap& bitmap,
76 std::string* base64_result) {
77 DCHECK(base64_result);
78 std::vector<unsigned char> data;
79 const bool should_discard_alpha = !ClientAllowsTransparency();
80 bool encoded = false;
81 std::string mime_type;
82 switch (image_format_) {
83 case api::extension_types::IMAGE_FORMAT_JPEG:
84 encoded = gfx::JPEGCodec::Encode(bitmap, image_quality_, &data);
85 mime_type = kMimeTypeJpeg;
86 break;
87 case api::extension_types::IMAGE_FORMAT_PNG:
88 encoded = gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, should_discard_alpha,
89 &data);
90 mime_type = kMimeTypePng;
91 break;
92 default:
93 NOTREACHED() << "Invalid image format.";
94 }
95
96 if (!encoded)
97 return false;
98
99 base::StringPiece stream_as_string(reinterpret_cast<const char*>(data.data()),
100 data.size());
101
102 base::Base64Encode(stream_as_string, base64_result);
103 base64_result->insert(
104 0, base::StringPrintf("data:%s;base64,", mime_type.c_str()));
105
106 return true;
107 }
108
109 } // namespace extensions
110