普通文本  |  881行  |  31.19 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 "android_webview/browser/browser_view_renderer.h"

#include "android_webview/browser/browser_view_renderer_client.h"
#include "android_webview/browser/shared_renderer_state.h"
#include "android_webview/common/aw_switches.h"
#include "android_webview/public/browser/draw_gl.h"
#include "base/android/jni_android.h"
#include "base/auto_reset.h"
#include "base/command_line.h"
#include "base/debug/trace_event.h"
#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "cc/output/compositor_frame.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_switches.h"
#include "gpu/command_buffer/service/gpu_switches.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkPicture.h"
#include "third_party/skia/include/core/SkPictureRecorder.h"
#include "ui/gfx/vector2d_conversions.h"

using base::android::AttachCurrentThread;
using base::android::JavaRef;
using base::android::ScopedJavaLocalRef;
using content::BrowserThread;
using content::SynchronousCompositorMemoryPolicy;

namespace android_webview {

namespace {

const int64 kFallbackTickTimeoutInMilliseconds = 100;

// Used to calculate memory allocation. Determined experimentally.
const size_t kMemoryMultiplier = 20;
const size_t kBytesPerPixel = 4;
const size_t kMemoryAllocationStep = 5 * 1024 * 1024;
uint64 g_memory_override_in_bytes = 0u;

// Used to calculate tile allocation. Determined experimentally.
const size_t kTileMultiplier = 12;
const size_t kTileAllocationStep = 20;
// This will be set by static function CalculateTileMemoryPolicy() during init.
// See AwMainDelegate::BasicStartupComplete.
size_t g_tile_area;

class TracedValue : public base::debug::ConvertableToTraceFormat {
 public:
  explicit TracedValue(base::Value* value) : value_(value) {}
  static scoped_refptr<base::debug::ConvertableToTraceFormat> FromValue(
      base::Value* value) {
    return scoped_refptr<base::debug::ConvertableToTraceFormat>(
        new TracedValue(value));
  }
  virtual void AppendAsTraceFormat(std::string* out) const OVERRIDE {
    std::string tmp;
    base::JSONWriter::Write(value_.get(), &tmp);
    *out += tmp;
  }

 private:
  virtual ~TracedValue() {}
  scoped_ptr<base::Value> value_;

  DISALLOW_COPY_AND_ASSIGN(TracedValue);
};

}  // namespace

// static
void BrowserViewRenderer::CalculateTileMemoryPolicy(bool use_zero_copy) {
  CommandLine* cl = CommandLine::ForCurrentProcess();

  // If the value was overridden on the command line, use the specified value.
  bool client_hard_limit_bytes_overridden =
      cl->HasSwitch(switches::kForceGpuMemAvailableMb);
  if (client_hard_limit_bytes_overridden) {
    base::StringToUint64(
        base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
            switches::kForceGpuMemAvailableMb),
        &g_memory_override_in_bytes);
    g_memory_override_in_bytes *= 1024 * 1024;
  }

  if (!use_zero_copy) {
    // Use chrome's default tile size, which varies from 256 to 512.
    // Be conservative here and use the smallest tile size possible.
    g_tile_area = 256 * 256;

    // Also use a high tile limit since there are no file descriptor issues.
    // There is no need to limit number of tiles, so use an effectively
    // unlimited value as the limit.
    GlobalTileManager::GetInstance()->SetTileLimit(10 * 1000 * 1000);
    return;
  }

  const char kDefaultTileSize[] = "384";

  if (!cl->HasSwitch(switches::kDefaultTileWidth))
    cl->AppendSwitchASCII(switches::kDefaultTileWidth, kDefaultTileSize);

  if (!cl->HasSwitch(switches::kDefaultTileHeight))
    cl->AppendSwitchASCII(switches::kDefaultTileHeight, kDefaultTileSize);

  size_t tile_size;
  base::StringToSizeT(kDefaultTileSize, &tile_size);
  g_tile_area = tile_size * tile_size;
}

