Add a setTimer() function.

Seemed simple, but required adding a Global class that contains all the global state (instance and context) for our running V8 instance. Also moved canvas.inval to be just inval() at the global level.

BUG=
R=robertphillips@google.com

Author: jcgregorio@google.com

Review URL: https://codereview.chromium.org/103143009

git-svn-id: http://skia.googlecode.com/svn/trunk@12730 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/experimental/SkV8Example/Global.cpp b/experimental/SkV8Example/Global.cpp
new file mode 100644
index 0000000..a99ef05
--- /dev/null
+++ b/experimental/SkV8Example/Global.cpp
@@ -0,0 +1,232 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ */
+#include "Global.h"
+
+#include "SkWindow.h"
+#include "SkEvent.h"
+
+
+Global* Global::gGlobal = NULL;
+
+// Extracts a C string from a V8 Utf8Value.
+static const char* to_cstring(const v8::String::Utf8Value& value) {
+    return *value ? *value : "<string conversion failed>";
+}
+
+// Slight modification to an original function found in the V8 sample shell.cc.
+void Global::reportException(TryCatch* tryCatch) {
+    HandleScope handleScope(fIsolate);
+    String::Utf8Value exception(tryCatch->Exception());
+    const char* exceptionString = to_cstring(exception);
+    Handle<Message> message = tryCatch->Message();
+    if (message.IsEmpty()) {
+        // V8 didn't provide any extra information about this error; just
+        // print the exception.
+        fprintf(stderr, "%s\n", exceptionString);
+    } else {
+        // Print (filename):(line number): (message).
+        String::Utf8Value filename(message->GetScriptResourceName());
+        const char* filenameString = to_cstring(filename);
+        int linenum = message->GetLineNumber();
+        fprintf(stderr,
+                "%s:%i: %s\n", filenameString, linenum, exceptionString);
+        // Print line of source code.
+        String::Utf8Value sourceline(message->GetSourceLine());
+        const char* sourceLineString = to_cstring(sourceline);
+        fprintf(stderr, "%s\n", sourceLineString);
+        // Print wavy underline.
+        int start = message->GetStartColumn();
+        for (int i = 0; i < start; i++) {
+            fprintf(stderr, " ");
+        }
+        int end = message->GetEndColumn();
+        for (int i = start; i < end; i++) {
+            fprintf(stderr, "^");
+        }
+        fprintf(stderr, "\n");
+        String::Utf8Value stackTrace(tryCatch->StackTrace());
+        if (stackTrace.length() > 0) {
+            const char* stackTraceString = to_cstring(stackTrace);
+            fprintf(stderr, "%s\n", stackTraceString);
+        }
+    }
+}
+
+// The callback that implements the JavaScript 'inval' function.
+// Invalidates the current window, forcing a redraw.
+//
+// JS: inval();
+void Global::Inval(const v8::FunctionCallbackInfo<Value>& args) {
+    gGlobal->getWindow()->inval(NULL);
+}
+
+// The callback that is invoked by v8 whenever the JavaScript 'print'
+// function is called. Prints its arguments on stdout separated by
+// spaces and ending with a newline.
+//
+// JS: print("foo", "bar");
+void Global::Print(const v8::FunctionCallbackInfo<v8::Value>& args) {
+    bool first = true;
+    HandleScope handleScope(args.GetIsolate());
+    for (int i = 0; i < args.Length(); i++) {
+        if (first) {
+            first = false;
+        } else {
+            printf(" ");
+        }
+        v8::String::Utf8Value str(args[i]);
+        printf("%s", to_cstring(str));
+    }
+    printf("\n");
+    fflush(stdout);
+}
+
+// The callback that is invoked by v8 whenever the JavaScript 'setTimeout'
+// function is called.
+//
+// JS: setTimeout(on_timeout, 500);
+//
+// TODO(jcgregorio) Currently only handles one timeout, should support any
+// number.
+void Global::SetTimeout(const v8::FunctionCallbackInfo<v8::Value>& args) {
+    if (args.Length() != 2) {
+        args.GetIsolate()->ThrowException(
+                v8::String::NewFromUtf8(
+                        args.GetIsolate(), "Error: 2 arguments required."));
+        return;
+    }
+
+    // Pull out the first arg, make sure it's a function.
+    if (!args[0]->IsFunction()) {
+        printf("Not a function passed to setTimeout.\n");
+        return;
+    }
+    Handle<Function> timeoutFn = Handle<Function>::Cast(args[0]);
+    gGlobal->fTimeout.Reset(args.GetIsolate(), timeoutFn);
+
+    double delay = args[1]->NumberValue();
+
+    // Create an SkEvent and add it with the right delay.
+    (new SkEvent())->setTargetProc(Global::TimeOutProc)->postDelay(delay);
+
+    // TODO(jcgregorio) Return the ID as the return value.
+}
+
+// Callback function for SkEvents used to implement timeouts.
+bool Global::TimeOutProc(const SkEvent& evt) {
+    // Create a handle scope to keep the temporary object references.
+    HandleScope handleScope(gGlobal->getIsolate());
+
+    // Create a local context from our global context.
+    Local<Context> context = gGlobal->getContext();
+
+    // Enter the context so all the remaining operations take place there.
+    Context::Scope contextScope(context);
+
+    // Set up an exception handler before calling the Process function.
+    TryCatch tryCatch;
+
+    const int argc = 0;
+    Local<Function> onTimeout =
+            Local<Function>::New(gGlobal->getIsolate(), gGlobal->fTimeout);
+    Handle<Value> result = onTimeout->Call(context->Global(), argc, NULL);
+
+    // Handle any exceptions or output.
+    if (result.IsEmpty()) {
+        SkASSERT(tryCatch.HasCaught());
+        // Print errors that happened during execution.
+        gGlobal->reportException(&tryCatch);
+    } else {
+        SkASSERT(!tryCatch.HasCaught());
+        if (!result->IsUndefined()) {
+            // If all went well and the result wasn't undefined then print the
+            // returned value.
+            String::Utf8Value str(result);
+            const char* cstr = to_cstring(str);
+            printf("%s\n", cstr);
+        }
+    }
+    return true;
+}
+
+// Creates a new execution environment containing the built-in functions.
+Handle<Context> Global::createRootContext() {
+  // Create a template for the global object.
+  Handle<ObjectTemplate> global = ObjectTemplate::New();
+
+  global->Set(v8::String::NewFromUtf8(fIsolate, "print"),
+              v8::FunctionTemplate::New(Global::Print));
+  global->Set(v8::String::NewFromUtf8(fIsolate, "setTimeout"),
+              v8::FunctionTemplate::New(Global::SetTimeout));
+  global->Set(v8::String::NewFromUtf8(fIsolate, "inval"),
+              v8::FunctionTemplate::New(Global::Inval));
+
+
+  return Context::New(fIsolate, NULL, global);
+}
+
+
+// Creates the root context, parses the script into it, then stores the
+// context in a global.
+//
+// TODO(jcgregorio) Currently only handles one script. Need to move
+// createRootContext to another call that's only done once.
+bool Global::parseScript(const char script[]) {
+
+    // Create a stack-allocated handle scope.
+    HandleScope handleScope(fIsolate);
+
+    printf("Before create context\n");
+
+    // Create a new context.
+    Handle<Context> context = this->createRootContext();
+
+    // Enter the scope so all operations take place in the scope.
+    Context::Scope contextScope(context);
+
+    v8::TryCatch tryCatch;
+
+    // Compile the source code.
+    Handle<String> source = String::NewFromUtf8(fIsolate, script);
+    printf("Before Compile\n");
+    Handle<Script> compiledScript = Script::Compile(source);
+    printf("After Compile\n");
+
+    if (compiledScript.IsEmpty()) {
+        // Print errors that happened during compilation.
+        this->reportException(&tryCatch);
+        return false;
+    }
+    printf("After Exception.\n");
+
+    // Try running it now to create the onDraw function.
+    Handle<Value> result = compiledScript->Run();
+
+    // Handle any exceptions or output.
+    if (result.IsEmpty()) {
+        SkASSERT(tryCatch.HasCaught());
+        // Print errors that happened during execution.
+        this->reportException(&tryCatch);
+        return false;
+    } else {
+        SkASSERT(!tryCatch.HasCaught());
+        if (!result->IsUndefined()) {
+            // If all went well and the result wasn't undefined then print
+            // the returned value.
+            String::Utf8Value str(result);
+            const char* cstr = to_cstring(str);
+            printf("%s\n", cstr);
+            return false;
+        }
+    }
+
+    // Also make the context persistent.
+    fContext.Reset(fIsolate, context);
+    return true;
+}
diff --git a/experimental/SkV8Example/Global.h b/experimental/SkV8Example/Global.h
new file mode 100644
index 0000000..aa05b01
--- /dev/null
+++ b/experimental/SkV8Example/Global.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ */
+
+#ifndef SkV8Example_Global_DEFINED
+#define SkV8Example_Global_DEFINED
+
+#include <v8.h>
+
+using namespace v8;
+
+#include "SkTypes.h"
+#include "SkEvent.h"
+
+class SkOSWindow;
+
+// Provides the global isolate and context for our V8 instance.
+// Also implements all the global level functions.
+class Global : SkNoncopyable  {
+public:
+    Global(Isolate* isolate)
+        : fIsolate(isolate)
+        , fWindow(NULL)
+    {
+        gGlobal = this;
+    }
+    virtual ~Global() {}
+
+    // The script will be parsed into the context this Global contains.
+    bool parseScript(const char script[]);
+
+    Local<Context> getContext() {
+        return Local<Context>::New(fIsolate, fContext);
+    }
+
+    Isolate* getIsolate() {
+        return fIsolate;
+    }
+
+    void setWindow(SkOSWindow* win) {
+        fWindow = win;
+    }
+    SkOSWindow* getWindow() {
+        return fWindow;
+    }
+
+    void reportException(TryCatch* tryCatch);
+
+private:
+    Handle<Context> createRootContext();
+
+    static bool TimeOutProc(const SkEvent& evt);
+
+    // Static functions that implement the global JS functions we add to
+    // the context.
+    static void SetTimeout(const v8::FunctionCallbackInfo<v8::Value>& args);
+    static void Print(const v8::FunctionCallbackInfo<v8::Value>& args);
+    static void Inval(const v8::FunctionCallbackInfo<Value>& args);
+
+    Persistent<Context> fContext;
+    Isolate*            fIsolate;
+    SkOSWindow*         fWindow;
+    static Global*      gGlobal;
+
+    // Handle to the function to call when the timeout triggers.
+    Persistent<Function> fTimeout;
+};
+
+#endif
diff --git a/experimental/SkV8Example/SkV8Example.cpp b/experimental/SkV8Example/SkV8Example.cpp
index a62b919..7c66226 100644
--- a/experimental/SkV8Example/SkV8Example.cpp
+++ b/experimental/SkV8Example/SkV8Example.cpp
@@ -11,6 +11,7 @@
 using namespace v8;
 
 #include "SkV8Example.h"
