Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / extensions / browser / api / capture_web_contents_function.cc
blob7fa61894d81a071be6363f28f75adffbafbf3d8c
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.
5 #include "extensions/browser/api/capture_web_contents_function.h"
7 #include "base/base64.h"
8 #include "base/strings/stringprintf.h"
9 #include "content/public/browser/render_widget_host.h"
10 #include "content/public/browser/render_widget_host_view.h"
11 #include "content/public/browser/web_contents.h"
12 #include "extensions/browser/extension_function.h"
13 #include "extensions/common/constants.h"
14 #include "ui/gfx/codec/jpeg_codec.h"
15 #include "ui/gfx/codec/png_codec.h"
16 #include "ui/gfx/display.h"
17 #include "ui/gfx/geometry/size_conversions.h"
18 #include "ui/gfx/screen.h"
20 using content::RenderWidgetHost;
21 using content::RenderWidgetHostView;
22 using content::WebContents;
24 namespace extensions {
26 using core_api::extension_types::ImageDetails;
28 bool CaptureWebContentsFunction::HasPermission() {
29 return true;
32 bool CaptureWebContentsFunction::RunAsync() {
33 EXTENSION_FUNCTION_VALIDATE(args_);
35 context_id_ = extension_misc::kCurrentWindowId;
36 args_->GetInteger(0, &context_id_);
38 scoped_ptr<ImageDetails> image_details;
39 if (args_->GetSize() > 1) {
40 base::Value* spec = NULL;
41 EXTENSION_FUNCTION_VALIDATE(args_->Get(1, &spec) && spec);
42 image_details = ImageDetails::FromValue(*spec);
45 if (!IsScreenshotEnabled())
46 return false;
48 WebContents* contents = GetWebContentsForID(context_id_);
49 if (!contents)
50 return false;
52 // The default format and quality setting used when encoding jpegs.
53 const core_api::extension_types::ImageFormat kDefaultFormat =
54 core_api::extension_types::IMAGE_FORMAT_JPEG;
55 const int kDefaultQuality = 90;
57 image_format_ = kDefaultFormat;
58 image_quality_ = kDefaultQuality;
60 if (image_details) {
61 if (image_details->format !=
62 core_api::extension_types::IMAGE_FORMAT_NONE)
63 image_format_ = image_details->format;
64 if (image_details->quality.get())
65 image_quality_ = *image_details->quality;
68 // TODO(miu): Account for fullscreen render widget? http://crbug.com/419878
69 RenderWidgetHostView* const view = contents->GetRenderWidgetHostView();
70 RenderWidgetHost* const host = view ? view->GetRenderWidgetHost() : nullptr;
71 if (!view || !host) {
72 OnCaptureFailure(FAILURE_REASON_VIEW_INVISIBLE);
73 return false;
76 // By default, the requested bitmap size is the view size in screen
77 // coordinates. However, if there's more pixel detail available on the
78 // current system, increase the requested bitmap size to capture it all.
79 const gfx::Size view_size = view->GetViewBounds().size();
80 gfx::Size bitmap_size = view_size;
81 const gfx::NativeView native_view = view->GetNativeView();
82 gfx::Screen* const screen = gfx::Screen::GetScreenFor(native_view);
83 const float scale =
84 screen->GetDisplayNearestWindow(native_view).device_scale_factor();
85 if (scale > 1.0f)
86 bitmap_size = gfx::ToCeiledSize(gfx::ScaleSize(view_size, scale));
88 host->CopyFromBackingStore(
89 gfx::Rect(view_size),
90 bitmap_size,
91 base::Bind(&CaptureWebContentsFunction::CopyFromBackingStoreComplete,
92 this),
93 kN32_SkColorType);
94 return true;
97 void CaptureWebContentsFunction::CopyFromBackingStoreComplete(
98 const SkBitmap& bitmap,
99 content::ReadbackResponse response) {
100 if (response == content::READBACK_SUCCESS) {
101 OnCaptureSuccess(bitmap);
102 return;
104 OnCaptureFailure(FAILURE_REASON_UNKNOWN);
107 void CaptureWebContentsFunction::OnCaptureSuccess(const SkBitmap& bitmap) {
108 std::vector<unsigned char> data;
109 SkAutoLockPixels screen_capture_lock(bitmap);
110 bool encoded = false;
111 std::string mime_type;
112 switch (image_format_) {
113 case core_api::extension_types::IMAGE_FORMAT_JPEG:
114 encoded = gfx::JPEGCodec::Encode(
115 reinterpret_cast<unsigned char*>(bitmap.getAddr32(0, 0)),
116 gfx::JPEGCodec::FORMAT_SkBitmap,
117 bitmap.width(),
118 bitmap.height(),
119 static_cast<int>(bitmap.rowBytes()),
120 image_quality_,
121 &data);
122 mime_type = kMimeTypeJpeg;
123 break;
124 case core_api::extension_types::IMAGE_FORMAT_PNG:
125 encoded =
126 gfx::PNGCodec::EncodeBGRASkBitmap(bitmap,
127 true, // Discard transparency.
128 &data);
129 mime_type = kMimeTypePng;
130 break;
131 default:
132 NOTREACHED() << "Invalid image format.";
135 if (!encoded) {
136 OnCaptureFailure(FAILURE_REASON_ENCODING_FAILED);
137 return;
140 std::string base64_result;
141 base::StringPiece stream_as_string(
142 reinterpret_cast<const char*>(vector_as_array(&data)), data.size());
144 base::Base64Encode(stream_as_string, &base64_result);
145 base64_result.insert(
146 0, base::StringPrintf("data:%s;base64,", mime_type.c_str()));
147 SetResult(new base::StringValue(base64_result));
148 SendResponse(true);
151 } // namespace extensions