BrowserViewRenderer::BrowserViewRenderer(
    BrowserViewRendererClient* client,
    SharedRendererState* shared_renderer_state,
    content::WebContents* web_contents,
    const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner)
    : client_(client),
      shared_renderer_state_(shared_renderer_state),
      web_contents_(web_contents),
      ui_task_runner_(ui_task_runner),
      compositor_(NULL),
      is_paused_(false),
      view_visible_(false),
      window_visible_(false),
      attached_to_window_(false),
      hardware_enabled_(false),
      dip_scale_(0.0),
      page_scale_factor_(1.0),
      on_new_picture_enable_(false),
      clear_view_(false),
      compositor_needs_continuous_invalidate_(false),
      invalidate_after_composite_(false),
      block_invalidates_(false),
      fallback_tick_pending_(false),
      width_(0),
      height_(0) {
  CHECK(web_contents_);
  content::SynchronousCompositor::SetClientForWebContents(web_contents_, this);

  // Currently the logic in this class relies on |compositor_| remaining
  // NULL until the DidInitializeCompositor() call, hence it is not set here.
}

BrowserViewRenderer::~BrowserViewRenderer() {
  content::SynchronousCompositor::SetClientForWebContents(web_contents_, NULL);
  // OnDetachedFromWindow should be called before the destructor, so the memory
  // policy should have already been updated.
}

// This function updates the resource allocation in GlobalTileManager.
void BrowserViewRenderer::TrimMemory(const int level, const bool visible) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  // Constants from Android ComponentCallbacks2.
  enum {
    TRIM_MEMORY_RUNNING_LOW = 10,
    TRIM_MEMORY_UI_HIDDEN = 20,
    TRIM_MEMORY_BACKGROUND = 40,
  };

  // Not urgent enough. TRIM_MEMORY_UI_HIDDEN is treated specially because
  // it does not indicate memory pressure, but merely that the app is
  // backgrounded.
  if (level < TRIM_MEMORY_RUNNING_LOW || level == TRIM_MEMORY_UI_HIDDEN)
    return;

  // Do not release resources on view we expect to get DrawGL soon.
  if (level < TRIM_MEMORY_BACKGROUND && visible)
    return;

  // Just set the memory limit to 0 and drop all tiles. This will be reset to
  // normal levels in the next DrawGL call.
  SynchronousCompositorMemoryPolicy zero_policy;
  if (memory_policy_ == zero_policy)
    return;

  TRACE_EVENT0("android_webview", "BrowserViewRenderer::TrimMemory");
  DCHECK(hardware_enabled_);
  DCHECK(compositor_);

  RequestMemoryPolicy(zero_policy);
  EnforceMemoryPolicyImmediately(zero_policy);
}

SynchronousCompositorMemoryPolicy
BrowserViewRenderer::CalculateDesiredMemoryPolicy() {
  SynchronousCompositorMemoryPolicy policy;
  size_t width = last_on_draw_global_visible_rect_.width();
  size_t height = last_on_draw_global_visible_rect_.height();
  policy.bytes_limit = kMemoryMultiplier * kBytesPerPixel * width * height;
  // Round up to a multiple of kMemoryAllocationStep.
  policy.bytes_limit =
      (policy.bytes_limit / kMemoryAllocationStep + 1) * kMemoryAllocationStep;

  if (g_memory_override_in_bytes)
    policy.bytes_limit = static_cast<size_t>(g_memory_override_in_bytes);

  size_t tiles = width * height * kTileMultiplier / g_tile_area;
  // Round up to a multiple of kTileAllocationStep. The minimum number of tiles
  // is also kTileAllocationStep.
  tiles = (tiles / kTileAllocationStep + 1) * kTileAllocationStep;
  policy.num_resources_limit = tiles;
  return policy;
}

// This function updates the cached memory policy in shared renderer state, as
// well as the tile resource allocation in GlobalTileManager.
void BrowserViewRenderer::RequestMemoryPolicy(
    SynchronousCompositorMemoryPolicy& new_policy) {
  DCHECK(compositor_);
  GlobalTileManager* manager = GlobalTileManager::GetInstance();

  // The following line will call BrowserViewRenderer::SetMemoryPolicy().
  manager->RequestTiles(new_policy, tile_manager_key_);
}

void BrowserViewRenderer::SetMemoryPolicy(
    SynchronousCompositorMemoryPolicy new_policy,
    bool effective_immediately) {
  memory_policy_ = new_policy;
  if (effective_immediately)
    EnforceMemoryPolicyImmediately(memory_policy_);
}

void BrowserViewRenderer::EnforceMemoryPolicyImmediately(
    SynchronousCompositorMemoryPolicy new_policy) {
  compositor_->SetMemoryPolicy(new_policy);
  ForceFakeCompositeSW();
}

