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/capture_web_contents_function.h"
6
7#include "base/base64.h"
8#include "base/strings/stringprintf.h"
9#include "content/public/browser/render_view_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
17using content::RenderViewHost;
18using content::RenderWidgetHost;
19using content::RenderWidgetHostView;
20using content::WebContents;
21
22namespace extensions {
23
24bool CaptureWebContentsFunction::HasPermission() {
25  return true;
26}
27
28bool CaptureWebContentsFunction::RunAsync() {
29  EXTENSION_FUNCTION_VALIDATE(args_);
30
31  context_id_ = extension_misc::kCurrentWindowId;
32  args_->GetInteger(0, &context_id_);
33
34  scoped_ptr<ImageDetails> image_details;
35  if (args_->GetSize() > 1) {
36    base::Value* spec = NULL;
37    EXTENSION_FUNCTION_VALIDATE(args_->Get(1, &spec) && spec);
38    image_details = ImageDetails::FromValue(*spec);
39  }
40
41  if (!IsScreenshotEnabled())
42    return false;
43
44  WebContents* contents = GetWebContentsForID(context_id_);
45  if (!contents)
46    return false;
47
48  // The default format and quality setting used when encoding jpegs.
49  const ImageDetails::Format kDefaultFormat = ImageDetails::FORMAT_JPEG;
50  const int kDefaultQuality = 90;
51
52  image_format_ = kDefaultFormat;
53  image_quality_ = kDefaultQuality;
54
55  if (image_details) {
56    if (image_details->format != ImageDetails::FORMAT_NONE)
57      image_format_ = image_details->format;
58    if (image_details->quality.get())
59      image_quality_ = *image_details->quality;
60  }
61
62  RenderViewHost* render_view_host = contents->GetRenderViewHost();
63  RenderWidgetHostView* view = render_view_host->GetView();
64  if (!view) {
65    OnCaptureFailure(FAILURE_REASON_VIEW_INVISIBLE);
66    return false;
67  }
68  render_view_host->CopyFromBackingStore(
69      gfx::Rect(),
70      view->GetViewBounds().size(),
71      base::Bind(&CaptureWebContentsFunction::CopyFromBackingStoreComplete,
72                 this),
73      kN32_SkColorType);
74  return true;
75}
76
77void CaptureWebContentsFunction::CopyFromBackingStoreComplete(
78    bool succeeded,
79    const SkBitmap& bitmap) {
80  if (succeeded) {
81    OnCaptureSuccess(bitmap);
82    return;
83  }
84  OnCaptureFailure(FAILURE_REASON_UNKNOWN);
85}
86
87void CaptureWebContentsFunction::OnCaptureSuccess(const SkBitmap& bitmap) {
88  std::vector<unsigned char> data;
89  SkAutoLockPixels screen_capture_lock(bitmap);
90  bool encoded = false;
91  std::string mime_type;
92  switch (image_format_) {
93    case ImageDetails::FORMAT_JPEG:
94      encoded = gfx::JPEGCodec::Encode(
95          reinterpret_cast<unsigned char*>(bitmap.getAddr32(0, 0)),
96          gfx::JPEGCodec::FORMAT_SkBitmap,
97          bitmap.width(),
98          bitmap.height(),
99          static_cast<int>(bitmap.rowBytes()),
100          image_quality_,
101          &data);
102      mime_type = kMimeTypeJpeg;
103      break;
104    case ImageDetails::FORMAT_PNG:
105      encoded =
106          gfx::PNGCodec::EncodeBGRASkBitmap(bitmap,
107                                            true,  // Discard transparency.
108                                            &data);
109      mime_type = kMimeTypePng;
110      break;
111    default:
112      NOTREACHED() << "Invalid image format.";
113  }
114
115  if (!encoded) {
116    OnCaptureFailure(FAILURE_REASON_ENCODING_FAILED);
117    return;
118  }
119
120  std::string base64_result;
121  base::StringPiece stream_as_string(
122      reinterpret_cast<const char*>(vector_as_array(&data)), data.size());
123
124  base::Base64Encode(stream_as_string, &base64_result);
125  base64_result.insert(
126      0, base::StringPrintf("data:%s;base64,", mime_type.c_str()));
127  SetResult(new base::StringValue(base64_result));
128  SendResponse(true);
129}
130
131}  // namespace extensions
132