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