SynchronousCompositorMemoryPolicy BrowserViewRenderer::GetMemoryPolicy() const {
  return memory_policy_;
}

bool BrowserViewRenderer::OnDraw(jobject java_canvas,
                                 bool is_hardware_canvas,
                                 const gfx::Vector2d& scroll,
                                 const gfx::Rect& global_visible_rect) {
  last_on_draw_scroll_offset_ = scroll;
  last_on_draw_global_visible_rect_ = global_visible_rect;

  if (clear_view_)
    return false;

  if (is_hardware_canvas && attached_to_window_ &&
      !switches::ForceAuxiliaryBitmap()) {
    return OnDrawHardware(java_canvas);
  }

  // Perform a software draw
  return OnDrawSoftware(java_canvas);
}

bool BrowserViewRenderer::OnDrawHardware(jobject java_canvas) {
  TRACE_EVENT0("android_webview", "BrowserViewRenderer::OnDrawHardware");
  if (!compositor_)
    return false;

  shared_renderer_state_->SetScrollOffset(last_on_draw_scroll_offset_);

  if (!hardware_enabled_) {
    hardware_enabled_ = compositor_->InitializeHwDraw();
    if (hardware_enabled_) {
      tile_manager_key_ = GlobalTileManager::GetInstance()->PushBack(this);
    }
  }
  if (!hardware_enabled_)
    return false;

  if (last_on_draw_global_visible_rect_.IsEmpty() &&
      parent_draw_constraints_.surface_rect.IsEmpty()) {
    TRACE_EVENT_INSTANT0("android_webview",
                         "EarlyOut_EmptyVisibleRect",
                         TRACE_EVENT_SCOPE_THREAD);
    shared_renderer_state_->SetForceInvalidateOnNextDrawGL(true);
    return client_->RequestDrawGL(java_canvas, false);
  }

  ReturnResourceFromParent();
  if (shared_renderer_state_->HasCompositorFrame()) {
    TRACE_EVENT_INSTANT0("android_webview",
                         "EarlyOut_PreviousFrameUnconsumed",
                         TRACE_EVENT_SCOPE_THREAD);
    DidSkipCompositeInDraw();
    return client_->RequestDrawGL(java_canvas, false);
  }

  scoped_ptr<cc::CompositorFrame> frame = CompositeHw();
  if (!frame.get())
    return false;

  shared_renderer_state_->SetCompositorFrame(frame.Pass(), false);
  GlobalTileManager::GetInstance()->DidUse(tile_manager_key_);
  return client_->RequestDrawGL(java_canvas, false);
}

scoped_ptr<cc::CompositorFrame> BrowserViewRenderer::CompositeHw() {
  SynchronousCompositorMemoryPolicy new_policy = CalculateDesiredMemoryPolicy();
  RequestMemoryPolicy(new_policy);
  compositor_->SetMemoryPolicy(memory_policy_);

  parent_draw_constraints_ = shared_renderer_state_->ParentDrawConstraints();
  gfx::Size surface_size(width_, height_);
  gfx::Rect viewport(surface_size);
  gfx::Rect clip = viewport;
  gfx::Transform transform_for_tile_priority =
      parent_draw_constraints_.transform;

  // If the WebView is on a layer, WebView does not know what transform is
  // applied onto the layer so global visible rect does not make sense here.
  // In this case, just use the surface rect for tiling.
  gfx::Rect viewport_rect_for_tile_priority;
  if (parent_draw_constraints_.is_layer ||
      last_on_draw_global_visible_rect_.IsEmpty()) {
    viewport_rect_for_tile_priority = parent_draw_constraints_.surface_rect;
  } else {
    viewport_rect_for_tile_priority = last_on_draw_global_visible_rect_;
  }

  scoped_ptr<cc::CompositorFrame> frame =
      compositor_->DemandDrawHw(surface_size,
                                gfx::Transform(),
                                viewport,
                                clip,
                                viewport_rect_for_tile_priority,
                                transform_for_tile_priority);
  if (frame.get())
    DidComposite();
  return frame.Pass();
}

void BrowserViewRenderer::UpdateParentDrawConstraints() {
  // Post an invalidate if the parent draw constraints are stale and there is
  // no pending invalidate.
  bool needs_force_invalidate =
      shared_renderer_state_->NeedsForceInvalidateOnNextDrawGL();
  if (needs_force_invalidate ||
      !parent_draw_constraints_.Equals(
          shared_renderer_state_->ParentDrawConstraints())) {
    shared_renderer_state_->SetForceInvalidateOnNextDrawGL(false);
    EnsureContinuousInvalidation(true, needs_force_invalidate);
  }
}

