June 28, 2007

Calling JavaScript functions from C++ with WebKit (Part 2)

Filed under: Distribute — Ryan Wilcox @ 10:34 pm

I didn’t know there was going to be a part 2 to this little adventure: I thought Part 1 was enough. Well, as long as your program liked constructing JavaScript syntax by hand that is, which would work in a pinch.

Today I followed a rabbit hole and found a much better way to call a JavaScript function from C++/WebKit: one where we don’t have to manually build the calling line ourselves. So instead of having to build myFunction(param1, param2) we can just specify the function name to call, and the params to pass.

Below is the C++ function I wrote for the simplest case (calling a JavaScript function with no parameters).

void ClassListFrame::CallJSFunctionNameWithParams(const std::string& jsFunCall)
{
    //A C++ translation of [ WebScriptObject.mm callWebScriptMethod... ]
    
    WebCore::KJSProxy* js = frame->scriptProxy();
    KJS::ScriptInterpreter* interp = js->interpreter();
    KJS::ExecState* exec = interp->globalExec();
    //TODO: error checking ASSERT(!exec->hadException());
    
    KJS::JSLock lock;
    //look up the function object

    KJS::JSValue* functionName = KJS::jsString( jsFunCall.c_str() );    
    KJS::Identifier identifier( functionName->toString(exec) );
    KJS::JSObject* rootObj = interp->globalObject();    
    KJS::JSValue* functionObj = rootObj->get(exec, identifier);

//now that we've looked it up, make sure we have it (and it's a function) if ( !functionObj || !functionObj->isObject() ) throw RuntimeJSError("Requested Object could not Be Found!"); KJS::JSObject* funcImp = static_cast<KJS::JSObject*>( functionObj ); if ( !funcImp->implementsCall() ) throw RuntimeJSError("Retrieved JavaScript Object wasn't a function");
KJS::List paramLst; //TODO: allow parameters //KJS::JSValue* paramValue = ... (see kjs/value.h for jsString(), jsBoolean(), jsNumber() ) //and also JavaScriptCore/bindings/objc/objc_utility.mm for how Cocoa does the conversions //THEN: paramLst.append( paramValue ) //FINALLY! Call it! KJS::JSObject* thisObj = const_cast<KJS::JSObject*>(rootObj); KJS::JSValue* result = funcImp->call(exec, thisObj, paramLst); if (!result) throw RuntimeJSError("Nothing returned from JavaScript function"); }

(thank you CodeColorizer! Ugly non-CSS code, but hey…)

In the future we need to find a way to pass parameters through to JavaScript. One way would be to write several different versions of this function, each accepting 1 more parameter. The trick is to get the type-strict C++ to not care what types these C++ parameters are, instead only caring: “if we can convert it, it’s OK”. I see two ways of doing that right now: either with subclasses or with templates. I like the templates idea because then the caller doesn’t have to worry about creating “manually” translating their int into the appropriate subclass (a kind of encapsulation from what’s actually going on that I don’t think is doable with a subclass approach). Although beyond ease-of-calling (having my “user of the API” hat on) I don’t think I’ll see many benefits to the writer (having my “creator of the API” hat on) to using templates.

Next up to bat (really this time): Calling C++ functions from JavaScript.

June 19, 2007

Carbon “Death” (and my plans about it)

Filed under: Distribute,General Information,VonTrapp — Ryan Wilcox @ 10:13 pm

Given Apple’s 64 Bit Carbon plans, a lot of people are wondering if Carbon is dead (or not). (See one example of this over at Michael Tsai’s blog entry on 64-bit Carbon).

All I have to say here: there’s an advantage here that wxWidgets brings me: one can hope that the wxCocoa port of wxWidgets will step up to the plate. If it does (and I’ll probably pitch in here), then I just recompile my wxWidgets apps with wxCocoa, and I’ve made a major shift already. No rewriting all of my view (or controller) code, just recompile the underlaying library. Since wxWidgets is an abstraction, it just abstracted the API change away from me.

The only thing I would need to rework in my apps would be the places where I’ve called Carbon routines directly to provide Mac OS specific features, or where I’ve modified wxWidgets and now need to rewrite my modifications.

Calling JavaScript functions from C++ with WebKit

Filed under: Distribute — Ryan Wilcox @ 12:06 pm

I figured out how to call JavaScript functions from C++ with WebKit today.


WebCore::KJSProxy* js = frame->scriptProxy();
if (js)
{
js->evaluate(filename, linenum, scriptText, Node);
//filename can be "" and linenum can be 0 if you want to just execute the line in scriptText
}

Now the next task: calling C++ objects from JavaScript…

June 18, 2007

First Steps the Second: Calling into and out of JavaScript

Filed under: Distribute — Ryan Wilcox @ 11:40 pm

The reason I picked WebKit is that it (seemed) to be easy to call JavaScript from C++ and (hopefully) the reverse too (by some glue JavaScript magic).

I have a pointer on how to do the former (JS from C++). Getting this working is my next task.

June 17, 2007

Little First Steps

Filed under: Distribute — Ryan Wilcox @ 9:25 pm

In Project Distribute we want to display some HTML in a wxWidgets application. One such component is wxWebKit, and the one we’re trying out first. wxWebKit is mentioned as a WebKit project here. See also the wxWebKit sourceforge page

The last few weeks have been filled with build errors (both wxWebKit’s an my own), but tonight I finally got arbitrary HTML displaying! Now to write the code that generates that HTML string.

June 15, 2007

Trippy (Cocoa Notifications in a Mostly Carbon App)

Filed under: ResearchAndDevelopment,VonTrapp — Ryan Wilcox @ 11:58 am

So I’m using a little bit of Cocoa in a client’s application. The fun thing is that this application is mostly Carbon/wxWidgets. This isn’t too terribly odd: people use Cocoa in Carbon applications all the time (anymore). The wxWidgets part is different than normal, but whatever.

The trippy thing comes when I needed to adapt when the system Appearance color changed (so Aqua to Graphite, for example). You can get that via [NSColor currentControlTint], but the old Appearance Manager Apple Event (kAppearanceEventClass/kAEAppearanceChanged) must fire too soon, because looking at the currentControlTint color then just gives me the old color.

Cocoa has a notification for this NSControlTintDidChangeNotification, and using this in a Cocoa app, then retrieving the control tint, got me the new tint.

So I had an idea: Register for the (Cocoa) notification (via NSNotificationCenter) in my Carbon/wxWidgets app (in a wrapper function). Whatdayaknow… my notification got to me! (Which I then turned into a wxWidgets event and sent along its way).

So now my (Carbon/wxWidgets) + Cocoa app has three ways of getting events: Carbon Events, wxWidgets Events, and the occasional Cocoa Notification. It’s the first time I’ve used that last one, and I think it’s pretty trippy (and really really cool).