groups

Link to this snippet:


Download to Code Collector

language: Objective-C
licence: Other

getBoundingClientRect from WebKit using JavaScriptCore

options: send to code collectorview all myztikjenz's snippets
// 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