void BrowserViewRenderer::ReturnUnusedResource(
    scoped_ptr<cc::CompositorFrame> frame) {
  if (!frame.get())
    return;

  cc::CompositorFrameAck frame_ack;
  cc::TransferableResource::ReturnResources(
      frame->delegated_frame_data->resource_list, &frame_ack.resources);
  if (compositor_ && !frame_ack.resources.empty())
    compositor_->ReturnResources(frame_ack);
}

void BrowserViewRenderer::ReturnResourceFromParent() {
  cc::CompositorFrameAck frame_ack;
  shared_renderer_state_->SwapReturnedResources(&frame_ack.resources);
  if (compositor_ && !frame_ack.resources.empty()) {
    compositor_->ReturnResources(frame_ack);
  }
}

void BrowserViewRenderer::DidSkipCommitFrame() {
  // Treat it the same way as skipping onDraw.
  DidSkipCompositeInDraw();
}

bool BrowserViewRenderer::OnDrawSoftware(jobject java_canvas) {
  if (!compositor_) {
    TRACE_EVENT_INSTANT0(
        "android_webview", "EarlyOut_NoCompositor", TRACE_EVENT_SCOPE_THREAD);
    return false;
  }

  // TODO(hush): right now webview size is passed in as the auxiliary bitmap
  // size, which might hurt performace (only for software draws with auxiliary
  // bitmap). For better performance, get global visible rect, transform it
  // from screen space to view space, then intersect with the webview in
  // viewspace.  Use the resulting rect as the auxiliary
  // bitmap.
  return BrowserViewRendererJavaHelper::GetInstance()
      ->RenderViaAuxilaryBitmapIfNeeded(
          java_canvas,
          last_on_draw_scroll_offset_,
          gfx::Size(width_, height_),
          base::Bind(&BrowserViewRenderer::CompositeSW,
                     base::Unretained(this)));
}

skia::RefPtr<SkPicture> BrowserViewRenderer::CapturePicture(int width,
                                                            int height) {
  TRACE_EVENT0("android_webview", "BrowserViewRenderer::CapturePicture");

  // Return empty Picture objects for empty SkPictures.
  if (width <= 0 || height <= 0) {
    SkPictureRecorder emptyRecorder;
    emptyRecorder.beginRecording(0, 0);
    return skia::AdoptRef(emptyRecorder.endRecording());
  }

  // Reset scroll back to the origin, will go back to the old
  // value when scroll_reset is out of scope.
  base::AutoReset<gfx::Vector2dF> scroll_reset(&scroll_offset_dip_,
                                               gfx::Vector2dF());

  SkPictureRecorder recorder;
  SkCanvas* rec_canvas = recorder.beginRecording(width, height, NULL, 0);
  if (compositor_)
    CompositeSW(rec_canvas);
  return skia::AdoptRef(recorder.endRecording());
}

void BrowserViewRenderer::EnableOnNewPicture(bool enabled) {
  on_new_picture_enable_ = enabled;
}

void BrowserViewRenderer::ClearView() {
  TRACE_EVENT_INSTANT0("android_webview",
                       "BrowserViewRenderer::ClearView",
                       TRACE_EVENT_SCOPE_THREAD);
  if (clear_view_)
    return;

  clear_view_ = true;
  // Always invalidate ignoring the compositor to actually clear the webview.
  EnsureContinuousInvalidation(true, false);
}

void BrowserViewRenderer::SetIsPaused(bool paused) {
  TRACE_EVENT_INSTANT1("android_webview",
                       "BrowserViewRenderer::SetIsPaused",
                       TRACE_EVENT_SCOPE_THREAD,
                       "paused",
                       paused);
  is_paused_ = paused;
  EnsureContinuousInvalidation(false, false);
}

void BrowserViewRenderer::SetViewVisibility(bool view_visible) {
  TRACE_EVENT_INSTANT1("android_webview",
                       "BrowserViewRenderer::SetViewVisibility",
                       TRACE_EVENT_SCOPE_THREAD,
                       "view_visible",
                       view_visible);
  view_visible_ = view_visible;
}

