Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / content / renderer / gpu / gpu_benchmarking_extension.cc
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@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