• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "athena/content/content_proxy.h"
6 
7 #include "athena/activity/public/activity.h"
8 #include "athena/activity/public/activity_view_model.h"
9 #include "base/bind.h"
10 #include "base/threading/worker_pool.h"
11 #include "content/public/browser/render_view_host.h"
12 #include "content/public/browser/render_widget_host_view.h"
13 #include "content/public/browser/web_contents.h"
14 #include "ui/aura/window.h"
15 #include "ui/gfx/codec/png_codec.h"
16 #include "ui/gfx/geometry/rect.h"
17 #include "ui/gfx/image/image.h"
18 #include "ui/gfx/image/image_png_rep.h"
19 #include "ui/views/controls/webview/webview.h"
20 #include "ui/views/widget/widget.h"
21 
22 namespace athena {
23 
24 // Encodes an A8 SkBitmap to grayscale PNG in a worker thread.
25 class ProxyImageData : public base::RefCountedThreadSafe<ProxyImageData> {
26  public:
ProxyImageData()27   ProxyImageData() {
28   }
29 
EncodeImage(const SkBitmap & bitmap,base::Closure callback)30   void EncodeImage(const SkBitmap& bitmap, base::Closure callback) {
31     if (!base::WorkerPool::PostTaskAndReply(FROM_HERE,
32             base::Bind(&ProxyImageData::EncodeOnWorker,
33                        this,
34                        bitmap),
35             callback,
36             true)) {
37       // When coming here, the resulting image will be empty.
38       DCHECK(false) << "Cannot start bitmap encode task.";
39       callback.Run();
40     }
41   }
42 
data() const43   scoped_refptr<base::RefCountedBytes> data() const { return data_; }
44 
45  private:
46   friend class base::RefCountedThreadSafe<ProxyImageData>;
~ProxyImageData()47   virtual ~ProxyImageData() {
48   }
49 
EncodeOnWorker(const SkBitmap & bitmap)50   void EncodeOnWorker(const SkBitmap& bitmap) {
51     DCHECK_EQ(bitmap.colorType(), kAlpha_8_SkColorType);
52     // Encode the A8 bitmap to grayscale PNG treating alpha as color intensity.
53     std::vector<unsigned char> data;
54     if (gfx::PNGCodec::EncodeA8SkBitmap(bitmap, &data))
55       data_ = new base::RefCountedBytes(data);
56   }
57 
58   scoped_refptr<base::RefCountedBytes> data_;
59 
60   DISALLOW_COPY_AND_ASSIGN(ProxyImageData);
61 };
62 
ContentProxy(views::WebView * web_view,Activity * activity)63 ContentProxy::ContentProxy(views::WebView* web_view, Activity* activity)
64     : web_view_(web_view),
65       content_visible_(true),
66       content_loaded_(true),
67       content_creation_called_(false),
68       proxy_content_to_image_factory_(this) {
69   // Note: The content will be hidden once the image got created.
70   CreateProxyContent();
71 }
72 
~ContentProxy()73 ContentProxy::~ContentProxy() {
74   // If we still have a connection to the original Activity, we make it visible
75   // again.
76   ShowOriginalContent();
77 }
78 
ContentWillUnload()79 void ContentProxy::ContentWillUnload() {
80   content_loaded_ = false;
81 }
82 
GetContentImage()83 gfx::ImageSkia ContentProxy::GetContentImage() {
84   // While we compress to PNG, we use the original read back.
85   if (!raw_image_.isNull() || !png_data_.get())
86     return raw_image_;
87 
88   // Otherwise we convert the PNG.
89   std::vector<gfx::ImagePNGRep> image_reps;
90   image_reps.push_back(gfx::ImagePNGRep(png_data_, 0.0f));
91   return *(gfx::Image(image_reps).ToImageSkia());
92 }
93 
EvictContent()94 void ContentProxy::EvictContent() {
95   raw_image_ = gfx::ImageSkia();
96   png_data_->Release();
97 }
98 
OnPreContentDestroyed()99 void ContentProxy::OnPreContentDestroyed() {
100   // Since we are breaking now the connection to the old content, we make the
101   // content visible again before we continue.
102   // Note: Since the owning window is invisible, it does not matter that we
103   // make the web content visible if the window gets destroyed shortly after.
104   ShowOriginalContent();
105 
106   web_view_ = NULL;
107 }
108 
ShowOriginalContent()109 void ContentProxy::ShowOriginalContent() {
110   if (web_view_ && !content_visible_) {
111     // Show the original |web_view_| again.
112     web_view_->SetFastResize(false);
113     // If the content is loaded, we ask it to relayout itself since the
114     // dimensions might have changed. If not, we will reload new content and no
115     // layout is required for the old content.
116     if (content_loaded_)
117       web_view_->Layout();
118     web_view_->GetWebContents()->GetNativeView()->Show();
119     web_view_->SetVisible(true);
120     content_visible_ = true;
121   }
122 }
123 
HideOriginalContent()124 void ContentProxy::HideOriginalContent() {
125   if (web_view_ && content_visible_) {
126     // Hide the |web_view_|.
127     // TODO(skuhne): We might consider removing the view from the window while
128     // it's hidden - it should work the same way as show/hide and does not have
129     // any window re-ordering effect. Furthermore we want possibly to suppress
130     // any resizing of content (not only fast resize) here to avoid jank on
131     // rotation.
132     web_view_->GetWebContents()->GetNativeView()->Hide();
133     web_view_->SetVisible(false);
134     // Don't allow the content to get resized with window size changes.
135     web_view_->SetFastResize(true);
136     content_visible_ = false;
137   }
138 }
139 
CreateProxyContent()140 void ContentProxy::CreateProxyContent() {
141   DCHECK(!content_creation_called_);
142   content_creation_called_ = true;
143   // Unit tests might not have a |web_view_|.
144   if (!web_view_)
145     return;
146 
147   content::RenderViewHost* host =
148       web_view_->GetWebContents()->GetRenderViewHost();
149   DCHECK(host && 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(
153       gfx::Rect(),
154       target,
155       base::Bind(&ContentProxy::OnContentImageRead,
156                  proxy_content_to_image_factory_.GetWeakPtr()),
157       kAlpha_8_SkColorType);
158 }
159 
OnContentImageRead(bool success,const SkBitmap & bitmap)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();
164 
165   if (!success || bitmap.empty() || bitmap.isNull())
166     return;
167 
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);
172 
173   scoped_refptr<ProxyImageData> png_image = new ProxyImageData();
174   png_image->EncodeImage(
175       bitmap,
176       base::Bind(&ContentProxy::OnContentImageEncodeComplete,
177                  proxy_content_to_image_factory_.GetWeakPtr(),
178                  png_image));
179 }
180 
OnContentImageEncodeComplete(scoped_refptr<ProxyImageData> image)181 void ContentProxy::OnContentImageEncodeComplete(
182     scoped_refptr<ProxyImageData> image) {
183   png_data_ = image->data();
184 
185   // From now on we decode the image as needed to save memory.
186   raw_image_ = gfx::ImageSkia();
187 }
188 
189 }  // namespace athena
190