Browse files

Initial commit.

  • Loading branch information...
0 parents commit 2f7515491e4d0348fce5d9544a49d42b194f3590 @tristandunn committed Oct 22, 2011
Showing with 2,243 additions and 0 deletions.
  1. +7 −0 README.markdown
  2. +1 −0 index.js
  3. +84 −0 lib/browser.js
  4. +1 −0 lib/index.js
  5. +65 −0 lib/parser.js
  6. +15 −0 package.json
  7. +14 −0 script/install.js
  8. +7 −0 vendor/webkit-server/.gitignore
  9. +22 −0 vendor/webkit-server/LICENSE
  10. +41 −0 vendor/webkit-server/README.markdown
  11. +22 −0 vendor/webkit-server/bin/Info.plist
  12. +39 −0 vendor/webkit-server/build.sh
  13. +12 −0 vendor/webkit-server/src/Body.h
  14. +18 −0 vendor/webkit-server/src/ClearCookies.cpp
  15. +11 −0 vendor/webkit-server/src/ClearCookies.h
  16. +15 −0 vendor/webkit-server/src/Command.cpp
  17. +29 −0 vendor/webkit-server/src/Command.h
  18. +29 −0 vendor/webkit-server/src/CommandFactory.cpp
  19. +16 −0 vendor/webkit-server/src/CommandFactory.h
  20. +68 −0 vendor/webkit-server/src/CommandParser.cpp
  21. +29 −0 vendor/webkit-server/src/CommandParser.h
  22. +82 −0 vendor/webkit-server/src/Connection.cpp
  23. +36 −0 vendor/webkit-server/src/Connection.h
  24. +84 −0 vendor/webkit-server/src/Evaluate.cpp
  25. +22 −0 vendor/webkit-server/src/Evaluate.h
  26. +16 −0 vendor/webkit-server/src/Execute.cpp
  27. +12 −0 vendor/webkit-server/src/Execute.h
  28. +19 −0 vendor/webkit-server/src/Find.cpp
  29. +13 −0 vendor/webkit-server/src/Find.h
  30. +66 −0 vendor/webkit-server/src/FrameFocus.cpp
  31. +28 −0 vendor/webkit-server/src/FrameFocus.h
  32. +22 −0 vendor/webkit-server/src/GetCookies.cpp
  33. +14 −0 vendor/webkit-server/src/GetCookies.h
  34. +18 −0 vendor/webkit-server/src/Header.cpp
  35. +11 −0 vendor/webkit-server/src/Header.h
  36. +11 −0 vendor/webkit-server/src/Headers.cpp
  37. +12 −0 vendor/webkit-server/src/Headers.h
  38. +14 −0 vendor/webkit-server/src/JavascriptInvocation.cpp
  39. +19 −0 vendor/webkit-server/src/JavascriptInvocation.h
  40. +25 −0 vendor/webkit-server/src/NetworkAccessManager.cpp
  41. +18 −0 vendor/webkit-server/src/NetworkAccessManager.h
  42. +101 −0 vendor/webkit-server/src/NetworkCookieJar.cpp
  43. +15 −0 vendor/webkit-server/src/NetworkCookieJar.h
  44. +14 −0 vendor/webkit-server/src/Node.cpp
  45. +13 −0 vendor/webkit-server/src/Node.h
  46. +19 −0 vendor/webkit-server/src/Render.cpp
  47. +12 −0 vendor/webkit-server/src/Render.h
  48. +20 −0 vendor/webkit-server/src/Reset.cpp
  49. +12 −0 vendor/webkit-server/src/Reset.h
  50. +19 −0 vendor/webkit-server/src/Response.cpp
  51. +13 −0 vendor/webkit-server/src/Response.h
  52. +25 −0 vendor/webkit-server/src/Server.cpp
  53. +21 −0 vendor/webkit-server/src/Server.h
  54. +18 −0 vendor/webkit-server/src/SetCookie.cpp
  55. +11 −0 vendor/webkit-server/src/SetCookie.h
  56. +24 −0 vendor/webkit-server/src/SetProxy.cpp
  57. +11 −0 vendor/webkit-server/src/SetProxy.h
  58. +20 −0 vendor/webkit-server/src/Source.cpp
  59. +19 −0 vendor/webkit-server/src/Source.h
  60. +13 −0 vendor/webkit-server/src/Status.cpp
  61. +12 −0 vendor/webkit-server/src/Status.h
  62. +32 −0 vendor/webkit-server/src/UnsupportedContentHandler.cpp
  63. +18 −0 vendor/webkit-server/src/UnsupportedContentHandler.h
  64. +15 −0 vendor/webkit-server/src/Url.cpp
  65. +12 −0 vendor/webkit-server/src/Url.h
  66. +21 −0 vendor/webkit-server/src/Visit.cpp
  67. +15 −0 vendor/webkit-server/src/Visit.h
  68. +227 −0 vendor/webkit-server/src/WebPage.cpp
  69. +54 −0 vendor/webkit-server/src/WebPage.h
  70. +11 −0 vendor/webkit-server/src/body.cpp
  71. +24 −0 vendor/webkit-server/src/find_command.h
  72. +32 −0 vendor/webkit-server/src/main.cpp
  73. +205 −0 vendor/webkit-server/src/webkit_server.js
  74. +70 −0 vendor/webkit-server/src/webkit_server.pro
  75. +5 −0 vendor/webkit-server/src/webkit_server.qrc
  76. +3 −0 vendor/webkit-server/webkit_server.pro
