Home | History | Annotate | Download | only in gpu
      1 // Copyright (c) 2012 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 "content/renderer/gpu/gpu_benchmarking_extension.h"
      6 
      7 #include <string>
      8 
      9 #include "base/base64.h"
     10 #include "base/files/file_path.h"
     11 #include "base/files/file_util.h"
     12 #include "base/memory/scoped_vector.h"
     13 #include "base/strings/string_number_conversions.h"
     14 #include "cc/layers/layer.h"
     15 #include "content/common/input/synthetic_gesture_params.h"
     16 #include "content/common/input/synthetic_pinch_gesture_params.h"
     17 #include "content/common/input/synthetic_smooth_scroll_gesture_params.h"
     18 #include "content/common/input/synthetic_tap_gesture_params.h"
     19 #include "content/public/renderer/render_thread.h"
     20 #include "content/public/renderer/v8_value_converter.h"
     21 #include "content/renderer/gpu/render_widget_compositor.h"
     22 #include "content/renderer/render_thread_impl.h"
     23 #include "content/renderer/render_view_impl.h"
     24 #include "content/renderer/skia_benchmarking_extension.h"
     25 #include "third_party/WebKit/public/web/WebImageCache.h"
     26 #include "third_party/WebKit/public/web/WebLocalFrame.h"
     27 #include "third_party/WebKit/public/web/WebView.h"
     28 #include "third_party/skia/include/core/SkData.h"
     29 #include "third_party/skia/include/core/SkGraphics.h"
     30 #include "third_party/skia/include/core/SkPicture.h"
     31 #include "third_party/skia/include/core/SkPixelRef.h"
     32 #include "third_party/skia/include/core/SkStream.h"
     33 #include "ui/gfx/codec/png_codec.h"
     34 #include "v8/include/v8.h"
     35 
     36 using blink::WebCanvas;
     37 using blink::WebLocalFrame;
     38 using blink::WebImageCache;
     39 using blink::WebPrivatePtr;
     40 using blink::WebSize;
     41 using blink::WebView;
     42 
     43 const char kGpuBenchmarkingExtensionName[] = "v8/GpuBenchmarking";
     44 
     45 // offset parameter is deprecated/ignored, and will be remove from the
     46 // signature in a future skia release. <reed (at) google.com>
     47 static SkData* EncodeBitmapToData(size_t* offset, const SkBitmap& bm) {
     48     SkPixelRef* pr = bm.pixelRef();
     49     if (pr != NULL) {
     50         SkData* data = pr->refEncodedData();
     51         if (data != NULL)
     52             return data;
     53     }
     54     std::vector<unsigned char> vector;
     55     if (gfx::PNGCodec::EncodeBGRASkBitmap(bm, false, &vector)) {
     56         return SkData::NewWithCopy(&vector.front() , vector.size());
     57     }
     58     return NULL;
     59 }
     60 
     61 namespace {
     62 
     63 class SkPictureSerializer {
     64  public:
     65   explicit SkPictureSerializer(const base::FilePath& dirpath)
     66       : dirpath_(dirpath),
     67         layer_id_(0) {
     68     // Let skia register known effect subclasses. This basically enables
     69     // reflection on those subclasses required for picture serialization.
     70     content::SkiaBenchmarking::Initialize();
     71   }
     72 
     73   // Recursively serializes the layer tree.
     74   // Each layer in the tree is serialized into a separate skp file
     75   // in the given directory.
     76   void Serialize(const cc::Layer* layer) {
     77     const cc::LayerList& children = layer->children();
     78     for (size_t i = 0; i < children.size(); ++i) {
     79       Serialize(children[i].get());
     80     }
     81 
     82     skia::RefPtr<SkPicture> picture = layer->GetPicture();
     83     if (!picture)
     84       return;
     85 
     86     // Serialize picture to file.
     87     // TODO(alokp): Note that for this to work Chrome needs to be launched with
     88     // --no-sandbox command-line flag. Get rid of this limitation.
     89     // CRBUG: 139640.
     90     std::string filename = "layer_" + base::IntToString(layer_id_++) + ".skp";
     91     std::string filepath = dirpath_.AppendASCII(filename).MaybeAsASCII();
     92     DCHECK(!filepath.empty());
     93     SkFILEWStream file(filepath.c_str());
     94     DCHECK(file.isValid());
     95     picture->serialize(&file, &EncodeBitmapToData);
     96   }
     97 
     98  private:
     99   base::FilePath dirpath_;
    100   int layer_id_;
    101 };
    102 
    103 }  // namespace
    104 
    105 namespace content {
    106 
    107 namespace {
    108 
    109 class CallbackAndContext : public base::RefCounted<CallbackAndContext> {
    110  public:
    111   CallbackAndContext(v8::Isolate* isolate,
    112                      v8::Handle<v8::Function> callback,
    113                      v8::Handle<v8::Context> context)
    114       : isolate_(isolate) {
    115     callback_.Reset(isolate_, callback);
    116     context_.Reset(isolate_, context);
    117   }
    118 
    119   v8::Isolate* isolate() {
    120     return isolate_;
    121   }
    122 
    123   v8::Handle<v8::Function> GetCallback() {
    124     return v8::Local<v8::Function>::New(isolate_, callback_);
    125   }
    126 
    127   v8::Handle<v8::Context> GetContext() {
    128     return v8::Local<v8::Context>::New(isolate_, context_);
    129   }
    130 
    131  private:
    132   friend class base::RefCounted<CallbackAndContext>;
    133 
    134   virtual ~CallbackAndContext() {
    135     callback_.Reset();
    136     context_.Reset();
    137   }
    138 
    139   v8::Isolate* isolate_;
    140   v8::Persistent<v8::Function> callback_;
    141   v8::Persistent<v8::Context> context_;
    142   DISALLOW_COPY_AND_ASSIGN(CallbackAndContext);
    143 };
    144 
    145 class GpuBenchmarkingContext {
    146  public:
    147   GpuBenchmarkingContext()
    148       : web_frame_(NULL),
    149         web_view_(NULL),
    150         render_view_impl_(NULL),
    151         compositor_(NULL) {}
    152 
    153   bool Init(bool init_compositor) {
    154     web_frame_ = WebLocalFrame::frameForCurrentContext();
    155     if (!web_frame_)
    156       return false;
    157 
    158     web_view_ = web_frame_->view();
    159     if (!web_view_) {
    160       web_frame_ = NULL;
    161       return false;
    162     }
    163 
    164     render_view_impl_ = RenderViewImpl::FromWebView(web_view_);
    165     if (!render_view_impl_) {
    166       web_frame_ = NULL;
    167       web_view_ = NULL;
    168       return false;
    169     }
    170 
    171     if (!init_compositor)
    172       return true;
    173 
    174     compositor_ = render_view_impl_->compositor();
    175     if (!compositor_) {
    176       web_frame_ = NULL;
    177       web_view_ = NULL;
    178       render_view_impl_ = NULL;
    179       return false;
    180     }
    181 
    182     return true;
    183   }
    184 
    185   WebLocalFrame* web_frame() const {
    186     DCHECK(web_frame_ != NULL);
    187     return web_frame_;
    188   }
    189   WebView* web_view() const {
    190     DCHECK(web_view_ != NULL);
    191     return web_view_;
    192   }
    193   RenderViewImpl* render_view_impl() const {
    194     DCHECK(render_view_impl_ != NULL);
    195     return render_view_impl_;
    196   }
    197   RenderWidgetCompositor* compositor() const {
    198     DCHECK(compositor_ != NULL);
    199     return compositor_;
    200   }
    201 
    202  private:
    203   WebLocalFrame* web_frame_;
    204   WebView* web_view_;
    205   RenderViewImpl* render_view_impl_;
    206   RenderWidgetCompositor* compositor_;
    207 
    208   DISALLOW_COPY_AND_ASSIGN(GpuBenchmarkingContext);
    209 };
    210 
    211 }  // namespace
    212 
    213 class GpuBenchmarkingWrapper : public v8::Extension {
    214  public:
    215   GpuBenchmarkingWrapper() :
    216       v8::Extension(kGpuBenchmarkingExtensionName,
    217             "if (typeof(chrome) == 'undefined') {"
    218             "  chrome = {};"
    219             "};"
    220             "if (typeof(chrome.gpuBenchmarking) == 'undefined') {"
    221             "  chrome.gpuBenchmarking = {};"
    222             "};"
    223             "chrome.gpuBenchmarking.setNeedsDisplayOnAllLayers = function() {"
    224             "  native function SetNeedsDisplayOnAllLayers();"
    225             "  return SetNeedsDisplayOnAllLayers();"
    226             "};"
    227             "chrome.gpuBenchmarking.setRasterizeOnlyVisibleContent = "
    228             "function() {"
    229             "  native function SetRasterizeOnlyVisibleContent();"
    230             "  return SetRasterizeOnlyVisibleContent();"
    231             "};"
    232             "chrome.gpuBenchmarking.printToSkPicture = function(dirname) {"
    233             "  native function PrintToSkPicture();"
    234             "  return PrintToSkPicture(dirname);"
    235             "};"
    236             "chrome.gpuBenchmarking.DEFAULT_INPUT = 0;"
    237             "chrome.gpuBenchmarking.TOUCH_INPUT = 1;"
    238             "chrome.gpuBenchmarking.MOUSE_INPUT = 2;"
    239             "chrome.gpuBenchmarking.gestureSourceTypeSupported = "
    240             "    function(gesture_source_type) {"
    241             "  native function GestureSourceTypeSupported();"
    242             "  return GestureSourceTypeSupported(gesture_source_type);"
    243             "};"
    244             "chrome.gpuBenchmarking.smoothScrollBy = "
    245             "    function(pixels_to_scroll, opt_callback, opt_start_x,"
    246             "             opt_start_y, opt_gesture_source_type,"
    247             "             opt_direction, opt_speed_in_pixels_s) {"
    248             "  pixels_to_scroll = pixels_to_scroll || 0;"
    249             "  callback = opt_callback || function() { };"
    250             "  gesture_source_type = opt_gesture_source_type ||"
    251             "      chrome.gpuBenchmarking.DEFAULT_INPUT;"
    252             "  direction = opt_direction || 'down';"
    253             "  speed_in_pixels_s = opt_speed_in_pixels_s || 800;"
    254             "  native function BeginSmoothScroll();"
    255             "  return BeginSmoothScroll(pixels_to_scroll, callback,"
    256             "                           gesture_source_type, direction,"
    257             "                           speed_in_pixels_s, true,"
    258             "                           opt_start_x, opt_start_y);"
    259             "};"
    260             "chrome.gpuBenchmarking.swipe = "
    261             "    function(direction, distance, opt_callback,"
    262             "             opt_start_x, opt_start_y,"
    263             "             opt_speed_in_pixels_s) {"
    264             "  direction = direction || 'up';"
    265             "  distance = distance || 0;"
    266             "  callback = opt_callback || function() { };"
    267             "  speed_in_pixels_s = opt_speed_in_pixels_s || 800;"
    268             "  native function BeginSmoothScroll();"
    269             "  return BeginSmoothScroll(-distance, callback,"
    270             "                           chrome.gpuBenchmarking.TOUCH_INPUT,"
    271             "                           direction, speed_in_pixels_s, false,"
    272             "                           opt_start_x, opt_start_y);"
    273             "};"
    274             "chrome.gpuBenchmarking.scrollBounce = "
    275             "    function(direction, distance, overscroll, opt_repeat_count,"
    276             "             opt_callback, opt_start_x, opt_start_y,"
    277             "             opt_speed_in_pixels_s) {"
    278             "  direction = direction || 'down';"
    279             "  distance = distance || 0;"
    280             "  overscroll = overscroll || 0;"
    281             "  repeat_count = opt_repeat_count || 1;"
    282             "  callback = opt_callback || function() { };"
    283             "  speed_in_pixels_s = opt_speed_in_pixels_s || 800;"
    284             "  native function BeginScrollBounce();"
    285             "  return BeginScrollBounce(direction, distance, overscroll,"
    286             "                           repeat_count, callback,"
    287             "                           speed_in_pixels_s,"
    288             "                           opt_start_x, opt_start_y);"
    289             "};"
    290             // TODO(dominikg): Remove once JS interface changes have rolled into
    291             //                 stable.
    292             "chrome.gpuBenchmarking.newPinchInterface = true;"
    293             "chrome.gpuBenchmarking.pinchBy = "
    294             "    function(scale_factor, anchor_x, anchor_y,"
    295             "             opt_callback, "
    296             "opt_relative_pointer_speed_in_pixels_s) {"
    297             "  callback = opt_callback || function() { };"
    298             "  relative_pointer_speed_in_pixels_s ="
    299             "      opt_relative_pointer_speed_in_pixels_s || 800;"
    300             "  native function BeginPinch();"
    301             "  return BeginPinch(scale_factor, anchor_x, anchor_y, callback,"
    302             "                    relative_pointer_speed_in_pixels_s);"
    303             "};"
    304             "chrome.gpuBenchmarking.tap = "
    305             "    function(position_x, position_y, opt_callback, "
    306             "opt_duration_ms,"
    307             "             opt_gesture_source_type) {"
    308             "  callback = opt_callback || function() { };"
    309             "  duration_ms = opt_duration_ms || 50;"
    310             "  gesture_source_type = opt_gesture_source_type ||"
    311             "      chrome.gpuBenchmarking.DEFAULT_INPUT;"
    312             "  native function BeginTap();"
    313             "  return BeginTap(position_x, position_y, callback, duration_ms,"
    314             "                  gesture_source_type);"
    315             "};"
    316             "chrome.gpuBenchmarking.beginWindowSnapshotPNG = "
    317             "function(callback) {"
    318             "  native function BeginWindowSnapshotPNG();"
    319             "  BeginWindowSnapshotPNG(callback);"
    320             "};"
    321             "chrome.gpuBenchmarking.clearImageCache = function() {"
    322             "  native function ClearImageCache();"
    323             "  ClearImageCache();"
    324             "};"
    325             "chrome.gpuBenchmarking.runMicroBenchmark ="
    326             "    function(name, callback, opt_arguments) {"
    327             "  arguments = opt_arguments || {};"
    328             "  native function RunMicroBenchmark();"
    329             "  return RunMicroBenchmark(name, callback, arguments);"
    330             "};"
    331             "chrome.gpuBenchmarking.sendMessageToMicroBenchmark ="
    332             "    function(id, arguments) {"
    333             "  native function SendMessageToMicroBenchmark();"
    334             "  return SendMessageToMicroBenchmark(id, arguments);"
    335             "};"
    336             "chrome.gpuBenchmarking.hasGpuProcess = function() {"
    337             "  native function HasGpuProcess();"
    338             "  return HasGpuProcess();"
    339             "};") {}
    340 
    341   virtual v8::Handle<v8::FunctionTemplate> GetNativeFunctionTemplate(
    342       v8::Isolate* isolate,
    343       v8::Handle<v8::String> name) OVERRIDE {
    344     if (name->Equals(
    345             v8::String::NewFromUtf8(isolate, "SetNeedsDisplayOnAllLayers")))
    346       return v8::FunctionTemplate::New(isolate, SetNeedsDisplayOnAllLayers);
    347     if (name->Equals(
    348             v8::String::NewFromUtf8(isolate, "SetRasterizeOnlyVisibleContent")))
    349       return v8::FunctionTemplate::New(isolate, SetRasterizeOnlyVisibleContent);
    350     if (name->Equals(v8::String::NewFromUtf8(isolate, "PrintToSkPicture")))
    351       return v8::FunctionTemplate::New(isolate, PrintToSkPicture);
    352     if (name->Equals(
    353             v8::String::NewFromUtf8(isolate, "GestureSourceTypeSupported")))
    354       return v8::FunctionTemplate::New(isolate, GestureSourceTypeSupported);
    355     if (name->Equals(v8::String::NewFromUtf8(isolate, "BeginSmoothScroll")))
    356       return v8::FunctionTemplate::New(isolate, BeginSmoothScroll);
    357     if (name->Equals(v8::String::NewFromUtf8(isolate, "BeginScrollBounce")))
    358       return v8::FunctionTemplate::New(isolate, BeginScrollBounce);
    359     if (name->Equals(v8::String::NewFromUtf8(isolate, "BeginPinch")))
    360       return v8::FunctionTemplate::New(isolate, BeginPinch);
    361     if (name->Equals(v8::String::NewFromUtf8(isolate, "BeginTap")))
    362       return v8::FunctionTemplate::New(isolate, BeginTap);
    363     if (name->Equals(
    364             v8::String::NewFromUtf8(isolate, "BeginWindowSnapshotPNG")))
    365       return v8::FunctionTemplate::New(isolate, BeginWindowSnapshotPNG);
    366     if (name->Equals(v8::String::NewFromUtf8(isolate, "ClearImageCache")))
    367       return v8::FunctionTemplate::New(isolate, ClearImageCache);
    368     if (name->Equals(v8::String::NewFromUtf8(isolate, "RunMicroBenchmark")))
    369       return v8::FunctionTemplate::New(isolate, RunMicroBenchmark);
    370     if (name->Equals(
    371             v8::String::NewFromUtf8(isolate, "SendMessageToMicroBenchmark")))
    372       return v8::FunctionTemplate::New(isolate, SendMessageToMicroBenchmark);
    373     if (name->Equals(v8::String::NewFromUtf8(isolate, "HasGpuProcess")))
    374       return v8::FunctionTemplate::New(isolate, HasGpuProcess);
    375 
    376     return v8::Handle<v8::FunctionTemplate>();
    377   }
    378 
    379   static void SetNeedsDisplayOnAllLayers(
    380       const v8::FunctionCallbackInfo<v8::Value>& args) {
    381     GpuBenchmarkingContext context;
    382     if (!context.Init(true))
    383       return;
    384 
    385     context.compositor()->SetNeedsDisplayOnAllLayers();
    386   }
    387 
    388   static void SetRasterizeOnlyVisibleContent(
    389       const v8::FunctionCallbackInfo<v8::Value>& args) {
    390     GpuBenchmarkingContext context;
    391     if (!context.Init(true))
    392       return;
    393 
    394     context.compositor()->SetRasterizeOnlyVisibleContent();
    395   }
    396 
    397   static void PrintToSkPicture(
    398       const v8::FunctionCallbackInfo<v8::Value>& args) {
    399     if (args.Length() != 1)
    400       return;
    401 
    402     v8::String::Utf8Value dirname(args[0]);
    403     if (dirname.length() == 0)
    404       return;
    405 
    406     GpuBenchmarkingContext context;
    407     if (!context.Init(true))
    408       return;
    409 
    410     const cc::Layer* root_layer = context.compositor()->GetRootLayer();
    411     if (!root_layer)
    412       return;
    413 
    414     base::FilePath dirpath(
    415         base::FilePath::StringType(*dirname, *dirname + dirname.length()));
    416     if (!base::CreateDirectory(dirpath) ||
    417         !base::PathIsWritable(dirpath)) {
    418       std::string msg("Path is not writable: ");
    419       msg.append(dirpath.MaybeAsASCII());
    420       v8::Isolate* isolate = args.GetIsolate();
    421       isolate->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8(
    422           isolate, msg.c_str(), v8::String::kNormalString, msg.length())));
    423       return;
    424     }
    425 
    426     SkPictureSerializer serializer(dirpath);
    427     serializer.Serialize(root_layer);
    428   }
    429 
    430   static void OnSyntheticGestureCompleted(
    431       CallbackAndContext* callback_and_context) {
    432     v8::Isolate* isolate = callback_and_context->isolate();
    433     v8::HandleScope scope(isolate);
    434     v8::Handle<v8::Context> context = callback_and_context->GetContext();
    435     v8::Context::Scope context_scope(context);
    436     WebLocalFrame* frame = WebLocalFrame::frameForContext(context);
    437     if (frame) {
    438       frame->callFunctionEvenIfScriptDisabled(
    439           callback_and_context->GetCallback(),
    440           v8::Object::New(isolate),
    441           0,
    442           NULL);
    443     }
    444   }
    445 
    446   static void GestureSourceTypeSupported(
    447       const v8::FunctionCallbackInfo<v8::Value>& args) {
    448     if (args.Length() != 1 || !args[0]->IsNumber()) {
    449       args.GetReturnValue().Set(false);
    450       return;
    451     }
    452 
    453     int gesture_source_type = args[0]->IntegerValue();
    454     if (gesture_source_type < 0 ||
    455         gesture_source_type > SyntheticGestureParams::GESTURE_SOURCE_TYPE_MAX) {
    456       args.GetReturnValue().Set(false);
    457       return;
    458     }
    459 
    460     bool is_supported = SyntheticGestureParams::IsGestureSourceTypeSupported(
    461         static_cast<SyntheticGestureParams::GestureSourceType>(
    462             gesture_source_type));
    463     args.GetReturnValue().Set(is_supported);
    464   }
    465 
    466   static void BeginSmoothScroll(
    467       const v8::FunctionCallbackInfo<v8::Value>& args) {
    468     GpuBenchmarkingContext context;
    469     if (!context.Init(false))
    470       return;
    471 
    472     // The last two arguments can be undefined. We check their validity later.
    473     int arglen = args.Length();
    474     if (arglen < 8 ||
    475         !args[0]->IsNumber() ||
    476         !args[1]->IsFunction() ||
    477         !args[2]->IsNumber() ||
    478         !args[3]->IsString() ||
    479         !args[4]->IsNumber() ||
    480         !args[5]->IsBoolean()) {
    481       args.GetReturnValue().Set(false);
    482       return;
    483     }
    484 
    485     v8::Local<v8::Function> callback_local =
    486         v8::Local<v8::Function>::Cast(args[1]);
    487 
    488     scoped_refptr<CallbackAndContext> callback_and_context =
    489         new CallbackAndContext(args.GetIsolate(),
    490                                callback_local,
    491                                context.web_frame()->mainWorldScriptContext());
    492 
    493     scoped_ptr<SyntheticSmoothScrollGestureParams> gesture_params(
    494         new SyntheticSmoothScrollGestureParams);
    495 
    496     // Convert coordinates from CSS pixels to density independent pixels (DIPs).
    497     float page_scale_factor = context.web_view()->pageScaleFactor();
    498 
    499     int gesture_source_type = args[2]->IntegerValue();
    500     if (gesture_source_type < 0 ||
    501         gesture_source_type > SyntheticGestureParams::GESTURE_SOURCE_TYPE_MAX) {
    502       args.GetReturnValue().Set(false);
    503       return;
    504     }
    505     gesture_params->gesture_source_type =
    506         static_cast<SyntheticGestureParams::GestureSourceType>(
    507             gesture_source_type);
    508 
    509     gesture_params->speed_in_pixels_s = args[4]->IntegerValue();
    510     gesture_params->prevent_fling = args[5]->BooleanValue();
    511 
    512     // Account for the 2 optional arguments, start_x and start_y.
    513     gfx::Point anchor;
    514     if (args[6]->IsUndefined() || args[7]->IsUndefined()) {
    515       blink::WebRect rect = context.render_view_impl()->windowRect();
    516       anchor.SetPoint(rect.width / 2, rect.height / 2);
    517     } else if (args[6]->IsNumber() && args[7]->IsNumber()) {
    518       anchor.SetPoint(args[6]->IntegerValue() * page_scale_factor,
    519                       args[7]->IntegerValue() * page_scale_factor);
    520     } else {
    521       args.GetReturnValue().Set(false);
    522       return;
    523     }
    524     gesture_params->anchor = anchor;
    525 
    526     int distance_length = args[0]->IntegerValue() * page_scale_factor;
    527     gfx::Vector2d distance;
    528     v8::String::Utf8Value direction(args[3]);
    529     DCHECK(*direction);
    530     std::string direction_str(*direction);
    531     if (direction_str == "down")
    532       distance.set_y(-distance_length);
    533     else if (direction_str == "up")
    534       distance.set_y(distance_length);
    535     else if (direction_str == "right")
    536       distance.set_x(-distance_length);
    537     else if (direction_str == "left")
    538       distance.set_x(distance_length);
    539     else {
    540       args.GetReturnValue().Set(false);
    541       return;
    542     }
    543     gesture_params->distances.push_back(distance);
    544 
    545     // TODO(nduca): If the render_view_impl is destroyed while the gesture is in
    546     // progress, we will leak the callback and context. This needs to be fixed,
    547     // somehow.
    548     context.render_view_impl()->QueueSyntheticGesture(
    549         gesture_params.PassAs<SyntheticGestureParams>(),
    550         base::Bind(&OnSyntheticGestureCompleted,
    551                    callback_and_context));
    552 
    553     args.GetReturnValue().Set(true);
    554   }
    555 
    556   static void BeginScrollBounce(
    557       const v8::FunctionCallbackInfo<v8::Value>& args) {
    558     GpuBenchmarkingContext context;
    559     if (!context.Init(false))
    560       return;
    561 
    562     // The last two arguments can be undefined. We check their validity later.
    563     int arglen = args.Length();
    564     if (arglen < 8 ||
    565         !args[0]->IsString() ||
    566         !args[1]->IsNumber() ||
    567         !args[2]->IsNumber() ||
    568         !args[3]->IsNumber() ||
    569         !args[4]->IsFunction() ||
    570         !args[5]->IsNumber()) {
    571       args.GetReturnValue().Set(false);
    572       return;
    573     }
    574 
    575     v8::Local<v8::Function> callback_local =
    576         v8::Local<v8::Function>::Cast(args[4]);
    577 
    578     scoped_refptr<CallbackAndContext> callback_and_context =
    579         new CallbackAndContext(args.GetIsolate(),
    580                                callback_local,
    581                                context.web_frame()->mainWorldScriptContext());
    582 
    583     scoped_ptr<SyntheticSmoothScrollGestureParams> gesture_params(
    584         new SyntheticSmoothScrollGestureParams);
    585 
    586     // Convert coordinates from CSS pixels to density independent pixels (DIPs).
    587     float page_scale_factor = context.web_view()->pageScaleFactor();
    588 
    589     gesture_params->speed_in_pixels_s = args[5]->IntegerValue();
    590 
    591     // Account for the 2 optional arguments, start_x and start_y.
    592     gfx::Point start;
    593     if (args[6]->IsUndefined() || args[7]->IsUndefined()) {
    594       blink::WebRect rect = context.render_view_impl()->windowRect();
    595       start.SetPoint(rect.width / 2, rect.height / 2);
    596     } else if (args[6]->IsNumber() && args[7]->IsNumber()) {
    597       start.SetPoint(args[6]->IntegerValue() * page_scale_factor,
    598                      args[7]->IntegerValue() * page_scale_factor);
    599     } else {
    600       args.GetReturnValue().Set(false);
    601       return;
    602     }
    603 
    604     int distance_length = args[1]->IntegerValue() * page_scale_factor;
    605     int overscroll_length = args[2]->IntegerValue() * page_scale_factor;
    606     gfx::Vector2d distance;
    607     gfx::Vector2d overscroll;
    608     v8::String::Utf8Value direction(args[0]);
    609     DCHECK(*direction);
    610     std::string direction_str(*direction);
    611     if (direction_str == "down") {
    612       distance.set_y(-distance_length);
    613       overscroll.set_y(overscroll_length);
    614     }
    615     else if (direction_str == "up") {
    616       distance.set_y(distance_length);
    617       overscroll.set_y(-overscroll_length);
    618     }
    619     else if (direction_str == "right") {
    620       distance.set_x(-distance_length);
    621       overscroll.set_x(overscroll_length);
    622     }
    623     else if (direction_str == "left") {
    624       distance.set_x(distance_length);
    625       overscroll.set_x(-overscroll_length);
    626     }
    627     else {
    628       args.GetReturnValue().Set(false);
    629       return;
    630     }
    631 
    632     int repeat_count = args[3]->IntegerValue();
    633     gesture_params->anchor = start;
    634     for (int i = 0; i < repeat_count; i++) {
    635       gesture_params->distances.push_back(distance);
    636       gesture_params->distances.push_back(-distance + overscroll);
    637     }
    638 
    639     // TODO(nduca): If the render_view_impl is destroyed while the gesture is in
    640     // progress, we will leak the callback and context. This needs to be fixed,
    641     // somehow.
    642     context.render_view_impl()->QueueSyntheticGesture(
    643         gesture_params.PassAs<SyntheticGestureParams>(),
    644         base::Bind(&OnSyntheticGestureCompleted,
    645                    callback_and_context));
    646 
    647     args.GetReturnValue().Set(true);
    648   }
    649 
    650   static void BeginPinch(
    651       const v8::FunctionCallbackInfo<v8::Value>& args) {
    652     GpuBenchmarkingContext context;
    653     if (!context.Init(false))
    654       return;
    655 
    656     int arglen = args.Length();
    657     if (arglen < 5 ||
    658         !args[0]->IsNumber() ||
    659         !args[1]->IsNumber() ||
    660         !args[2]->IsNumber() ||
    661         !args[3]->IsFunction() ||
    662         !args[4]->IsNumber()) {
    663       args.GetReturnValue().Set(false);
    664       return;
    665     }
    666 
    667     scoped_ptr<SyntheticPinchGestureParams> gesture_params(
    668         new SyntheticPinchGestureParams);
    669 
    670     // Convert coordinates from CSS pixels to density independent pixels (DIPs).
    671     float page_scale_factor = context.web_view()->pageScaleFactor();
    672 
    673     gesture_params->scale_factor = args[0]->NumberValue();
    674     gesture_params->anchor.SetPoint(
    675         args[1]->IntegerValue() * page_scale_factor,
    676         args[2]->IntegerValue() * page_scale_factor);
    677     gesture_params->relative_pointer_speed_in_pixels_s =
    678         args[4]->IntegerValue();
    679 
    680     v8::Local<v8::Function> callback_local =
    681         v8::Local<v8::Function>::Cast(args[3]);
    682 
    683     scoped_refptr<CallbackAndContext> callback_and_context =
    684         new CallbackAndContext(args.GetIsolate(),
    685                                callback_local,
    686                                context.web_frame()->mainWorldScriptContext());
    687 
    688 
    689     // TODO(nduca): If the render_view_impl is destroyed while the gesture is in
    690     // progress, we will leak the callback and context. This needs to be fixed,
    691     // somehow.
    692     context.render_view_impl()->QueueSyntheticGesture(
    693         gesture_params.PassAs<SyntheticGestureParams>(),
    694         base::Bind(&OnSyntheticGestureCompleted,
    695                    callback_and_context));
    696 
    697     args.GetReturnValue().Set(true);
    698   }
    699 
    700   static void BeginTap(
    701       const v8::FunctionCallbackInfo<v8::Value>& args) {
    702     GpuBenchmarkingContext context;
    703     if (!context.Init(false))
    704       return;
    705 
    706     int arglen = args.Length();
    707     if (arglen < 5 ||
    708         !args[0]->IsNumber() ||
    709         !args[1]->IsNumber() ||
    710         !args[2]->IsFunction() ||
    711         !args[3]->IsNumber() ||
    712         !args[4]->IsNumber()) {
    713       args.GetReturnValue().Set(false);
    714       return;
    715     }
    716 
    717     scoped_ptr<SyntheticTapGestureParams> gesture_params(
    718         new SyntheticTapGestureParams);
    719 
    720     // Convert coordinates from CSS pixels to density independent pixels (DIPs).
    721     float page_scale_factor = context.web_view()->pageScaleFactor();
    722 
    723     gesture_params->position.SetPoint(
    724         args[0]->IntegerValue() * page_scale_factor,
    725         args[1]->IntegerValue() * page_scale_factor);
    726     gesture_params->duration_ms = args[3]->IntegerValue();
    727 
    728     int gesture_source_type = args[4]->IntegerValue();
    729     if (gesture_source_type < 0 ||
    730         gesture_source_type > SyntheticGestureParams::GESTURE_SOURCE_TYPE_MAX) {
    731       args.GetReturnValue().Set(false);
    732       return;
    733     }
    734     gesture_params->gesture_source_type =
    735         static_cast<SyntheticGestureParams::GestureSourceType>(
    736             gesture_source_type);
    737 
    738     v8::Local<v8::Function> callback_local =
    739         v8::Local<v8::Function>::Cast(args[2]);
    740 
    741     scoped_refptr<CallbackAndContext> callback_and_context =
    742         new CallbackAndContext(args.GetIsolate(),
    743                                callback_local,
    744                                context.web_frame()->mainWorldScriptContext());
    745 
    746 
    747     // TODO(nduca): If the render_view_impl is destroyed while the gesture is in
    748     // progress, we will leak the callback and context. This needs to be fixed,
    749     // somehow.
    750     context.render_view_impl()->QueueSyntheticGesture(
    751         gesture_params.PassAs<SyntheticGestureParams>(),
    752         base::Bind(&OnSyntheticGestureCompleted,
    753                    callback_and_context));
    754 
    755     args.GetReturnValue().Set(true);
    756   }
    757 
    758   static void OnSnapshotCompleted(CallbackAndContext* callback_and_context,
    759                                   const gfx::Size& size,
    760                                   const std::vector<unsigned char>& png) {
    761     v8::Isolate* isolate = callback_and_context->isolate();
    762     v8::HandleScope scope(isolate);
    763     v8::Handle<v8::Context> context = callback_and_context->GetContext();
    764     v8::Context::Scope context_scope(context);
    765     WebLocalFrame* frame = WebLocalFrame::frameForContext(context);
    766     if (frame) {
    767 
    768       v8::Handle<v8::Value> result;
    769 
    770       if(!size.IsEmpty()) {
    771         v8::Handle<v8::Object> result_object;
    772         result_object = v8::Object::New(isolate);
    773 
    774         result_object->Set(v8::String::NewFromUtf8(isolate, "width"),
    775                            v8::Number::New(isolate, size.width()));
    776         result_object->Set(v8::String::NewFromUtf8(isolate, "height"),
    777                            v8::Number::New(isolate, size.height()));
    778 
    779         std::string base64_png;
    780         base::Base64Encode(base::StringPiece(
    781             reinterpret_cast<const char*>(&*png.begin()), png.size()),
    782             &base64_png);
    783 
    784         result_object->Set(v8::String::NewFromUtf8(isolate, "data"),
    785                            v8::String::NewFromUtf8(isolate,
    786                                                    base64_png.c_str(),
    787                                                    v8::String::kNormalString,
    788                                                    base64_png.size()));
    789 
    790         result = result_object;
    791       } else {
    792         result = v8::Null(isolate);
    793       }
    794 
    795       v8::Handle<v8::Value> argv[] = { result };
    796 
    797       frame->callFunctionEvenIfScriptDisabled(
    798           callback_and_context->GetCallback(),
    799           v8::Object::New(isolate),
    800           1,
    801           argv);
    802     }
    803   }
    804 
    805   static void BeginWindowSnapshotPNG(
    806       const v8::FunctionCallbackInfo<v8::Value>& args) {
    807     GpuBenchmarkingContext context;
    808     if (!context.Init(false))
    809       return;
    810 
    811     if (!args[0]->IsFunction())
    812       return;
    813 
    814     v8::Local<v8::Function> callback_local =
    815         v8::Local<v8::Function>::Cast(args[0]);
    816 
    817     scoped_refptr<CallbackAndContext> callback_and_context =
    818         new CallbackAndContext(args.GetIsolate(),
    819                                callback_local,
    820                                context.web_frame()->mainWorldScriptContext());
    821 
    822     context.render_view_impl()->GetWindowSnapshot(
    823         base::Bind(&OnSnapshotCompleted, callback_and_context));
    824   }
    825 
    826   static void ClearImageCache(
    827       const v8::FunctionCallbackInfo<v8::Value>& args) {
    828     WebImageCache::clear();
    829   }
    830 
    831   static void OnMicroBenchmarkCompleted(
    832       CallbackAndContext* callback_and_context,
    833       scoped_ptr<base::Value> result) {
    834     v8::Isolate* isolate = callback_and_context->isolate();
    835     v8::HandleScope scope(isolate);
    836     v8::Handle<v8::Context> context = callback_and_context->GetContext();
    837     v8::Context::Scope context_scope(context);
    838     WebLocalFrame* frame = WebLocalFrame::frameForContext(context);
    839     if (frame) {
    840       scoped_ptr<V8ValueConverter> converter =
    841           make_scoped_ptr(V8ValueConverter::create());
    842       v8::Handle<v8::Value> value = converter->ToV8Value(result.get(), context);
    843       v8::Handle<v8::Value> argv[] = { value };
    844 
    845       frame->callFunctionEvenIfScriptDisabled(
    846           callback_and_context->GetCallback(),
    847           v8::Object::New(isolate),
    848           1,
    849           argv);
    850     }
    851   }
    852 
    853   static void RunMicroBenchmark(
    854       const v8::FunctionCallbackInfo<v8::Value>& args) {
    855     GpuBenchmarkingContext context;
    856     if (!context.Init(true)) {
    857       args.GetReturnValue().Set(0);
    858       return;
    859     }
    860 
    861     if (args.Length() != 3 ||
    862         !args[0]->IsString() ||
    863         !args[1]->IsFunction() ||
    864         !args[2]->IsObject()) {
    865       args.GetReturnValue().Set(0);
    866       return;
    867     }
    868 
    869     v8::Local<v8::Function> callback_local =
    870         v8::Local<v8::Function>::Cast(args[1]);
    871 
    872     scoped_refptr<CallbackAndContext> callback_and_context =
    873         new CallbackAndContext(args.GetIsolate(),
    874                                callback_local,
    875                                context.web_frame()->mainWorldScriptContext());
    876 
    877     scoped_ptr<V8ValueConverter> converter =
    878         make_scoped_ptr(V8ValueConverter::create());
    879     v8::Handle<v8::Context> v8_context = callback_and_context->GetContext();
    880     scoped_ptr<base::Value> value =
    881         make_scoped_ptr(converter->FromV8Value(args[2], v8_context));
    882 
    883     v8::String::Utf8Value benchmark(args[0]);
    884     DCHECK(*benchmark);
    885     args.GetReturnValue().Set(context.compositor()->ScheduleMicroBenchmark(
    886         std::string(*benchmark),
    887         value.Pass(),
    888         base::Bind(&OnMicroBenchmarkCompleted, callback_and_context)));
    889   }
    890 
    891   static void SendMessageToMicroBenchmark(
    892       const v8::FunctionCallbackInfo<v8::Value>& args) {
    893     GpuBenchmarkingContext context;
    894     if (!context.Init(true)) {
    895       args.GetReturnValue().Set(0);
    896       return;
    897     }
    898 
    899     if (args.Length() != 2 || !args[0]->IsNumber() || !args[1]->IsObject()) {
    900       args.GetReturnValue().Set(0);
    901       return;
    902     }
    903 
    904     scoped_ptr<V8ValueConverter> converter =
    905         make_scoped_ptr(V8ValueConverter::create());
    906     v8::Handle<v8::Context> v8_context =
    907         context.web_frame()->mainWorldScriptContext();
    908     scoped_ptr<base::Value> value =
    909         make_scoped_ptr(converter->FromV8Value(args[1], v8_context));
    910 
    911     int id = 0;
    912     converter->FromV8Value(args[0], v8_context)->GetAsInteger(&id);
    913     args.GetReturnValue().Set(
    914         context.compositor()->SendMessageToMicroBenchmark(id, value.Pass()));
    915   }
    916 
    917   static void HasGpuProcess(const v8::FunctionCallbackInfo<v8::Value>& args) {
    918     GpuChannelHost* gpu_channel = RenderThreadImpl::current()->GetGpuChannel();
    919     args.GetReturnValue().Set(!!gpu_channel);
    920   }
    921 };
    922 
    923 v8::Extension* GpuBenchmarkingExtension::Get() {
    924   return new GpuBenchmarkingWrapper();
    925 }
    926 
    927 }  // namespace content
    928