+#include "Global.h"
 
 #include "gl/GrGLUtil.h"
 #include "gl/GrGLDefines.h"
@@ -37,57 +38,11 @@
 }
 
 // Extracts a C string from a V8 Utf8Value.
-const char* ToCString(const v8::String::Utf8Value& value) {
+// TODO(jcgregrio) Currently dup'd in two files, fix.
+static const char* to_cstring(const v8::String::Utf8Value& value) {
     return *value ? *value : "<string conversion failed>";
 }
 
-// Slight modification to an original function found in the V8 sample shell.cc.
-void reportException(Isolate* isolate, TryCatch* try_catch) {
-    HandleScope handleScope(isolate);
-    String::Utf8Value exception(try_catch->Exception());
-    const char* exception_string = ToCString(exception);
-    Handle<Message> message = try_catch->Message();
-    if (message.IsEmpty()) {
-        // V8 didn't provide any extra information about this error; just
-        // print the exception.
-        fprintf(stderr, "%s\n", exception_string);
-    } else {
-        // Print (filename):(line number): (message).
-        String::Utf8Value filename(message->GetScriptResourceName());
-        const char* filename_string = ToCString(filename);
-        int linenum = message->GetLineNumber();
-        fprintf(stderr,
-                "%s:%i: %s\n", filename_string, linenum, exception_string);
-        // Print line of source code.
-        String::Utf8Value sourceline(message->GetSourceLine());
-        const char* sourceline_string = ToCString(sourceline);
-        fprintf(stderr, "%s\n", sourceline_string);
-        // Print wavy underline.
-        int start = message->GetStartColumn();
-        for (int i = 0; i < start; i++) {
-            fprintf(stderr, " ");
-        }
-        int end = message->GetEndColumn();
-        for (int i = start; i < end; i++) {
-            fprintf(stderr, "^");
-        }
-        fprintf(stderr, "\n");
-        String::Utf8Value stack_trace(try_catch->StackTrace());
-        if (stack_trace.length() > 0) {
-            const char* stack_trace_string = ToCString(stack_trace);
-            fprintf(stderr, "%s\n", stack_trace_string);
-        }
-    }
-}
-
-SkV8ExampleWindow::SkV8ExampleWindow(void* hwnd, JsCanvas* canvas)
-    : INHERITED(hwnd)
-    , fJsCanvas(canvas)
-{
-    this->setConfig(SkBitmap::kARGB_8888_Config);
-    this->setVisibleP(true);
-    this->setClipToBounds(false);
-}
 
 JsCanvas* JsCanvas::Unwrap(Handle<Object> obj) {
     Handle<External> field = Handle<External>::Cast(obj->GetInternalField(0));
@@ -95,10 +50,6 @@
     return static_cast<JsCanvas*>(ptr);
 }
 
-void JsCanvas::Inval(const v8::FunctionCallbackInfo<Value>& args) {
-    Unwrap(args.This())->fWindow->inval(NULL);
-}
-
 void JsCanvas::FillRect(const v8::FunctionCallbackInfo<Value>& args) {
     JsCanvas* jsCanvas = Unwrap(args.This());
     SkCanvas* canvas = jsCanvas->fCanvas;
@@ -164,7 +115,7 @@
 Persistent<ObjectTemplate> JsCanvas::fCanvasTemplate;
 
 Handle<ObjectTemplate> JsCanvas::makeCanvasTemplate() {
-    EscapableHandleScope handleScope(fIsolate);
+    EscapableHandleScope handleScope(fGlobal->getIsolate());
 
     Local<ObjectTemplate> result = ObjectTemplate::New();
 
@@ -173,18 +124,16 @@
 
     // Add accessors for each of the fields of the canvas object.
     result->SetAccessor(
-      String::NewFromUtf8(fIsolate, "fillStyle", String::kInternalizedString),
+      String::NewFromUtf8(
+        fGlobal->getIsolate(), "fillStyle", String::kInternalizedString),
       GetFillStyle, SetFillStyle);
 
     // Add methods.
     result->Set(
             String::NewFromUtf8(
-                    fIsolate, "fillRect", String::kInternalizedString),
+                    fGlobal->getIsolate(), "fillRect",
+                    String::kInternalizedString),
             FunctionTemplate::New(FillRect));
-    result->Set(
-            String::NewFromUtf8(
-                    fIsolate, "inval", String::kInternalizedString),
-            FunctionTemplate::New(Inval));
 
     // Return the result through the current handle scope.
     return handleScope.Escape(result);
@@ -194,23 +143,23 @@
 // Wraps 'this' in a Javascript object.
 Handle<Object> JsCanvas::wrap() {
     // Handle scope for temporary handles.
-    EscapableHandleScope handleScope(fIsolate);
+    EscapableHandleScope handleScope(fGlobal->getIsolate());
 
     // Fetch the template for creating JavaScript JsCanvas wrappers.
     // It only has to be created once, which we do on demand.
     if (fCanvasTemplate.IsEmpty()) {
         Handle<ObjectTemplate> raw_template = this->makeCanvasTemplate();
-        fCanvasTemplate.Reset(fIsolate, raw_template);
+        fCanvasTemplate.Reset(fGlobal->getIsolate(), raw_template);
     }
     Handle<ObjectTemplate> templ =
-            Local<ObjectTemplate>::New(fIsolate, fCanvasTemplate);
+            Local<ObjectTemplate>::New(fGlobal->getIsolate(), fCanvasTemplate);
 
     // Create an empty JsCanvas wrapper.
     Local<Object> result = templ->NewInstance();
 
     // Wrap the raw C++ pointer in an External so it can be referenced
     // from within JavaScript.
-    Handle<External> canvasPtr = External::New(fIsolate, this);
+    Handle<External> canvasPtr = External::New(fGlobal->getIsolate(), this);
 
     // Store the canvas pointer in the JavaScript wrapper.
     result->SetInternalField(0, canvasPtr);
@@ -222,16 +171,15 @@
     return handleScope.Escape(result);
 }
 
-void JsCanvas::onDraw(SkCanvas* canvas, SkOSWindow* window) {
+void JsCanvas::onDraw(SkCanvas* canvas) {
     // Record canvas and window in this.
     fCanvas = canvas;
-    fWindow = window;
 
     // Create a handle scope to keep the temporary object references.
-    HandleScope handleScope(fIsolate);
+    HandleScope handleScope(fGlobal->getIsolate());
 
     // Create a local context from our global context.
-    Local<Context> context = Local<Context>::New(fIsolate, fContext);
+    Local<Context> context = fGlobal->getContext();
 
     // Enter the context so all the remaining operations take place there.
     Context::Scope contextScope(context);
@@ -247,33 +195,76 @@
     const int argc = 1;
     Handle<Value> argv[argc] = { canvasObj };
     Local<Function> onDraw =
-            Local<Function>::New(fIsolate, fOnDraw);
+            Local<Function>::New(fGlobal->getIsolate(), fOnDraw);
     Handle<Value> result = onDraw->Call(context->Global(), argc, argv);
 
     // Handle any exceptions or output.
     if (result.IsEmpty()) {
         SkASSERT(tryCatch.HasCaught());
         // Print errors that happened during execution.
-        reportException(fIsolate, &tryCatch);
+        fGlobal->reportException(&tryCatch);
     } else {
         SkASSERT(!tryCatch.HasCaught());
         if (!result->IsUndefined()) {
             // If all went well and the result wasn't undefined then print
             // the returned value.
             String::Utf8Value str(result);
-            const char* cstr = ToCString(str);
+            const char* cstr = to_cstring(str);
             printf("%s\n", cstr);
         }
     }
 }
 
+// Fetch the onDraw function from the global context.
+bool JsCanvas::initialize() {
+
+    // Create a stack-allocated handle scope.
+    HandleScope handleScope(fGlobal->getIsolate());
+
+    // Create a local context from our global context.
+    Local<Context> context = fGlobal->getContext();
+
+    // Enter the scope so all operations take place in the scope.
+    Context::Scope contextScope(context);
+
+    v8::TryCatch try_catch;
+
+    Handle<String> fn_name = String::NewFromUtf8(
+        fGlobal->getIsolate(), "onDraw");
+    Handle<Value> fn_val = context->Global()->Get(fn_name);
+
+    if (!fn_val->IsFunction()) {
+        printf("Not a function.\n");
+        return false;
+    }
+
+    // It is a function; cast it to a Function.
+    Handle<Function> fn_fun = Handle<Function>::Cast(fn_val);
+
+    // Store the function in a Persistent handle, since we also want that to
+    // remain after this call returns.
+    fOnDraw.Reset(fGlobal->getIsolate(), fn_fun);
+
+    return true;
+}
+
+
+SkV8ExampleWindow::SkV8ExampleWindow(void* hwnd, JsCanvas* canvas)
+    : INHERITED(hwnd)
+    , fJsCanvas(canvas)
+{
+    this->setConfig(SkBitmap::kARGB_8888_Config);
+    this->setVisibleP(true);
+    this->setClipToBounds(false);
+}
+
 void SkV8ExampleWindow::onDraw(SkCanvas* canvas) {
 
     canvas->save();
     canvas->drawColor(SK_ColorWHITE);
 
     // Now jump into JS and call the onDraw(canvas) method defined there.
-    fJsCanvas->onDraw(canvas, this);
+    fJsCanvas->onDraw(canvas);
 
     canvas->restore();
 
@@ -292,114 +283,6 @@
 }
 #endif
 
-
-// The callback that is invoked by v8 whenever the JavaScript 'print'
-// function is called.  Prints its arguments on stdout separated by
-// spaces and ending with a newline.
-void Print(const v8::FunctionCallbackInfo<v8::Value>& args) {
-    bool first = true;
-    HandleScope handle_scope(args.GetIsolate());
-    for (int i = 0; i < args.Length(); i++) {
-        if (first) {
-            first = false;
-        } else {
-            printf(" ");
-        }
-        v8::String::Utf8Value str(args[i]);
-        printf("%s", ToCString(str));
-    }
-    printf("\n");
-    fflush(stdout);
-}
-
-
-// Creates a new execution environment containing the built-in
-// function draw().
-Handle<Context> createRootContext(Isolate* isolate) {
-  // Create a template for the global object.
-  Handle<ObjectTemplate> global = ObjectTemplate::New();
-
-  // This is where we would inject any globals into the root Context
-  // using global->Set(...)
-
-  global->Set(v8::String::NewFromUtf8(isolate, "print"),
-              v8::FunctionTemplate::New(Print));
-
-  return Context::New(isolate, NULL, global);
-}
-
-
-// Parse and run script. Then fetch out the onDraw function from the global
-// object.
-bool JsCanvas::initialize(const char script[]) {
-
-    // Create a stack-allocated handle scope.
-    HandleScope handleScope(fIsolate);
-
-    printf("Before create context\n");
-
-    // Create a new context.
-    Handle<Context> context = createRootContext(fIsolate);
-
-    // Enter the scope so all operations take place in the scope.
-    Context::Scope contextScope(context);
-
-    v8::TryCatch try_catch;
-
-    // Compile the source code.
-    Handle<String> source = String::NewFromUtf8(fIsolate, script);
-    printf("Before Compile\n");
-    Handle<Script> compiled_script = Script::Compile(source);
-    printf("After Compile\n");
-
-    if (compiled_script.IsEmpty()) {
-        // Print errors that happened during compilation.
-        reportException(fIsolate, &try_catch);
-        return false;
-    }
-    printf("After Exception.\n");
-
-    // Try running it now to create the onDraw function.
-    Handle<Value> result = compiled_script->Run();
-
-    // Handle any exceptions or output.
-    if (result.IsEmpty()) {
-        SkASSERT(try_catch.HasCaught());
-        // Print errors that happened during execution.
-        reportException(fIsolate, &try_catch);
-        return false;
-    } else {
-        SkASSERT(!try_catch.HasCaught());
-        if (!result->IsUndefined()) {
-            // If all went well and the result wasn't undefined then print
-            // the returned value.
-            String::Utf8Value str(result);
-            const char* cstr = ToCString(str);
-            printf("%s\n", cstr);
-            return false;
-        }
-    }
-
-    Handle<String> fn_name = String::NewFromUtf8(fIsolate, "onDraw");
-    Handle<Value> fn_val = context->Global()->Get(fn_name);
-
-    if (!fn_val->IsFunction()) {
-        printf("Not a function.\n");
-        return false;
-    }
-
-    // It is a function; cast it to a Function.
-    Handle<Function> fn_fun = Handle<Function>::Cast(fn_val);
-
-    // Store the function in a Persistent handle, since we also want that to
-    // remain after this call returns.
-    fOnDraw.Reset(fIsolate, fn_fun);
-
-    // Also make the context persistent.
-    fContext.Reset(fIsolate, context);
-    return true;
-}
-
 SkOSWindow* create_sk_window(void* hwnd, int argc, char** argv) {
     printf("Started\n");
 
@@ -407,8 +290,7 @@
 
     // Get the default Isolate created at startup.
     Isolate* isolate = Isolate::GetCurrent();
-
-    JsCanvas* jsCanvas = new JsCanvas(isolate);
+    Global* global = new Global(isolate);
 
     const char* script =
 "function onDraw(canvas) {              \n"
@@ -426,9 +308,18 @@
         printf("Could not load file: %s.\n", FLAGS_infile[0]);
         exit(1);
     }
-    if (!jsCanvas->initialize(script)) {
+
+    if (!global->parseScript(script)) {
+        exit(1);
+    }
+
+    JsCanvas* jsCanvas = new JsCanvas(global);
+
+    if (!jsCanvas->initialize()) {
         printf("Failed to initialize.\n");
         exit(1);
     }
-    return new SkV8ExampleWindow(hwnd, jsCanvas);
+    SkV8ExampleWindow* win = new SkV8ExampleWindow(hwnd, jsCanvas);
+    global->setWindow(win);
+    return win;
 }
diff --git a/experimental/SkV8Example/SkV8Example.h b/experimental/SkV8Example/SkV8Example.h
index 9ed0ecc..3073796 100644
--- a/experimental/SkV8Example/SkV8Example.h
+++ b/experimental/SkV8Example/SkV8Example.h
@@ -19,6 +19,7 @@
 
 class SkCanvas;
 class JsCanvas;
+class Global;
 
 class SkV8ExampleWindow : public SkOSWindow {
 public:
@@ -43,24 +44,22 @@
 //  function onDraw(canvas) {
 //    canvas.fillStyle="#FF0000";
 //    canvas.fillRect(x, y, w, h);
-//    canvas.inval();
 //  }
 class JsCanvas {
 public:
-    JsCanvas(Isolate* isolate)
-            : fIsolate(isolate)
+    JsCanvas(Global* global)
+            : fGlobal(global)
             , fCanvas(NULL)
-            , fWindow(NULL)
     {
         fFillStyle.setColor(SK_ColorRED);
     }
     ~JsCanvas();
 
     // Parse the script.
-    bool initialize(const char script[]);
+    bool initialize();
 
     // Call this with the SkCanvas you want onDraw to draw on.
-    void onDraw(SkCanvas* canvas, SkOSWindow* window);
+    void onDraw(SkCanvas* canvas);
 
 private:
     // Implementation of the canvas.fillStyle field.
@@ -72,9 +71,6 @@
     // Implementation of the canvas.fillRect() JS function.
     static void FillRect(const v8::FunctionCallbackInfo<Value>& args);
 
-    // Implementation of the canvas.inval() JS function.
-    static void Inval(const v8::FunctionCallbackInfo<Value>& args);
-
     // Get the pointer out of obj.
     static JsCanvas* Unwrap(Handle<Object> obj);
 
@@ -85,19 +81,13 @@
     // Wrap the 'this' pointer into an Object. Can be retrieved via Unwrap.
     Handle<Object> wrap();
 
-    Isolate* fIsolate;
+    Global* fGlobal;
 
     // Only valid when inside OnDraw().
     SkCanvas* fCanvas;
 
     SkPaint fFillStyle;
 
-    // Only valid when inside OnDraw().
-    SkOSWindow* fWindow;
-
-    // The context that the script will be parsed into.
-    Persistent<Context> fContext;
-
     // A handle to the onDraw function defined in the script.
     Persistent<Function> fOnDraw;
 
diff --git a/experimental/SkV8Example/sample.js b/experimental/SkV8Example/sample.js
index 3a95e55..b516062 100644
--- a/experimental/SkV8Example/sample.js
+++ b/experimental/SkV8Example/sample.js
@@ -4,10 +4,17 @@
 var onDraw = function(){
   var tick = 0;
   function f(canvas) {
-    tick += 0.01;
+    tick += 0.1;
     canvas.fillStyle = '#0000ff';
     canvas.fillRect(100, 100, Math.sin(tick)*100, Math.cos(tick)*100);
-    canvas.inval();
   };
   return f;
 }();
+
+function onTimeout() {
+  inval();
+  print("Got a timeout!");
+  setTimeout(onTimeout, 33);
+}
+
+setTimeout(onTimeout, 33);
diff --git a/gyp/v8.gyp b/gyp/v8.gyp
index d515756..7d10fd1 100644
--- a/gyp/v8.gyp
+++ b/gyp/v8.gyp
@@ -13,6 +13,8 @@
        'sources': [
          '../experimental/SkV8Example/SkV8Example.h',
          '../experimental/SkV8Example/SkV8Example.cpp',
+         '../experimental/SkV8Example/Global.h',
+         '../experimental/SkV8Example/Global.cpp',
        ],
        'dependencies': [
          'skia_lib.gyp:skia_lib',