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