1 // Copyright 2014 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 "athena/content/content_proxy.h"
8 #include "base/threading/worker_pool.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 "ui/aura/window.h"
13 #include "ui/gfx/codec/png_codec.h"
14 #include "ui/gfx/geometry/rect.h"
15 #include "ui/gfx/image/image.h"
16 #include "ui/gfx/image/image_png_rep.h"
17 #include "ui/views/controls/webview/webview.h"
18 #include "ui/views/widget/widget.h"
22 // Encodes an A8 SkBitmap to grayscale PNG in a worker thread.
23 class ProxyImageData : public base::RefCountedThreadSafe<ProxyImageData> {
28 void EncodeImage(const SkBitmap& bitmap, base::Closure callback) {
29 if (!base::WorkerPool::PostTaskAndReply(FROM_HERE,
30 base::Bind(&ProxyImageData::EncodeOnWorker,
35 // When coming here, the resulting image will be empty.
36 DCHECK(false) << "Cannot start bitmap encode task.";
41 scoped_refptr<base::RefCountedBytes> data() const { return data_; }
44 friend class base::RefCountedThreadSafe<ProxyImageData>;
47 void EncodeOnWorker(const SkBitmap& bitmap) {
48 DCHECK_EQ(bitmap.colorType(), kAlpha_8_SkColorType);
49 // Encode the A8 bitmap to grayscale PNG treating alpha as color intensity.
50 std::vector<unsigned char> data;
51 if (gfx::PNGCodec::EncodeA8SkBitmap(bitmap, &data))
52 data_ = new base::RefCountedBytes(data);
55 scoped_refptr<base::RefCountedBytes> data_;
57 DISALLOW_COPY_AND_ASSIGN(ProxyImageData);
60 ContentProxy::ContentProxy(views::WebView* web_view)
61 : web_view_(web_view),
62 content_visible_(true),
63 content_loaded_(true),
64 content_creation_called_(false),
65 proxy_content_to_image_factory_(this) {
66 // Note: The content will be hidden once the image got created.
70 ContentProxy::~ContentProxy() {
71 // If we still have a connection to the original web contents, we make it
73 ShowOriginalContent();
76 void ContentProxy::ContentWillUnload() {
77 content_loaded_ = false;
80 gfx::ImageSkia ContentProxy::GetContentImage() {
81 // While we compress to PNG, we use the original read back.
85 // Otherwise we convert the PNG.
86 std::vector<gfx::ImagePNGRep> image_reps;
87 image_reps.push_back(gfx::ImagePNGRep(png_data_, 1.0f));
88 return *(gfx::Image(image_reps).ToImageSkia());
91 void ContentProxy::EvictContent() {
92 raw_image_ = gfx::ImageSkia();
96 void ContentProxy::OnPreContentDestroyed() {
97 // Since we are breaking now the connection to the old content, we make the
98 // content visible again before we continue.
99 // Note: Since the owning window is invisible, it does not matter that we
100 // make the web content visible if the window gets destroyed shortly after.
101 ShowOriginalContent();
106 void ContentProxy::ShowOriginalContent() {
107 if (web_view_ && !content_visible_) {
108 // Show the original |web_view_| again.
109 web_view_->SetFastResize(false);
110 // If the content is loaded, we ask it to relayout itself since the
111 // dimensions might have changed. If not, we will reload new content and no
112 // layout is required for the old content.
115 web_view_->GetWebContents()->GetNativeView()->Show();
116 web_view_->SetVisible(true);
117 content_visible_ = true;
121 void ContentProxy::HideOriginalContent() {
122 if (web_view_ && content_visible_) {
123 // Hide the |web_view_|.
124 // TODO(skuhne): We might consider removing the view from the window while
125 // it's hidden - it should work the same way as show/hide and does not have
126 // any window re-ordering effect. Furthermore we want possibly to suppress
127 // any resizing of content (not only fast resize) here to avoid jank on
129 web_view_->GetWebContents()->GetNativeView()->Hide();
130 web_view_->SetVisible(false);
131 // Don't allow the content to get resized with window size changes.
132 web_view_->SetFastResize(true);
133 content_visible_ = false;
137 void ContentProxy::CreateProxyContent() {
138 DCHECK(!content_creation_called_);
139 content_creation_called_ = true;
140 // Unit tests might not have a |web_view_|.
144 content::RenderViewHost* host =
145 web_view_->GetWebContents()->GetRenderViewHost();
147 // A never fully initialized content can come here with no view.
148 if (!host->GetView())
150 gfx::Size source = host->GetView()->GetViewBounds().size();
151 gfx::Size target = gfx::Size(source.width() / 2, source.height() / 2);
152 host->CopyFromBackingStore(
155 base::Bind(&ContentProxy::OnContentImageRead,
156 proxy_content_to_image_factory_.GetWeakPtr()),
157 kAlpha_8_SkColorType);
160 void ContentProxy::OnContentImageRead(bool success, const SkBitmap& bitmap) {
161 // Now we can hide the content. Note that after hiding we are freeing memory
162 // and if something goes wrong we will end up with an empty page.
163 HideOriginalContent();
165 if (!success || bitmap.empty() || bitmap.isNull())
168 // While we are encoding the image, we keep the current image as reference
169 // to have something for the overview mode to grab. Once we have the encoded
170 // PNG, we will get rid of this.
171 raw_image_ = gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
173 scoped_refptr<ProxyImageData> png_image = new ProxyImageData();
174 png_image->EncodeImage(
176 base::Bind(&ContentProxy::OnContentImageEncodeComplete,
177 proxy_content_to_image_factory_.GetWeakPtr(),
181 void ContentProxy::OnContentImageEncodeComplete(
182 scoped_refptr<ProxyImageData> image) {
183 png_data_ = image->data();
185 // From now on we decode the image as needed to save memory.
186 raw_image_ = gfx::ImageSkia();
189 } // namespace athena