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 36using blink::WebCanvas; 37using blink::WebLocalFrame; 38using blink::WebImageCache; 39using blink::WebPrivatePtr; 40using blink::WebSize; 41using blink::WebView; 42 43const 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> 47static 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 61namespace { 62 63class 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 105namespace content { 106 107namespace { 108 109class 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 145class 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 213class 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 923v8::Extension* GpuBenchmarkingExtension::Get() { 924 return new GpuBenchmarkingWrapper(); 925} 926 927} // namespace content 928