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: 27 ProxyImageData() { 28 } 29 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 43 scoped_refptr<base::RefCountedBytes> data() const { return data_; } 44 45 private: 46 friend class base::RefCountedThreadSafe<ProxyImageData>; 47 virtual ~ProxyImageData() { 48 } 49 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 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 73 ContentProxy::~ContentProxy() { 74 // If we still have a connection to the original Activity, we make it visible 75 // again. 76 ShowOriginalContent(); 77 } 78 79 void ContentProxy::ContentWillUnload() { 80 content_loaded_ = false; 81 } 82 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 94 void ContentProxy::EvictContent() { 95 raw_image_ = gfx::ImageSkia(); 96 png_data_->Release(); 97 } 98 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 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 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 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 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 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