7 README.markdown
@@ -0,0 +1,7 @@
+# node-webkit-server
+
+A node.js driver for [webkit-server](https://github.com/tristandunn/webkit-server).
+
+## License
+
+node-webkit-server uses the MIT license. See LICENSE for more details.
1 index.js
@@ -0,0 +1 @@
+module.exports = require("./lib");
84 lib/browser.js
@@ -0,0 +1,84 @@
+var ChildProcess = require("child_process"),
+ Parser = require("./parser"),
+ Path = require("path"),
+ Socket = require("net").Socket;
+
+var WEBKIT_SERVER_BINARY = Path.join(
+ __dirname, "..", "vendor", "webkit-server", "bin", "webkit_server"
+);
+
+var Browser = function(callback) {
+ this.callbacks = [];
+
+ this.parser = new Parser();
+ this.parser.on("response", function(response) {
+ this.callbacks.shift()(response);
+ }.bind(this));
+
+ this.start(function(port) {
+ this.connect(port, callback.bind(this));
+ }.bind(this));
+
+ process.on("exit", this.stop.bind(this));
+};
+
+Browser.prototype.execute = function(code, callback) {
+ this.command("Execute", code, callback);
+};
+
+Browser.prototype.source = function(callback) {
+ this.command("Source", callback);
+};
+
+Browser.prototype.visit = function(path, callback) {
+ this.command("Visit", path, callback);
+};
+
+Browser.prototype.command = function() {
+ var options = Array.prototype.slice.call(arguments),
+ name = options.shift(),
+ callback = options.pop(),
+ length = options.length,
+ commands = [name + "\n"];
+
+ commands.push(length + "\n");
+
+ for (var index = 0; index < length; index++) {
+ var option = options[index];
+
+ commands.push(Buffer.byteLength(option) + "\n" + option);
+ }
+
+ this.callbacks.push(callback);
+ this.socket.write(commands.join(""), "utf8");
+};
+
+Browser.prototype.connect = function(port, callback) {
+ this.socket = new Socket();
+ this.socket.on("connect", callback);
+ this.socket.on("data", function(data) {
+ this.parser.execute(data);
+ }.bind(this));
+ this.socket.on("error", function(error) {
+ throw error;
+ }.bind(this));
+ this.socket.connect(port);
+};
+
+Browser.prototype.start = function(callback) {
+ this.server = ChildProcess.spawn(WEBKIT_SERVER_BINARY);
+ this.server.stdout.once("data", function(data) {
+ var matches = data.toString().match(/port: (\d+)/);
+
+ if (matches) {
+ callback(parseInt(matches[1], 10));
+ }
+ });
+};
+
+Browser.prototype.stop = function(error) {
+ this.socket && this.socket.end();
+ this.server && this.server.kill("SIGHUP");
+};
+
+module.exports = Browser;
1 lib/index.js
@@ -0,0 +1 @@
+exports.Browser = require("./browser");
65 lib/parser.js
@@ -0,0 +1,65 @@
+var Events = require("events"),
+ Utilities = require("util");
+
+var STATE_WAITING = 0,
+ STATE_LENGTH = 1,
+ STATE_RESPONSE = 2;
+
+var Parser = function() {
+ this.reset();
+
+ Events.EventEmitter.call(this);
+};
+
+Utilities.inherits(Parser, Events.EventEmitter);
+
+Parser.prototype.reset = function() {
+ this.state = STATE_WAITING;
+ this.response = null;
+ this.bytes_received = 0;
+};
+
+Parser.prototype.execute = function(incoming) {
+ var position = 0;
+
+ while (position < incoming.length) {
+ switch (this.state) {
+ case STATE_WAITING:
+ if (incoming.toString("utf8", 0, 2) == "ok") {
+ position += 3;
+
+ this.state = STATE_LENGTH;
+ } else {
+ throw new Error("Invalid response.");
+ }
+ break;
+
+ case STATE_LENGTH:
+ var length = "",
+ character = null;
+
+ while ((character = incoming.toString("utf8", position, ++position)) !== "\n") {
+ length += character;
+ }
+
+ this.state = STATE_RESPONSE;
+ this.response = new Buffer(parseInt(length, 10));
+
+ case STATE_RESPONSE:
+ var remaining = Math.min(this.response.length - this.bytes_received, incoming.length - position);
+
+ incoming.copy(this.response, this.bytes_received, position, position + remaining);
+
+ position += remaining;
+ this.bytes_received += remaining;
+
+ if (this.bytes_received === this.response.length) {
+ this.emit("response", this.response.toString("utf8"));
+ this.reset();
+ }
+ break;
+ }
+ }
+};
+
+module.exports = Parser;
15 package.json
@@ -0,0 +1,15 @@
+{ "name" : "webkit-server",
+ "version" : "0.1.0",
+ "description" : "A driver for webkit-server.",
+ "keywords" : ["driver", "headless", "webkit"],
+ "homepage" : "https://github.com/tristandunn/node-webkit-server",
+ "author" : "Tristan Dunn <hello@tristandunn.com> (http://tristandunn.com)",
+ "main" : "index.js",
+ "repository" : {
+ "type" : "git",
+ "url" : "https://github.com/tristandunn/node-webkit-server.git"
+ },
+ "scripts" : {
+ "postinstall" : "node script/install.js"
+ }
+}
14 script/install.js
@@ -0,0 +1,14 @@
+#!/usr/bin/env node
+
+console.info("Building webkit_server executable. This could take a while...");
+
+require("child_process")
+ .exec("vendor/webkit-server/build.sh", function(error, stdout, stderr) {
+ if (error) {
+ throw error;
+ }
+
+ if (stderr) {
+ console.error(stderr);
+ }
+ });
7 vendor/webkit-server/.gitignore
@@ -0,0 +1,7 @@
+*.o
+*.moc
+Makefile*
+qrc_*
+moc_*.cpp
+bin/webkit_server
+src/webkit_server
22 vendor/webkit-server/LICENSE
@@ -0,0 +1,22 @@
+The MIT License
+
+Copyright (c) 2011 thoughtbot, inc.
+Copyright (c) 2011 Tristan Dunn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
41 vendor/webkit-server/README.markdown
@@ -0,0 +1,41 @@
+# webkit-server
+
+A standalone version of the WebKit server in [capybara-webkit](https://github.com/thoughtbot/capybara-webkit).
+
+## Dependencies
+
+webkit-server depends on a WebKit implementation from Qt, a cross-platform development toolkit. You'll need to download the Qt libraries to build the binary.
+
+**OS X 10.7:**
+
+ brew install qt
+
+**OS X < 10.7:**
+
+[Download the non-debug Cocoa package](http://qt.nokia.com/downloads/qt-for-open-source-cpp-development-on-mac-os-x).
+
+**Ubuntu:**
+
+ apt-get install libqt4-dev
+
+**Fedora:**
+
+ yum install qt-webkit-devel
+
+**Gentoo Linux:**
+
+ emerge x11-libs/qt-webkit
+
+**Other Linux distributions:**
+
+[Download this package](http://qt.nokia.com/downloads/linux-x11-cpp).
+
+## Building
+
+To generate the necessary files and build the binary:
+
+ ./build.sh
+
+## License
+
+webkit-server uses the MIT license. See LICENSE for more details.
22 vendor/webkit-server/bin/Info.plist
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
+<plist version="0.9">
+ <dict>
+ <key>CFBundleIconFile</key>
+ <string></string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleGetInfoString</key>
+ <string>Created by Qt/QMake</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleExecutable</key>
+ <string>webkit_server</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.yourcompany.webkit_server</string>
+ <key>NOTE</key>
+ <string>This file was generated by Qt/QMake.</string>
+ <key>LSUIElement</key>
+ <string>1</string>
+ </dict>
+</plist>
39 vendor/webkit-server/build.sh
@@ -0,0 +1,39 @@
+if hash qmake 2> /dev/null; then
+ QMAKE_BINARY="qmake"
+elif hash qmake-qt4 2> /dev/null; then
+ QMAKE_BINARY="qmake-qt4"
+else
+ echo "The qmake or qmake-qt4 binary could not be found. Please ensure Qt is installed and in your PATH."
+ exit 1
+fi
+
+PLATFORM=`uname -s`
+
+if [ $PLATFORM == "Linux" ]; then
+ SPEC="linux-g++"
+elif [ $PLATFORM == "FreeBSD" ]; then
+ SPEC="freebsd-g++"
+elif [ $PLATFORM == "Darwin" ]; then
+ SPEC="macx-g++"
+else
+ echo "The $PLATFORM platform is not currently supported."
+ exit 2
+fi
+
+if hash gmake 2> /dev/null; then
+ MAKE_BINARY="gmake"
+elif hash make 2> /dev/null; then
+ MAKE_BINARY="make"
+else
+ echo "The make or gmake binary count not be found. Please ensure it is in your PATH."
+ exit 3
+fi
+
+cd `dirname $0`
+
+$QMAKE_BINARY -spec $SPEC
+$MAKE_BINARY qmake
+$MAKE_BINARY
+
+mkdir -p bin
+mv src/webkit_server bin/
12 vendor/webkit-server/src/Body.h
@@ -0,0 +1,12 @@
+#include "Command.h"
+
+class WebPage;
+
+class Body : public Command {
+ Q_OBJECT
+
+ public:
+ Body(WebPage *page, QObject *parent = 0);
+ virtual void start(QStringList &arguments);
+};
+
18 vendor/webkit-server/src/ClearCookies.cpp
@@ -0,0 +1,18 @@
+#include "ClearCookies.h"
+#include "WebPage.h"
+#include "NetworkCookieJar.h"
+#include <QNetworkCookie>
+
+ClearCookies::ClearCookies(WebPage *page, QObject *parent)
+ : Command(page, parent)
+{ }
+
+void ClearCookies::start(QStringList &arguments)
+{
+ Q_UNUSED(arguments);
+ NetworkCookieJar *jar = qobject_cast<NetworkCookieJar*>(page()
+ ->networkAccessManager()
+ ->cookieJar());
+ jar->clearCookies();
+ emit finished(new Response(true));
+}
11 vendor/webkit-server/src/ClearCookies.h
@@ -0,0 +1,11 @@
+#include "Command.h"
+
+class WebPage;
+
+class ClearCookies : public Command {
+ Q_OBJECT;
+
+ public:
+ ClearCookies(WebPage *page, QObject *parent = 0);
+ virtual void start(QStringList &arguments);
+};
15 vendor/webkit-server/src/Command.cpp
@@ -0,0 +1,15 @@
+#include "Command.h"
+#include "WebPage.h"
+
+Command::Command(WebPage *page, QObject *parent) : QObject(parent) {
+ m_page = page;
+}
+
+void Command::start(QStringList &arguments) {
+ Q_UNUSED(arguments);
+}
+
+WebPage *Command::page() {
+ return m_page;
+}
+
29 vendor/webkit-server/src/Command.h
@@ -0,0 +1,29 @@
+#ifndef COMMAND_H
+#define COMMAND_H
+
+#include <QObject>
+#include <QStringList>
+#include "Response.h"
+
+class WebPage;
+
+class Command : public QObject {
+ Q_OBJECT
+
+ public:
+ Command(WebPage *page, QObject *parent = 0);
+ virtual void start(QStringList &arguments);
+
+ signals:
+ void finished(Response *response);
+
+ protected:
+ WebPage *page();
+
+ private:
+ WebPage *m_page;
+
+};
+
+#endif
+
29 vendor/webkit-server/src/CommandFactory.cpp
@@ -0,0 +1,29 @@
+#include "CommandFactory.h"
+#include "Visit.h"
+#include "Find.h"
+#include "Command.h"
+#include "Reset.h"
+#include "Node.h"
+#include "Url.h"
+#include "Source.h"
+#include "Evaluate.h"
+#include "Execute.h"
+#include "FrameFocus.h"
+#include "Header.h"
+#include "Render.h"
+#include "Body.h"
+#include "Status.h"
+#include "Headers.h"
+#include "SetCookie.h"
+#include "ClearCookies.h"
+#include "GetCookies.h"
+#include "SetProxy.h"
+
+CommandFactory::CommandFactory(WebPage *page, QObject *parent) : QObject(parent) {
+ m_page = page;
+}
+
+Command *CommandFactory::createCommand(const char *name) {
+ #include "find_command.h"
+ return NULL;
+}
16 vendor/webkit-server/src/CommandFactory.h
@@ -0,0 +1,16 @@
+#include <QObject>
+
+class Command;
+class WebPage;
+
+class CommandFactory : public QObject {
+ Q_OBJECT
+
+ public:
+ CommandFactory(WebPage *page, QObject *parent = 0);
+ Command *createCommand(const char *name);
+
+ private:
+ WebPage *m_page;
+};
+
68 vendor/webkit-server/src/CommandParser.cpp
@@ -0,0 +1,68 @@
+#include "CommandParser.h"
+
+#include <QIODevice>
+
+CommandParser::CommandParser(QIODevice *device, QObject *parent) :
+ QObject(parent) {
+ m_device = device;
+ m_expectingDataSize = -1;
+ connect(m_device, SIGNAL(readyRead()), this, SLOT(checkNext()));
+}
+
+void CommandParser::checkNext() {
+ if (m_expectingDataSize == -1) {
+ if (m_device->canReadLine()) {
+ readLine();
+ checkNext();
+ }
+ } else {
+ if (m_device->bytesAvailable() >= m_expectingDataSize) {
+ readDataBlock();
+ checkNext();
+ }
+ }
+}
+
+void CommandParser::readLine() {
+ char buffer[128];
+ qint64 lineLength = m_device->readLine(buffer, 128);
+ if (lineLength != -1) {
+ buffer[lineLength - 1] = 0;
+ processNext(buffer);
+ }
+}
+
+void CommandParser::readDataBlock() {
+ char *buffer = new char[m_expectingDataSize + 1];
+ m_device->read(buffer, m_expectingDataSize);
+ buffer[m_expectingDataSize] = 0;
+ processNext(buffer);
+ m_expectingDataSize = -1;
+ delete[] buffer;
+}
+
+void CommandParser::processNext(const char *data) {
+ if (m_commandName.isNull()) {
+ m_commandName = data;
+ m_argumentsExpected = -1;
+ } else {
+ processArgument(data);
+ }
+}
+
+void CommandParser::processArgument(const char *data) {
+ if (m_argumentsExpected == -1) {
+ m_argumentsExpected = QString(data).toInt();
+ } else if (m_expectingDataSize == -1) {
+ m_expectingDataSize = QString(data).toInt();
+ } else {
+ m_arguments.append(QString::fromUtf8(data));
+ }
+
+ if (m_arguments.length() == m_argumentsExpected) {
+ emit commandReady(m_commandName, m_arguments);
+ m_commandName = QString();
+ m_arguments.clear();
+ m_argumentsExpected = -1;
+ }
+}
29 vendor/webkit-server/src/CommandParser.h
@@ -0,0 +1,29 @@
+#include <QObject>
+#include <QStringList>
+
+class QIODevice;
+
+class CommandParser : public QObject {
+ Q_OBJECT
+
+ public:
+ CommandParser(QIODevice *device, QObject *parent = 0);
+
+ public slots:
+ void checkNext();
+
+ signals:
+ void commandReady(QString commandName, QStringList arguments);
+
+ private:
+ void readLine();
+ void readDataBlock();
+ void processNext(const char *line);
+ void processArgument(const char *data);
+ QIODevice *m_device;
+ QString m_commandName;
+ QStringList m_arguments;
+ int m_argumentsExpected;
+ int m_expectingDataSize;
+};
+
82 vendor/webkit-server/src/Connection.cpp
@@ -0,0 +1,82 @@
+#include "Connection.h"
+#include "WebPage.h"
+#include "UnsupportedContentHandler.h"
+#include "CommandParser.h"
+#include "CommandFactory.h"
+#include "Command.h"
+
+#include <QTcpSocket>
+#include <iostream>
+
+Connection::Connection(QTcpSocket *socket, WebPage *page, QObject *parent) :
+ QObject(parent) {
+ m_socket = socket;
+ m_page = page;
+ m_commandParser = new CommandParser(socket, this);
+ m_commandFactory = new CommandFactory(page, this);
+ m_command = NULL;
+ m_pageSuccess = true;
+ m_commandWaiting = false;
+ connect(m_socket, SIGNAL(readyRead()), m_commandParser, SLOT(checkNext()));
+ connect(m_commandParser, SIGNAL(commandReady(QString, QStringList)), this, SLOT(commandReady(QString, QStringList)));
+ connect(m_page, SIGNAL(pageFinished(bool)), this, SLOT(pendingLoadFinished(bool)));
+}
+
+
+void Connection::commandReady(QString commandName, QStringList arguments) {
+ m_commandName = commandName;
+ m_arguments = arguments;
+
+ if (m_page->isLoading())
+ m_commandWaiting = true;
+ else
+ startCommand();
+}
+
+void Connection::startCommand() {
+ m_commandWaiting = false;
+ if (m_pageSuccess) {
+ m_command = m_commandFactory->createCommand(m_commandName.toAscii().constData());
+ if (m_command) {
+ connect(m_command,
+ SIGNAL(finished(Response *)),
+ this,
+ SLOT(finishCommand(Response *)));
+ m_command->start(m_arguments);
+ } else {
+ QString failure = QString("[WebKitServer] Unknown command: ") + m_commandName + "\n";
+ writeResponse(new Response(false, failure));
+ }
+ m_commandName = QString();
+ } else {
+ m_pageSuccess = true;
+ QString message = m_page->failureString();
+ writeResponse(new Response(false, message));
+ }
+}
+
+void Connection::pendingLoadFinished(bool success) {
+ m_pageSuccess = success;
+ if (m_commandWaiting)
+ startCommand();
+}
+
+void Connection::finishCommand(Response *response) {
+ m_command->deleteLater();
+ m_command = NULL;
+ writeResponse(response);
+}
+
+void Connection::writeResponse(Response *response) {
+ if (response->isSuccess())
+ m_socket->write("ok\n");
+ else
+ m_socket->write("failure\n");
+
+ QByteArray messageUtf8 = response->message().toUtf8();
+ QString messageLength = QString::number(messageUtf8.size()) + "\n";
+ m_socket->write(messageLength.toAscii());
+ m_socket->write(messageUtf8);
+ delete response;
+}
+
36 vendor/webkit-server/src/Connection.h
@@ -0,0 +1,36 @@
+#include <QObject>
+#include <QStringList>
+
+class QTcpSocket;
+class WebPage;
+class Command;
+class Response;
+class CommandParser;
+class CommandFactory;
+
+class Connection : public QObject {
+ Q_OBJECT
+
+ public:
+ Connection(QTcpSocket *socket, WebPage *page, QObject *parent = 0);
+
+ public slots:
+ void commandReady(QString commandName, QStringList arguments);
+ void finishCommand(Response *response);
+ void pendingLoadFinished(bool success);
+
+ private:
+ void startCommand();
+ void writeResponse(Response *response);
+
+ QTcpSocket *m_socket;
+ QString m_commandName;
+ Command *m_command;
+ QStringList m_arguments;
+ WebPage *m_page;
+ CommandParser *m_commandParser;
+ CommandFactory *m_commandFactory;
+ bool m_pageSuccess;
+ bool m_commandWaiting;
+};
+
84 vendor/webkit-server/src/Evaluate.cpp
@@ -0,0 +1,84 @@
+#include "Evaluate.h"
+#include "WebPage.h"
+#include <iostream>
+
+Evaluate::Evaluate(WebPage *page, QObject *parent) : Command(page, parent) {
+ m_buffer = "";
+}
+
+void Evaluate::start(QStringList &arguments) {
+ QVariant result = page()->currentFrame()->evaluateJavaScript(arguments[0]);
+ addVariant(result);
+ emit finished(new Response(true, m_buffer));
+}
+
+void Evaluate::addVariant(QVariant &object) {
+ if (object.isValid()) {
+ switch(object.type()) {
+ case QMetaType::QString:
+ {
+ QString string = object.toString();
+ addString(string);
+ }
+ break;
+ case QMetaType::QVariantList:
+ {
+ QVariantList list = object.toList();
+ addArray(list);
+ }
+ break;
+ case QMetaType::Double:
+ m_buffer.append(object.toString());
+ break;
+ case QMetaType::QVariantMap:
+ {
+ QVariantMap map = object.toMap();
+ addMap(map);
+ break;
+ }
+ case QMetaType::Bool:
+ {
+ m_buffer.append(object.toString());
+ break;
+ }
+ default:
+ m_buffer.append("null");
+ }
+ } else {
+ m_buffer.append("null");
+ }
+}
+
+void Evaluate::addString(QString &string) {
+ QString escapedString(string);
+ escapedString.replace("\"", "\\\"");
+ m_buffer.append("\"");
+ m_buffer.append(escapedString);
+ m_buffer.append("\"");
+}
+
+void Evaluate::addArray(QVariantList &list) {
+ m_buffer.append("[");
+ for (int i = 0; i < list.length(); i++) {
+ if (i > 0)
+ m_buffer.append(",");
+ addVariant(list[i]);
+ }
+ m_buffer.append("]");
+}
+
+void Evaluate::addMap(QVariantMap &map) {
+ m_buffer.append("{");
+ QMapIterator<QString, QVariant> iterator(map);
+ while (iterator.hasNext()) {
+ iterator.next();
+ QString key = iterator.key();
+ QVariant value = iterator.value();
+ addString(key);
+ m_buffer.append(":");
+ addVariant(value);
+ if (iterator.hasNext())
+ m_buffer.append(",");
+ }
+ m_buffer.append("}");
+}
22 vendor/webkit-server/src/Evaluate.h
@@ -0,0 +1,22 @@
+#include "Command.h"
+
+#include <QVariantList>
+
+class WebPage;
+
+class Evaluate : public Command {
+ Q_OBJECT
+
+ public:
+ Evaluate(WebPage *page, QObject *parent = 0);
+ virtual void start(QStringList &arguments);
+
+ private:
+ void addVariant(QVariant &object);
+ void addString(QString &string);
+ void addArray(QVariantList &list);
+ void addMap(QVariantMap &map);
+
+ QString m_buffer;
+};
+
16 vendor/webkit-server/src/Execute.cpp
@@ -0,0 +1,16 @@
+#include "Execute.h"
+#include "WebPage.h"
+
+Execute::Execute(WebPage *page, QObject *parent) : Command(page, parent) {
+}
+
+void Execute::start(QStringList &arguments) {
+ QString script = arguments[0] + QString("; 'success'");
+ QVariant result = page()->currentFrame()->evaluateJavaScript(script);
+ if (result.isValid()) {
+ emit finished(new Response(true));
+ } else {
+ emit finished(new Response(false, "Javascript failed to execute"));
+ }
+}
+
12 vendor/webkit-server/src/Execute.h
@@ -0,0 +1,12 @@
+#include "Command.h"
+
+class WebPage;
+
+class Execute : public Command {
+ Q_OBJECT
+
+ public:
+ Execute(WebPage *page, QObject *parent = 0);
+ virtual void start(QStringList &arguments);
+};
+
19 vendor/webkit-server/src/Find.cpp
@@ -0,0 +1,19 @@
+#include "Find.h"
+#include "Command.h"
+#include "WebPage.h"
+
+Find::Find(WebPage *page, QObject *parent) : Command(page, parent) {
+}
+
+void Find::start(QStringList &arguments) {
+ QString message;
+ QVariant result = page()->invokeWebKitServerFunction("find", arguments);
+
+ if (result.isValid()) {
+ message = result.toString();
+ emit finished(new Response(true, message));
+ } else {
+ emit finished(new Response(false, "Invalid XPath expression"));
+ }
+}
+
13 vendor/webkit-server/src/Find.h
@@ -0,0 +1,13 @@
+#include "Command.h"
+
+class WebPage;
+
+class Find : public Command {
+ Q_OBJECT
+
+ public:
+ Find(WebPage *page, QObject *parent = 0);
+ virtual void start(QStringList &arguments);
+};
+
+
66 vendor/webkit-server/src/FrameFocus.cpp
@@ -0,0 +1,66 @@
+#include "FrameFocus.h"
+#include "Command.h"
+#include "WebPage.h"
+
+FrameFocus::FrameFocus(WebPage *page, QObject *parent) : Command(page, parent) {
+}
+
+void FrameFocus::start(QStringList &arguments) {
+ findFrames();
+ switch(arguments.length()) {
+ case 1:
+ focusId(arguments[0]);
+ break;
+ case 2:
+ focusIndex(arguments[1].toInt());
+ break;
+ default:
+ focusParent();
+ }
+}
+
+void FrameFocus::findFrames() {
+ frames = page()->currentFrame()->childFrames();
+}
+
+void FrameFocus::focusIndex(int index) {
+ if (isFrameAtIndex(index)) {
+ frames[index]->setFocus();
+ success();
+ } else {
+ frameNotFound();
+ }
+}
+
+bool FrameFocus::isFrameAtIndex(int index) {
+ return 0 <= index && index < frames.length();
+}
+
+void FrameFocus::focusId(QString name) {
+ for (int i = 0; i < frames.length(); i++) {
+ if (frames[i]->frameName().compare(name) == 0) {
+ frames[i]->setFocus();
+ success();
+ return;
+ }
+ }
+
+ frameNotFound();
+}
+
+void FrameFocus::focusParent() {
+ if (page()->currentFrame()->parentFrame() == 0) {
+ emit finished(new Response(false, "Already at parent frame."));
+ } else {
+ page()->currentFrame()->parentFrame()->setFocus();
+ success();
+ }
+}
+
+void FrameFocus::frameNotFound() {
+ emit finished(new Response(false, "Unable to locate frame. "));
+}
+
+void FrameFocus::success() {
+ emit finished(new Response(true));
+}
28 vendor/webkit-server/src/FrameFocus.h
@@ -0,0 +1,28 @@
+#include "Command.h"
+
+class WebPage;
+class QWebFrame;
+
+class FrameFocus : public Command {
+ Q_OBJECT
+
+ public:
+ FrameFocus(WebPage *page, QObject *parent = 0);
+ virtual void start(QStringList &arguments);
+
+ private:
+ void findFrames();
+
+ void focusParent();
+
+ void focusIndex(int index);
+ bool isFrameAtIndex(int index);
+
+ void focusId(QString id);
+
+ void success();
+ void frameNotFound();
+
+ QList<QWebFrame *> frames;
+};
+
22 vendor/webkit-server/src/GetCookies.cpp
@@ -0,0 +1,22 @@
+#include "GetCookies.h"
+#include "WebPage.h"
+#include "NetworkCookieJar.h"
+
+GetCookies::GetCookies(WebPage *page, QObject *parent)
+ : Command(page, parent)
+{
+ m_buffer = "";
+}
+
+void GetCookies::start(QStringList &arguments)
+{
+ Q_UNUSED(arguments);
+ NetworkCookieJar *jar = qobject_cast<NetworkCookieJar*>(page()
+ ->networkAccessManager()
+ ->cookieJar());
+ foreach (QNetworkCookie cookie, jar->getAllCookies()) {
+ m_buffer.append(cookie.toRawForm());
+ m_buffer.append("\n");
+ }
+ emit finished(new Response(true, m_buffer));
+}
14 vendor/webkit-server/src/GetCookies.h
@@ -0,0 +1,14 @@
+#include "Command.h"
+
+class WebPage;
+
+class GetCookies : public Command {
+ Q_OBJECT;
+
+ public:
+ GetCookies(WebPage *page, QObject *parent = 0);
+ virtual void start(QStringList &arguments);
+
+ private:
+ QString m_buffer;
+};
18 vendor/webkit-server/src/Header.cpp
@@ -0,0 +1,18 @@
+#include "Header.h"
+#include "WebPage.h"
+#include "NetworkAccessManager.h"
+
+Header::Header(WebPage *page, QObject *parent) : Command(page, parent) {
+}
+
+void Header::start(QStringList &arguments) {
+ QString key = arguments[0];
+ QString value = arguments[1];
+ NetworkAccessManager* networkAccessManager = qobject_cast<NetworkAccessManager*>(page()->networkAccessManager());
+ if (key.toLower().replace("-", "_") == "user_agent") {
+ page()->setUserAgent(value);
+ } else {
+ networkAccessManager->addHeader(key, value);
+ }
+ emit finished(new Response(true));
+}
11 vendor/webkit-server/src/Header.h
@@ -0,0 +1,11 @@
+#include "Command.h"
+
+class WebPage;
+
+class Header : public Command {
+ Q_OBJECT
+
+ public:
+ Header(WebPage *page, QObject *parent = 0);
+ virtual void start(QStringList &arguments);
+};
11 vendor/webkit-server/src/Headers.cpp
@@ -0,0 +1,11 @@
+#include "Headers.h"
+#include "WebPage.h"
+
+Headers::Headers(WebPage *page, QObject *parent) : Command(page, parent) {
+}
+
+void Headers::start(QStringList &arguments) {
+ Q_UNUSED(arguments);
+ emit finished(new Response(true, page()->pageHeaders()));
+}
+
12 vendor/webkit-server/src/Headers.h
@@ -0,0 +1,12 @@
+#include "Command.h"
+
+class WebPage;
+
+class Headers : public Command {
+ Q_OBJECT
+
+ public:
+ Headers(WebPage *page, QObject *parent = 0);
+ virtual void start(QStringList &arguments);
+};
+
14 vendor/webkit-server/src/JavascriptInvocation.cpp
@@ -0,0 +1,14 @@
+#include "JavascriptInvocation.h"
+
+JavascriptInvocation::JavascriptInvocation(QString &functionName, QStringList &arguments, QObject *parent) : QObject(parent) {
+ m_functionName = functionName;
+ m_arguments = arguments;
+}
+
+QString &JavascriptInvocation::functionName() {
+ return m_functionName;
+}
+
+QStringList &JavascriptInvocation::arguments() {
+ return m_arguments;
+}
19 vendor/webkit-server/src/JavascriptInvocation.h
@@ -0,0 +1,19 @@
+#include <QObject>
+#include <QString>
+#include <QStringList>
+
+class JavascriptInvocation : public QObject {
+ Q_OBJECT
+ Q_PROPERTY(QString functionName READ functionName)
+ Q_PROPERTY(QStringList arguments READ arguments)
+
+ public:
+ JavascriptInvocation(QString &functionName, QStringList &arguments, QObject *parent = 0);
+ QString &functionName();
+ QStringList &arguments();
+
+ private:
+ QString m_functionName;
+ QStringList m_arguments;
+};
+
25 vendor/webkit-server/src/NetworkAccessManager.cpp
@@ -0,0 +1,25 @@
+#include "NetworkAccessManager.h"
+#include "WebPage.h"
+#include <iostream>
+
+
+NetworkAccessManager::NetworkAccessManager(QObject *parent):QNetworkAccessManager(parent) {
+}
+
+QNetworkReply* NetworkAccessManager::createRequest(QNetworkAccessManager::Operation operation, const QNetworkRequest &request, QIODevice * outgoingData = 0) {
+ QNetworkRequest new_request(request);
+ if (operation != QNetworkAccessManager::PostOperation && operation != QNetworkAccessManager::PutOperation) {
+ new_request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant());
+ }
+ QHashIterator<QString, QString> item(m_headers);
+ while (item.hasNext()) {
+ item.next();
+ new_request.setRawHeader(item.key().toAscii(), item.value().toAscii());
+ }
+ return QNetworkAccessManager::createRequest(operation, new_request, outgoingData);
+};
+
+void NetworkAccessManager::addHeader(QString key, QString value) {
+ m_headers.insert(key, value);
+};
+
18 vendor/webkit-server/src/NetworkAccessManager.h
@@ -0,0 +1,18 @@
+#include <QtNetwork/QNetworkAccessManager>
+#include <QtNetwork/QNetworkRequest>
+#include <QtNetwork/QNetworkReply>
+
+class NetworkAccessManager : public QNetworkAccessManager {
+
+ Q_OBJECT
+
+ public:
+ NetworkAccessManager(QObject *parent = 0);
+ void addHeader(QString key, QString value);
+
+ protected:
+ QNetworkReply* createRequest(QNetworkAccessManager::Operation op, const QNetworkRequest &req, QIODevice * outgoingData);
+
+ private:
+ QHash<QString, QString> m_headers;
+};
101 vendor/webkit-server/src/NetworkCookieJar.cpp
@@ -0,0 +1,101 @@
+#include "NetworkCookieJar.h"
+#include "QtCore/qdatetime.h"
+
+NetworkCookieJar::NetworkCookieJar(QObject *parent)
+ : QNetworkCookieJar(parent)
+{ }
+
+QList<QNetworkCookie> NetworkCookieJar::getAllCookies() const
+{
+ return allCookies();
+}
+
+void NetworkCookieJar::clearCookies()
+{
+ setAllCookies(QList<QNetworkCookie>());
+}
+
+static inline bool isParentDomain(QString domain, QString reference)
+{
+ if (!reference.startsWith(QLatin1Char('.')))
+ return domain == reference;
+
+ return domain.endsWith(reference) || domain == reference.mid(1);
+}
+
+void NetworkCookieJar::overwriteCookies(const QList<QNetworkCookie>& cookieList)
+{
+ /* this function is basically a copy-and-paste of the original
+ QNetworkCookieJar::setCookiesFromUrl with the domain and
+ path validations removed */
+
+ QString defaultPath(QLatin1Char('/'));
+ QDateTime now = QDateTime::currentDateTime();
+ QList<QNetworkCookie> newCookies = allCookies();
+
+ foreach (QNetworkCookie cookie, cookieList) {
+ bool isDeletion = (!cookie.isSessionCookie() &&
+ cookie.expirationDate() < now);
+
+ // validate the cookie & set the defaults if unset
+ if (cookie.path().isEmpty())
+ cookie.setPath(defaultPath);
+
+ // don't do path checking. See http://bugreports.qt.nokia.com/browse/QTBUG-5815
+ // else if (!isParentPath(pathAndFileName, cookie.path())) {
+ // continue; // not accepted
+ // }
+
+ if (cookie.domain().isEmpty()) {
+ continue;
+ } else {
+ // Ensure the domain starts with a dot if its field was not empty
+ // in the HTTP header. There are some servers that forget the
+ // leading dot and this is actually forbidden according to RFC 2109,
+ // but all browsers accept it anyway so we do that as well.
+ if (!cookie.domain().startsWith(QLatin1Char('.')))
+ cookie.setDomain(QLatin1Char('.') + cookie.domain());
+
+ QString domain = cookie.domain();
+
+ // the check for effective TLDs makes the "embedded dot" rule from RFC 2109 section 4.3.2
+ // redundant; the "leading dot" rule has been relaxed anyway, see above
+ // we remove the leading dot for this check
+ /*
+ if (QNetworkCookieJarPrivate::isEffectiveTLD(domain.remove(0, 1)))
+ continue; // not accepted
+ */
+ }
+
+ for (int i = 0; i < newCookies.size(); ++i) {
+ // does this cookie already exist?
+ const QNetworkCookie &current = newCookies.at(i);
+ if (cookie.name() == current.name() &&
+ cookie.domain() == current.domain() &&
+ cookie.path() == current.path()) {
+ // found a match
+ newCookies.removeAt(i);
+ break;
+ }
+ }
+
+ // did not find a match
+ if (!isDeletion) {
+ int countForDomain = 0;
+ for (int i = newCookies.size() - 1; i >= 0; --i) {
+ // Start from the end and delete the oldest cookies to keep a maximum count of 50.
+ const QNetworkCookie &current = newCookies.at(i);
+ if (isParentDomain(cookie.domain(), current.domain())
+ || isParentDomain(current.domain(), cookie.domain())) {
+ if (countForDomain >= 49)
+ newCookies.removeAt(i);
+ else
+ ++countForDomain;
+ }
+ }
+
+ newCookies += cookie;
+ }
+ }
+ setAllCookies(newCookies);
+}
15 vendor/webkit-server/src/NetworkCookieJar.h
@@ -0,0 +1,15 @@
+#include <QtNetwork/QNetworkCookieJar>
+#include <QtNetwork/QNetworkCookie>
+
+class NetworkCookieJar : public QNetworkCookieJar {
+
+ Q_OBJECT;
+
+ public:
+
+ NetworkCookieJar(QObject *parent = 0);
+
+ QList<QNetworkCookie> getAllCookies() const;
+ void clearCookies();
+ void overwriteCookies(const QList<QNetworkCookie>& cookieList);
+};
14 vendor/webkit-server/src/Node.cpp
@@ -0,0 +1,14 @@
+#include "Node.h"
+#include "WebPage.h"
+
+Node::Node(WebPage *page, QObject *parent) : Command(page, parent) {
+}
+
+void Node::start(QStringList &arguments) {
+ QStringList functionArguments(arguments);
+ QString functionName = functionArguments.takeFirst();
+ QVariant result = page()->invokeWebKitServerFunction(functionName, functionArguments);
+ QString attributeValue = result.toString();
+ emit finished(new Response(true, attributeValue));
+}
+
13 vendor/webkit-server/src/Node.h
@@ -0,0 +1,13 @@
+#include "Command.h"
+#include <QStringList>
+
+class WebPage;
+
+class Node : public Command {
+ Q_OBJECT
+
+ public:
+ Node(WebPage *page, QObject *parent = 0);
+ virtual void start(QStringList &arguments);
+};
+
19 vendor/webkit-server/src/Render.cpp
@@ -0,0 +1,19 @@
+#include "Render.h"
+#include "WebPage.h"
+
+Render::Render(WebPage *page, QObject *parent) : Command(page, parent) {
+}
+
+void Render::start(QStringList &arguments) {
+ QStringList functionArguments(arguments);
+ QString imagePath = functionArguments.takeFirst();
+ int width = functionArguments.takeFirst().toInt();
+ int height = functionArguments.takeFirst().toInt();
+
+ QSize size(width, height);
+ page()->setViewportSize(size);
+
+ bool result = page()->render( imagePath );
+
+ emit finished(new Response(result));
+}
12 vendor/webkit-server/src/Render.h
@@ -0,0 +1,12 @@
+#include "Command.h"
+#include <QStringList>
+
+class WebPage;
+
+class Render : public Command {
+ Q_OBJECT
+
+ public:
+ Render(WebPage *page, QObject *parent = 0);
+ virtual void start(QStringList &arguments);
+};
20 vendor/webkit-server/src/Reset.cpp
@@ -0,0 +1,20 @@
+#include "Reset.h"
+#include "WebPage.h"
+#include "NetworkAccessManager.h"
+#include "NetworkCookieJar.h"
+
+Reset::Reset(WebPage *page, QObject *parent) : Command(page, parent) {
+}
+
+void Reset::start(QStringList &arguments) {
+ Q_UNUSED(arguments);
+
+ page()->triggerAction(QWebPage::Stop);
+ page()->currentFrame()->setHtml("<html><body></body></html>");
+ page()->networkAccessManager()->setCookieJar(new NetworkCookieJar());
+ page()->setCustomNetworkAccessManager();
+ page()->setUserAgent(NULL);
+ page()->resetResponseHeaders();
+ emit finished(new Response(true));
+}
+
12 vendor/webkit-server/src/Reset.h
@@ -0,0 +1,12 @@
+#include "Command.h"
+
+class WebPage;
+
+class Reset : public Command {
+ Q_OBJECT
+
+ public:
+ Reset(WebPage *page, QObject *parent = 0);
+ virtual void start(QStringList &arguments);
+};
+
19 vendor/webkit-server/src/Response.cpp
@@ -0,0 +1,19 @@
+#include "Response.h"
+#include <iostream>
+
+Response::Response(bool success, QString message) {
+ m_success = success;
+ m_message = message;
+}
+
+Response::Response(bool success) {
+ m_success = success;
+}
+
+bool Response::isSuccess() const {
+ return m_success;
+}
+
+QString Response::message() const {
+ return m_message;
+}
13 vendor/webkit-server/src/Response.h
@@ -0,0 +1,13 @@
+#include <QString>
+
+class Response {
+ public:
+ Response(bool success, QString message);
+ Response(bool success);
+ bool isSuccess() const;
+ QString message() const;
+
+ private:
+ bool m_success;
+ QString m_message;
+};
25 vendor/webkit-server/src/Server.cpp
@@ -0,0 +1,25 @@
+#include "Server.h"
+#include "WebPage.h"
+#include "Connection.h"
+
+#include <QTcpServer>
+
+Server::Server(QObject *parent, bool ignoreSslErrors) : QObject(parent) {
+ m_tcp_server = new QTcpServer(this);
+ m_page = new WebPage(this);
+ m_page->setIgnoreSslErrors(ignoreSslErrors);
+}
+
+bool Server::start() {
+ connect(m_tcp_server, SIGNAL(newConnection()), this, SLOT(handleConnection()));
+ return m_tcp_server->listen(QHostAddress::Any, 0);
+}
+
+quint16 Server::server_port() const {
+ return m_tcp_server->serverPort();
+}
+
+void Server::handleConnection() {
+ QTcpSocket *socket = m_tcp_server->nextPendingConnection();
+ new Connection(socket, m_page, this);
+}
21 vendor/webkit-server/src/Server.h
@@ -0,0 +1,21 @@
+#include <QObject>
+
+class QTcpServer;
+class WebPage;
+
+class Server : public QObject {
+ Q_OBJECT
+
+ public:
+ Server(QObject *parent, bool ignoreSslErrors);
+ bool start();
+ quint16 server_port() const;
+
+ public slots:
+ void handleConnection();
+
+ private:
+ QTcpServer *m_tcp_server;
+ WebPage *m_page;
+};
+
18 vendor/webkit-server/src/SetCookie.cpp
@@ -0,0 +1,18 @@
+#include "SetCookie.h"
+#include "WebPage.h"
+#include "NetworkCookieJar.h"
+#include <QNetworkCookie>
+
+SetCookie::SetCookie(WebPage *page, QObject *parent)
+ : Command(page, parent)
+{ }
+
+void SetCookie::start(QStringList &arguments)
+{
+ QList<QNetworkCookie> cookies = QNetworkCookie::parseCookies(arguments[0].toAscii());
+ NetworkCookieJar *jar = qobject_cast<NetworkCookieJar*>(page()
+ ->networkAccessManager()
+ ->cookieJar());
+ jar->overwriteCookies(cookies);
+ emit finished(new Response(true));
+}
11 vendor/webkit-server/src/SetCookie.h
@@ -0,0 +1,11 @@
+#include "Command.h"
+
+class WebPage;
+
+class SetCookie : public Command {
+ Q_OBJECT;
+
+ public:
+ SetCookie(WebPage *page, QObject *parent = 0);
+ virtual void start(QStringList &arguments);
+};
24 vendor/webkit-server/src/SetProxy.cpp
@@ -0,0 +1,24 @@
+#include "SetProxy.h"
+#include "WebPage.h"
+#include <QNetworkAccessManager>
+#include <QNetworkProxy>
+
+SetProxy::SetProxy(WebPage *page, QObject *parent)
+ : Command(page, parent)
+{ }
+
+void SetProxy::start(QStringList &arguments)
+{
+ // default to empty proxy
+ QNetworkProxy proxy;
+
+ if (arguments.size() > 0)
+ proxy = QNetworkProxy(QNetworkProxy::HttpProxy,
+ arguments[0],
+ (quint16)(arguments[1].toInt()),
+ arguments[2],
+ arguments[3]);
+
+ page()->networkAccessManager()->setProxy(proxy);
+ emit finished(new Response(true));
+}
11 vendor/webkit-server/src/SetProxy.h
@@ -0,0 +1,11 @@
+#include "Command.h"
+
+class WebPage;
+
+class SetProxy : public Command {
+ Q_OBJECT;
+
+ public:
+ SetProxy(WebPage *page, QObject *parent = 0);
+ virtual void start(QStringList &arguments);
+};
20 vendor/webkit-server/src/Source.cpp
@@ -0,0 +1,20 @@
+#include "Source.h"
+#include "WebPage.h"
+
+Source::Source(WebPage *page, QObject *parent) : Command(page, parent) {
+}
+
+void Source::start(QStringList &arguments) {
+ Q_UNUSED(arguments)
+
+ QNetworkAccessManager* accessManager = page()->networkAccessManager();
+ QNetworkRequest request(page()->currentFrame()->url());
+ reply = accessManager->get(request);
+
+ connect(reply, SIGNAL(finished()), this, SLOT(sourceLoaded()));
+}
+
+void Source::sourceLoaded() {
+ emit finished(new Response(true, reply->readAll()));
+}
+
19 vendor/webkit-server/src/Source.h
@@ -0,0 +1,19 @@
+#include "Command.h"
+
+class WebPage;
+class QNetworkReply;
+
+class Source : public Command {
+ Q_OBJECT
+
+ public:
+ Source(WebPage *page, QObject *parent = 0);
+ virtual void start(QStringList &arguments);
+
+ public slots:
+ void sourceLoaded();
+
+ private:
+ QNetworkReply *reply;
+};
+
13 vendor/webkit-server/src/Status.cpp
@@ -0,0 +1,13 @@
+#include "Status.h"
+#include "WebPage.h"
+#include <sstream>
+
+Status::Status(WebPage *page, QObject *parent) : Command(page, parent) {
+}
+
+void Status::start(QStringList &arguments) {
+ Q_UNUSED(arguments);
+ int status = page()->getLastStatus();
+ emit finished(new Response(true, QString::number(status)));
+}
+
12 vendor/webkit-server/src/Status.h
@@ -0,0 +1,12 @@
+#include "Command.h"
+
+class WebPage;
+
+class Status : public Command {
+ Q_OBJECT
+
+ public:
+ Status(WebPage *page, QObject *parent = 0);
+ virtual void start(QStringList &arguments);
+};
+
32 vendor/webkit-server/src/UnsupportedContentHandler.cpp
@@ -0,0 +1,32 @@
+#include "UnsupportedContentHandler.h"
+#include "WebPage.h"
+#include <QNetworkReply>
+
+UnsupportedContentHandler::UnsupportedContentHandler(WebPage *page, QNetworkReply *reply, QObject *parent) : QObject(parent) {
+ m_page = page;
+ m_reply = reply;
+ connect(m_reply, SIGNAL(finished()), this, SLOT(handleUnsupportedContent()));
+ disconnect(m_page, SIGNAL(loadFinished(bool)), m_page, SLOT(loadFinished(bool)));
+}
+
+void UnsupportedContentHandler::handleUnsupportedContent() {
+ QVariant contentMimeType = m_reply->header(QNetworkRequest::ContentTypeHeader);
+ if(contentMimeType.isNull()) {
+ this->finish(false);
+ } else {
+ this->loadUnsupportedContent();
+ this->finish(true);
+ }
+ this->deleteLater();
+}
+
+void UnsupportedContentHandler::loadUnsupportedContent() {
+ QByteArray text = m_reply->readAll();
+ m_page->mainFrame()->setContent(text, QString("text/plain"), m_reply->url());
+}
+
+void UnsupportedContentHandler::finish(bool success) {
+ connect(m_page, SIGNAL(loadFinished(bool)), m_page, SLOT(loadFinished(bool)));
+ m_page->replyFinished(m_reply);
+ m_page->loadFinished(success);
+}
18 vendor/webkit-server/src/UnsupportedContentHandler.h
@@ -0,0 +1,18 @@
+#include <QObject>
+class WebPage;
+class QNetworkReply;
+class UnsupportedContentHandler : public QObject {
+ Q_OBJECT
+
+ public:
+ UnsupportedContentHandler(WebPage *page, QNetworkReply *reply, QObject *parent = 0);
+
+ public slots:
+ void handleUnsupportedContent();
+
+ private:
+ WebPage *m_page;
+ QNetworkReply *m_reply;
+ void loadUnsupportedContent();
+ void finish(bool success);
+};
15 vendor/webkit-server/src/Url.cpp
@@ -0,0 +1,15 @@
+#include "Url.h"
+#include "WebPage.h"
+
+Url::Url(WebPage *page, QObject *parent) : Command(page, parent) {
+}
+
+void Url::start(QStringList &argments) {
+ Q_UNUSED(argments);
+
+ QUrl humanUrl = page()->currentFrame()->url();
+ QByteArray encodedBytes = humanUrl.toEncoded();
+ QString urlString = QString(encodedBytes);
+ emit finished(new Response(true, urlString));
+}
+
12 vendor/webkit-server/src/Url.h
@@ -0,0 +1,12 @@
+#include "Command.h"
+
+class WebPage;
+
+class Url : public Command {
+ Q_OBJECT
+
+ public:
+ Url(WebPage *page, QObject *parent = 0);
+ virtual void start(QStringList &argments);
+};
+
21 vendor/webkit-server/src/Visit.cpp
@@ -0,0 +1,21 @@
+#include "Visit.h"
+#include "Command.h"
+#include "WebPage.h"
+
+Visit::Visit(WebPage *page, QObject *parent) : Command(page, parent) {
+ connect(page, SIGNAL(pageFinished(bool)), this, SLOT(loadFinished(bool)));
+}
+
+void Visit::start(QStringList &arguments) {
+ QUrl requestedUrl = QUrl(arguments[0]);
+ page()->currentFrame()->load(QUrl(requestedUrl));
+}
+
+void Visit::loadFinished(bool success) {
+ QString message;
+ if (!success)
+ message = page()->failureString();
+
+ disconnect(page(), SIGNAL(pageFinished(bool)), this, SLOT(loadFinished(bool)));
+ emit finished(new Response(success, message));
+}
15 vendor/webkit-server/src/Visit.h
@@ -0,0 +1,15 @@
+#include "Command.h"
+
+class WebPage;
+
+class Visit : public Command {
+ Q_OBJECT
+
+ public:
+ Visit(WebPage *page, QObject *parent = 0);
+ virtual void start(QStringList &arguments);
+
+ private slots:
+ void loadFinished(bool success);
+};
+
227 vendor/webkit-server/src/WebPage.cpp
@@ -0,0 +1,227 @@
+#include "WebPage.h"
+#include "JavascriptInvocation.h"
+#include "NetworkAccessManager.h"
+#include "NetworkCookieJar.h"
+#include "UnsupportedContentHandler.h"
+#include <QResource>
+#include <iostream>
+
+WebPage::WebPage(QObject *parent) : QWebPage(parent) {
+ setForwardUnsupportedContent(true);
+ loadJavascript();
+ setUserStylesheet();
+
+ m_loading = false;
+ this->setCustomNetworkAccessManager();
+
+ connect(this, SIGNAL(loadStarted()), this, SLOT(loadStarted()));
+ connect(this, SIGNAL(loadFinished(bool)), this, SLOT(loadFinished(bool)));
+ connect(this, SIGNAL(frameCreated(QWebFrame *)),
+ this, SLOT(frameCreated(QWebFrame *)));
+ connect(this, SIGNAL(unsupportedContent(QNetworkReply*)),
+ this, SLOT(handleUnsupportedContent(QNetworkReply*)));
+ this->setViewportSize(QSize(1680, 1050));
+}
+
+void WebPage::setCustomNetworkAccessManager() {
+ NetworkAccessManager *manager = new NetworkAccessManager();
+ manager->setCookieJar(new NetworkCookieJar());
+ this->setNetworkAccessManager(manager);
+ connect(manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(replyFinished(QNetworkReply *)));
+ connect(manager, SIGNAL(sslErrors(QNetworkReply *, QList<QSslError>)), this, SLOT(ignoreSslErrors(QNetworkReply *, QList<QSslError>)));
+}
+
+void WebPage::loadJavascript() {
+ QResource javascript(":/webkit_server.js");
+ if (javascript.isCompressed()) {
+ QByteArray uncompressedBytes(qUncompress(javascript.data(), javascript.size()));
+ m_javascriptString = QString(uncompressedBytes);
+ } else {
+ char * javascriptString = new char[javascript.size() + 1];
+ strcpy(javascriptString, (const char *)javascript.data());
+ javascriptString[javascript.size()] = 0;
+ m_javascriptString = javascriptString;
+ }
+}
+
+void WebPage::setUserStylesheet() {
+ QString data = QString("* { font-family: 'Arial' ! important; }").toUtf8().toBase64();
+ QUrl url = QUrl(QString("data:text/css;charset=utf-8;base64,") + data);
+ settings()->setUserStyleSheetUrl(url);
+}
+
+QString WebPage::userAgentForUrl(const QUrl &url ) const {
+ if (!m_userAgent.isEmpty()) {
+ return m_userAgent;
+ } else {
+ return QWebPage::userAgentForUrl(url);
+ }
+}
+
+void WebPage::setUserAgent(QString userAgent) {
+ m_userAgent = userAgent;
+}
+
+void WebPage::frameCreated(QWebFrame * frame) {
+ connect(frame, SIGNAL(javaScriptWindowObjectCleared()),
+ this, SLOT(injectJavascriptHelpers()));
+}
+
+void WebPage::injectJavascriptHelpers() {
+ QWebFrame* frame = qobject_cast<QWebFrame *>(QObject::sender());
+ frame->evaluateJavaScript(m_javascriptString);
+}
+
+bool WebPage::shouldInterruptJavaScript() {
+ return false;
+}
+
+QVariant WebPage::invokeWebKitServerFunction(const char *name, QStringList &arguments) {
+ QString qname(name);
+ QString objectName("WebKitServerInvocation");
+ JavascriptInvocation invocation(qname, arguments);
+ currentFrame()->addToJavaScriptWindowObject(objectName, &invocation);
+ QString javascript = QString("WebKitServer.invoke()");
+ return currentFrame()->evaluateJavaScript(javascript);
+}
+
+QVariant WebPage::invokeWebKitServerFunction(QString &name, QStringList &arguments) {
+ return invokeWebKitServerFunction(name.toAscii().data(), arguments);
+}
+
+void WebPage::javaScriptConsoleMessage(const QString &message, int lineNumber, const QString &sourceID) {
+ if (!sourceID.isEmpty())
+ std::cout << qPrintable(sourceID) << ":" << lineNumber << " ";
+ std::cout << qPrintable(message) << std::endl;
+}
+
+void WebPage::javaScriptAlert(QWebFrame *frame, const QString &message) {
+ Q_UNUSED(frame);
+ std::cout << "ALERT: " << qPrintable(message) << std::endl;
+}
+
+bool WebPage::javaScriptConfirm(QWebFrame *frame, const QString &message) {
+ Q_UNUSED(frame);
+ Q_UNUSED(message);
+ return true;
+}
+
+bool WebPage::javaScriptPrompt(QWebFrame *frame, const QString &message, const QString &defaultValue, QString *result) {
+ Q_UNUSED(frame)
+ Q_UNUSED(message)
+ Q_UNUSED(defaultValue)
+ Q_UNUSED(result)
+ return false;
+}
+
+void WebPage::loadStarted() {
+ m_loading = true;
+}
+
+void WebPage::loadFinished(bool success) {
+ m_loading = false;
+ emit pageFinished(success);
+}
+
+bool WebPage::isLoading() const {
+ return m_loading;
+}
+
+QString WebPage::failureString() {
+ return QString("Unable to load URL: ") + currentFrame()->requestedUrl().toString();
+}
+
+bool WebPage::render(const QString &fileName) {
+ QFileInfo fileInfo(fileName);
+ QDir dir;
+ dir.mkpath(fileInfo.absolutePath());
+
+ QSize viewportSize = this->viewportSize();
+ QSize pageSize = this->mainFrame()->contentsSize();
+ if (pageSize.isEmpty()) {
+ return false;
+ }
+
+ QImage buffer(pageSize, QImage::Format_ARGB32);
+ buffer.fill(qRgba(255, 255, 255, 0));
+
+ QPainter p(&buffer);
+ p.setRenderHint( QPainter::Antialiasing, true);
+ p.setRenderHint( QPainter::TextAntialiasing, true);
+ p.setRenderHint( QPainter::SmoothPixmapTransform, true);
+
+ this->setViewportSize(pageSize);
+ this->mainFrame()->render(&p);
+ p.end();
+ this->setViewportSize(viewportSize);
+
+ return buffer.save(fileName);
+}
+
+QString WebPage::chooseFile(QWebFrame *parentFrame, const QString &suggestedFile) {
+ Q_UNUSED(parentFrame);
+ Q_UNUSED(suggestedFile);
+
+ return getLastAttachedFileName();
+}
+
+bool WebPage::extension(Extension extension, const ExtensionOption *option, ExtensionReturn *output) {
+ Q_UNUSED(option);
+ if (extension == ChooseMultipleFilesExtension) {
+ QStringList names = QStringList() << getLastAttachedFileName();
+ static_cast<ChooseMultipleFilesExtensionReturn*>(output)->fileNames = names;
+ return true;
+ }
+ return false;
+}
+
+QString WebPage::getLastAttachedFileName() {
+ return currentFrame()->evaluateJavaScript(QString("WebKitServer.lastAttachedFile")).toString();
+}
+
+void WebPage::replyFinished(QNetworkReply *reply) {
+ if (reply->url() == this->currentFrame()->url()) {
+ QStringList headers;
+ m_lastStatus = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+ QList<QByteArray> list = reply->rawHeaderList();
+
+ int length = list.size();
+ for(int i = 0; i < length; i++) {
+ headers << list.at(i)+": "+reply->rawHeader(list.at(i));
+ }
+
+ m_pageHeaders = headers.join("\n");
+ }
+}
+
+void WebPage::ignoreSslErrors(QNetworkReply *reply, const QList<QSslError> &errors) {
+ if (m_ignoreSslErrors)
+ reply->ignoreSslErrors(errors);
+}
+
+void WebPage::setIgnoreSslErrors(bool ignore) {
+ m_ignoreSslErrors = ignore;
+}
+
+bool WebPage::ignoreSslErrors() {
+ return m_ignoreSslErrors;
+}
+
+
+int WebPage::getLastStatus() {
+ return m_lastStatus;
+}
+
+void WebPage::resetResponseHeaders() {
+ m_lastStatus = 0;
+ m_pageHeaders = QString();
+}
+
+QString WebPage::pageHeaders() {
+ return m_pageHeaders;
+}
+
+void WebPage::handleUnsupportedContent(QNetworkReply *reply) {
+ UnsupportedContentHandler *handler = new UnsupportedContentHandler(this, reply);
+ Q_UNUSED(handler);
+}
54 vendor/webkit-server/src/WebPage.h
@@ -0,0 +1,54 @@
+#include <QtWebKit>
+
+class WebPage : public QWebPage {
+ Q_OBJECT
+
+ public:
+ WebPage(QObject *parent = 0);
+ QVariant invokeWebKitServerFunction(const char *name, QStringList &arguments);
+ QVariant invokeWebKitServerFunction(QString &name, QStringList &arguments);
+ QString failureString();
+ QString userAgentForUrl(const QUrl &url ) const;
+ void setUserAgent(QString userAgent);
+ int getLastStatus();
+ void resetResponseHeaders();
+ void setCustomNetworkAccessManager();
+ bool render(const QString &fileName);
+ virtual bool extension (Extension extension, const ExtensionOption *option=0, ExtensionReturn *output=0);
+ void setIgnoreSslErrors(bool ignore);
+ bool ignoreSslErrors();
+
+ public slots:
+ bool shouldInterruptJavaScript();
+ void injectJavascriptHelpers();
+ void loadStarted();
+ void loadFinished(bool);
+ bool isLoading() const;
+ QString pageHeaders();
+ void frameCreated(QWebFrame *);
+ void replyFinished(QNetworkReply *reply);
+ void ignoreSslErrors(QNetworkReply *reply, const QList<QSslError> &);
+ void handleUnsupportedContent(QNetworkReply *reply);
+
+ signals:
+ void pageFinished(bool);
+
+ protected:
+ virtual void javaScriptConsoleMessage(const QString &message, int lineNumber, const QString &sourceID);
+ virtual void javaScriptAlert(QWebFrame *frame, const QString &message);
+ virtual bool javaScriptConfirm(QWebFrame *frame, const QString &message);
+ virtual bool javaScriptPrompt(QWebFrame *frame, const QString &message, const QString &defaultValue, QString *result);
+ virtual QString chooseFile(QWebFrame * parentFrame, const QString &suggestedFile);
+
+ private:
+ QString m_javascriptString;
+ QString m_userAgent;
+ bool m_loading;
+ QString getLastAttachedFileName();
+ void loadJavascript();
+ void setUserStylesheet();
+ int m_lastStatus;
+ QString m_pageHeaders;
+ bool m_ignoreSslErrors;
+};
+
11 vendor/webkit-server/src/body.cpp
@@ -0,0 +1,11 @@
+#include "Body.h"
+#include "WebPage.h"
+
+Body::Body(WebPage *page, QObject *parent) : Command(page, parent) {
+}
+
+void Body::start(QStringList &arguments) {
+ Q_UNUSED(arguments);
+ QString result = page()->currentFrame()->toHtml();
+ emit finished(new Response(true, result));
+}
24 vendor/webkit-server/src/find_command.h
@@ -0,0 +1,24 @@
+#define CHECK_COMMAND(expectedName) \
+ if (strcmp(#expectedName, name) == 0) { \
+ return new expectedName(m_page, this); \
+ }
+
+CHECK_COMMAND(Visit)
+CHECK_COMMAND(Find)
+CHECK_COMMAND(Reset)
+CHECK_COMMAND(Node)
+CHECK_COMMAND(Url)
+CHECK_COMMAND(Source)
+CHECK_COMMAND(Evaluate)
+CHECK_COMMAND(Execute)
+CHECK_COMMAND(FrameFocus)
+CHECK_COMMAND(Header)
+CHECK_COMMAND(Render)
+CHECK_COMMAND(Body)
+CHECK_COMMAND(Status)
+CHECK_COMMAND(Headers)
+CHECK_COMMAND(SetCookie)
+CHECK_COMMAND(ClearCookies)
+CHECK_COMMAND(GetCookies)
+CHECK_COMMAND(Headers)
+CHECK_COMMAND(SetProxy)
32 vendor/webkit-server/src/main.cpp
@@ -0,0 +1,32 @@
+#include "Server.h"
+#include <QtGui>
+#include <iostream>
+#ifdef Q_OS_UNIX
+ #include <unistd.h>
+#endif
+
+int main(int argc, char **argv) {
+#ifdef Q_OS_UNIX
+ if (setpgid(0, 0) < 0) {
+ std::cerr << "Unable to set new process group." << std::endl;
+ return 1;
+ }
+#endif
+
+ QApplication app(argc, argv);
+ app.setApplicationName("webkit_server");
+
+ QStringList args = app.arguments();
+ bool ignoreSslErrors = args.contains("--ignore-ssl-errors");
+
+ Server server(0, ignoreSslErrors);
+
+ if (server.start()) {
+ std::cout << "webkit_server server started, listening on port: " << server.server_port() << std::endl;
+ return app.exec();
+ } else {
+ std::cerr << "Couldn't start webkit_server." << std::endl;
+ return 1;
+ }
+}
+
205 vendor/webkit-server/src/webkit_server.js
@@ -0,0 +1,205 @@
+window.WebKitServer = {
+ nextIndex: 0,
+ nodes: {},
+ lastAttachedFile: "",
+
+ invoke: function () {
+ return this[WebKitServerInvocation.functionName].apply(this, WebKitServerInvocation.arguments);
+ },
+
+ find: function (xpath) {
+ return this.findRelativeTo(document, xpath);
+ },
+
+ findWithin: function (index, xpath) {
+ return this.findRelativeTo(this.nodes[index], xpath);
+ },
+
+ findRelativeTo: function (reference, xpath) {
+ var iterator = document.evaluate(xpath, reference, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);
+ var node;
+ var results = [];
+ while (node = iterator.iterateNext()) {
+ this.nextIndex++;
+ this.nodes[this.nextIndex] = node;
+ results.push(this.nextIndex);
+ }
+ return results.join(",");
+ },
+
+ isAttached: function(index) {
+ return document.evaluate("ancestor-or-self::html", this.nodes[index], null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue != null;
+ },
+
+ text: function (index) {
+ var node = this.nodes[index];
+ var type = (node.type || node.tagName).toLowerCase();
+ if (type == "textarea") {
+ return node.innerHTML;
+ } else {
+ return node.innerText;
+ }
+ },
+
+ attribute: function (index, name) {
+ switch(name) {
+ case 'checked':
+ return this.nodes[index].checked;
+ break;
+
+ case 'disabled':
+ return this.nodes[index].disabled;
+ break;
+
+ default:
+ return this.nodes[index].getAttribute(name);
+ }
+ },
+
+ tagName: function(index) {
+ return this.nodes[index].tagName.toLowerCase();
+ },
+
+ click: function (index) {
+ var clickEvent = document.createEvent('MouseEvents');
+ clickEvent.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
+ this.nodes[index].dispatchEvent(clickEvent);
+ },
+
+ trigger: function (index, eventName) {
+ var eventObject = document.createEvent("HTMLEvents");
+ eventObject.initEvent(eventName, true, true);
+ this.nodes[index].dispatchEvent(eventObject);
+ },
+
+ keypress: function(index, altKey, ctrlKey, shiftKey, metaKey, keyCode, charCode) {
+ var eventObject = document.createEvent("Events");
+ eventObject.initEvent('keypress', true, true);
+ eventObject.window = window;
+ eventObject.altKey = altKey;
+ eventObject.ctrlKey = ctrlKey;
+ eventObject.shiftKey = shiftKey;
+ eventObject.metaKey = metaKey;
+ eventObject.keyCode = keyCode;
+ eventObject.charCode = charCode;
+ this.nodes[index].dispatchEvent(eventObject);
+ },
+
+ visible: function (index) {
+ var element = this.nodes[index];
+ while (element) {
+ if (element.ownerDocument.defaultView.getComputedStyle(element, null).getPropertyValue("display") == 'none')
+ return false;
+ element = element.parentElement;
+ }
+ return true;
+ },
+
+ selected: function (index) {
+ return this.nodes[index].selected;
+ },
+
+ value: function(index) {
+ return this.nodes[index].value;
+ },
+
+ set: function(index, value) {
+ var node = this.nodes[index];
+ var type = (node.type || node.tagName).toLowerCase();
+ if (type == "text" || type == "textarea" || type == "password") {
+ this.trigger(index, "focus");
+ node.value = "";
+ var maxLength = this.attribute(index, "maxlength"),
+ length;
+ if (maxLength && value.length > maxLength) {
+ length = maxLength;
+ } else {
+ length = value.length;
+ }
+
+ for(var strindex = 0; strindex < length; strindex++) {
+ node.value += value[strindex];
+ this.trigger(index, "keydown");
+ this.keypress(index, false, false, false, false, 0, value[strindex]);
+ this.trigger(index, "keyup");
+ }
+ this.trigger(index, "change");
+ this.trigger(index, "blur");
+ } else if(type == "checkbox" || type == "radio") {
+ node.checked = (value == "true");
+ this.trigger(index, "click");
+ this.trigger(index, "change");
+ } else if(type == "file") {
+ this.lastAttachedFile = value;
+ this.trigger(index, "click");
+ } else {
+ node.value = value;
+ }
+ },
+
+ selectOption: function(index) {
+ this.nodes[index].selected = true;
+ this.nodes[index].setAttribute("selected", "selected");
+ this.trigger(index, "change");
+ },
+
+ unselectOption: function(index) {
+ this.nodes[index].selected = false;
+ this.nodes[index].removeAttribute("selected");
+ this.trigger(index, "change");
+ },
+
+ centerPostion: function(element) {
+ this.reflow(element);
+ var rect = element.getBoundingClientRect();
+ var position = {
+ x: rect.width / 2,
+ y: rect.height / 2
+ };
+ do {
+ position.x += element.offsetLeft;
+ position.y += element.offsetTop;
+ } while ((element = element.offsetParent));
+ position.x = Math.floor(position.x), position.y = Math.floor(position.y);
+
+ return position;
+ },
+
+ reflow: function(element, force) {
+ if (force || element.offsetWidth === 0) {
+ var prop, oldStyle = {}, newStyle = {position: "absolute", visibility : "hidden", display: "block" };
+ for (prop in newStyle) {
+ oldStyle[prop] = element.style[prop];
+ element.style[prop] = newStyle[prop];
+ }
+ element.offsetWidth, element.offsetHeight; // force reflow
+ for (prop in oldStyle)
+ element.style[prop] = oldStyle[prop];
+ }
+ },
+
+ dragTo: function (index, targetIndex) {
+ var element = this.nodes[index], target = this.nodes[targetIndex];
+ var position = this.centerPostion(element);
+ var options = {
+ clientX: position.x,
+ clientY: position.y
+ };
+ var mouseTrigger = function(eventName, options) {
+ var eventObject = document.createEvent("MouseEvents");
+ eventObject.initMouseEvent(eventName, true, true, window, 0, 0, 0, options.clientX || 0, options.clientY || 0, false, false, false, false, 0, null);
+ element.dispatchEvent(eventObject);
+ }
+ mouseTrigger('mousedown', options);
+ options.clientX += 1, options.clientY += 1;
+ mouseTrigger('mousemove', options);
+
+ position = this.centerPostion(target), options = {
+ clientX: position.x,
+ clientY: position.y
+ };
+ mouseTrigger('mousemove', options);
+ mouseTrigger('mouseup', options);
+ }
+};
+
70 vendor/webkit-server/src/webkit_server.pro