// Copyright (c) 2012 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 "content/renderer/gpu/gpu_benchmarking_extension.h"
#include <string>
#include "base/base64.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/memory/scoped_vector.h"
#include "base/strings/string_number_conversions.h"
#include "cc/layers/layer.h"
#include "content/common/input/synthetic_gesture_params.h"
#include "content/common/input/synthetic_pinch_gesture_params.h"
#include "content/common/input/synthetic_smooth_scroll_gesture_params.h"
#include "content/common/input/synthetic_tap_gesture_params.h"
#include "content/public/renderer/render_thread.h"
#include "content/public/renderer/v8_value_converter.h"
#include "content/renderer/gpu/render_widget_compositor.h"
#include "content/renderer/render_thread_impl.h"
#include "content/renderer/render_view_impl.h"
#include "content/renderer/skia_benchmarking_extension.h"
#include "third_party/WebKit/public/web/WebImageCache.h"
#include "third_party/WebKit/public/web/WebLocalFrame.h"
#include "third_party/WebKit/public/web/WebView.h"
#include "third_party/skia/include/core/SkData.h"
#include "third_party/skia/include/core/SkGraphics.h"
#include "third_party/skia/include/core/SkPicture.h"
#include "third_party/skia/include/core/SkPixelRef.h"
#include "third_party/skia/include/core/SkStream.h"
#include "ui/gfx/codec/png_codec.h"
#include "v8/include/v8.h"
using blink::WebCanvas;
using blink::WebLocalFrame;
using blink::WebImageCache;
using blink::WebPrivatePtr;
using blink::WebSize;
using blink::WebView;
const char kGpuBenchmarkingExtensionName[] = "v8/GpuBenchmarking";
// offset parameter is deprecated/ignored, and will be remove from the
// signature in a future skia release. <reed@google.com>
static SkData* EncodeBitmapToData(size_t* offset, const SkBitmap& bm) {
SkPixelRef* pr = bm.pixelRef();
if (pr != NULL) {
SkData* data = pr->refEncodedData();
if (data != NULL)
return data;
}
std::vector<unsigned char> vector;
if (gfx::PNGCodec::EncodeBGRASkBitmap(bm, false, &vector)) {
return SkData::NewWithCopy(&vector.front() , vector.size());
}
return NULL;
}
namespace {
class SkPictureSerializer {
public:
explicit SkPictureSerializer(const base::FilePath& dirpath)
: dirpath_(dirpath),
layer_id_(0) {
// Let skia register known effect subclasses. This basically enables
// reflection on those subclasses required for picture serialization.
content::SkiaBenchmarking::Initialize();
}
// Recursively serializes the layer tree.
// Each layer in the tree is serialized into a separate skp file
// in the given directory.
void Serialize(const cc::Layer* layer) {
const cc::LayerList& children = layer->children();
for (size_t i = 0; i < children.size(); ++i) {
Serialize(children[i].get());
}
skia::RefPtr<SkPicture> picture = layer->GetPicture();
if (!picture)
return;
// Serialize picture to file.
// TODO(alokp): Note that for this to work Chrome needs to be launched with
// --no-sandbox command-line flag. Get rid of this limitation.
// CRBUG: 139640.
std::string filename = "layer_" + base::IntToString(layer_id_++) + ".skp";
std::string filepath = dirpath_.AppendASCII(filename).MaybeAsASCII();
DCHECK(!filepath.empty());
SkFILEWStream file(filepath.c_str());
DCHECK(file.isValid());
picture->serialize(&file, &EncodeBitmapToData);
}
private:
base::FilePath dirpath_;
int layer_id_;
};
} // namespace
namespace content {
namespace {
class CallbackAndContext : public base::RefCounted<CallbackAndContext> {
public:
CallbackAndContext(v8::Isolate* isolate,
v8::Handle<v8::Function> callback,
v8::Handle<v8::Context> context)
: isolate_(isolate) {
callback_.Reset(isolate_, callback);
context_.Reset(isolate_, context);
}
v8::Isolate* isolate() {
return isolate_;
}
v8::Handle<v8::Function> GetCallback() {
return v8::Local<v8::Function>::New(isolate_, callback_);
}
v8::Handle<v8::Context> GetContext() {
return v8::Local<v8::Context>::New(isolate_, context_);
}
private:
friend class base::RefCounted<CallbackAndContext>;
virtual ~CallbackAndContext() {
callback_.Reset();
context_.Reset();
}
v8::Isolate* isolate_;
v8::Persistent<v8::Function> callback_;
v8::Persistent<v8::Context> context_;
DISALLOW_COPY_AND_ASSIGN(CallbackAndContext);
};
class GpuBenchmarkingContext {
public:
GpuBenchmarkingContext()
: web_frame_(NULL),
web_view_(NULL),
render_view_impl_(NULL),
compositor_(NULL) {}
bool Init(bool init_compositor) {
web_frame_ = WebLocalFrame::frameForCurrentContext();
if (!web_frame_)
return false;
web_view_ = web_frame_->view();
if (!web_view_) {
web_frame_ = NULL;
return false;
}
render_view_impl_ = RenderViewImpl::FromWebView(web_view_);
if (!render_view_impl_) {
web_frame_ = NULL;
web_view_ = NULL;
return false;
}
if (!init_compositor)
return true;
compositor_ = render_view_impl_->compositor();
if (!compositor_) {
web_frame_ = NULL;
web_view_ = NULL;
render_view_impl_ = NULL;
return false;
}
return true;
}
WebLocalFrame* web_frame() const {
DCHECK(web_frame_ != NULL);
return web_frame_;
}
WebView* web_view() const {
DCHECK(web_view_ != NULL);
return web_view_;
}
RenderViewImpl* render_view_impl() const {
DCHECK(render_view_impl_ != NULL);
return render_view_impl_;
}
RenderWidgetCompositor* compositor() const {
DCHECK(compositor_ != NULL);
return compositor_;
}
private:
WebLocalFrame* web_frame_;
WebView* web_view_;
RenderViewImpl* render_view_impl_;
RenderWidgetCompositor* compositor_;
DISALLOW_COPY_AND_ASSIGN(GpuBenchmarkingContext);
};
} // namespace
class GpuBenchmarkingWrapper : public v8::Extension {
public:
GpuBenchmarkingWrapper() :
v8::Extension(kGpuBenchmarkingExtensionName,
"if (typeof(chrome) == 'undefined') {"
" chrome = {};"
"};"
"if (typeof(chrome.gpuBenchmarking) == 'undefined') {"
" chrome.gpuBenchmarking = {};"
"};"
"chrome.gpuBenchmarking.setNeedsDisplayOnAllLayers = function() {"
" native function SetNeedsDisplayOnAllLayers();"
" return SetNeedsDisplayOnAllLayers();"
"};"
"chrome.gpuBenchmarking.setRasterizeOnlyVisibleContent = "
"function() {"
" native function SetRasterizeOnlyVisibleContent();"
" return SetRasterizeOnlyVisibleContent();"
"};"
"chrome.gpuBenchmarking.printToSkPicture = function(dirname) {"
" native function PrintToSkPicture();"
" return PrintToSkPicture(dirname);"
"};"
"chrome.gpuBenchmarking.DEFAULT_INPUT = 0;"
"chrome.gpuBenchmarking.TOUCH_INPUT = 1;"
"chrome.gpuBenchmarking.MOUSE_INPUT = 2;"
"chrome.gpuBenchmarking.gestureSourceTypeSupported = "
" function(gesture_source_type) {"
" native function GestureSourceTypeSupported();"
" return GestureSourceTypeSupported(gesture_source_type);"
"};"
"chrome.gpuBenchmarking.smoothScrollBy = "
" function(pixels_to_scroll, opt_callback, opt_start_x,"
" opt_start_y, opt_gesture_source_type,"
" opt_direction, opt_speed_in_pixels_s) {"
" pixels_to_scroll = pixels_to_scroll || 0;"
" callback = opt_callback || function() { };"
" gesture_source_type = opt_gesture_source_type ||"
" chrome.gpuBenchmarking.DEFAULT_INPUT;"
" direction = opt_direction || 'down';"
" speed_in_pixels_s = opt_speed_in_pixels_s || 800;"
" native function BeginSmoothScroll();"
" return BeginSmoothScroll(pixels_to_scroll, callback,"
" gesture_source_type, direction,"
" speed_in_pixels_s, true,"
" opt_start_x, opt_start_y);"
"};"
"chrome.gpuBenchmarking.swipe = "
" function(direction, distance, opt_callback,"
" opt_start_x, opt_start_y,"
" opt_speed_in_pixels_s) {"
" direction = direction || 'up';"
" distance = distance || 0;"
" callback = opt_callback || function() { };"
" speed_in_pixels_s = opt_speed_in_pixels_s || 800;"
" native function BeginSmoothScroll();"
" return BeginSmoothScroll(-distance, callback,"
" chrome.gpuBenchmarking.TOUCH_INPUT,"
" direction, speed_in_pixels_s, false,"
" opt_start_x, opt_start_y);"
"};"
"chrome.gpuBenchmarking.scrollBounce = "
" function(direction, distance, overscroll, opt_repeat_count,"
" opt_callback, opt_start_x, opt_start_y,"
" opt_speed_in_pixels_s) {"
" direction = direction || 'down';"
" distance = distance || 0;"
" overscroll = overscroll || 0;"
" repeat_count = opt_repeat_count || 1;"
" callback = opt_callback || function() { };"
" speed_in_pixels_s = opt_speed_in_pixels_s || 800;"
" native function BeginScrollBounce();"
" return BeginScrollBounce(direction, distance, overscroll,"
" repeat_count, callback,"
" speed_in_pixels_s,"
" opt_start_x, opt_start_y);"
"};"
// TODO(dominikg): Remove once JS interface changes have rolled into
// stable.
"chrome.gpuBenchmarking.newPinchInterface = true;"
"chrome.gpuBenchmarking.pinchBy = "
" function(scale_factor, anchor_x, anchor_y,"
" opt_callback, "
"opt_relative_pointer_speed_in_pixels_s) {"
" callback = opt_callback || function() { };"
" relative_pointer_speed_in_pixels_s ="
" opt_relative_pointer_speed_in_pixels_s || 800;"
" native function BeginPinch();"
" return BeginPinch(scale_factor, anchor_x, anchor_y, callback,"
" relative_pointer_speed_in_pixels_s);"
"};"
"chrome.gpuBenchmarking.tap = "
" function(position_x, position_y, opt_callback, "
"opt_duration_ms,"
" opt_gesture_source_type) {"
" callback = opt_callback || function() { };"
" duration_ms = opt_duration_ms || 50;"
" gesture_source_type = opt_gesture_source_type ||"
" chrome.gpuBenchmarking.DEFAULT_INPUT;"
" native function BeginTap();"
" return BeginTap(position_x, position_y, callback, duration_ms,"
" gesture_source_type);"
"};"
"chrome.gpuBenchmarking.beginWindowSnapshotPNG = "
"function(callback) {"
" native function BeginWindowSnapshotPNG();"
" BeginWindowSnapshotPNG(callback);"
"};"
"chrome.gpuBenchmarking.clearImageCache = function() {"
" native function ClearImageCache();"
" ClearImageCache();"
"};"
"chrome.gpuBenchmarking.runMicroBenchmark ="
" function(name, callback, opt_arguments) {"
" arguments = opt_arguments || {};"
" native function RunMicroBenchmark();"
" return RunMicroBenchmark(name, callback, arguments);"
"};"
"chrome.gpuBenchmarking.sendMessageToMicroBenchmark ="
" function(id, arguments) {"
" native function SendMessageToMicroBenchmark();"
" return SendMessageToMicroBenchmark(id, arguments);"
"};"
"chrome.gpuBenchmarking.hasGpuProcess = function() {"
" native function HasGpuProcess();"
" return HasGpuProcess();"
"};") {}
virtual v8::Handle<v8::FunctionTemplate> GetNativeFunctionTemplate(
v8::Isolate* isolate,
v8::Handle<v8::String> name) OVERRIDE {
if (name->Equals(
v8::String::NewFromUtf8(isolate, "SetNeedsDisplayOnAllLayers")))
return v8::FunctionTemplate::New(isolate, SetNeedsDisplayOnAllLayers);
if (name->Equals(
v8::String::NewFromUtf8(isolate, "SetRasterizeOnlyVisibleContent")))
return v8::FunctionTemplate::New(isolate, SetRasterizeOnlyVisibleContent);
if (name->Equals(v8::String::NewFromUtf8(isolate, "PrintToSkPicture")))
return v8::FunctionTemplate::New(isolate, PrintToSkPicture);
if (name->Equals(
v8::String::NewFromUtf8(isolate, "GestureSourceTypeSupported")))
return v8::FunctionTemplate::New(isolate, GestureSourceTypeSupported);
if (name->Equals(v8::String::NewFromUtf8(isolate, "BeginSmoothScroll")))
return v8::FunctionTemplate::New(isolate, BeginSmoothScroll);
if (name->Equals(v8::String::NewFromUtf8(isolate, "BeginScrollBounce")))
return v8::FunctionTemplate::New(isolate, BeginScrollBounce);
if (name->Equals(v8::String::NewFromUtf8(isolate, "BeginPinch")))
return v8::FunctionTemplate::New(isolate, BeginPinch);
if (name->Equals(v8::String::NewFromUtf8(isolate, "BeginTap")))
return v8::FunctionTemplate::New(isolate, BeginTap);
if (name->Equals(
v8::String::NewFromUtf8(isolate, "BeginWindowSnapshotPNG")))
return v8::FunctionTemplate::New(isolate, BeginWindowSnapshotPNG);
if (name->Equals(v8::String::NewFromUtf8(isolate, "ClearImageCache")))
return v8::FunctionTemplate::New(isolate, ClearImageCache);
if (name->Equals(v8::String::NewFromUtf8(isolate, "RunMicroBenchmark")))
return v8::FunctionTemplate::New(isolate, RunMicroBenchmark);
if (name->Equals(
v8::String::NewFromUtf8(isolate, "SendMessageToMicroBenchmark")))
return v8::FunctionTemplate::New(isolate, SendMessageToMicroBenchmark);
if (name->Equals(v8::String::NewFromUtf8(isolate, "HasGpuProcess")))
return v8::FunctionTemplate::New(isolate, HasGpuProcess);
return v8::Handle<v8::FunctionTemplate>();
}
static void SetNeedsDisplayOnAllLayers(
const v8::FunctionCallbackInfo<v8::Value>& args) {
GpuBenchmarkingContext context;
if (!context.Init(true))
return;
context.compositor()->SetNeedsDisplayOnAllLayers();
}
static void SetRasterizeOnlyVisibleContent(
const v8::FunctionCallbackInfo<v8::Value>& args) {
GpuBenchmarkingContext context;
if (!context.Init(true))
return;
context.compositor()->SetRasterizeOnlyVisibleContent();
}
static void PrintToSkPicture(
const v8::FunctionCallbackInfo<v8::Value>& args) {
if (args.Length() != 1)
return;
v8::String::Utf8Value dirname(args[0]);
if (dirname.length() == 0)
return;
GpuBenchmarkingContext context;
if (!context.Init(true))
return;
const cc::Layer* root_layer = context.compositor()->GetRootLayer();
if (!root_layer)
return;
base::FilePath dirpath(
base::FilePath::StringType(*dirname, *dirname + dirname.length()));
if (!base::CreateDirectory(dirpath) ||
!base::PathIsWritable(dirpath)) {
std::string msg("Path is not writable: ");
msg.append(dirpath.MaybeAsASCII());
v8::Isolate* isolate = args.GetIsolate();
isolate->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8(
isolate, msg.c_str(), v8::String::kNormalString, msg.length())));
return;
}
SkPictureSerializer serializer(dirpath);
serializer.Serialize(root_layer);
}
static void OnSyntheticGestureCompleted(
CallbackAndContext* callback_and_context) {
v8::Isolate* isolate = callback_and_context->isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::Context> context = callback_and_context->GetContext();
v8::Context::Scope context_scope(context);
WebLocalFrame* frame = WebLocalFrame::frameForContext(context);
if (frame) {
frame->callFunctionEvenIfScriptDisabled(
callback_and_context->GetCallback(),
v8::Object::New(isolate),
0,
NULL);
}
}
static void GestureSourceTypeSupported(
const v8::FunctionCallbackInfo<v8::Value>& args) {
if (args.Length() != 1 || !args[0]->IsNumber()) {
args.GetReturnValue().Set(false);
return;
}
int gesture_source_type = args[0]->IntegerValue();
if (gesture_source_type < 0 ||
gesture_source_type > SyntheticGestureParams::GESTURE_SOURCE_TYPE_MAX) {
args.GetReturnValue().Set(false);
return;
}
bool is_supported = SyntheticGestureParams::IsGestureSourceTypeSupported(
static_cast<SyntheticGestureParams::GestureSourceType>(
gesture_source_type));
args.GetReturnValue().Set(is_supported);
}
static void BeginSmoothScroll(
const v8::FunctionCallbackInfo<v8::Value>& args) {
GpuBenchmarkingContext context;
if (!context.Init(false))
return;
// The last two arguments can be undefined. We check their validity later.
int arglen = args.Length();
if (arglen < 8 ||
!args[0]->IsNumber() ||
!args[1]->IsFunction() ||
!args[2]->IsNumber() ||
!args[3]->IsString() ||
!args[4]->IsNumber() ||
!args[5]->IsBoolean()) {
args.GetReturnValue().Set(false);
return;
}
v8::Local<v8::Function> callback_local =
v8::Local<v8::Function>::Cast(args[1]);
scoped_refptr<CallbackAndContext> callback_and_context =
new CallbackAndContext(args.GetIsolate(),
callback_local,
context.web_frame()->mainWorldScriptContext());
scoped_ptr<SyntheticSmoothScrollGestureParams> gesture_params(
new SyntheticSmoothScrollGestureParams);
// Convert coordinates from CSS pixels to density independent pixels (DIPs).
float page_scale_factor = context.web_view()->pageScaleFactor();
int gesture_source_type = args[2]->IntegerValue();
if (gesture_source_type < 0 ||
gesture_source_type > SyntheticGestureParams::GESTURE_SOURCE_TYPE_MAX) {
args.GetReturnValue().Set(false);
return;
}
gesture_params->gesture_source_type =
static_cast<SyntheticGestureParams::GestureSourceType>(
gesture_source_type);
gesture_params->speed_in_pixels_s = args[4]->IntegerValue();
gesture_params->prevent_fling = args[5]->BooleanValue();
// Account for the 2 optional arguments, start_x and start_y.
gfx::Point anchor;
if (args[6]->IsUndefined() || args[7]->IsUndefined()) {
blink::WebRect rect = context.render_view_impl()->windowRect();
anchor.SetPoint(rect.width / 2, rect.height / 2);
} else if (args[6]->IsNumber() && args[7]->IsNumber()) {
anchor.SetPoint(args[6]->IntegerValue() * page_scale_factor,
args[7]->IntegerValue() * page_scale_factor);
} else {
args.GetReturnValue().Set(false);
return;
}
gesture_params->anchor = anchor;
int distance_length = args[0]->IntegerValue() * page_scale_factor;
gfx::Vector2d distance;
v8::String::Utf8Value direction(args[3]);
DCHECK(*direction);
std::string direction_str(*direction);
if (direction_str == "down")
distance.set_y(-distance_length);
else if (direction_str == "up")
distance.set_y(distance_length);
else if (direction_str == "right")
distance.set_x(-distance_length);
else if (direction_str == "left")
distance.set_x(distance_length);
else {
args.GetReturnValue().Set(false);
return;
}
gesture_params->distances.push_back(distance);
// TODO(nduca): If the render_view_impl is destroyed while the gesture is in
// progress, we will leak the callback and context. This needs to be fixed,
// somehow.
context.render_view_impl()->QueueSyntheticGesture(
gesture_params.PassAs<SyntheticGestureParams>(),
base::Bind(&OnSyntheticGestureCompleted,
callback_and_context));
args.GetReturnValue().Set(true);
}
static void BeginScrollBounce(
const v8::FunctionCallbackInfo<v8::Value>& args) {
GpuBenchmarkingContext context;
if (!context.Init(false))
return;
// The last two arguments can be undefined. We check their validity later.
int arglen = args.Length();
if (arglen < 8 ||
!args[0]->IsString() ||
!args[1]->IsNumber() ||
!args[2]->IsNumber() ||
!args[3]->IsNumber() ||
!args[4]->IsFunction() ||
!args[5]->IsNumber()) {
args.GetReturnValue().Set(false);
return;
}
v8::Local<v8::Function> callback_local =
v8::Local<v8::Function>::Cast(args[4]);
scoped_refptr<CallbackAndContext> callback_and_context =
new CallbackAndContext(args.GetIsolate(),
callback_local,
context.web_frame()->mainWorldScriptContext());
scoped_ptr<SyntheticSmoothScrollGestureParams> gesture_params(
new SyntheticSmoothScrollGestureParams);
// Convert coordinates from CSS pixels to density independent pixels (DIPs).
float page_scale_factor = context.web_view()->pageScaleFactor();
gesture_params->speed_in_pixels_s = args[5]->IntegerValue();
// Account for the 2 optional arguments, start_x and start_y.
gfx::Point start;
if (args[6]->IsUndefined() || args[7]->IsUndefined()) {
blink::WebRect rect = context.render_view_impl()->windowRect();
start.SetPoint(rect.width / 2, rect.height / 2);
} else if (args[6]->IsNumber() && args[7]->IsNumber()) {
start.SetPoint(args[6]->IntegerValue() * page_scale_factor,
args[7]->IntegerValue() * page_scale_factor);
} else {
args.GetReturnValue().Set(false);
return;
}
int distance_length = args[1]->IntegerValue() * page_scale_factor;
int overscroll_length = args[2]->IntegerValue() * page_scale_factor;
gfx::Vector2d distance;
gfx::Vector2d overscroll;
v8::String::Utf8Value direction(args[0]);
DCHECK(*direction);
std::string direction_str(*direction);
if (direction_str == "down") {
distance.set_y(-distance_length);
overscroll.set_y(overscroll_length);
}
else if (direction_str == "up") {
distance.set_y(distance_length);
overscroll.set_y(-overscroll_length);
}
else if (direction_str == "right") {
distance.set_x(-distance_length);
overscroll.set_x(overscroll_length);
}
else if (direction_str == "left") {
distance.set_x(distance_length);
overscroll.set_x(-overscroll_length);
}
else {
args.GetReturnValue().Set(false);
return;
}
int repeat_count = args[3]->IntegerValue();
gesture_params->anchor = start;
for (int i = 0; i < repeat_count; i++) {
gesture_params->distances.push_back(distance);
gesture_params->distances.push_back(-distance + overscroll);
}
// TODO(nduca): If the render_view_impl is destroyed while the gesture is in
// progress, we will leak the callback and context. This needs to be fixed,
// somehow.
context.render_view_impl()->QueueSyntheticGesture(
gesture_params.PassAs<SyntheticGestureParams>(),
base::Bind(&OnSyntheticGestureCompleted,
callback_and_context));
args.GetReturnValue().Set(true);
}
static void BeginPinch(
const v8::FunctionCallbackInfo<v8::Value>& args) {
GpuBenchmarkingContext context;
if (!context.Init(false))
return;
int arglen = args.Length();
if (arglen < 5 ||
!args[0]->IsNumber() ||
!args[1]->IsNumber() ||
!args[2]->IsNumber() ||
!args[3]->IsFunction() ||
!args[4]->IsNumber()) {
args.GetReturnValue().Set(false);
return;
}
scoped_ptr<SyntheticPinchGestureParams> gesture_params(
new SyntheticPinchGestureParams);
// Convert coordinates from CSS pixels to density independent pixels (DIPs).
float page_scale_factor = context.web_view()->pageScaleFactor();
gesture_params->scale_factor = args[0]->NumberValue();
gesture_params->anchor.SetPoint(
args[1]->IntegerValue() * page_scale_factor,
args[2]->IntegerValue() * page_scale_factor);
gesture_params->relative_pointer_speed_in_pixels_s =
args[4]->IntegerValue();
v8::Local<v8::Function> callback_local =
v8::Local<v8::Function>::Cast(args[3]);
scoped_refptr<CallbackAndContext> callback_and_context =
new CallbackAndContext(args.GetIsolate(),
callback_local,
context.web_frame()->mainWorldScriptContext());
// TODO(nduca): If the render_view_impl is destroyed while the gesture is in
// progress, we will leak the callback and context. This needs to be fixed,
// somehow.
context.render_view_impl()->QueueSyntheticGesture(
gesture_params.PassAs<SyntheticGestureParams>(),
base::Bind(&OnSyntheticGestureCompleted,
callback_and_context));
args.GetReturnValue().Set(true);
}
static void BeginTap(
const v8::FunctionCallbackInfo<v8::Value>& args) {
GpuBenchmarkingContext context;
if (!context.Init(false))
return;
int arglen = args.Length();
if (arglen < 5 ||
!args[0]->IsNumber() ||
!args[1]->IsNumber() ||
!args[2]->IsFunction() ||
!args[3]->IsNumber() ||
!args[4]->IsNumber()) {
args.GetReturnValue().Set(false);
return;
}
scoped_ptr<SyntheticTapGestureParams> gesture_params(
new SyntheticTapGestureParams);
// Convert coordinates from CSS pixels to density independent pixels (DIPs).
float page_scale_factor = context.web_view()->pageScaleFactor();
gesture_params->position.SetPoint(
args[0]->IntegerValue() * page_scale_factor,
args[1]->IntegerValue() * page_scale_factor);
gesture_params->duration_ms = args[3]->IntegerValue();
int gesture_source_type = args[4]->IntegerValue();
if (gesture_source_type < 0 ||
gesture_source_type > SyntheticGestureParams::GESTURE_SOURCE_TYPE_MAX) {
args.GetReturnValue().Set(false);
return;
}
gesture_params->gesture_source_type =
static_cast<SyntheticGestureParams::GestureSourceType>(
gesture_source_type);
v8::Local<v8::Function> callback_local =
v8::Local<v8::Function>::Cast(args[2]);
scoped_refptr<CallbackAndContext> callback_and_context =
new CallbackAndContext(args.GetIsolate(),
callback_local,
context.web_frame()->mainWorldScriptContext());
// TODO(nduca): If the render_view_impl is destroyed while the gesture is in
// progress, we will leak the callback and context. This needs to be fixed,
// somehow.
context.render_view_impl()->QueueSyntheticGesture(
gesture_params.PassAs<SyntheticGestureParams>(),
base::Bind(&OnSyntheticGestureCompleted,
callback_and_context));
args.GetReturnValue().Set(true);
}
static void OnSnapshotCompleted(CallbackAndContext* callback_and_context,
const gfx::Size& size,
const std::vector<unsigned char>& png) {
v8::Isolate* isolate = callback_and_context->isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::Context> context = callback_and_context->GetContext();
v8::Context::Scope context_scope(context);
WebLocalFrame* frame = WebLocalFrame::frameForContext(context);
if (frame) {
v8::Handle<v8::Value> result;
if(!size.IsEmpty()) {
v8::Handle<v8::Object> result_object;
result_object = v8::Object::New(isolate);
result_object->Set(v8::String::NewFromUtf8(isolate, "width"),
v8::Number::New(isolate, size.width()));
result_object->Set(v8::String::NewFromUtf8(isolate, "height"),
v8::Number::New(isolate, size.height()));
std::string base64_png;
base::Base64Encode(base::StringPiece(
reinterpret_cast<const char*>(&*png.begin()), png.size()),
&base64_png);
result_object->Set(v8::String::NewFromUtf8(isolate, "data"),
v8::String::NewFromUtf8(isolate,
base64_png.c_str(),
v8::String::kNormalString,
base64_png.size()));
result = result_object;
} else {
result = v8::Null(isolate);
}
v8::Handle<v8::Value> argv[] = { result };
frame->callFunctionEvenIfScriptDisabled(
callback_and_context->GetCallback(),
v8::Object::New(isolate),
1,
argv);
}
}
static void BeginWindowSnapshotPNG(
const v8::FunctionCallbackInfo<v8::Value>& args) {
GpuBenchmarkingContext context;
if (!context.Init(false))
return;
if (!args[0]->IsFunction())
return;
v8::Local<v8::Function> callback_local =
v8::Local<v8::Function>::Cast(args[0]);
scoped_refptr<CallbackAndContext> callback_and_context =
new CallbackAndContext(args.GetIsolate(),
callback_local,
context.web_frame()->mainWorldScriptContext());
context.render_view_impl()->GetWindowSnapshot(
base::Bind(&OnSnapshotCompleted, callback_and_context));
}
static void ClearImageCache(
const v8::FunctionCallbackInfo<v8::Value>& args) {
WebImageCache::clear();
}
static void OnMicroBenchmarkCompleted(
CallbackAndContext* callback_and_context,
scoped_ptr<base::Value> result) {
v8::Isolate* isolate = callback_and_context->isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::Context> context = callback_and_context->GetContext();
v8::Context::Scope context_scope(context);
WebLocalFrame* frame = WebLocalFrame::frameForContext(context);
if (frame) {
scoped_ptr<V8ValueConverter> converter =
make_scoped_ptr(V8ValueConverter::create());
v8::Handle<v8::Value> value = converter->ToV8Value(result.get(), context);
v8::Handle<v8::Value> argv[] = { value };
frame->callFunctionEvenIfScriptDisabled(
callback_and_context->GetCallback(),
v8::Object::New(isolate),
1,
argv);
}
}
static void RunMicroBenchmark(
const v8::FunctionCallbackInfo<v8::Value>& args) {
GpuBenchmarkingContext context;
if (!context.Init(true)) {
args.GetReturnValue().Set(0);
return;
}
if (args.Length() != 3 ||
!args[0]->IsString() ||
!args[1]->IsFunction() ||
!args[2]->IsObject()) {
args.GetReturnValue().Set(0);
return;
}
v8::Local<v8::Function> callback_local =
v8::Local<v8::Function>::Cast(args[1]);
scoped_refptr<CallbackAndContext> callback_and_context =
new CallbackAndContext(args.GetIsolate(),
callback_local,
context.web_frame()->mainWorldScriptContext());
scoped_ptr<V8ValueConverter> converter =
make_scoped_ptr(V8ValueConverter::create());
v8::Handle<v8::Context> v8_context = callback_and_context->GetContext();
scoped_ptr<base::Value> value =
make_scoped_ptr(converter->FromV8Value(args[2], v8_context));
v8::String::Utf8Value benchmark(args[0]);
DCHECK(*benchmark);
args.GetReturnValue().Set(context.compositor()->ScheduleMicroBenchmark(
std::string(*benchmark),
value.Pass(),
base::Bind(&OnMicroBenchmarkCompleted, callback_and_context)));
}
static void SendMessageToMicroBenchmark(
const v8::FunctionCallbackInfo<v8::Value>& args) {
GpuBenchmarkingContext context;
if (!context.Init(true)) {
args.GetReturnValue().Set(0);
return;
}
if (args.Length() != 2 || !args[0]->IsNumber() || !args[1]->IsObject()) {
args.GetReturnValue().Set(0);
return;
}
scoped_ptr<V8ValueConverter> converter =
make_scoped_ptr(V8ValueConverter::create());
v8::Handle<v8::Context> v8_context =
context.web_frame()->mainWorldScriptContext();
scoped_ptr<base::Value> value =
make_scoped_ptr(converter->FromV8Value(args[1], v8_context));
int id = 0;
converter->FromV8Value(args[0], v8_context)->GetAsInteger(&id);
args.GetReturnValue().Set(
context.compositor()->SendMessageToMicroBenchmark(id, value.Pass()));
}
static void HasGpuProcess(const v8::FunctionCallbackInfo<v8::Value>& args) {
GpuChannelHost* gpu_channel = RenderThreadImpl::current()->GetGpuChannel();
args.GetReturnValue().Set(!!gpu_channel);
}
};
v8::Extension* GpuBenchmarkingExtension::Get() {
return new GpuBenchmarkingWrapper();
}
} // namespace content