| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/renderer/gpu/gpu_benchmarking_extension.h" |
| |
| #include <string> |
| |
| #include "base/base64.h" |
| #include "base/file_util.h" |
| #include "base/files/file_path.h" |
| #include "base/memory/scoped_vector.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "cc/layers/layer.h" |
| #include "content/common/browser_rendering_stats.h" |
| #include "content/common/gpu/gpu_rendering_stats.h" |
| #include "content/common/input/synthetic_gesture_params.h" |
| #include "content/common/input/synthetic_pinch_gesture_params.h" |
| #include "content/common/input/synthetic_smooth_scroll_gesture_params.h" |
| #include "content/common/input/synthetic_tap_gesture_params.h" |
| #include "content/public/renderer/render_thread.h" |
| #include "content/public/renderer/v8_value_converter.h" |
| #include "content/renderer/gpu/render_widget_compositor.h" |
| #include "content/renderer/render_thread_impl.h" |
| #include "content/renderer/render_view_impl.h" |
| #include "content/renderer/skia_benchmarking_extension.h" |
| #include "third_party/WebKit/public/web/WebFrame.h" |
| #include "third_party/WebKit/public/web/WebImageCache.h" |
| #include "third_party/WebKit/public/web/WebView.h" |
| #include "third_party/skia/include/core/SkData.h" |
| #include "third_party/skia/include/core/SkGraphics.h" |
| #include "third_party/skia/include/core/SkPicture.h" |
| #include "third_party/skia/include/core/SkPixelRef.h" |
| #include "third_party/skia/include/core/SkStream.h" |
| #include "ui/gfx/codec/png_codec.h" |
| #include "v8/include/v8.h" |
| #include "webkit/renderer/compositor_bindings/web_rendering_stats_impl.h" |
| |
| using blink::WebCanvas; |
| using blink::WebFrame; |
| using blink::WebImageCache; |
| using blink::WebPrivatePtr; |
| using blink::WebRenderingStatsImpl; |
| using blink::WebSize; |
| using blink::WebView; |
| |
| const char kGpuBenchmarkingExtensionName[] = "v8/GpuBenchmarking"; |
| |
| static SkData* EncodeBitmapToData(size_t* offset, const SkBitmap& bm) { |
| SkPixelRef* pr = bm.pixelRef(); |
| if (pr != NULL) { |
| SkData* data = pr->refEncodedData(); |
| if (data != NULL) { |
| *offset = bm.pixelRefOffset(); |
| return data; |
| } |
| } |
| std::vector<unsigned char> vector; |
| if (gfx::PNGCodec::EncodeBGRASkBitmap(bm, false, &vector)) { |
| return SkData::NewWithCopy(&vector.front() , vector.size()); |
| } |
| return NULL; |
| } |
| |
| namespace { |
| |
| class SkPictureSerializer { |
| public: |
| explicit SkPictureSerializer(const base::FilePath& dirpath) |
| : dirpath_(dirpath), |
| layer_id_(0) { |
| // Let skia register known effect subclasses. This basically enables |
| // reflection on those subclasses required for picture serialization. |
| content::SkiaBenchmarkingExtension::InitSkGraphics(); |
| } |
| |
| // Recursively serializes the layer tree. |
| // Each layer in the tree is serialized into a separate skp file |
| // in the given directory. |
| void Serialize(const cc::Layer* layer) { |
| const cc::LayerList& children = layer->children(); |
| for (size_t i = 0; i < children.size(); ++i) { |
| Serialize(children[i].get()); |
| } |
| |
| skia::RefPtr<SkPicture> picture = layer->GetPicture(); |
| if (!picture) |
| return; |
| |
| // Serialize picture to file. |
| // TODO(alokp): Note that for this to work Chrome needs to be launched with |
| // --no-sandbox command-line flag. Get rid of this limitation. |
| // CRBUG: 139640. |
| std::string filename = "layer_" + base::IntToString(layer_id_++) + ".skp"; |
| std::string filepath = dirpath_.AppendASCII(filename).MaybeAsASCII(); |
| DCHECK(!filepath.empty()); |
| SkFILEWStream file(filepath.c_str()); |
| DCHECK(file.isValid()); |
| picture->serialize(&file, &EncodeBitmapToData); |
| } |
| |
| private: |
| base::FilePath dirpath_; |
| int layer_id_; |
| }; |
| |
| class RenderingStatsEnumerator : public cc::RenderingStats::Enumerator { |
| public: |
| RenderingStatsEnumerator(v8::Isolate* isolate, |
| v8::Handle<v8::Object> stats_object) |
| : isolate(isolate), stats_object(stats_object) {} |
| |
| virtual void AddInt64(const char* name, int64 value) OVERRIDE { |
| stats_object->Set(v8::String::NewFromUtf8(isolate, name), |
| v8::Number::New(isolate, value)); |
| } |
| |
| virtual void AddDouble(const char* name, double value) OVERRIDE { |
| stats_object->Set(v8::String::NewFromUtf8(isolate, name), |
| v8::Number::New(isolate, value)); |
| } |
| |
| virtual void AddInt(const char* name, int value) OVERRIDE { |
| stats_object->Set(v8::String::NewFromUtf8(isolate, name), |
| v8::Integer::New(value)); |
| } |
| |
| virtual void AddTimeDeltaInSecondsF(const char* name, |
| const base::TimeDelta& value) OVERRIDE { |
| stats_object->Set(v8::String::NewFromUtf8(isolate, name), |
| v8::Number::New(isolate, value.InSecondsF())); |
| } |
| |
| private: |
| v8::Isolate* isolate; |
| v8::Handle<v8::Object> stats_object; |
| }; |
| |
| } // namespace |
| |
| namespace content { |
| |
| namespace { |
| |
| class CallbackAndContext : public base::RefCounted<CallbackAndContext> { |
| public: |
| CallbackAndContext(v8::Isolate* isolate, |
| v8::Handle<v8::Function> callback, |
| v8::Handle<v8::Context> context) |
| : isolate_(isolate) { |
| callback_.Reset(isolate_, callback); |
| context_.Reset(isolate_, context); |
| } |
| |
| v8::Isolate* isolate() { |
| return isolate_; |
| } |
| |
| v8::Handle<v8::Function> GetCallback() { |
| return v8::Local<v8::Function>::New(isolate_, callback_); |
| } |
| |
| v8::Handle<v8::Context> GetContext() { |
| return v8::Local<v8::Context>::New(isolate_, context_); |
| } |
| |
| private: |
| friend class base::RefCounted<CallbackAndContext>; |
| |
| virtual ~CallbackAndContext() { |
| callback_.Reset(); |
| context_.Reset(); |
| } |
| |
| v8::Isolate* isolate_; |
| v8::Persistent<v8::Function> callback_; |
| v8::Persistent<v8::Context> context_; |
| DISALLOW_COPY_AND_ASSIGN(CallbackAndContext); |
| }; |
| |
| class GpuBenchmarkingContext { |
| public: |
| GpuBenchmarkingContext() |
| : web_frame_(NULL), |
| web_view_(NULL), |
| render_view_impl_(NULL), |
| compositor_(NULL) {} |
| |
| bool Init(bool init_compositor) { |
| web_frame_ = WebFrame::frameForCurrentContext(); |
| if (!web_frame_) |
| return false; |
| |
| web_view_ = web_frame_->view(); |
| if (!web_view_) { |
| web_frame_ = NULL; |
| return false; |
| } |
| |
| render_view_impl_ = RenderViewImpl::FromWebView(web_view_); |
| if (!render_view_impl_) { |
| web_frame_ = NULL; |
| web_view_ = NULL; |
| return false; |
| } |
| |
| if (!init_compositor) |
| return true; |
| |
| compositor_ = render_view_impl_->compositor(); |
| if (!compositor_) { |
| web_frame_ = NULL; |
| web_view_ = NULL; |
| render_view_impl_ = NULL; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| WebFrame* web_frame() const { |
| DCHECK(web_frame_ != NULL); |
| return web_frame_; |
| } |
| WebView* web_view() const { |
| DCHECK(web_view_ != NULL); |
| return web_view_; |
| } |
| RenderViewImpl* render_view_impl() const { |
| DCHECK(render_view_impl_ != NULL); |
| return render_view_impl_; |
| } |
| RenderWidgetCompositor* compositor() const { |
| DCHECK(compositor_ != NULL); |
| return compositor_; |
| } |
| |
| private: |
| WebFrame* web_frame_; |
| WebView* web_view_; |
| RenderViewImpl* render_view_impl_; |
| RenderWidgetCompositor* compositor_; |
| |
| DISALLOW_COPY_AND_ASSIGN(GpuBenchmarkingContext); |
| }; |
| |
| } // namespace |
| |
| class GpuBenchmarkingWrapper : public v8::Extension { |
| public: |
| GpuBenchmarkingWrapper() : |
| v8::Extension(kGpuBenchmarkingExtensionName, |
| "if (typeof(chrome) == 'undefined') {" |
| " chrome = {};" |
| "};" |
| "if (typeof(chrome.gpuBenchmarking) == 'undefined') {" |
| " chrome.gpuBenchmarking = {};" |
| "};" |
| "chrome.gpuBenchmarking.setNeedsDisplayOnAllLayers = function() {" |
| " native function SetNeedsDisplayOnAllLayers();" |
| " return SetNeedsDisplayOnAllLayers();" |
| "};" |
| "chrome.gpuBenchmarking.setRasterizeOnlyVisibleContent = function() {" |
| " native function SetRasterizeOnlyVisibleContent();" |
| " return SetRasterizeOnlyVisibleContent();" |
| "};" |
| "chrome.gpuBenchmarking.renderingStats = function() {" |
| " native function GetRenderingStats();" |
| " return GetRenderingStats();" |
| "};" |
| "chrome.gpuBenchmarking.gpuRenderingStats = function() {" |
| " native function GetGpuRenderingStats();" |
| " return GetGpuRenderingStats();" |
| "};" |
| "chrome.gpuBenchmarking.printToSkPicture = function(dirname) {" |
| " native function PrintToSkPicture();" |
| " return PrintToSkPicture(dirname);" |
| "};" |
| "chrome.gpuBenchmarking.DEFAULT_INPUT = 0;" |
| "chrome.gpuBenchmarking.TOUCH_INPUT = 1;" |
| "chrome.gpuBenchmarking.MOUSE_INPUT = 2;" |
| "chrome.gpuBenchmarking.smoothScrollBy = " |
| " function(pixels_to_scroll, opt_callback, opt_start_x," |
| " opt_start_y, opt_gesture_source_type," |
| " opt_direction, opt_speed_in_pixels_s) {" |
| " pixels_to_scroll = pixels_to_scroll || 0;" |
| " callback = opt_callback || function() { };" |
| " gesture_source_type = opt_gesture_source_type ||" |
| " chrome.gpuBenchmarking.DEFAULT_INPUT;" |
| " direction = opt_direction || 'down';" |
| " speed_in_pixels_s = opt_speed_in_pixels_s || 800;" |
| " native function BeginSmoothScroll();" |
| " return BeginSmoothScroll(pixels_to_scroll, callback," |
| " gesture_source_type, direction," |
| " speed_in_pixels_s, true," |
| " opt_start_x, opt_start_y);" |
| "};" |
| "chrome.gpuBenchmarking.smoothScrollBySendsTouch = function() {" |
| " native function SmoothScrollSendsTouch();" |
| " return SmoothScrollSendsTouch();" |
| "};" |
| "chrome.gpuBenchmarking.swipe = " |
| " function(direction, distance, opt_callback," |
| " opt_start_x, opt_start_y," |
| " opt_speed_in_pixels_s) {" |
| " direction = direction || 'up';" |
| " distance = distance || 0;" |
| " callback = opt_callback || function() { };" |
| " speed_in_pixels_s = opt_speed_in_pixels_s || 800;" |
| " native function BeginSmoothScroll();" |
| " return BeginSmoothScroll(-distance, callback," |
| " chrome.gpuBenchmarking.TOUCH_INPUT," |
| " direction, speed_in_pixels_s, false," |
| " opt_start_x, opt_start_y);" |
| "};" |
| "chrome.gpuBenchmarking.pinchBy = " |
| " function(zoom_in, pixels_to_cover, anchor_x, anchor_y," |
| " opt_callback, opt_relative_pointer_speed_in_pixels_s) {" |
| " callback = opt_callback || function() { };" |
| " relative_pointer_speed_in_pixels_s =" |
| " opt_relative_pointer_speed_in_pixels_s || 800;" |
| " native function BeginPinch();" |
| " return BeginPinch(zoom_in, pixels_to_cover," |
| " anchor_x, anchor_y, callback," |
| " relative_pointer_speed_in_pixels_s);" |
| "};" |
| "chrome.gpuBenchmarking.tap = " |
| " function(position_x, position_y, opt_callback, opt_duration_ms," |
| " opt_gesture_source_type) {" |
| " callback = opt_callback || function() { };" |
| " duration_ms = opt_duration_ms || 0;" |
| " gesture_source_type = opt_gesture_source_type ||" |
| " chrome.gpuBenchmarking.DEFAULT_INPUT;" |
| " native function BeginTap();" |
| " return BeginTap(position_x, position_y, callback, duration_ms," |
| " gesture_source_type);" |
| "};" |
| "chrome.gpuBenchmarking.beginWindowSnapshotPNG = function(callback) {" |
| " native function BeginWindowSnapshotPNG();" |
| " BeginWindowSnapshotPNG(callback);" |
| "};" |
| "chrome.gpuBenchmarking.clearImageCache = function() {" |
| " native function ClearImageCache();" |
| " ClearImageCache();" |
| "};" |
| "chrome.gpuBenchmarking.runMicroBenchmark =" |
| " function(name, callback, opt_arguments) {" |
| " arguments = opt_arguments || {};" |
| " native function RunMicroBenchmark();" |
| " return RunMicroBenchmark(name, callback, arguments);" |
| "};" |
| "chrome.gpuBenchmarking.hasGpuProcess = function() {" |
| " native function HasGpuProcess();" |
| " return HasGpuProcess();" |
| "};") {} |
| |
| virtual v8::Handle<v8::FunctionTemplate> GetNativeFunctionTemplate( |
| v8::Isolate* isolate, |
| v8::Handle<v8::String> name) OVERRIDE { |
| if (name->Equals( |
| v8::String::NewFromUtf8(isolate, "SetNeedsDisplayOnAllLayers"))) |
| return v8::FunctionTemplate::New(isolate, SetNeedsDisplayOnAllLayers); |
| if (name->Equals( |
| v8::String::NewFromUtf8(isolate, "SetRasterizeOnlyVisibleContent"))) |
| return v8::FunctionTemplate::New(isolate, SetRasterizeOnlyVisibleContent); |
| if (name->Equals(v8::String::NewFromUtf8(isolate, "GetRenderingStats"))) |
| return v8::FunctionTemplate::New(isolate, GetRenderingStats); |
| if (name->Equals(v8::String::NewFromUtf8(isolate, "GetGpuRenderingStats"))) |
| return v8::FunctionTemplate::New(isolate, GetGpuRenderingStats); |
| if (name->Equals(v8::String::NewFromUtf8(isolate, "PrintToSkPicture"))) |
| return v8::FunctionTemplate::New(isolate, PrintToSkPicture); |
| if (name->Equals(v8::String::NewFromUtf8(isolate, "BeginSmoothScroll"))) |
| return v8::FunctionTemplate::New(isolate, BeginSmoothScroll); |
| if (name->Equals( |
| v8::String::NewFromUtf8(isolate, "SmoothScrollSendsTouch"))) |
| return v8::FunctionTemplate::New(isolate, SmoothScrollSendsTouch); |
| if (name->Equals(v8::String::NewFromUtf8(isolate, "BeginPinch"))) |
| return v8::FunctionTemplate::New(isolate, BeginPinch); |
| if (name->Equals(v8::String::NewFromUtf8(isolate, "BeginTap"))) |
| return v8::FunctionTemplate::New(isolate, BeginTap); |
| if (name->Equals( |
| v8::String::NewFromUtf8(isolate, "BeginWindowSnapshotPNG"))) |
| return v8::FunctionTemplate::New(isolate, BeginWindowSnapshotPNG); |
| if (name->Equals(v8::String::NewFromUtf8(isolate, "ClearImageCache"))) |
| return v8::FunctionTemplate::New(isolate, ClearImageCache); |
| if (name->Equals(v8::String::NewFromUtf8(isolate, "RunMicroBenchmark"))) |
| return v8::FunctionTemplate::New(isolate, RunMicroBenchmark); |
| if (name->Equals(v8::String::NewFromUtf8(isolate, "HasGpuProcess"))) |
| return v8::FunctionTemplate::New(isolate, HasGpuProcess); |
| |
| return v8::Handle<v8::FunctionTemplate>(); |
| } |
| |
| static void SetNeedsDisplayOnAllLayers( |
| const v8::FunctionCallbackInfo<v8::Value>& args) { |
| GpuBenchmarkingContext context; |
| if (!context.Init(true)) |
| return; |
| |
| context.compositor()->SetNeedsDisplayOnAllLayers(); |
| } |
| |
| static void SetRasterizeOnlyVisibleContent( |
| const v8::FunctionCallbackInfo<v8::Value>& args) { |
| GpuBenchmarkingContext context; |
| if (!context.Init(true)) |
| return; |
| |
| context.compositor()->SetRasterizeOnlyVisibleContent(); |
| } |
| |
| static void GetRenderingStats( |
| const v8::FunctionCallbackInfo<v8::Value>& args) { |
| |
| GpuBenchmarkingContext context; |
| if (!context.Init(false)) |
| return; |
| |
| WebRenderingStatsImpl stats; |
| context.render_view_impl()->GetRenderingStats(stats); |
| |
| content::GpuRenderingStats gpu_stats; |
| context.render_view_impl()->GetGpuRenderingStats(&gpu_stats); |
| BrowserRenderingStats browser_stats; |
| context.render_view_impl()->GetBrowserRenderingStats(&browser_stats); |
| v8::Handle<v8::Object> stats_object = v8::Object::New(); |
| |
| RenderingStatsEnumerator enumerator(args.GetIsolate(), stats_object); |
| stats.rendering_stats.EnumerateFields(&enumerator); |
| gpu_stats.EnumerateFields(&enumerator); |
| browser_stats.EnumerateFields(&enumerator); |
| |
| args.GetReturnValue().Set(stats_object); |
| } |
| |
| static void GetGpuRenderingStats( |
| const v8::FunctionCallbackInfo<v8::Value>& args) { |
| |
| GpuBenchmarkingContext context; |
| if (!context.Init(false)) |
| return; |
| |
| content::GpuRenderingStats gpu_stats; |
| context.render_view_impl()->GetGpuRenderingStats(&gpu_stats); |
| |
| v8::Isolate* isolate = args.GetIsolate(); |
| v8::Handle<v8::Object> stats_object = v8::Object::New(isolate); |
| RenderingStatsEnumerator enumerator(isolate, stats_object); |
| gpu_stats.EnumerateFields(&enumerator); |
| |
| args.GetReturnValue().Set(stats_object); |
| } |
| |
| static void PrintToSkPicture( |
| const v8::FunctionCallbackInfo<v8::Value>& args) { |
| if (args.Length() != 1) |
| return; |
| |
| v8::String::Utf8Value dirname(args[0]); |
| if (dirname.length() == 0) |
| return; |
| |
| GpuBenchmarkingContext context; |
| if (!context.Init(true)) |
| return; |
| |
| const cc::Layer* root_layer = context.compositor()->GetRootLayer(); |
| if (!root_layer) |
| return; |
| |
| base::FilePath dirpath( |
| base::FilePath::StringType(*dirname, *dirname + dirname.length())); |
| if (!base::CreateDirectory(dirpath) || |
| !base::PathIsWritable(dirpath)) { |
| std::string msg("Path is not writable: "); |
| msg.append(dirpath.MaybeAsASCII()); |
| v8::Isolate* isolate = args.GetIsolate(); |
| isolate->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8( |
| isolate, msg.c_str(), v8::String::kNormalString, msg.length()))); |
| return; |
| } |
| |
| SkPictureSerializer serializer(dirpath); |
| serializer.Serialize(root_layer); |
| } |
| |
| static void OnSyntheticGestureCompleted( |
| CallbackAndContext* callback_and_context) { |
| v8::HandleScope scope(callback_and_context->isolate()); |
| v8::Handle<v8::Context> context = callback_and_context->GetContext(); |
| v8::Context::Scope context_scope(context); |
| WebFrame* frame = WebFrame::frameForContext(context); |
| if (frame) { |
| frame->callFunctionEvenIfScriptDisabled( |
| callback_and_context->GetCallback(), v8::Object::New(), 0, NULL); |
| } |
| } |
| |
| static void SmoothScrollSendsTouch( |
| const v8::FunctionCallbackInfo<v8::Value>& args) { |
| // TODO(epenner): Should other platforms emulate touch events? |
| #if defined(OS_ANDROID) || defined(OS_CHROMEOS) |
| args.GetReturnValue().Set(true); |
| #else |
| args.GetReturnValue().Set(false); |
| #endif |
| } |
| |
| static void BeginSmoothScroll( |
| const v8::FunctionCallbackInfo<v8::Value>& args) { |
| GpuBenchmarkingContext context; |
| if (!context.Init(false)) |
| return; |
| |
| // The last two arguments can be undefined. We check their validity later. |
| int arglen = args.Length(); |
| if (arglen < 8 || |
| !args[0]->IsNumber() || |
| !args[1]->IsFunction() || |
| !args[2]->IsNumber() || |
| !args[3]->IsString() || |
| !args[4]->IsNumber() || |
| !args[5]->IsBoolean()) { |
| args.GetReturnValue().Set(false); |
| return; |
| } |
| |
| v8::Local<v8::Function> callback_local = |
| v8::Local<v8::Function>::Cast(args[1]); |
| |
| scoped_refptr<CallbackAndContext> callback_and_context = |
| new CallbackAndContext(args.GetIsolate(), |
| callback_local, |
| context.web_frame()->mainWorldScriptContext()); |
| |
| scoped_ptr<SyntheticSmoothScrollGestureParams> gesture_params( |
| new SyntheticSmoothScrollGestureParams); |
| |
| // Convert coordinates from CSS pixels to density independent pixels (DIPs). |
| float page_scale_factor = context.web_view()->pageScaleFactor(); |
| |
| int gesture_source_type = args[2]->IntegerValue(); |
| if (gesture_source_type < 0 || |
| gesture_source_type > SyntheticGestureParams::GESTURE_SOURCE_TYPE_MAX) { |
| args.GetReturnValue().Set(false); |
| return; |
| } |
| gesture_params->gesture_source_type = |
| static_cast<SyntheticGestureParams::GestureSourceType>( |
| gesture_source_type); |
| |
| int distance = args[0]->IntegerValue() * page_scale_factor; |
| v8::String::Utf8Value direction(args[3]); |
| DCHECK(*direction); |
| std::string direction_str(*direction); |
| if (direction_str == "down") |
| gesture_params->distance.set_y(distance); |
| else if (direction_str == "up") |
| gesture_params->distance.set_y(-distance); |
| else if (direction_str == "right") |
| gesture_params->distance.set_x(distance); |
| else if (direction_str == "left") |
| gesture_params->distance.set_x(-distance); |
| else { |
| args.GetReturnValue().Set(false); |
| return; |
| } |
| |
| gesture_params->speed_in_pixels_s = args[4]->IntegerValue(); |
| gesture_params->prevent_fling = args[5]->BooleanValue(); |
| |
| // Account for the 2 optional arguments, start_x and start_y. |
| if (args[6]->IsUndefined() || args[7]->IsUndefined()) { |
| blink::WebRect rect = context.render_view_impl()->windowRect(); |
| gesture_params->anchor.SetPoint(rect.width / 2, rect.height / 2); |
| } else if (args[6]->IsNumber() && args[7]->IsNumber()) { |
| gesture_params->anchor.SetPoint( |
| args[6]->IntegerValue() * page_scale_factor, |
| args[7]->IntegerValue() * page_scale_factor); |
| } else { |
| args.GetReturnValue().Set(false); |
| return; |
| } |
| |
| // TODO(nduca): If the render_view_impl is destroyed while the gesture is in |
| // progress, we will leak the callback and context. This needs to be fixed, |
| // somehow. |
| context.render_view_impl()->QueueSyntheticGesture( |
| gesture_params.PassAs<SyntheticGestureParams>(), |
| base::Bind(&OnSyntheticGestureCompleted, |
| callback_and_context)); |
| |
| args.GetReturnValue().Set(true); |
| } |
| |
| static void BeginPinch( |
| const v8::FunctionCallbackInfo<v8::Value>& args) { |
| GpuBenchmarkingContext context; |
| if (!context.Init(false)) |
| return; |
| |
| int arglen = args.Length(); |
| if (arglen < 6 || |
| !args[0]->IsBoolean() || |
| !args[1]->IsNumber() || |
| !args[2]->IsNumber() || |
| !args[3]->IsNumber() || |
| !args[4]->IsFunction() || |
| !args[5]->IsNumber()) { |
| args.GetReturnValue().Set(false); |
| return; |
| } |
| |
| scoped_ptr<SyntheticPinchGestureParams> gesture_params( |
| new SyntheticPinchGestureParams); |
| |
| // Convert coordinates from CSS pixels to density independent pixels (DIPs). |
| float page_scale_factor = context.web_view()->pageScaleFactor(); |
| |
| gesture_params->zoom_in = args[0]->BooleanValue(); |
| gesture_params->total_num_pixels_covered = |
| args[1]->IntegerValue() * page_scale_factor; |
| gesture_params->anchor.SetPoint( |
| args[2]->IntegerValue() * page_scale_factor, |
| args[3]->IntegerValue() * page_scale_factor); |
| gesture_params->relative_pointer_speed_in_pixels_s = |
| args[5]->IntegerValue(); |
| |
| v8::Local<v8::Function> callback_local = |
| v8::Local<v8::Function>::Cast(args[4]); |
| |
| scoped_refptr<CallbackAndContext> callback_and_context = |
| new CallbackAndContext(args.GetIsolate(), |
| callback_local, |
| context.web_frame()->mainWorldScriptContext()); |
| |
| |
| // TODO(nduca): If the render_view_impl is destroyed while the gesture is in |
| // progress, we will leak the callback and context. This needs to be fixed, |
| // somehow. |
| context.render_view_impl()->QueueSyntheticGesture( |
| gesture_params.PassAs<SyntheticGestureParams>(), |
| base::Bind(&OnSyntheticGestureCompleted, |
| callback_and_context)); |
| |
| args.GetReturnValue().Set(true); |
| } |
| |
| static void BeginTap( |
| const v8::FunctionCallbackInfo<v8::Value>& args) { |
| GpuBenchmarkingContext context; |
| if (!context.Init(false)) |
| return; |
| |
| int arglen = args.Length(); |
| if (arglen < 5 || |
| !args[0]->IsNumber() || |
| !args[1]->IsNumber() || |
| !args[2]->IsFunction() || |
| !args[3]->IsNumber() || |
| !args[4]->IsNumber()) { |
| args.GetReturnValue().Set(false); |
| return; |
| } |
| |
| scoped_ptr<SyntheticTapGestureParams> gesture_params( |
| new SyntheticTapGestureParams); |
| |
| // Convert coordinates from CSS pixels to density independent pixels (DIPs). |
| float page_scale_factor = context.web_view()->pageScaleFactor(); |
| |
| gesture_params->position.SetPoint( |
| args[0]->IntegerValue() * page_scale_factor, |
| args[1]->IntegerValue() * page_scale_factor); |
| gesture_params->duration_ms = args[3]->IntegerValue(); |
| |
| int gesture_source_type = args[4]->IntegerValue(); |
| if (gesture_source_type < 0 || |
| gesture_source_type > SyntheticGestureParams::GESTURE_SOURCE_TYPE_MAX) { |
| args.GetReturnValue().Set(false); |
| return; |
| } |
| gesture_params->gesture_source_type = |
| static_cast<SyntheticGestureParams::GestureSourceType>( |
| gesture_source_type); |
| |
| v8::Local<v8::Function> callback_local = |
| v8::Local<v8::Function>::Cast(args[2]); |
| |
| scoped_refptr<CallbackAndContext> callback_and_context = |
| new CallbackAndContext(args.GetIsolate(), |
| callback_local, |
| context.web_frame()->mainWorldScriptContext()); |
| |
| |
| // TODO(nduca): If the render_view_impl is destroyed while the gesture is in |
| // progress, we will leak the callback and context. This needs to be fixed, |
| // somehow. |
| context.render_view_impl()->QueueSyntheticGesture( |
| gesture_params.PassAs<SyntheticGestureParams>(), |
| base::Bind(&OnSyntheticGestureCompleted, |
| callback_and_context)); |
| |
| args.GetReturnValue().Set(true); |
| } |
| |
| static void OnSnapshotCompleted(CallbackAndContext* callback_and_context, |
| const gfx::Size& size, |
| const std::vector<unsigned char>& png) { |
| v8::Isolate* isolate = callback_and_context->isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Handle<v8::Context> context = callback_and_context->GetContext(); |
| v8::Context::Scope context_scope(context); |
| WebFrame* frame = WebFrame::frameForContext(context); |
| if (frame) { |
| |
| v8::Handle<v8::Value> result; |
| |
| if(!size.IsEmpty()) { |
| v8::Handle<v8::Object> result_object; |
| result_object = v8::Object::New(isolate); |
| |
| result_object->Set(v8::String::NewFromUtf8(isolate, "width"), |
| v8::Number::New(isolate, size.width())); |
| result_object->Set(v8::String::NewFromUtf8(isolate, "height"), |
| v8::Number::New(isolate, size.height())); |
| |
| std::string base64_png; |
| base::Base64Encode(base::StringPiece( |
| reinterpret_cast<const char*>(&*png.begin()), png.size()), |
| &base64_png); |
| |
| result_object->Set(v8::String::NewFromUtf8(isolate, "data"), |
| v8::String::NewFromUtf8(isolate, |
| base64_png.c_str(), |
| v8::String::kNormalString, |
| base64_png.size())); |
| |
| result = result_object; |
| } else { |
| result = v8::Null(isolate); |
| } |
| |
| v8::Handle<v8::Value> argv[] = { result }; |
| |
| frame->callFunctionEvenIfScriptDisabled( |
| callback_and_context->GetCallback(), v8::Object::New(), 1, argv); |
| } |
| } |
| |
| static void BeginWindowSnapshotPNG( |
| const v8::FunctionCallbackInfo<v8::Value>& args) { |
| GpuBenchmarkingContext context; |
| if (!context.Init(false)) |
| return; |
| |
| if (!args[0]->IsFunction()) |
| return; |
| |
| v8::Local<v8::Function> callback_local = |
| v8::Local<v8::Function>::Cast(args[0]); |
| |
| scoped_refptr<CallbackAndContext> callback_and_context = |
| new CallbackAndContext(args.GetIsolate(), |
| callback_local, |
| context.web_frame()->mainWorldScriptContext()); |
| |
| context.render_view_impl()->GetWindowSnapshot( |
| base::Bind(&OnSnapshotCompleted, callback_and_context)); |
| } |
| |
| static void ClearImageCache( |
| const v8::FunctionCallbackInfo<v8::Value>& args) { |
| WebImageCache::clear(); |
| } |
| |
| static void OnMicroBenchmarkCompleted( |
| CallbackAndContext* callback_and_context, |
| scoped_ptr<base::Value> result) { |
| v8::HandleScope scope(callback_and_context->isolate()); |
| v8::Handle<v8::Context> context = callback_and_context->GetContext(); |
| v8::Context::Scope context_scope(context); |
| WebFrame* frame = WebFrame::frameForContext(context); |
| if (frame) { |
| scoped_ptr<V8ValueConverter> converter = |
| make_scoped_ptr(V8ValueConverter::create()); |
| v8::Handle<v8::Value> value = converter->ToV8Value(result.get(), context); |
| v8::Handle<v8::Value> argv[] = { value }; |
| |
| frame->callFunctionEvenIfScriptDisabled( |
| callback_and_context->GetCallback(), v8::Object::New(), 1, argv); |
| } |
| } |
| |
| static void RunMicroBenchmark( |
| const v8::FunctionCallbackInfo<v8::Value>& args) { |
| GpuBenchmarkingContext context; |
| if (!context.Init(true)) { |
| args.GetReturnValue().Set(false); |
| return; |
| } |
| |
| if (args.Length() != 3 || |
| !args[0]->IsString() || |
| !args[1]->IsFunction() || |
| !args[2]->IsObject()) { |
| args.GetReturnValue().Set(false); |
| return; |
| } |
| |
| v8::Local<v8::Function> callback_local = |
| v8::Local<v8::Function>::Cast(args[1]); |
| |
| scoped_refptr<CallbackAndContext> callback_and_context = |
| new CallbackAndContext(args.GetIsolate(), |
| callback_local, |
| context.web_frame()->mainWorldScriptContext()); |
| |
| scoped_ptr<V8ValueConverter> converter = |
| make_scoped_ptr(V8ValueConverter::create()); |
| v8::Handle<v8::Context> v8_context = callback_and_context->GetContext(); |
| scoped_ptr<base::Value> value = |
| make_scoped_ptr(converter->FromV8Value(args[2], v8_context)); |
| |
| v8::String::Utf8Value benchmark(args[0]); |
| DCHECK(*benchmark); |
| args.GetReturnValue().Set(context.compositor()->ScheduleMicroBenchmark( |
| std::string(*benchmark), |
| value.Pass(), |
| base::Bind(&OnMicroBenchmarkCompleted, callback_and_context))); |
| } |
| |
| static void HasGpuProcess(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| GpuChannelHost* gpu_channel = RenderThreadImpl::current()->GetGpuChannel(); |
| args.GetReturnValue().Set(!!gpu_channel); |
| } |
| }; |
| |
| v8::Extension* GpuBenchmarkingExtension::Get() { |
| return new GpuBenchmarkingWrapper(); |
| } |
| |
| } // namespace content |