void BrowserViewRenderer::SetWindowVisibility(bool window_visible) {
  TRACE_EVENT_INSTANT1("android_webview",
                       "BrowserViewRenderer::SetWindowVisibility",
                       TRACE_EVENT_SCOPE_THREAD,
                       "window_visible",
                       window_visible);
  window_visible_ = window_visible;
  EnsureContinuousInvalidation(false, false);
}

void BrowserViewRenderer::OnSizeChanged(int width, int height) {
  TRACE_EVENT_INSTANT2("android_webview",
                       "BrowserViewRenderer::OnSizeChanged",
                       TRACE_EVENT_SCOPE_THREAD,
                       "width",
                       width,
                       "height",
                       height);
  width_ = width;
  height_ = height;
}

void BrowserViewRenderer::OnAttachedToWindow(int width, int height) {
  TRACE_EVENT2("android_webview",
               "BrowserViewRenderer::OnAttachedToWindow",
               "width",
               width,
               "height",
               height);
  attached_to_window_ = true;
  width_ = width;
  height_ = height;
}

void BrowserViewRenderer::OnDetachedFromWindow() {
  TRACE_EVENT0("android_webview", "BrowserViewRenderer::OnDetachedFromWindow");
  attached_to_window_ = false;
  DCHECK(!hardware_enabled_);
}

void BrowserViewRenderer::ReleaseHardware() {
  DCHECK(hardware_enabled_);
  ReturnUnusedResource(shared_renderer_state_->PassCompositorFrame());
  ReturnResourceFromParent();
  DCHECK(shared_renderer_state_->ReturnedResourcesEmpty());

  if (compositor_) {
    compositor_->ReleaseHwDraw();
    SynchronousCompositorMemoryPolicy zero_policy;
    RequestMemoryPolicy(zero_policy);
  }

  hardware_enabled_ = false;
  GlobalTileManager::GetInstance()->Remove(tile_manager_key_);
}

bool BrowserViewRenderer::IsVisible() const {
  // Ignore |window_visible_| if |attached_to_window_| is false.
  return view_visible_ && (!attached_to_window_ || window_visible_);
}

gfx::Rect BrowserViewRenderer::GetScreenRect() const {
  return gfx::Rect(client_->GetLocationOnScreen(), gfx::Size(width_, height_));
}

void BrowserViewRenderer::DidInitializeCompositor(
    content::SynchronousCompositor* compositor) {
  TRACE_EVENT0("android_webview",
               "BrowserViewRenderer::DidInitializeCompositor");
  DCHECK(compositor);
  DCHECK(!compositor_);
  compositor_ = compositor;
}

void BrowserViewRenderer::DidDestroyCompositor(
    content::SynchronousCompositor* compositor) {
  TRACE_EVENT0("android_webview", "BrowserViewRenderer::DidDestroyCompositor");
  DCHECK(compositor_);
  SynchronousCompositorMemoryPolicy zero_policy;
  if (hardware_enabled_) {
    RequestMemoryPolicy(zero_policy);
  }
  DCHECK(memory_policy_ == zero_policy);
  compositor_ = NULL;
}

void BrowserViewRenderer::SetContinuousInvalidate(bool invalidate) {
  if (compositor_needs_continuous_invalidate_ == invalidate)
    return;

  TRACE_EVENT_INSTANT1("android_webview",
                       "BrowserViewRenderer::SetContinuousInvalidate",
                       TRACE_EVENT_SCOPE_THREAD,
                       "invalidate",
                       invalidate);
  compositor_needs_continuous_invalidate_ = invalidate;

  EnsureContinuousInvalidation(false, false);
}

void BrowserViewRenderer::SetDipScale(float dip_scale) {
  dip_scale_ = dip_scale;
  CHECK_GT(dip_scale_, 0);
}

gfx::Vector2d BrowserViewRenderer::max_scroll_offset() const {
  DCHECK_GT(dip_scale_, 0);
  return gfx::ToCeiledVector2d(gfx::ScaleVector2d(
      max_scroll_offset_dip_, dip_scale_ * page_scale_factor_));
}

