| Index: athena/content/content_proxy.cc
|
| diff --git a/athena/content/content_proxy.cc b/athena/content/content_proxy.cc
|
| index a63aa35ed08ddf20da32a8e2ab0e3595d071db28..7dce0e9266ec0ede53b7d2f90095e9215afa6245 100644
|
| --- a/athena/content/content_proxy.cc
|
| +++ b/athena/content/content_proxy.cc
|
| @@ -6,32 +6,74 @@
|
|
|
| #include "athena/activity/public/activity.h"
|
| #include "athena/activity/public/activity_view_model.h"
|
| +#include "base/bind.h"
|
| +#include "base/threading/worker_pool.h"
|
| +#include "content/public/browser/render_view_host.h"
|
| +#include "content/public/browser/render_widget_host_view.h"
|
| #include "content/public/browser/web_contents.h"
|
| #include "ui/aura/window.h"
|
| -#include "ui/views/background.h"
|
| +#include "ui/gfx/codec/png_codec.h"
|
| +#include "ui/gfx/geometry/rect.h"
|
| +#include "ui/gfx/image/image.h"
|
| +#include "ui/gfx/image/image_png_rep.h"
|
| #include "ui/views/controls/webview/webview.h"
|
| #include "ui/views/widget/widget.h"
|
|
|
| namespace athena {
|
|
|
| +// Encodes an A8 SkBitmap to grayscale PNG in a worker thread.
|
| +class ProxyImageData : public base::RefCountedThreadSafe<ProxyImageData> {
|
| + public:
|
| + ProxyImageData() {
|
| + }
|
| +
|
| + void EncodeImage(const SkBitmap& bitmap, base::Closure callback) {
|
| + if (!base::WorkerPool::PostTaskAndReply(FROM_HERE,
|
| + base::Bind(&ProxyImageData::EncodeOnWorker,
|
| + this,
|
| + bitmap),
|
| + callback,
|
| + true)) {
|
| + // When coming here, the resulting image will be empty.
|
| + DCHECK(false) << "Cannot start bitmap encode task.";
|
| + callback.Run();
|
| + }
|
| + }
|
| +
|
| + scoped_refptr<base::RefCountedBytes> data() const { return data_; }
|
| +
|
| + private:
|
| + friend class base::RefCountedThreadSafe<ProxyImageData>;
|
| + virtual ~ProxyImageData() {
|
| + }
|
| +
|
| + void EncodeOnWorker(const SkBitmap& bitmap) {
|
| + DCHECK_EQ(bitmap.colorType(), kAlpha_8_SkColorType);
|
| + // Encode the A8 bitmap to grayscale PNG treating alpha as color intensity.
|
| + std::vector<unsigned char> data;
|
| + if (gfx::PNGCodec::EncodeA8SkBitmap(bitmap, &data))
|
| + data_ = new base::RefCountedBytes(data);
|
| + }
|
| +
|
| + scoped_refptr<base::RefCountedBytes> data_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(ProxyImageData);
|
| +};
|
| +
|
| ContentProxy::ContentProxy(views::WebView* web_view, Activity* activity)
|
| : web_view_(web_view),
|
| - window_(activity->GetWindow()),
|
| - background_color_(
|
| - activity->GetActivityViewModel()->GetRepresentativeColor()),
|
| - content_loaded_(true) {
|
| + content_visible_(true),
|
| + content_loaded_(true),
|
| + content_creation_called_(false),
|
| + proxy_content_to_image_factory_(this) {
|
| + // Note: The content will be hidden once the image got created.
|
| CreateProxyContent();
|
| - HideOriginalContent();
|
| }
|
|
|
| ContentProxy::~ContentProxy() {
|
| // If we still have a connection to the original Activity, we make it visible
|
| // again.
|
| ShowOriginalContent();
|
| - // At this point we should either have no view - or the view should not be
|
| - // shown by its parent anymore.
|
| - DCHECK(!proxy_content_.get() || !proxy_content_->parent());
|
| - proxy_content_.reset();
|
| }
|
|
|
| void ContentProxy::ContentWillUnload() {
|
| @@ -39,39 +81,33 @@ void ContentProxy::ContentWillUnload() {
|
| }
|
|
|
| gfx::ImageSkia ContentProxy::GetContentImage() {
|
| - // For the time being we keep this here and return an empty image.
|
| - return image_;
|
| + // While we compress to PNG, we use the original read back.
|
| + if (!raw_image_.isNull() || !png_data_.get())
|
| + return raw_image_;
|
| +
|
| + // Otherwise we convert the PNG.
|
| + std::vector<gfx::ImagePNGRep> image_reps;
|
| + image_reps.push_back(gfx::ImagePNGRep(png_data_, 0.0f));
|
| + return *(gfx::Image(image_reps).ToImageSkia());
|
| }
|
|
|
| void ContentProxy::EvictContent() {
|
| - HideProxyContent();
|
| - CreateSolidProxyContent();
|
| - ShowProxyContent();
|
| + raw_image_ = gfx::ImageSkia();
|
| + png_data_->Release();
|
| }
|
|
|
| -void ContentProxy::Reparent(aura::Window* new_parent_window) {
|
| - if (new_parent_window == window_)
|
| - return;
|
| -
|
| +void ContentProxy::OnPreContentDestroyed() {
|
| // Since we are breaking now the connection to the old content, we make the
|
| // content visible again before we continue.
|
| // Note: Since the owning window is invisible, it does not matter that we
|
| // make the web content visible if the window gets destroyed shortly after.
|
| ShowOriginalContent();
|
|
|
| - // Transfer the |proxy_content_| to the passed window.
|
| - window_ = new_parent_window;
|
| web_view_ = NULL;
|
| -
|
| - // Move the view to the new window and show it there.
|
| - HideOriginalContent();
|
| }
|
|
|
| void ContentProxy::ShowOriginalContent() {
|
| - // Hide our content.
|
| - HideProxyContent();
|
| -
|
| - if (web_view_) {
|
| + if (web_view_ && !content_visible_) {
|
| // Show the original |web_view_| again.
|
| web_view_->SetFastResize(false);
|
| // If the content is loaded, we ask it to relayout itself since the
|
| @@ -81,52 +117,73 @@ void ContentProxy::ShowOriginalContent() {
|
| web_view_->Layout();
|
| web_view_->GetWebContents()->GetNativeView()->Show();
|
| web_view_->SetVisible(true);
|
| + content_visible_ = true;
|
| }
|
| }
|
|
|
| void ContentProxy::HideOriginalContent() {
|
| - if (web_view_) {
|
| + if (web_view_ && content_visible_) {
|
| // Hide the |web_view_|.
|
| // TODO(skuhne): We might consider removing the view from the window while
|
| // it's hidden - it should work the same way as show/hide and does not have
|
| - // any window re-ordering effect.
|
| + // any window re-ordering effect. Furthermore we want possibly to suppress
|
| + // any resizing of content (not only fast resize) here to avoid jank on
|
| + // rotation.
|
| web_view_->GetWebContents()->GetNativeView()->Hide();
|
| web_view_->SetVisible(false);
|
| // Don't allow the content to get resized with window size changes.
|
| web_view_->SetFastResize(true);
|
| + content_visible_ = false;
|
| }
|
| -
|
| - // Show our replacement content.
|
| - ShowProxyContent();
|
| }
|
|
|
| void ContentProxy::CreateProxyContent() {
|
| - // For the time being we create only a solid color here.
|
| - // TODO(skuhne): Replace with copying the drawn content into |proxy_content_|
|
| - // instead.
|
| - CreateSolidProxyContent();
|
| -}
|
| + DCHECK(!content_creation_called_);
|
| + content_creation_called_ = true;
|
| + // Unit tests might not have a |web_view_|.
|
| + if (!web_view_)
|
| + return;
|
|
|
| -void ContentProxy::CreateSolidProxyContent() {
|
| - proxy_content_.reset(new views::View());
|
| - proxy_content_->set_owned_by_client();
|
| - proxy_content_->set_background(
|
| - views::Background::CreateSolidBackground(background_color_));
|
| + content::RenderViewHost* host =
|
| + web_view_->GetWebContents()->GetRenderViewHost();
|
| + DCHECK(host && host->GetView());
|
| + gfx::Size source = host->GetView()->GetViewBounds().size();
|
| + gfx::Size target = gfx::Size(source.width() / 2, source.height() / 2);
|
| + host->CopyFromBackingStore(
|
| + gfx::Rect(),
|
| + target,
|
| + base::Bind(&ContentProxy::OnContentImageRead,
|
| + proxy_content_to_image_factory_.GetWeakPtr()),
|
| + kAlpha_8_SkColorType);
|
| }
|
|
|
| -void ContentProxy::ShowProxyContent() {
|
| - views::Widget* widget = views::Widget::GetWidgetForNativeWindow(window_);
|
| - DCHECK(widget);
|
| - views::View* client_view = widget->client_view();
|
| - // Place the view in front of all others.
|
| - client_view->AddChildView(proxy_content_.get());
|
| - proxy_content_->SetSize(client_view->bounds().size());
|
| +void ContentProxy::OnContentImageRead(bool success, const SkBitmap& bitmap) {
|
| + // Now we can hide the content. Note that after hiding we are freeing memory
|
| + // and if something goes wrong we will end up with an empty page.
|
| + HideOriginalContent();
|
| +
|
| + if (!success || bitmap.empty() || bitmap.isNull())
|
| + return;
|
| +
|
| + // While we are encoding the image, we keep the current image as reference
|
| + // to have something for the overview mode to grab. Once we have the encoded
|
| + // PNG, we will get rid of this.
|
| + raw_image_ = gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
|
| +
|
| + scoped_refptr<ProxyImageData> png_image = new ProxyImageData();
|
| + png_image->EncodeImage(
|
| + bitmap,
|
| + base::Bind(&ContentProxy::OnContentImageEncodeComplete,
|
| + proxy_content_to_image_factory_.GetWeakPtr(),
|
| + png_image));
|
| }
|
|
|
| -void ContentProxy::HideProxyContent() {
|
| - views::Widget* widget = views::Widget::GetWidgetForNativeWindow(window_);
|
| - views::View* client_view = widget->client_view();
|
| - client_view->RemoveChildView(proxy_content_.get());
|
| +void ContentProxy::OnContentImageEncodeComplete(
|
| + scoped_refptr<ProxyImageData> image) {
|
| + png_data_ = image->data();
|
| +
|
| + // From now on we decode the image as needed to save memory.
|
| + raw_image_ = gfx::ImageSkia();
|
| }
|
|
|
| } // namespace athena
|
|
|