gpu_benchmarking_extension.cc revision 68043e1e95eeb07d5cae7aca370b26518b0867d6
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/renderer/gpu/gpu_benchmarking_extension.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/base64.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/file_util.h"
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/files/file_path.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/memory/scoped_vector.h"
13868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/string_number_conversions.h"
143551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)#include "cc/layers/layer.h"
15868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "content/common/browser_rendering_stats.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/common/gpu/gpu_rendering_stats.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/renderer/render_thread.h"
18c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "content/renderer/gpu/render_widget_compositor.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/renderer/render_view_impl.h"
20a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)#include "content/renderer/skia_benchmarking_extension.h"
217d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "third_party/WebKit/public/web/WebFrame.h"
227d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "third_party/WebKit/public/web/WebImageCache.h"
237d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "third_party/WebKit/public/web/WebView.h"
2490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "third_party/skia/include/core/SkData.h"
2590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "third_party/skia/include/core/SkGraphics.h"
2690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "third_party/skia/include/core/SkPicture.h"
2790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "third_party/skia/include/core/SkPixelRef.h"
2890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "third_party/skia/include/core/SkStream.h"
29c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ui/gfx/codec/png_codec.h"
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "v8/include/v8.h"
3190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "webkit/renderer/compositor_bindings/web_rendering_stats_impl.h"
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using WebKit::WebCanvas;
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using WebKit::WebFrame;
35868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)using WebKit::WebImageCache;
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using WebKit::WebPrivatePtr;
372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using WebKit::WebRenderingStatsImpl;
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using WebKit::WebSize;
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using WebKit::WebView;
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kGpuBenchmarkingExtensionName[] = "v8/GpuBenchmarking";
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)static SkData* EncodeBitmapToData(size_t* offset, const SkBitmap& bm) {
4490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    SkPixelRef* pr = bm.pixelRef();
4590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    if (pr != NULL) {
4690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        SkData* data = pr->refEncodedData();
4790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        if (data != NULL) {
4890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)            *offset = bm.pixelRefOffset();
4990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)            return data;
5090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        }
5190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    }
52c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    std::vector<unsigned char> vector;
53eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if (gfx::PNGCodec::EncodeBGRASkBitmap(bm, false, &vector)) {
5490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        return SkData::NewWithCopy(&vector.front() , vector.size());
55c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    }
5690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    return NULL;
57c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
58c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
613551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)class SkPictureSerializer {
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
633551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  explicit SkPictureSerializer(const base::FilePath& dirpath)
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      : dirpath_(dirpath),
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        layer_id_(0) {
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Let skia register known effect subclasses. This basically enables
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // reflection on those subclasses required for picture serialization.
68a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    content::SkiaBenchmarkingExtension::InitSkGraphics();
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
713551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  // Recursively serializes the layer tree.
723551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  // Each layer in the tree is serialized into a separate skp file
733551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  // in the given directory.
743551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  void Serialize(const cc::Layer* layer) {
753551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    const cc::LayerList& children = layer->children();
763551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    for (size_t i = 0; i < children.size(); ++i) {
773551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      Serialize(children[i].get());
783551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    }
793551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
803551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    skia::RefPtr<SkPicture> picture = layer->GetPicture();
813551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    if (!picture)
823551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      return;
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Serialize picture to file.
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // TODO(alokp): Note that for this to work Chrome needs to be launched with
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // --no-sandbox command-line flag. Get rid of this limitation.
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // CRBUG: 139640.
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string filename = "layer_" + base::IntToString(layer_id_++) + ".skp";
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string filepath = dirpath_.AppendASCII(filename).MaybeAsASCII();
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(!filepath.empty());
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SkFILEWStream file(filepath.c_str());
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(file.isValid());
933551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    picture->serialize(&file, &EncodeBitmapToData);
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::FilePath dirpath_;
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int layer_id_;
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class RenderingStatsEnumerator : public cc::RenderingStats::Enumerator {
1022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) public:
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  RenderingStatsEnumerator(v8::Handle<v8::Object> stats_object)
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      : stats_object(stats_object) { }
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual void AddInt64(const char* name, int64 value) OVERRIDE {
1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    stats_object->Set(v8::String::New(name), v8::Number::New(value));
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual void AddDouble(const char* name, double value) OVERRIDE {
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    stats_object->Set(v8::String::New(name), v8::Number::New(value));
1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual void AddInt(const char* name, int value) OVERRIDE {
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    stats_object->Set(v8::String::New(name), v8::Integer::New(value));
1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual void AddTimeDeltaInSecondsF(const char* name,
1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                      const base::TimeDelta& value) OVERRIDE {
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    stats_object->Set(v8::String::New(name),
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                      v8::Number::New(value.InSecondsF()));
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) private:
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  v8::Handle<v8::Object> stats_object;
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace content {
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
132868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)namespace {
133868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
134868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)class CallbackAndContext : public base::RefCounted<CallbackAndContext> {
135868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) public:
136868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  CallbackAndContext(v8::Isolate* isolate,
137868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                     v8::Handle<v8::Function> callback,
138868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                     v8::Handle<v8::Context> context)
139868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      : isolate_(isolate) {
140868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    callback_.Reset(isolate_, callback);
141868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    context_.Reset(isolate_, context);
142868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  }
143868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
144868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  v8::Isolate* isolate() {
145868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    return isolate_;
146868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  }
147868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
148868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  v8::Handle<v8::Function> GetCallback() {
149868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    return v8::Local<v8::Function>::New(isolate_, callback_);
150868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  }
151868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
152868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  v8::Handle<v8::Context> GetContext() {
153868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    return v8::Local<v8::Context>::New(isolate_, context_);
154868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  }
155868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
156868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) private:
157868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  friend class base::RefCounted<CallbackAndContext>;
158868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
159868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  virtual ~CallbackAndContext() {
160868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    callback_.Dispose();
161868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    context_.Dispose();
162868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  }
163868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
164868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  v8::Isolate* isolate_;
165868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  v8::Persistent<v8::Function> callback_;
166868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  v8::Persistent<v8::Context> context_;
167868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(CallbackAndContext);
168868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)};
169868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
170d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)class GpuBenchmarkingContext {
171d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) public:
172d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  GpuBenchmarkingContext()
17368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      : web_frame_(NULL),
17468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        web_view_(NULL),
17568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        render_view_impl_(NULL),
17668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        compositor_(NULL) {}
177d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
17868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  bool Init(bool init_compositor) {
179d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    web_frame_ = WebFrame::frameForCurrentContext();
180d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    if (!web_frame_)
181d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      return false;
182d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
183d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    web_view_ = web_frame_->view();
184d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    if (!web_view_) {
185d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      web_frame_ = NULL;
186d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      return false;
187d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    }
188d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
189d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    render_view_impl_ = RenderViewImpl::FromWebView(web_view_);
190d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    if (!render_view_impl_) {
191d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      web_frame_ = NULL;
192d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      web_view_ = NULL;
193d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      return false;
194d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    }
195d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
19668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    if (!init_compositor)
19768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      return true;
19868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
199d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    compositor_ = render_view_impl_->compositor();
200d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    if (!compositor_) {
201d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      web_frame_ = NULL;
202d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      web_view_ = NULL;
203d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      render_view_impl_ = NULL;
204d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      return false;
205d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    }
206d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
207d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    return true;
208d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  }
209d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
210d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  WebFrame* web_frame() const {
211d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    DCHECK(web_frame_ != NULL);
212d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    return web_frame_;
213d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  }
214d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  WebView* web_view() const {
215d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    DCHECK(web_view_ != NULL);
216d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    return web_view_;
217d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  }
218d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  RenderViewImpl* render_view_impl() const {
219d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    DCHECK(render_view_impl_ != NULL);
220d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    return render_view_impl_;
221d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  }
222d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  RenderWidgetCompositor* compositor() const {
223d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    DCHECK(compositor_ != NULL);
224d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    return compositor_;
225d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  }
226d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
227d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) private:
228d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  WebFrame* web_frame_;
229d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  WebView* web_view_;
230d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  RenderViewImpl* render_view_impl_;
231d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  RenderWidgetCompositor* compositor_;
23268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
233d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(GpuBenchmarkingContext);
234d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)};
235d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
236868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}  // namespace
237868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class GpuBenchmarkingWrapper : public v8::Extension {
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GpuBenchmarkingWrapper() :
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      v8::Extension(kGpuBenchmarkingExtensionName,
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "if (typeof(chrome) == 'undefined') {"
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "  chrome = {};"
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "};"
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "if (typeof(chrome.gpuBenchmarking) == 'undefined') {"
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "  chrome.gpuBenchmarking = {};"
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "};"
248c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          "chrome.gpuBenchmarking.setNeedsDisplayOnAllLayers = function() {"
249c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          "  native function SetNeedsDisplayOnAllLayers();"
250c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          "  return SetNeedsDisplayOnAllLayers();"
251c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          "};"
25290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)          "chrome.gpuBenchmarking.setRasterizeOnlyVisibleContent = function() {"
25390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)          "  native function SetRasterizeOnlyVisibleContent();"
25490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)          "  return SetRasterizeOnlyVisibleContent();"
25590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)          "};"
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "chrome.gpuBenchmarking.renderingStats = function() {"
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "  native function GetRenderingStats();"
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "  return GetRenderingStats();"
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "};"
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "chrome.gpuBenchmarking.printToSkPicture = function(dirname) {"
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "  native function PrintToSkPicture();"
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "  return PrintToSkPicture(dirname);"
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "};"
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "chrome.gpuBenchmarking.smoothScrollBy = "
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "    function(pixels_to_scroll, opt_callback, opt_mouse_event_x,"
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "             opt_mouse_event_y) {"
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "  pixels_to_scroll = pixels_to_scroll || 0;"
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "  callback = opt_callback || function() { };"
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "  native function BeginSmoothScroll();"
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "  if (typeof opt_mouse_event_x !== 'undefined' &&"
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "      typeof opt_mouse_event_y !== 'undefined') {"
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "    return BeginSmoothScroll(pixels_to_scroll >= 0, callback,"
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "                             Math.abs(pixels_to_scroll),"
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "                             opt_mouse_event_x, opt_mouse_event_y);"
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "  } else {"
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "    return BeginSmoothScroll(pixels_to_scroll >= 0, callback,"
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "                             Math.abs(pixels_to_scroll));"
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "  }"
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "};"
2807dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          "chrome.gpuBenchmarking.smoothScrollBySendsTouch = function() {"
2817dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          "  native function SmoothScrollSendsTouch();"
2827dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          "  return SmoothScrollSendsTouch();"
2837dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          "};"
28458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)          "chrome.gpuBenchmarking.pinchBy = "
28558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)          "    function(zoom_in, pixels_to_move, anchor_x, anchor_y,"
28658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)          "             opt_callback) {"
28758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)          "  callback = opt_callback || function() { };"
28858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)          "  native function BeginPinch();"
28958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)          "  return BeginPinch(zoom_in, pixels_to_move,"
29058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)          "                    anchor_x, anchor_y, callback);"
29158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)          "};"
2922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          "chrome.gpuBenchmarking.beginWindowSnapshotPNG = function(callback) {"
2932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          "  native function BeginWindowSnapshotPNG();"
2942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          "  BeginWindowSnapshotPNG(callback);"
295868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)          "};"
296868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)          "chrome.gpuBenchmarking.clearImageCache = function() {"
297868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)          "  native function ClearImageCache();"
298868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)          "  ClearImageCache();"
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "};") {}
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
3022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      v8::Handle<v8::String> name) OVERRIDE {
303c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (name->Equals(v8::String::New("SetNeedsDisplayOnAllLayers")))
304c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return v8::FunctionTemplate::New(SetNeedsDisplayOnAllLayers);
30590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    if (name->Equals(v8::String::New("SetRasterizeOnlyVisibleContent")))
30690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      return v8::FunctionTemplate::New(SetRasterizeOnlyVisibleContent);
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (name->Equals(v8::String::New("GetRenderingStats")))
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return v8::FunctionTemplate::New(GetRenderingStats);
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (name->Equals(v8::String::New("PrintToSkPicture")))
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return v8::FunctionTemplate::New(PrintToSkPicture);
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (name->Equals(v8::String::New("BeginSmoothScroll")))
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return v8::FunctionTemplate::New(BeginSmoothScroll);
3137dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    if (name->Equals(v8::String::New("SmoothScrollSendsTouch")))
3147dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      return v8::FunctionTemplate::New(SmoothScrollSendsTouch);
31558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    if (name->Equals(v8::String::New("BeginPinch")))
31658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      return v8::FunctionTemplate::New(BeginPinch);
3172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (name->Equals(v8::String::New("BeginWindowSnapshotPNG")))
3182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return v8::FunctionTemplate::New(BeginWindowSnapshotPNG);
319868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    if (name->Equals(v8::String::New("ClearImageCache")))
320868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      return v8::FunctionTemplate::New(ClearImageCache);
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return v8::Handle<v8::FunctionTemplate>();
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3257d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  static void SetNeedsDisplayOnAllLayers(
3267d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      const v8::FunctionCallbackInfo<v8::Value>& args) {
327d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    GpuBenchmarkingContext context;
32868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    if (!context.Init(true))
3297d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      return;
330c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
331d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    context.compositor()->SetNeedsDisplayOnAllLayers();
332c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
333c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
3347d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  static void SetRasterizeOnlyVisibleContent(
3357d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      const v8::FunctionCallbackInfo<v8::Value>& args) {
336d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    GpuBenchmarkingContext context;
33768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    if (!context.Init(true))
3387d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      return;
33990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
340d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    context.compositor()->SetRasterizeOnlyVisibleContent();
34190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  }
34290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
3437d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  static void GetRenderingStats(
3447d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      const v8::FunctionCallbackInfo<v8::Value>& args) {
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
346d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    GpuBenchmarkingContext context;
34768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    if (!context.Init(false))
3487d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      return;
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    WebRenderingStatsImpl stats;
351d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    context.render_view_impl()->GetRenderingStats(stats);
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    content::GpuRenderingStats gpu_stats;
354d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    context.render_view_impl()->GetGpuRenderingStats(&gpu_stats);
355868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    BrowserRenderingStats browser_stats;
356d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    context.render_view_impl()->GetBrowserRenderingStats(&browser_stats);
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    v8::Handle<v8::Object> stats_object = v8::Object::New();
3582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    RenderingStatsEnumerator enumerator(stats_object);
3602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    stats.rendering_stats.EnumerateFields(&enumerator);
3612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    gpu_stats.EnumerateFields(&enumerator);
362868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    browser_stats.EnumerateFields(&enumerator);
3632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3647d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    args.GetReturnValue().Set(stats_object);
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3677d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  static void PrintToSkPicture(
3687d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      const v8::FunctionCallbackInfo<v8::Value>& args) {
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (args.Length() != 1)
3707d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      return;
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    v8::String::AsciiValue dirname(args[0]);
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (dirname.length() == 0)
3747d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      return;
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
376d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    GpuBenchmarkingContext context;
37768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    if (!context.Init(true))
3783551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      return;
3793551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
380d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    const cc::Layer* root_layer = context.compositor()->GetRootLayer();
3813551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    if (!root_layer)
3827d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      return;
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    base::FilePath dirpath(
3852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        base::FilePath::StringType(*dirname, *dirname + dirname.length()));
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!file_util::CreateDirectory(dirpath) ||
3877dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        !base::PathIsWritable(dirpath)) {
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      std::string msg("Path is not writable: ");
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      msg.append(dirpath.MaybeAsASCII());
3907d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      v8::ThrowException(v8::Exception::Error(
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          v8::String::New(msg.c_str(), msg.length())));
3927d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      return;
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3953551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    SkPictureSerializer serializer(dirpath);
3963551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    serializer.Serialize(root_layer);
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
399868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  static void OnSmoothScrollCompleted(
400868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      CallbackAndContext* callback_and_context) {
401868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    v8::HandleScope scope(callback_and_context->isolate());
402868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    v8::Handle<v8::Context> context = callback_and_context->GetContext();
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    v8::Context::Scope context_scope(context);
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    WebFrame* frame = WebFrame::frameForContext(context);
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (frame) {
406868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      frame->callFunctionEvenIfScriptDisabled(
407868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)          callback_and_context->GetCallback(), v8::Object::New(), 0, NULL);
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4117dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  static void SmoothScrollSendsTouch(
4127dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      const v8::FunctionCallbackInfo<v8::Value>& args) {
4137dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    // TODO(epenner): Should other platforms emulate touch events?
4147dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#if defined(OS_ANDROID) || defined(OS_CHROMEOS)
4157dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    args.GetReturnValue().Set(true);
4167dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#else
4177dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    args.GetReturnValue().Set(false);
4187dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#endif
4197dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  }
4207dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
4217d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  static void BeginSmoothScroll(
4227d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      const v8::FunctionCallbackInfo<v8::Value>& args) {
423d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    GpuBenchmarkingContext context;
42468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    if (!context.Init(false))
4257d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      return;
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Account for the 2 optional arguments, mouse_event_x and mouse_event_y.
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int arglen = args.Length();
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (arglen < 3 ||
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        !args[0]->IsBoolean() ||
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        !args[1]->IsFunction() ||
4327d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)        !args[2]->IsNumber()) {
4337d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      args.GetReturnValue().Set(false);
4347d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      return;
4357d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    }
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    bool scroll_down = args[0]->BooleanValue();
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    v8::Local<v8::Function> callback_local =
439868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        v8::Local<v8::Function>::Cast(args[1]);
440868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
441868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    scoped_refptr<CallbackAndContext> callback_and_context =
442868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        new CallbackAndContext(args.GetIsolate(),
443868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                               callback_local,
444d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                               context.web_frame()->mainWorldScriptContext());
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int pixels_to_scroll = args[2]->IntegerValue();
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int mouse_event_x = 0;
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int mouse_event_y = 0;
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (arglen == 3) {
452d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      WebKit::WebRect rect = context.render_view_impl()->windowRect();
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      mouse_event_x = rect.x + rect.width / 2;
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      mouse_event_y = rect.y + rect.height / 2;
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (arglen != 5 ||
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          !args[3]->IsNumber() ||
4587d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)          !args[4]->IsNumber()) {
4597d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)        args.GetReturnValue().Set(false);
4607d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)        return;
4617d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      }
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
463d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      mouse_event_x = args[3]->IntegerValue() *
464d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          context.web_view()->pageScaleFactor();
465d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      mouse_event_y = args[4]->IntegerValue() *
466d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          context.web_view()->pageScaleFactor();
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // TODO(nduca): If the render_view_impl is destroyed while the gesture is in
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // progress, we will leak the callback and context. This needs to be fixed,
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // somehow.
472d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    context.render_view_impl()->BeginSmoothScroll(
4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        scroll_down,
4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::Bind(&OnSmoothScrollCompleted,
475868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                   callback_and_context),
4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        pixels_to_scroll,
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        mouse_event_x,
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        mouse_event_y);
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4807d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    args.GetReturnValue().Set(true);
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
48358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  static void BeginPinch(
48458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      const v8::FunctionCallbackInfo<v8::Value>& args) {
485d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    GpuBenchmarkingContext context;
48668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    if (!context.Init(false))
48758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      return;
48858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
48958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    int arglen = args.Length();
49058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    if (arglen < 5 ||
49158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        !args[0]->IsBoolean() ||
49258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        !args[1]->IsNumber() ||
49358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        !args[2]->IsNumber() ||
49458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        !args[3]->IsNumber() ||
49558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        !args[4]->IsFunction()) {
49658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      args.GetReturnValue().Set(false);
49758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      return;
49858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    }
49958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
50058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    bool zoom_in = args[0]->BooleanValue();
50158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    int pixels_to_move = args[1]->IntegerValue();
50258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    int anchor_x = args[2]->IntegerValue();
50358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    int anchor_y = args[3]->IntegerValue();
50458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
50558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    v8::Local<v8::Function> callback_local =
50658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        v8::Local<v8::Function>::Cast(args[4]);
50758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
50858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    scoped_refptr<CallbackAndContext> callback_and_context =
50958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        new CallbackAndContext(args.GetIsolate(),
51058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                               callback_local,
511d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                               context.web_frame()->mainWorldScriptContext());
51258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
51358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
51458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    // TODO(nduca): If the render_view_impl is destroyed while the gesture is in
51558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    // progress, we will leak the callback and context. This needs to be fixed,
51658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    // somehow.
517d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    context.render_view_impl()->BeginPinch(
51858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        zoom_in,
51958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        pixels_to_move,
52058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        anchor_x,
52158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        anchor_y,
52258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        base::Bind(&OnSmoothScrollCompleted,
52358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                   callback_and_context));
52458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
52558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    args.GetReturnValue().Set(true);
52658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  }
52758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
528868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  static void OnSnapshotCompleted(CallbackAndContext* callback_and_context,
5292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                  const gfx::Size& size,
5302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                  const std::vector<unsigned char>& png) {
531868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    v8::HandleScope scope(callback_and_context->isolate());
532868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    v8::Handle<v8::Context> context = callback_and_context->GetContext();
5332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    v8::Context::Scope context_scope(context);
5342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    WebFrame* frame = WebFrame::frameForContext(context);
5352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (frame) {
5362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      v8::Handle<v8::Value> result;
5382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if(!size.IsEmpty()) {
5402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        v8::Handle<v8::Object> result_object;
5412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        result_object = v8::Object::New();
5422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        result_object->Set(v8::String::New("width"),
5442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                           v8::Number::New(size.width()));
5452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        result_object->Set(v8::String::New("height"),
5462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                           v8::Number::New(size.height()));
5472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        std::string base64_png;
5492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        base::Base64Encode(base::StringPiece(
5502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            reinterpret_cast<const char*>(&*png.begin()), png.size()),
5512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            &base64_png);
5522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        result_object->Set(v8::String::New("data"),
5542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            v8::String::New(base64_png.c_str(), base64_png.size()));
5552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        result = result_object;
5572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      } else {
5582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        result = v8::Null();
5592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
5602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      v8::Handle<v8::Value> argv[] = { result };
5622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
563868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      frame->callFunctionEvenIfScriptDisabled(
564868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)          callback_and_context->GetCallback(), v8::Object::New(), 1, argv);
5652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
5662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
5672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5687d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  static void BeginWindowSnapshotPNG(
5697d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      const v8::FunctionCallbackInfo<v8::Value>& args) {
570d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    GpuBenchmarkingContext context;
57168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    if (!context.Init(false))
5727d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      return;
5732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (!args[0]->IsFunction())
5757d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      return;
5762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    v8::Local<v8::Function> callback_local =
578868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        v8::Local<v8::Function>::Cast(args[0]);
579868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
580868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    scoped_refptr<CallbackAndContext> callback_and_context =
581868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        new CallbackAndContext(args.GetIsolate(),
582868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                               callback_local,
583d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                               context.web_frame()->mainWorldScriptContext());
5842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
585d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    context.render_view_impl()->GetWindowSnapshot(
586868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        base::Bind(&OnSnapshotCompleted, callback_and_context));
587868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  }
588868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
5897d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  static void ClearImageCache(
5907d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      const v8::FunctionCallbackInfo<v8::Value>& args) {
591868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    WebImageCache::clear();
5922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
5935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
5945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)v8::Extension* GpuBenchmarkingExtension::Get() {
5965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return new GpuBenchmarkingWrapper();
5975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace content
600