void BrowserViewRenderer::ScrollTo(gfx::Vector2d scroll_offset) {
  gfx::Vector2d max_offset = max_scroll_offset();
  gfx::Vector2dF scroll_offset_dip;
  // To preserve the invariant that scrolling to the maximum physical pixel
  // value also scrolls to the maximum dip pixel value we transform the physical
  // offset into the dip offset by using a proportion (instead of dividing by
  // dip_scale * page_scale_factor).
  if (max_offset.x()) {
    scroll_offset_dip.set_x((scroll_offset.x() * max_scroll_offset_dip_.x()) /
                            max_offset.x());
  }
  if (max_offset.y()) {
    scroll_offset_dip.set_y((scroll_offset.y() * max_scroll_offset_dip_.y()) /
                            max_offset.y());
  }

  DCHECK_LE(0, scroll_offset_dip.x());
  DCHECK_LE(0, scroll_offset_dip.y());
  DCHECK_LE(scroll_offset_dip.x(), max_scroll_offset_dip_.x());
  DCHECK_LE(scroll_offset_dip.y(), max_scroll_offset_dip_.y());

  if (scroll_offset_dip_ == scroll_offset_dip)
    return;

  scroll_offset_dip_ = scroll_offset_dip;

  TRACE_EVENT_INSTANT2("android_webview",
               "BrowserViewRenderer::ScrollTo",
               TRACE_EVENT_SCOPE_THREAD,
               "x",
               scroll_offset_dip.x(),
               "y",
               scroll_offset_dip.y());

  if (compositor_)
    compositor_->DidChangeRootLayerScrollOffset();
}

void BrowserViewRenderer::DidUpdateContent() {
  TRACE_EVENT_INSTANT0("android_webview",
                       "BrowserViewRenderer::DidUpdateContent",
                       TRACE_EVENT_SCOPE_THREAD);
  clear_view_ = false;
  if (on_new_picture_enable_)
    client_->OnNewPicture();
}

void BrowserViewRenderer::SetTotalRootLayerScrollOffset(
    gfx::Vector2dF scroll_offset_dip) {
  // TOOD(mkosiba): Add a DCHECK to say that this does _not_ get called during
  // DrawGl when http://crbug.com/249972 is fixed.
  if (scroll_offset_dip_ == scroll_offset_dip)
    return;

  scroll_offset_dip_ = scroll_offset_dip;

  gfx::Vector2d max_offset = max_scroll_offset();
  gfx::Vector2d scroll_offset;
  // For an explanation as to why this is done this way see the comment in
  // BrowserViewRenderer::ScrollTo.
  if (max_scroll_offset_dip_.x()) {
    scroll_offset.set_x((scroll_offset_dip.x() * max_offset.x()) /
                        max_scroll_offset_dip_.x());
  }

  if (max_scroll_offset_dip_.y()) {
    scroll_offset.set_y((scroll_offset_dip.y() * max_offset.y()) /
                        max_scroll_offset_dip_.y());
  }

  DCHECK_LE(0, scroll_offset.x());
  DCHECK_LE(0, scroll_offset.y());
  DCHECK_LE(scroll_offset.x(), max_offset.x());
  DCHECK_LE(scroll_offset.y(), max_offset.y());

  client_->ScrollContainerViewTo(scroll_offset);
}

gfx::Vector2dF BrowserViewRenderer::GetTotalRootLayerScrollOffset() {
  return scroll_offset_dip_;
}

bool BrowserViewRenderer::IsExternalFlingActive() const {
  return client_->IsFlingActive();
}

void BrowserViewRenderer::UpdateRootLayerState(
    const gfx::Vector2dF& total_scroll_offset_dip,
    const gfx::Vector2dF& max_scroll_offset_dip,
    const gfx::SizeF& scrollable_size_dip,
    float page_scale_factor,
    float min_page_scale_factor,
    float max_page_scale_factor) {
  TRACE_EVENT_INSTANT1(
      "android_webview",
      "BrowserViewRenderer::UpdateRootLayerState",
      TRACE_EVENT_SCOPE_THREAD,
      "state",
      TracedValue::FromValue(
          RootLayerStateAsValue(total_scroll_offset_dip, scrollable_size_dip)
              .release()));

  DCHECK_GT(dip_scale_, 0);

  max_scroll_offset_dip_ = max_scroll_offset_dip;
  DCHECK_LE(0, max_scroll_offset_dip_.x());
  DCHECK_LE(0, max_scroll_offset_dip_.y());

  page_scale_factor_ = page_scale_factor;
  DCHECK_GT(page_scale_factor_, 0);

  client_->UpdateScrollState(max_scroll_offset(),
                             scrollable_size_dip,
                             page_scale_factor,
                             min_page_scale_factor,
                             max_page_scale_factor);
  SetTotalRootLayerScrollOffset(total_scroll_offset_dip);
}

