普通文本  |  190行  |  6.23 KB

// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "athena/content/content_proxy.h"

#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/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),
      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();
}

ContentProxy::~ContentProxy() {
  // If we still have a connection to the original Activity, we make it visible
  // again.
  ShowOriginalContent();
}

void ContentProxy::ContentWillUnload() {
  content_loaded_ = false;
}

gfx::ImageSkia ContentProxy::GetContentImage() {
  // 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() {
  raw_image_ = gfx::ImageSkia();
  png_data_->Release();
}

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();

  web_view_ = NULL;
}

void ContentProxy::ShowOriginalContent() {
  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
    // dimensions might have changed. If not, we will reload new content and no
    // layout is required for the old content.
    if (content_loaded_)
      web_view_->Layout();
    web_view_->GetWebContents()->GetNativeView()->Show();
    web_view_->SetVisible(true);
    content_visible_ = true;
  }
}

void ContentProxy::HideOriginalContent() {
  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. 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;
  }
}

void ContentProxy::CreateProxyContent() {
  DCHECK(!content_creation_called_);
  content_creation_called_ = true;
  // Unit tests might not have a |web_view_|.
  if (!web_view_)
    return;

  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::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::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