| Index: experimental/SkV8Example/JsContext.cpp | 
| diff --git a/experimental/SkV8Example/JsContext.cpp b/experimental/SkV8Example/JsContext.cpp | 
| index c84962190508fb38f1f93fd5f98982093bd64bd8..8b0b0729797427ce6bf7291fe997d98b93a29f8e 100644 | 
| --- a/experimental/SkV8Example/JsContext.cpp | 
| +++ b/experimental/SkV8Example/JsContext.cpp | 
| @@ -23,257 +23,8 @@ static const char* to_cstring(const v8::String::Utf8Value& value) { | 
| return *value ? *value : "<string conversion failed>"; | 
| } | 
|  | 
| -JsContext* JsContext::Unwrap(Handle<Object> obj) { | 
| -    Handle<External> field = Handle<External>::Cast(obj->GetInternalField(0)); | 
| -    void* ptr = field->Value(); | 
| -    return static_cast<JsContext*>(ptr); | 
| -} | 
| - | 
| -void JsContext::FillRect(const v8::FunctionCallbackInfo<Value>& args) { | 
| -    JsContext* jsContext = Unwrap(args.This()); | 
| -    SkCanvas* canvas = jsContext->fCanvas; | 
| - | 
| -    if (args.Length() != 4) { | 
| -        args.GetIsolate()->ThrowException( | 
| -                v8::String::NewFromUtf8( | 
| -                        args.GetIsolate(), "Error: 4 arguments required.")); | 
| -        return; | 
| -    } | 
| -    // TODO(jcgregorio) Really figure out the conversion from JS numbers to | 
| -    // SkScalars. Maybe test if int first? Not sure of the performance impact. | 
| -    double x = args[0]->NumberValue(); | 
| -    double y = args[1]->NumberValue(); | 
| -    double w = args[2]->NumberValue(); | 
| -    double h = args[3]->NumberValue(); | 
| - | 
| -    SkRect rect = { | 
| -        SkDoubleToScalar(x), | 
| -        SkDoubleToScalar(y), | 
| -        SkDoubleToScalar(x) + SkDoubleToScalar(w), | 
| -        SkDoubleToScalar(y) + SkDoubleToScalar(h) | 
| -    }; | 
| -    canvas->drawRect(rect, jsContext->fFillStyle); | 
| -} | 
| - | 
| -void JsContext::Save(const v8::FunctionCallbackInfo<Value>& args) { | 
| -    JsContext* jsContext = Unwrap(args.This()); | 
| -    SkCanvas* canvas = jsContext->fCanvas; | 
| - | 
| -    canvas->save(); | 
| -} | 
| - | 
| -void JsContext::Restore(const v8::FunctionCallbackInfo<Value>& args) { | 
| -    JsContext* jsContext = Unwrap(args.This()); | 
| -    SkCanvas* canvas = jsContext->fCanvas; | 
| - | 
| -    canvas->restore(); | 
| -} | 
| - | 
| -void JsContext::Rotate(const v8::FunctionCallbackInfo<Value>& args) { | 
| -    JsContext* jsContext = Unwrap(args.This()); | 
| -    SkCanvas* canvas = jsContext->fCanvas; | 
| - | 
| -    if (args.Length() != 1) { | 
| -        args.GetIsolate()->ThrowException( | 
| -                v8::String::NewFromUtf8( | 
| -                        args.GetIsolate(), "Error: 1 arguments required.")); | 
| -        return; | 
| -    } | 
| -    double angle = args[0]->NumberValue(); | 
| -    canvas->rotate(SkRadiansToDegrees(angle)); | 
| -} | 
| - | 
| -void JsContext::Translate(const v8::FunctionCallbackInfo<Value>& args) { | 
| -    JsContext* jsContext = Unwrap(args.This()); | 
| -    SkCanvas* canvas = jsContext->fCanvas; | 
| - | 
| -    if (args.Length() != 2) { | 
| -        args.GetIsolate()->ThrowException( | 
| -                v8::String::NewFromUtf8( | 
| -                        args.GetIsolate(), "Error: 2 arguments required.")); | 
| -        return; | 
| -    } | 
| -    double dx = args[0]->NumberValue(); | 
| -    double dy = args[1]->NumberValue(); | 
| -    canvas->translate(SkDoubleToScalar(dx), SkDoubleToScalar(dy)); | 
| -} | 
| - | 
| -void JsContext::ResetTransform(const v8::FunctionCallbackInfo<Value>& args) { | 
| -    JsContext* jsContext = Unwrap(args.This()); | 
| -    SkCanvas* canvas = jsContext->fCanvas; | 
| - | 
| -    canvas->resetMatrix(); | 
| -} | 
| - | 
| -void JsContext::Stroke(const v8::FunctionCallbackInfo<Value>& args) { | 
| -    JsContext* jsContext = Unwrap(args.This()); | 
| -    SkCanvas* canvas = jsContext->fCanvas; | 
| - | 
| -    if (args.Length() != 1) { | 
| -        args.GetIsolate()->ThrowException( | 
| -                v8::String::NewFromUtf8( | 
| -                        args.GetIsolate(), "Error: 1 arguments required.")); | 
| -        return; | 
| -    } | 
| - | 
| -    Handle<External> field = Handle<External>::Cast( | 
| -            args[0]->ToObject()->GetInternalField(0)); | 
| -    void* ptr = field->Value(); | 
| -    Path2D* path = static_cast<Path2D*>(ptr); | 
| - | 
| -    canvas->drawPath(path->getSkPath(), jsContext->fStrokeStyle); | 
| -} | 
| - | 
| -void JsContext::Fill(const v8::FunctionCallbackInfo<Value>& args) { | 
| -    JsContext* jsContext = Unwrap(args.This()); | 
| -    SkCanvas* canvas = jsContext->fCanvas; | 
| - | 
| -    if (args.Length() != 1) { | 
| -        args.GetIsolate()->ThrowException( | 
| -                v8::String::NewFromUtf8( | 
| -                        args.GetIsolate(), "Error: 1 arguments required.")); | 
| -        return; | 
| -    } | 
| - | 
| -    Handle<External> field = Handle<External>::Cast( | 
| -            args[0]->ToObject()->GetInternalField(0)); | 
| -    void* ptr = field->Value(); | 
| -    Path2D* path = static_cast<Path2D*>(ptr); | 
| - | 
| -    canvas->drawPath(path->getSkPath(), jsContext->fFillStyle); | 
| -} | 
| - | 
| -void JsContext::GetStyle(Local<String> name, | 
| -                         const PropertyCallbackInfo<Value>& info, | 
| -                         const SkPaint& style) { | 
| -    char buf[8]; | 
| -    SkColor color = style.getColor(); | 
| -    sprintf(buf, "#%02X%02X%02X", SkColorGetR(color), SkColorGetG(color), | 
| -            SkColorGetB(color)); | 
| - | 
| -    info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), buf)); | 
| -} | 
| - | 
| -void JsContext::SetStyle(Local<String> name, Local<Value> value, | 
| -                         const PropertyCallbackInfo<void>& info, | 
| -                         SkPaint& style) { | 
| -    Local<String> s = value->ToString(); | 
| -    if (s->Length() != 7 && s->Length() != 9) { | 
| -        info.GetIsolate()->ThrowException( | 
| -                v8::String::NewFromUtf8( | 
| -                        info.GetIsolate(), | 
| -                        "Invalid fill style format length.")); | 
| -        return; | 
| -    } | 
| -    char buf[10]; | 
| -    s->WriteUtf8(buf, sizeof(buf)); | 
| - | 
| -    if (buf[0] != '#') { | 
| -        info.GetIsolate()->ThrowException( | 
| -                v8::String::NewFromUtf8( | 
| -                        info.GetIsolate(), "Invalid fill style format.")); | 
| -        return; | 
| -    } | 
| - | 
| -    // Colors can be RRGGBBAA, but SkColor uses ARGB. | 
| -    long color = strtol(buf+1, NULL, 16); | 
| -    uint32_t alpha = SK_AlphaOPAQUE; | 
| -    if (s->Length() == 9) { | 
| -        alpha = color & 0xFF; | 
| -        color >>= 8; | 
| -    } | 
| -    style.setColor(SkColorSetA(SkColor(color), alpha)); | 
| -} | 
| - | 
| -void JsContext::GetFillStyle(Local<String> name, | 
| -                             const PropertyCallbackInfo<Value>& info) { | 
| -    JsContext* jsContext = Unwrap(info.This()); | 
| -    GetStyle(name, info, jsContext->fFillStyle); | 
| -} | 
| - | 
| -void JsContext::GetStrokeStyle(Local<String> name, | 
| -                               const PropertyCallbackInfo<Value>& info) { | 
| -    JsContext* jsContext = Unwrap(info.This()); | 
| -    GetStyle(name, info, jsContext->fStrokeStyle); | 
| -} | 
| - | 
| -void JsContext::SetFillStyle(Local<String> name, Local<Value> value, | 
| -                            const PropertyCallbackInfo<void>& info) { | 
| -    JsContext* jsContext = Unwrap(info.This()); | 
| -    SetStyle(name, value, info, jsContext->fFillStyle); | 
| -} | 
| - | 
| -void JsContext::SetStrokeStyle(Local<String> name, Local<Value> value, | 
| -                               const PropertyCallbackInfo<void>& info) { | 
| -    JsContext* jsContext = Unwrap(info.This()); | 
| -    SetStyle(name, value, info, jsContext->fStrokeStyle); | 
| -} | 
| - | 
| - | 
| -void JsContext::GetWidth(Local<String> name, | 
| -                         const PropertyCallbackInfo<Value>& info) { | 
| -    JsContext* jsContext = Unwrap(info.This()); | 
| -    SkISize size = jsContext->fCanvas->getDeviceSize(); | 
| - | 
| -    info.GetReturnValue().Set( | 
| -            Int32::New(jsContext->fGlobal->getIsolate(), size.fWidth)); | 
| -} | 
| - | 
| -void JsContext::GetHeight(Local<String> name, | 
| -                         const PropertyCallbackInfo<Value>& info) { | 
| -    JsContext* jsContext = Unwrap(info.This()); | 
| -    SkISize size = jsContext->fCanvas->getDeviceSize(); | 
| - | 
| -    info.GetReturnValue().Set( | 
| -            Int32::New(jsContext->fGlobal->getIsolate(), size.fHeight)); | 
| -} | 
| - | 
| - | 
| Persistent<ObjectTemplate> JsContext::gContextTemplate; | 
|  | 
| -#define ADD_METHOD(name, fn) \ | 
| -    result->Set(String::NewFromUtf8( \ | 
| -            fGlobal->getIsolate(), name, \ | 
| -            String::kInternalizedString), \ | 
| -                FunctionTemplate::New(fGlobal->getIsolate(), fn)) | 
| - | 
| -Handle<ObjectTemplate> JsContext::makeContextTemplate() { | 
| -    EscapableHandleScope handleScope(fGlobal->getIsolate()); | 
| - | 
| -    Local<ObjectTemplate> result = ObjectTemplate::New(); | 
| - | 
| -    // Add a field to store the pointer to a JsContext instance. | 
| -    result->SetInternalFieldCount(1); | 
| - | 
| -    // Add accessors for each of the fields of the context object. | 
| -    result->SetAccessor(String::NewFromUtf8( | 
| -            fGlobal->getIsolate(), "fillStyle", String::kInternalizedString), | 
| -                        GetFillStyle, SetFillStyle); | 
| -    result->SetAccessor(String::NewFromUtf8( | 
| -            fGlobal->getIsolate(), "strokeStyle", String::kInternalizedString), | 
| -                        GetStrokeStyle, SetStrokeStyle); | 
| -    result->SetAccessor(String::NewFromUtf8( | 
| -            fGlobal->getIsolate(), "width", String::kInternalizedString), | 
| -                        GetWidth); | 
| -    result->SetAccessor(String::NewFromUtf8( | 
| -            fGlobal->getIsolate(), "height", String::kInternalizedString), | 
| -                        GetHeight); | 
| - | 
| -    // Add methods. | 
| -    ADD_METHOD("fillRect", FillRect); | 
| -    ADD_METHOD("stroke", Stroke); | 
| -    ADD_METHOD("fill", Fill); | 
| -    ADD_METHOD("rotate", Rotate); | 
| -    ADD_METHOD("save", Save); | 
| -    ADD_METHOD("restore", Restore); | 
| -    ADD_METHOD("translate", Translate); | 
| -    ADD_METHOD("resetTransform", ResetTransform); | 
| - | 
| -    // Return the result through the current handle scope. | 
| -    return handleScope.Escape(result); | 
| -} | 
| - | 
| - | 
| // Wraps 'this' in a Javascript object. | 
| Handle<Object> JsContext::wrap() { | 
| // Handle scope for temporary handles. | 
| @@ -282,8 +33,14 @@ Handle<Object> JsContext::wrap() { | 
| // Fetch the template for creating JavaScript JsContext wrappers. | 
| // It only has to be created once, which we do on demand. | 
| if (gContextTemplate.IsEmpty()) { | 
| -        Handle<ObjectTemplate> raw_template = this->makeContextTemplate(); | 
| -        gContextTemplate.Reset(fGlobal->getIsolate(), raw_template); | 
| +        Local<ObjectTemplate> localTemplate = ObjectTemplate::New(); | 
| + | 
| +        // Add a field to store the pointer to a JsContext instance. | 
| +        localTemplate->SetInternalFieldCount(1); | 
| + | 
| +        this->addAttributesAndMethods(localTemplate); | 
| + | 
| +        gContextTemplate.Reset(fGlobal->getIsolate(), localTemplate); | 
| } | 
| Handle<ObjectTemplate> templ = | 
| Local<ObjectTemplate>::New(fGlobal->getIsolate(), gContextTemplate); | 
|  |