scoped_ptr<base::Value> BrowserViewRenderer::RootLayerStateAsValue(
    const gfx::Vector2dF& total_scroll_offset_dip,
    const gfx::SizeF& scrollable_size_dip) {
  scoped_ptr<base::DictionaryValue> state(new base::DictionaryValue);

  state->SetDouble("total_scroll_offset_dip.x", total_scroll_offset_dip.x());
  state->SetDouble("total_scroll_offset_dip.y", total_scroll_offset_dip.y());

  state->SetDouble("max_scroll_offset_dip.x", max_scroll_offset_dip_.x());
  state->SetDouble("max_scroll_offset_dip.y", max_scroll_offset_dip_.y());

  state->SetDouble("scrollable_size_dip.width", scrollable_size_dip.width());
  state->SetDouble("scrollable_size_dip.height", scrollable_size_dip.height());

  state->SetDouble("page_scale_factor", page_scale_factor_);
  return state.PassAs<base::Value>();
}

void BrowserViewRenderer::DidOverscroll(gfx::Vector2dF accumulated_overscroll,
                                        gfx::Vector2dF latest_overscroll_delta,
                                        gfx::Vector2dF current_fling_velocity) {
  const float physical_pixel_scale = dip_scale_ * page_scale_factor_;
  if (accumulated_overscroll == latest_overscroll_delta)
    overscroll_rounding_error_ = gfx::Vector2dF();
  gfx::Vector2dF scaled_overscroll_delta =
      gfx::ScaleVector2d(latest_overscroll_delta, physical_pixel_scale);
  gfx::Vector2d rounded_overscroll_delta = gfx::ToRoundedVector2d(
      scaled_overscroll_delta + overscroll_rounding_error_);
  overscroll_rounding_error_ =
      scaled_overscroll_delta - rounded_overscroll_delta;
  client_->DidOverscroll(rounded_overscroll_delta);
}

void BrowserViewRenderer::EnsureContinuousInvalidation(
    bool force_invalidate,
    bool skip_reschedule_tick) {
  if (force_invalidate)
    invalidate_after_composite_ = true;

  // This method should be called again when any of these conditions change.
  bool need_invalidate =
      compositor_needs_continuous_invalidate_ || invalidate_after_composite_;
  if (!need_invalidate || block_invalidates_)
    return;

  if (!compositor_needs_continuous_invalidate_ && invalidate_after_composite_)
    invalidate_after_composite_ = false;

  // Always call view invalidate. We rely the Android framework to ignore the
  // invalidate when it's not needed such as when view is not visible.
  client_->PostInvalidate();

  // Stop fallback ticks when one of these is true.
  // 1) Webview is paused. Also need to check we are not in clear view since
  // paused, offscreen still expect clear view to recover.
  // 2) If we are attached to window and the window is not visible (eg when
  // app is in the background). We are sure in this case the webview is used
  // "on-screen" but that updates are not needed when in the background.
  bool throttle_fallback_tick =
      (is_paused_ && !clear_view_) || (attached_to_window_ && !window_visible_);
  if (throttle_fallback_tick)
    return;

  block_invalidates_ = compositor_needs_continuous_invalidate_;
  if (skip_reschedule_tick && fallback_tick_pending_)
    return;

  // Unretained here is safe because the callbacks are cancelled when
  // they are destroyed.
  post_fallback_tick_.Reset(base::Bind(&BrowserViewRenderer::PostFallbackTick,
                                       base::Unretained(this)));
  fallback_tick_fired_.Cancel();
  fallback_tick_pending_ = false;

  // No need to reschedule fallback tick if compositor does not need to be
  // ticked. This can happen if this is reached because force_invalidate is
  // true.
  if (compositor_needs_continuous_invalidate_) {
    fallback_tick_pending_ = true;
    ui_task_runner_->PostTask(FROM_HERE, post_fallback_tick_.callback());
  }
}

