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