// WebKit build 40837 included support for getBoundingClientRect and getClientRects, which
// gives you the (hopefully) exact position and size of an element relative to the viewport.
// Which is cool, but unfortunately didn't come with Obj-C bindings to it.
// So this extension to DOMNode will retrieve a dictionary of the bounds inforation
// for the node given a JavaScript Context.
//
// http://trac.webkit.org/changeset/40837
#import <WebKit/WebKit.h>
#import <JavaScriptCore/JavaScriptCore.h>
@interface DOMNode (ClientRectsAdditions)
-(NSDictionary *)getBoundingClientRectForContext:(JSContextRef)jsCtx;
@end
@implementation DOMNode (ClientRectsAdditions)
-(NSDictionary *)getBoundingClientRectForContext:(JSContextRef)jsCtx
{
NSAssert( jsCtx != NULL, @"jsCtx is null" );
NSMutableDictionary *rectInfo = nil;
JSObjectRef jsObject = [self JSObject];
NSAssert( jsObject != NULL, @"jsObject is null" );
JSStringRef jsScript = JSStringCreateWithCFString( (CFStringRef)@"this.getBoundingClientRect()" );
JSValueRef jsValue = JSEvaluateScript( jsCtx, jsScript, jsObject, NULL, 0, NULL );
if( jsValue != NULL )
{
rectInfo = [NSMutableDictionary dictionaryWithCapacity:6];
if( JSValueIsObject( jsCtx, jsValue ) )
{
JSObjectRef jsClientRectObj = JSValueToObject( jsCtx, jsValue, NULL );
JSPropertyNameArrayRef jsNameArray = JSObjectCopyPropertyNames( jsCtx, jsClientRectObj );
size_t jsNameArraySize = JSPropertyNameArrayGetCount( jsNameArray );
size_t x;
for( x=0; x<jsNameArraySize; x++ )
{
JSStringRef jsPropName = JSPropertyNameArrayGetNameAtIndex( jsNameArray, x );
JSValueRef jsPropValue = JSObjectGetProperty( jsCtx, jsClientRectObj, jsPropName, NULL );
CFStringRef jsCFPropName = JSStringCopyCFString( kCFAllocatorDefault, jsPropName );
if( JSValueIsNumber( jsCtx, jsPropValue ) )
{
NSNumber *nsPropValue = nil;
nsPropValue = [NSNumber numberWithDouble:JSValueToNumber( jsCtx, jsPropValue, NULL )];
if( nsPropValue )
[rectInfo setObject:nsPropValue forKey:[NSString stringWithString:(NSString *)jsCFPropName]];
}
CFRelease( jsCFPropName );
}
JSPropertyNameArrayRelease( jsNameArray );
}
}
JSStringRelease( jsScript );
if( rectInfo )
{
// Return a rect origin'd to the top-left corner, relative to the viewport (the main enclosing web view)
// If the page is scrolled, the origin in this rect does not reflect that.
NSRect rect = NSMakeRect( [[rectInfo objectForKey:@"left"] floatValue], [[rectInfo objectForKey:@"top"] floatValue],
[[rectInfo objectForKey:@"width"] floatValue], [[rectInfo objectForKey:@"height"] floatValue] );
[rectInfo setObject:[NSValue valueWithRect:rect] forKey:@"NSRect"];
}
return( rectInfo );
}
@end