Upgrade to Pro — share decks privately, control downloads, hide ads and more …

PhantomJS: Автоматизация WebKit на JavaScript

PhantomJS: Автоматизация WebKit на JavaScript

Ilya Vassilevsky

September 20, 2014
Tweet

More Decks by Ilya Vassilevsky

Other Decks in Programming

Transcript

  1. 1995 — 2014 (19 лет) Trolltech → Nokia → Digia

    C++ Android, Embedded Linux, iOS, OS X, QNX / BlackBerry 10, Sailfish OS, VxWorks, Wayland, Windows, Windows CE, X11 (GNU/Linux, FreeBSD, HP-UX, Solaris, AIX) http://qt-project.org
  2. Qt Core Qt GUI Qt Widgets Qt QML and JavaScript

    Qt Quick Qt Quick Controls Qt Quick Layouts Qt Network Qt Multimedia Qt Multimedia Widgets Qt SQL Qt WebKit Qt WebKit Widgets Qt Test
  3. #include <QApplication> #include <QMenu> #include <QPixmap> #include <QWidget> #include <qmacfunctions.h>

    int main(int argc, char **argv) { QApplication app(argc, argv); QWidget widget; widget.show(); // Pixmap <-> CGImage conversion QPixmap pixmap(":qtlogo.png"); CGImageRef cgImage = QtMac::toCGImageRef(pixmap); QPixmap pixmap2 = QtMac::fromCGImageRef(cgImage); return app.exec(); }
  4. require 'Qt' require './qrc_systray.rb' require './window.rb' app = Qt::Application.new(ARGV) if

    !Qt::SystemTrayIcon.isSystemTrayAvailable Qt::MessageBox.critical( nil, Qt::Object.tr("Systray"), Qt::Object.tr("I couldn't detect any system tray on this system.") ) exit 1 end window = Window.new window.show app.exec
  5. Qt Core Qt GUI Qt Widgets Qt QML and JavaScript

    Qt Quick Qt Quick Controls Qt Quick Layouts Qt Network Qt Multimedia Qt Multimedia Widgets Qt SQL Qt WebKit Qt WebKit Widgets Qt Test
  6. Ariya Hidayat VP of Engineering at @ShapeSecurity Doctorate degree (with

    great honor) in Electrical Engineering from University of Paderborn (Germany) Master degree from Institute of Technology Bandung (Indonesia) with an exchange program with Technical University Munich (Germany) Bachelor degree (with honor) from Institute of Technology Bandung (Indonesia) Indonesian, English, German Indonesia → Mountain View, California @AriyaHidayat
  7. Qt WebKit Ghost Driver Remote Selenium WebDriver (Wire Protocol) JavaScript

    API Engine Web Server Mongoose JavaScript Engine
  8. $ ls -l phantomjs-1.9.7-linux-x86_64/bin total 74896 -rwxr-xr-x@ 1 vassilevsky staff

    38346752 26 янв 2014 phantomjs X11 (v1.5+) freetype fontconfig
  9. // screenshot.js var page = require('webpage').create(); page.open('http://www.therestartpage.com', function(status) { if

    (status == 'success') { page.render('restart.png'); } phantom.exit(); }); $ phantomjs screenshot.js $ open restart.png
  10. // stealing.js var page = require('webpage').create(); page.open('http://victim.com', function(status) { if

    (status == 'success') { var usefulValue = page.evaluate(function() { return document.getElementById('secretId').textContent; // runs on page }); console.log('Stolen: ' + usefulValue); } phantom.exit(); }); $ phantomjs stealing.js Stolen: uid123456789
  11. Qt WebKit JavaScript API Engine JavaScript Engine function() { return

    document.getElementById('secretId').textContent; } var page = require('webpage').create(); page.open('http://victim.com', function(status) { if (status == 'success') { var usefulValue = page.evaluate( ); console.log('Stolen: ' + usefulValue); } phantom.exit(); }); Qt WebKit
  12. var page = require('webpage').create(); doSomeWork = function(param1, param2, param3) {

    doSomethingWith(param1); // runs on page useSomehow(param2); // runs on page disregard(param3); // runs on page } page.open('http://example.com', function() { page.evaluate(doSomeWork, value1, value2, value3); phantom.exit(); });
  13. Qt WebKit JavaScript API Engine JavaScript Engine function(param1, param2, param3)

    { doSomethingWith(param1); useSomehow(param2); disregard(param3); } var page = require('webpage').create(); page.open('http://example.com', function() { page.evaluate(doSomeWork, value1, value2, value3); phantom.exit(); }); Qt WebKit
  14. Qt WebKit JavaScript API Engine JavaScript Engine function(param1, param2, param3)

    { doSomethingWith(param1); useSomehow(param2); disregard(param3); } var page = require('webpage').create(); page.open('http://example.com', function() { page.evaluate(doSomeWork, value1, value2, value3); phantom.exit(); }); Qt WebKit J S O N
  15. PROPERTIES canGoBack canGoForward clipRect content cookies customHeaders event focusedFrameName frameContent

    frameName framePlainText frameTitle frameUrl framesCount framesName libraryPath navigationLocked offlineStoragePath offlineStorageQuota ownsPages pages pagesWindowName paperSize plainText scrollPosition settings title url viewportSize windowName zoomFactor METHODS addCookie childFramesCount childFramesName clearCookies close currentFrameName deleteCookie evaluate evaluateAsync evaluateJavaScript getPage go goBack goForward includeJs injectJs open openUrl release reload render renderBase64 sendEvent setContent stop switchToChildFrame switchToFocusedFrame switchToFrame switchToMainFrame switchToParentFrame uploadFile HANDLERS onAlert onCallback onClosing onConfirm onConsoleMessage onError onFilePicker onInitialized onLoadFinished onLoadStarted onNavigationRequested onPageCreated onPrompt onResourceError onResourceReceived onResourceRequested onResourceTimeout onUrlChanged
  16. require 'childprocess' def phantomjs(script, timeout) output, input = IO.pipe phantomjs

    = ChildProcess.new("/usr/local/bin/phantomjs", "--disk-cache=true", script) phantomjs.io.stdout = input phantomjs.io.stderr = input Timeout.timeout(timeout) do phantomjs.start input.close logger.info("Attaching to PhantomJS's STDOUT and STDERR...") output.each_line{|line| logger.info("[PhantomJS] #{line}") } end rescue Timeout::Error logger.warn("PhantomJS is running longer than expected. Shutting it down...") phantomjs.stop end
  17. page = require('webpage').create() page.viewportSize = {width, height} prepareTrack = (i)

    -> track = tracks[i] page.evaluate(drawTrack, track.bounds, track.locations) setTimeout(renderTrack, MAP_LOADING_TIME, i) renderTrack = (i) -> track = tracks[i] page.render(track.imagePath) if i + 1 == tracks.length phantom.exit() else prepareTrack(i + 1) drawTrack = (bounds, locations) -> setBounds(bounds) drawTrackLine(locations) page.open(hostPage) setTimeout(renderTrack, MAP_LOADING_TIME, 0)
  18. !!! 5 %html %head %meta{charset: "utf-8"} :css #map_container { position:

    absolute; left: 0; right: 0; top: 0; bottom: 0 } %body #map_container %script{src: "http://api-maps.yandex.ru/2.1/?lang=ru_RU"} :coffeescript @map = null ymaps.ready => @map = new ymaps.Map 'map_container' @setBounds = (bounds) -> @map.setBounds(bounds) @drawTrackLine = (locations) -> points = (location.point for location in locations) @map.geoObjects.add(new ymaps.Polyline(points))
  19. require 'capybara/poltergeist' Capybara.javascript_driver = :poltergeist Capybara.methods += page.evaluate_script page.execute_script page.within_frame

    page.within_window page.status_code page.response_headers page.save_screenshot page.driver.render_base64(format, options) page.driver.scroll_to(left, top) page.driver.basic_authorize(user, password) element.native.send_keys(*keys)
  20. JS API class Poltergeist.Connection constructor: (@owner, @port) -> @socket =

    new WebSocket "ws://127.0.0.1:#{@port}/" @socket.onmessage = this.commandReceived @socket.onclose = -> phantom.exit() WebKit module Capybara::Poltergeist class Server attr_reader :socket, :fixed_port, :timeout def start @socket = WebSocketServer.new(fixed_port, timeout) end def send(message) @socket.send(message) or raise DeadClient.new(message) end end end
  21. class Poltergeist.WebPage onErrorNative: (message, stack) -> stackString = message stack.forEach

    (frame) -> stackString += "\n" stackString += " at #{frame.file}:#{frame.line}" stackString += " in #{frame.function}" if frame.function && frame.function != '' @errors.push(message: message, stack: stackString) class Poltergeist.Browser sendResponse: (response) -> errors = @currentPage.errors @currentPage.clearErrors() if errors.length > 0 && @js_errors @owner.sendError(new Poltergeist.JavascriptError(errors)) else @owner.sendResponse(response)
  22. Casper.js Chutzpah Ghostbuster GhostDriver Lotte Poltergeist Capybara pjscrape WebSpecter conjure

    PhantomJS Google Charts capturejs pageres phantomjs-screenshots screenshot-app screenshot-as-a-service screenshot-service screenshot screenshot-webservice pyshotx node-webshot pageres django-screamshot PHP Screen grabshot basset Compass Magick Confess GhostStory Grover Grunt Guard PhantomJS phridge phantomjs-node node-phantom phantom-proxy phantomas PhantomCSS PhantomFlow phantomjs-maven-plugin grunt-lib-phantomjs grunt-contrib-qunit PhantomLint PhantomXHR shortcut.io Slippy SpookyJS Yeoman