void BrowserViewRenderer::PostFallbackTick() {
  DCHECK(fallback_tick_fired_.IsCancelled());
  fallback_tick_fired_.Reset(base::Bind(&BrowserViewRenderer::FallbackTickFired,
                                        base::Unretained(this)));
  if (compositor_needs_continuous_invalidate_) {
    ui_task_runner_->PostDelayedTask(
        FROM_HERE,
        fallback_tick_fired_.callback(),
        base::TimeDelta::FromMilliseconds(kFallbackTickTimeoutInMilliseconds));
  } else {
    // Pretend we just composited to unblock further invalidates.
    DidComposite();
  }
}

void BrowserViewRenderer::FallbackTickFired() {
  TRACE_EVENT1("android_webview",
               "BrowserViewRenderer::FallbackTickFired",
               "compositor_needs_continuous_invalidate_",
               compositor_needs_continuous_invalidate_);

  // This should only be called if OnDraw or DrawGL did not come in time, which
  // means block_invalidates_ must still be true.
  DCHECK(block_invalidates_);
  fallback_tick_pending_ = false;
  if (compositor_needs_continuous_invalidate_ && compositor_) {
    if (hardware_enabled_) {
      ReturnResourceFromParent();
      ReturnUnusedResource(shared_renderer_state_->PassCompositorFrame());
      scoped_ptr<cc::CompositorFrame> frame = CompositeHw();
      if (frame.get()) {
        shared_renderer_state_->SetCompositorFrame(frame.Pass(), true);
      }
    } else {
      ForceFakeCompositeSW();
    }
  } else {
    // Pretend we just composited to unblock further invalidates.
    DidComposite();
  }
}

void BrowserViewRenderer::ForceFakeCompositeSW() {
  DCHECK(compositor_);
  SkBitmap bitmap;
  bitmap.allocN32Pixels(1, 1);
  bitmap.eraseColor(0);
  SkCanvas canvas(bitmap);
  CompositeSW(&canvas);
}

bool BrowserViewRenderer::CompositeSW(SkCanvas* canvas) {
  DCHECK(compositor_);
  ReturnResourceFromParent();
  bool result = compositor_->DemandDrawSw(canvas);
  DidComposite();
  return result;
}

void BrowserViewRenderer::DidComposite() {
  block_invalidates_ = false;
  post_fallback_tick_.Cancel();
  fallback_tick_fired_.Cancel();
  fallback_tick_pending_ = false;
  EnsureContinuousInvalidation(false, false);
}

void BrowserViewRenderer::DidSkipCompositeInDraw() {
  block_invalidates_ = false;
  EnsureContinuousInvalidation(true, true);
}

std::string BrowserViewRenderer::ToString(AwDrawGLInfo* draw_info) const {
  std::string str;
  base::StringAppendF(&str, "is_paused: %d ", is_paused_);
  base::StringAppendF(&str, "view_visible: %d ", view_visible_);
  base::StringAppendF(&str, "window_visible: %d ", window_visible_);
  base::StringAppendF(&str, "dip_scale: %f ", dip_scale_);
  base::StringAppendF(&str, "page_scale_factor: %f ", page_scale_factor_);
  base::StringAppendF(&str,
                      "compositor_needs_continuous_invalidate: %d ",
                      compositor_needs_continuous_invalidate_);
  base::StringAppendF(&str, "block_invalidates: %d ", block_invalidates_);
  base::StringAppendF(&str, "view width height: [%d %d] ", width_, height_);
  base::StringAppendF(&str, "attached_to_window: %d ", attached_to_window_);
  base::StringAppendF(&str,
                      "global visible rect: %s ",
                      last_on_draw_global_visible_rect_.ToString().c_str());
  base::StringAppendF(
      &str, "scroll_offset_dip: %s ", scroll_offset_dip_.ToString().c_str());
  base::StringAppendF(&str,
                      "overscroll_rounding_error_: %s ",
                      overscroll_rounding_error_.ToString().c_str());
  base::StringAppendF(
      &str, "on_new_picture_enable: %d ", on_new_picture_enable_);
  base::StringAppendF(&str, "clear_view: %d ", clear_view_);
  if (draw_info) {
    base::StringAppendF(&str,
                        "clip left top right bottom: [%d %d %d %d] ",
                        draw_info->clip_left,
                        draw_info->clip_top,
                        draw_info->clip_right,
                        draw_info->clip_bottom);
    base::StringAppendF(&str,
                        "surface width height: [%d %d] ",
                        draw_info->width,
                        draw_info->height);
    base::StringAppendF(&str, "is_layer: %d ", draw_info->is_layer);
  }
  return str;
}

}  // namespace android_webview