require 'mechanize' module Grope class Env def initialize(options = {}) @options = { :timeout => 60, :use_shared_cookie => false, :init_width => 1024, :init_height => 600 }.merge(options) NSApplication.sharedApplication @webview = WebView.alloc @webview.initWithFrame(rect) @webview.setPreferencesIdentifier('Grope') @webview.preferences.setShouldPrintBackgrounds(true) @webview.preferences.setAllowsAnimatedImages(false) @webview.mainFrame.frameView.setAllowsScrolling(false) @webview.setMediaStyle('screen') create_window @frame_load_delegate = FrameLoadDelegate.alloc.init @webview.setFrameLoadDelegate(@frame_load_delegate) unless @options[:use_shared_cookie] @resource_load_delegate = WebResourceLoadDelegate.alloc.init @resource_load_delegate.cookie_storage = Mechanize::CookieJar.new @webview.setResourceLoadDelegate(@resource_load_delegate) end end def load(url) run do @webview.setMainFrameURL(url) if !@webview.mainFrame.provisionalDataSource && url !~ /^about:/ raise " ... not a proper url?" end end end def eval(js) value = nil run do wso = @webview.windowScriptObject value = WSOWrapper.wrap(wso.evaluateWebScript(< 10 }.merge(options) start = Time.now.to_i begin result = yield self if result return result end wait(1) end until Time.now.to_i >= start + options[:timeout] raise TimeoutError end def document eval('return document;') end def window eval('return window;') end def all(xpath, node = nil) node ||= document js.xpath(xpath, node) end def find(xpath, node = nil) all(xpath, node)[0] end def capture(elem = nil, filename = "capture.png") view = @webview.mainFrame.frameView.documentView bounds = view.bounds if elem position = js.getElementPosition(elem) raise "element's width is 0" if position.width.zero? raise "element's height is 0" if position.height.zero? bounds.origin.x = position.left bounds.origin.y = position.top bounds.size.width = position.width bounds.size.height = position.height end wait view.display view.window.setContentSize(NSUnionRect(view.bounds, bounds).size) view.setFrame(NSUnionRect(view.bounds, bounds)) view.lockFocus bitmapdata = NSBitmapImageRep.alloc bitmapdata.initWithFocusedViewRect(bounds) view.unlockFocus bitmapdata.representationUsingType_properties(NSPNGFileType, nil). writeToFile_atomically(filename.to_s, 1) end private def run(wait_sec = 0) @frame_load_delegate.performSelector_withObject_afterDelay('timeout:', @webview, @options[:timeout]) result = yield run_loop = NSRunLoop.currentRunLoop run_loop.runMode_beforeDate(NSDefaultRunLoopMode, Time.now) while(@webview.isLoading? && @frame_load_delegate.should_keep_running && run_loop.runMode_beforeDate(NSDefaultRunLoopMode, Time.now + 0.1)); end run_loop.runUntilDate(Time.now + wait_sec) result ensure NSObject.cancelPreviousPerformRequestsWithTarget_selector_object(@frame_load_delegate, 'timeout:', @webview) end def js eval('return Grope') end def create_window unless @window @window = NSWindow.alloc.initWithContentRect_styleMask_backing_defer_(rect, NSBorderlessWindowMask, 2, false) @window.setContentView(@webview) end end def rect NSMakeRect(0,0,@options[:init_width],@options[:init_height]) end end end