imseng-capybara-webkit 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. data/.gitignore +21 -0
  2. data/.rspec +2 -0
  3. data/Appraisals +7 -0
  4. data/CONTRIBUTING.md +47 -0
  5. data/ChangeLog +70 -0
  6. data/Gemfile +3 -0
  7. data/LICENSE +19 -0
  8. data/NEWS.md +36 -0
  9. data/README.md +114 -0
  10. data/Rakefile +65 -0
  11. data/bin/Info.plist +22 -0
  12. data/capybara-webkit.gemspec +28 -0
  13. data/extconf.rb +2 -0
  14. data/gemfiles/1.0.gemfile +7 -0
  15. data/gemfiles/1.0.gemfile.lock +70 -0
  16. data/gemfiles/1.1.gemfile +7 -0
  17. data/gemfiles/1.1.gemfile.lock +70 -0
  18. data/lib/capybara/driver/webkit/browser.rb +164 -0
  19. data/lib/capybara/driver/webkit/connection.rb +120 -0
  20. data/lib/capybara/driver/webkit/cookie_jar.rb +55 -0
  21. data/lib/capybara/driver/webkit/node.rb +118 -0
  22. data/lib/capybara/driver/webkit/socket_debugger.rb +43 -0
  23. data/lib/capybara/driver/webkit/version.rb +7 -0
  24. data/lib/capybara/driver/webkit.rb +136 -0
  25. data/lib/capybara/webkit/matchers.rb +37 -0
  26. data/lib/capybara/webkit.rb +13 -0
  27. data/lib/capybara-webkit.rb +1 -0
  28. data/lib/capybara_webkit_builder.rb +68 -0
  29. data/spec/browser_spec.rb +173 -0
  30. data/spec/capybara_webkit_builder_spec.rb +37 -0
  31. data/spec/connection_spec.rb +54 -0
  32. data/spec/cookie_jar_spec.rb +48 -0
  33. data/spec/driver_rendering_spec.rb +80 -0
  34. data/spec/driver_resize_window_spec.rb +59 -0
  35. data/spec/driver_spec.rb +1552 -0
  36. data/spec/integration/driver_spec.rb +20 -0
  37. data/spec/integration/session_spec.rb +137 -0
  38. data/spec/self_signed_ssl_cert.rb +42 -0
  39. data/spec/spec_helper.rb +46 -0
  40. data/src/Body.h +12 -0
  41. data/src/ClearCookies.cpp +15 -0
  42. data/src/ClearCookies.h +11 -0
  43. data/src/Command.cpp +19 -0
  44. data/src/Command.h +31 -0
  45. data/src/CommandFactory.cpp +37 -0
  46. data/src/CommandFactory.h +16 -0
  47. data/src/CommandParser.cpp +76 -0
  48. data/src/CommandParser.h +33 -0
  49. data/src/Connection.cpp +71 -0
  50. data/src/Connection.h +37 -0
  51. data/src/ConsoleMessages.cpp +10 -0
  52. data/src/ConsoleMessages.h +12 -0
  53. data/src/CurrentUrl.cpp +68 -0
  54. data/src/CurrentUrl.h +16 -0
  55. data/src/Evaluate.cpp +84 -0
  56. data/src/Evaluate.h +22 -0
  57. data/src/Execute.cpp +16 -0
  58. data/src/Execute.h +12 -0
  59. data/src/Find.cpp +19 -0
  60. data/src/Find.h +13 -0
  61. data/src/FrameFocus.cpp +66 -0
  62. data/src/FrameFocus.h +28 -0
  63. data/src/GetCookies.cpp +20 -0
  64. data/src/GetCookies.h +14 -0
  65. data/src/Header.cpp +18 -0
  66. data/src/Header.h +11 -0
  67. data/src/Headers.cpp +10 -0
  68. data/src/Headers.h +12 -0
  69. data/src/IgnoreSslErrors.cpp +12 -0
  70. data/src/IgnoreSslErrors.h +12 -0
  71. data/src/JavascriptInvocation.cpp +14 -0
  72. data/src/JavascriptInvocation.h +19 -0
  73. data/src/NetworkAccessManager.cpp +29 -0
  74. data/src/NetworkAccessManager.h +19 -0
  75. data/src/NetworkCookieJar.cpp +101 -0
  76. data/src/NetworkCookieJar.h +15 -0
  77. data/src/Node.cpp +14 -0
  78. data/src/Node.h +13 -0
  79. data/src/NullCommand.cpp +10 -0
  80. data/src/NullCommand.h +11 -0
  81. data/src/PageLoadingCommand.cpp +46 -0
  82. data/src/PageLoadingCommand.h +40 -0
  83. data/src/Render.cpp +18 -0
  84. data/src/Render.h +12 -0
  85. data/src/RequestedUrl.cpp +12 -0
  86. data/src/RequestedUrl.h +12 -0
  87. data/src/Reset.cpp +29 -0
  88. data/src/Reset.h +15 -0
  89. data/src/ResizeWindow.cpp +16 -0
  90. data/src/ResizeWindow.h +12 -0
  91. data/src/Response.cpp +24 -0
  92. data/src/Response.h +15 -0
  93. data/src/Server.cpp +24 -0
  94. data/src/Server.h +21 -0
  95. data/src/SetCookie.cpp +16 -0
  96. data/src/SetCookie.h +11 -0
  97. data/src/SetProxy.cpp +22 -0
  98. data/src/SetProxy.h +11 -0
  99. data/src/Source.cpp +18 -0
  100. data/src/Source.h +19 -0
  101. data/src/Status.cpp +12 -0
  102. data/src/Status.h +12 -0
  103. data/src/UnsupportedContentHandler.cpp +32 -0
  104. data/src/UnsupportedContentHandler.h +18 -0
  105. data/src/Url.cpp +12 -0
  106. data/src/Url.h +12 -0
  107. data/src/Visit.cpp +12 -0
  108. data/src/Visit.h +12 -0
  109. data/src/WebPage.cpp +239 -0
  110. data/src/WebPage.h +58 -0
  111. data/src/body.cpp +10 -0
  112. data/src/capybara.js +315 -0
  113. data/src/find_command.h +29 -0
  114. data/src/main.cpp +33 -0
  115. data/src/webkit_server.pro +85 -0
  116. data/src/webkit_server.qrc +5 -0
  117. data/templates/Command.cpp +10 -0
  118. data/templates/Command.h +12 -0
  119. data/webkit_server.pro +4 -0
  120. metadata +298 -0
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+ require 'capybara/driver/webkit'
3
+
4
+ describe Capybara::Driver::Webkit do
5
+ before do
6
+ @driver = Capybara::Driver::Webkit.new(TestApp, :browser => $webkit_browser)
7
+ end
8
+
9
+ it_should_behave_like "driver"
10
+ it_should_behave_like "driver with javascript support"
11
+ it_should_behave_like "driver with cookies support"
12
+ it_should_behave_like "driver with header support"
13
+ it_should_behave_like "driver with status code support"
14
+ it_should_behave_like "driver with frame support"
15
+
16
+ it "returns the rack server port" do
17
+ @driver.server_port.should eq(@driver.instance_variable_get(:@rack_server).port)
18
+ end
19
+
20
+ end
@@ -0,0 +1,137 @@
1
+ # -*- encoding: UTF-8 -*-
2
+
3
+ require 'spec_helper'
4
+ require 'capybara/webkit'
5
+
6
+ describe Capybara::Session do
7
+ subject { Capybara::Session.new(:reusable_webkit, @app) }
8
+ after { subject.reset! }
9
+
10
+ context "slow javascript app" do
11
+ before(:all) do
12
+ @app = lambda do |env|
13
+ body = <<-HTML
14
+ <html><body>
15
+ <form action="/next" id="submit_me"><input type="submit" value="Submit" /></form>
16
+ <p id="change_me">Hello</p>
17
+
18
+ <script type="text/javascript">
19
+ var form = document.getElementById('submit_me');
20
+ form.addEventListener("submit", function (event) {
21
+ event.preventDefault();
22
+ setTimeout(function () {
23
+ document.getElementById("change_me").innerHTML = 'Good' + 'bye';
24
+ }, 500);
25
+ });
26
+ </script>
27
+ </body></html>
28
+ HTML
29
+ [200,
30
+ { 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s },
31
+ [body]]
32
+ end
33
+ end
34
+
35
+ before do
36
+ @default_wait_time = Capybara.default_wait_time
37
+ Capybara.default_wait_time = 1
38
+ end
39
+
40
+ after { Capybara.default_wait_time = @default_wait_time }
41
+
42
+ it "waits for a request to load" do
43
+ subject.visit("/")
44
+ subject.find_button("Submit").click
45
+ subject.should have_content("Goodbye");
46
+ end
47
+ end
48
+
49
+ context "simple app" do
50
+ before(:all) do
51
+ @app = lambda do |env|
52
+ body = <<-HTML
53
+ <html><body>
54
+ <strong>Hello</strong>
55
+ <span>UTF8文字列</span>
56
+ <input type="button" value="ボタン" />
57
+ </body></html>
58
+ HTML
59
+ [200,
60
+ { 'Content-Type' => 'text/html; charset=UTF-8', 'Content-Length' => body.length.to_s },
61
+ [body]]
62
+ end
63
+ end
64
+
65
+ before do
66
+ subject.visit("/")
67
+ end
68
+
69
+ it "inspects nodes" do
70
+ subject.all(:xpath, "//strong").first.inspect.should include("strong")
71
+ end
72
+
73
+ it "can read utf8 string" do
74
+ utf8str = subject.all(:xpath, "//span").first.text
75
+ utf8str.should eq('UTF8文字列')
76
+ end
77
+
78
+ it "can click utf8 string" do
79
+ subject.click_button('ボタン')
80
+ end
81
+ end
82
+
83
+ context "response headers with status code" do
84
+ before(:all) do
85
+ @app = lambda do |env|
86
+ params = ::Rack::Utils.parse_query(env['QUERY_STRING'])
87
+ if params["img"] == "true"
88
+ body = 'not found'
89
+ return [404, { 'Content-Type' => 'image/gif', 'Content-Length' => body.length.to_s }, [body]]
90
+ end
91
+ body = <<-HTML
92
+ <html>
93
+ <body>
94
+ <img src="?img=true">
95
+ </body>
96
+ </html>
97
+ HTML
98
+ [200,
99
+ { 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s, 'X-Capybara' => 'WebKit'},
100
+ [body]]
101
+ end
102
+ end
103
+
104
+ it "should get status code" do
105
+ subject.visit '/'
106
+ subject.status_code.should == 200
107
+ end
108
+
109
+ it "should reset status code" do
110
+ subject.visit '/'
111
+ subject.status_code.should == 200
112
+ subject.reset!
113
+ subject.status_code.should == 0
114
+ end
115
+
116
+ it "should get response headers" do
117
+ subject.visit '/'
118
+ subject.response_headers['X-Capybara'].should == 'WebKit'
119
+ end
120
+
121
+ it "should reset response headers" do
122
+ subject.visit '/'
123
+ subject.response_headers['X-Capybara'].should == 'WebKit'
124
+ subject.reset!
125
+ subject.response_headers['X-Capybara'].should == nil
126
+ end
127
+ end
128
+ end
129
+
130
+ describe Capybara::Session, "with TestApp" do
131
+ before do
132
+ @session = Capybara::Session.new(:reusable_webkit, TestApp)
133
+ end
134
+
135
+ it_should_behave_like "session"
136
+ it_should_behave_like "session with javascript support"
137
+ end
@@ -0,0 +1,42 @@
1
+ require 'openssl'
2
+
3
+ pem = <<-PEM_ENCODED
4
+ -----BEGIN PRIVATE KEY-----
5
+ MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKW+grT6YW3gv79y
6
+ P9JkQDtm3cDSUhAhd/TEyRBt/pSKz3pNSygsleBJl2d7g8k0fteec95a7YnYRKGH
7
+ XhIpUOvl/3uaV2NVipqxwB+Z+0M+7HegxL3e4unaRFy9kf9/UXJzmuA9BTMLrm/w
8
+ IoW17f+dz7BIFZhCvRurkrmvzraNAgMBAAECgYBv4uB3bYJx20N16Jk+3OAjeXh/
9
+ Hzu4me9Rc7pLdgVinyYaaK0wrJBsfSFRASdgnyh1RAjx9K3f3PfPlwMg/XUbA6Yd
10
+ YOYlMnBUwCJX09TH8RFFCzJzbBylpk/sTF1geICln2O2BloT2cM24PlEPvyz1xLa
11
+ XvxCOnJJfgNU1K6kvQJBANcEVyOMJ9RBfI8gj1o7S70J9yJRI4jvqxuvcOxJBCi6
12
+ CDatkh/getHswsE3sLj25VhrNsi6UQcN8Bjm8Yjt8BsCQQDFVe0uCwobseprMOuW
13
+ dPU4+saN1cFnIT5Gp0iwYRPinjUlkh6H/AuUtUulKFXVmxk1KElpp1E3bxpCDgCp
14
+ oe53AkArO1Mt8Ys8kSIzQO+xy8RRsQRAoSHM8atsuJyy1YeBjM4D+GguApuPQ9Rw
15
+ tvrQZcv9OCleuJ98FKBW0XB1AKpLAkEAmOR3bJofDdAuWTjA/4TEzo32MsRwIZBv
16
+ KNzJg+bjOkzrzp1EzIVrD5/b6S20O1j9EeOR5as+UN3jEVS6DLQrBwJAe5OTrDiQ
17
+ vKtUaAwquC4f4Ia05KwJw+vFrPVaOqgc4QLdxRwx4PfV/Uw3OOqMolpPAvpUi9JI
18
+ LAwIaTtCvo18OQ==
19
+ -----END PRIVATE KEY-----
20
+ -----BEGIN CERTIFICATE-----
21
+ MIICgDCCAemgAwIBAgIJANWcyeZB2ql1MA0GCSqGSIb3DQEBBQUAMFkxCzAJBgNV
22
+ BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
23
+ aWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0xMTA5MjgyMDIx
24
+ MTFaFw0xMjA5MjcyMDIxMTFaMFkxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21l
25
+ LVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNV
26
+ BAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEApb6CtPph
27
+ beC/v3I/0mRAO2bdwNJSECF39MTJEG3+lIrPek1LKCyV4EmXZ3uDyTR+155z3lrt
28
+ idhEoYdeEilQ6+X/e5pXY1WKmrHAH5n7Qz7sd6DEvd7i6dpEXL2R/39RcnOa4D0F
29
+ Mwuub/AihbXt/53PsEgVmEK9G6uSua/Oto0CAwEAAaNQME4wHQYDVR0OBBYEFBAm
30
+ x19zpY8M8FEcMXB4WQeUhqIiMB8GA1UdIwQYMBaAFBAmx19zpY8M8FEcMXB4WQeU
31
+ hqIiMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAcznCusWS5Ws77IUl
32
+ b87vdfCPVphICyfGHGWhHp3BZ3WLOphauAMdOYIiJGtPExyWr4DYpzbvx0+Ljg7G
33
+ 2FNaI0QRXqbR5ccpvwm6KELmU0XDhykNaXiXSdnvIanr3z/hZ5e03mXAywo+nGQj
34
+ UYTVCb6g/uHVNzXq1NgHGuqogKY=
35
+ -----END CERTIFICATE-----
36
+ PEM_ENCODED
37
+
38
+ key = OpenSSL::PKey::RSA.new(pem)
39
+ cert = OpenSSL::X509::Certificate.new(pem)
40
+ $openssl_self_signed_ctx = OpenSSL::SSL::SSLContext.new
41
+ $openssl_self_signed_ctx.key = key
42
+ $openssl_self_signed_ctx.cert = cert
@@ -0,0 +1,46 @@
1
+ require 'rspec'
2
+ require 'rspec/autorun'
3
+ require 'rbconfig'
4
+
5
+ PROJECT_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..')).freeze
6
+
7
+ $LOAD_PATH << File.join(PROJECT_ROOT, 'lib')
8
+
9
+ Dir[File.join(PROJECT_ROOT, 'spec', 'support', '**', '*.rb')].each { |file| require(file) }
10
+
11
+ spec_dir = nil
12
+ $:.detect do |dir|
13
+ if File.exists? File.join(dir, "capybara.rb")
14
+ spec_dir = File.expand_path(File.join(dir,"..","spec"))
15
+ $:.unshift( spec_dir )
16
+ end
17
+ end
18
+
19
+ RSpec.configure do |c|
20
+ c.filter_run_excluding :skip_on_windows => !(RbConfig::CONFIG['host_os'] =~ /mingw32/).nil?
21
+ end
22
+
23
+ require File.join(spec_dir, "spec_helper")
24
+
25
+ require 'capybara/driver/webkit/connection'
26
+ require 'capybara/driver/webkit/browser'
27
+ connection = Capybara::Driver::Webkit::Connection.new(:socket_class => TCPSocket, :stdout => nil)
28
+ $webkit_browser = Capybara::Driver::Webkit::Browser.new(connection)
29
+
30
+ Capybara.register_driver :reusable_webkit do |app|
31
+ Capybara::Driver::Webkit.new(app, :browser => $webkit_browser)
32
+ end
33
+
34
+ def with_env_vars(vars)
35
+ old_env_variables = {}
36
+ vars.each do |key, value|
37
+ old_env_variables[key] = ENV[key]
38
+ ENV[key] = value
39
+ end
40
+
41
+ yield
42
+
43
+ old_env_variables.each do |key, value|
44
+ ENV[key] = value
45
+ end
46
+ end
data/src/Body.h ADDED
@@ -0,0 +1,12 @@
1
+ #include "Command.h"
2
+
3
+ class WebPage;
4
+
5
+ class Body : public Command {
6
+ Q_OBJECT
7
+
8
+ public:
9
+ Body(WebPage *page, QStringList &arguments, QObject *parent = 0);
10
+ virtual void start();
11
+ };
12
+
@@ -0,0 +1,15 @@
1
+ #include "ClearCookies.h"
2
+ #include "WebPage.h"
3
+ #include "NetworkCookieJar.h"
4
+ #include <QNetworkCookie>
5
+
6
+ ClearCookies::ClearCookies(WebPage *page, QStringList &arguments, QObject *parent) : Command(page, arguments, parent) {}
7
+
8
+ void ClearCookies::start()
9
+ {
10
+ NetworkCookieJar *jar = qobject_cast<NetworkCookieJar*>(page()
11
+ ->networkAccessManager()
12
+ ->cookieJar());
13
+ jar->clearCookies();
14
+ emit finished(new Response(true));
15
+ }
@@ -0,0 +1,11 @@
1
+ #include "Command.h"
2
+
3
+ class WebPage;
4
+
5
+ class ClearCookies : public Command {
6
+ Q_OBJECT;
7
+
8
+ public:
9
+ ClearCookies(WebPage *page, QStringList &arguments, QObject *parent = 0);
10
+ virtual void start();
11
+ };
data/src/Command.cpp ADDED
@@ -0,0 +1,19 @@
1
+ #include "Command.h"
2
+ #include "WebPage.h"
3
+
4
+ Command::Command(WebPage *page, QStringList &arguments, QObject *parent) : QObject(parent) {
5
+ m_page = page;
6
+ m_arguments = arguments;
7
+ }
8
+
9
+ void Command::start() {
10
+ }
11
+
12
+ WebPage *Command::page() {
13
+ return m_page;
14
+ }
15
+
16
+ QStringList &Command::arguments() {
17
+ return m_arguments;
18
+ }
19
+
data/src/Command.h ADDED
@@ -0,0 +1,31 @@
1
+ #ifndef COMMAND_H
2
+ #define COMMAND_H
3
+
4
+ #include <QObject>
5
+ #include <QStringList>
6
+ #include "Response.h"
7
+
8
+ class WebPage;
9
+
10
+ class Command : public QObject {
11
+ Q_OBJECT
12
+
13
+ public:
14
+ Command(WebPage *page, QStringList &arguments, QObject *parent = 0);
15
+ virtual void start();
16
+
17
+ signals:
18
+ void finished(Response *response);
19
+
20
+ protected:
21
+ WebPage *page();
22
+ QStringList &arguments();
23
+
24
+ private:
25
+ WebPage *m_page;
26
+ QStringList m_arguments;
27
+
28
+ };
29
+
30
+ #endif
31
+
@@ -0,0 +1,37 @@
1
+ #include "CommandFactory.h"
2
+ #include "NullCommand.h"
3
+ #include "Visit.h"
4
+ #include "Find.h"
5
+ #include "Command.h"
6
+ #include "Reset.h"
7
+ #include "Node.h"
8
+ #include "Url.h"
9
+ #include "Source.h"
10
+ #include "Evaluate.h"
11
+ #include "Execute.h"
12
+ #include "FrameFocus.h"
13
+ #include "Header.h"
14
+ #include "Render.h"
15
+ #include "Body.h"
16
+ #include "Status.h"
17
+ #include "Headers.h"
18
+ #include "SetCookie.h"
19
+ #include "ClearCookies.h"
20
+ #include "GetCookies.h"
21
+ #include "SetProxy.h"
22
+ #include "ConsoleMessages.h"
23
+ #include "RequestedUrl.h"
24
+ #include "CurrentUrl.h"
25
+ #include "ResizeWindow.h"
26
+ #include "IgnoreSslErrors.h"
27
+
28
+ CommandFactory::CommandFactory(WebPage *page, QObject *parent) : QObject(parent) {
29
+ m_page = page;
30
+ }
31
+
32
+ Command *CommandFactory::createCommand(const char *name, QStringList &arguments) {
33
+ #include "find_command.h"
34
+ arguments.clear();
35
+ arguments.append(QString(name));
36
+ return new NullCommand(m_page, arguments);
37
+ }
@@ -0,0 +1,16 @@
1
+ #include <QObject>
2
+
3
+ class Command;
4
+ class WebPage;
5
+
6
+ class CommandFactory : public QObject {
7
+ Q_OBJECT
8
+
9
+ public:
10
+ CommandFactory(WebPage *page, QObject *parent = 0);
11
+ Command *createCommand(const char *name, QStringList &arguments);
12
+
13
+ private:
14
+ WebPage *m_page;
15
+ };
16
+
@@ -0,0 +1,76 @@
1
+ #include "CommandParser.h"
2
+ #include "CommandFactory.h"
3
+ #include "Command.h"
4
+
5
+ #include <QIODevice>
6
+
7
+ CommandParser::CommandParser(QIODevice *device, CommandFactory *commandFactory, QObject *parent) :
8
+ QObject(parent) {
9
+ m_device = device;
10
+ m_expectingDataSize = -1;
11
+ m_commandFactory = commandFactory;
12
+ connect(m_device, SIGNAL(readyRead()), this, SLOT(checkNext()));
13
+ }
14
+
15
+ void CommandParser::checkNext() {
16
+ if (m_expectingDataSize == -1) {
17
+ if (m_device->canReadLine()) {
18
+ readLine();
19
+ checkNext();
20
+ }
21
+ } else {
22
+ if (m_device->bytesAvailable() >= m_expectingDataSize) {
23
+ readDataBlock();
24
+ checkNext();
25
+ }
26
+ }
27
+ }
28
+
29
+ void CommandParser::readLine() {
30
+ char buffer[128];
31
+ qint64 lineLength = m_device->readLine(buffer, 128);
32
+ if (lineLength != -1) {
33
+ buffer[lineLength - 1] = 0;
34
+ processNext(buffer);
35
+ }
36
+ }
37
+
38
+ void CommandParser::readDataBlock() {
39
+ char *buffer = new char[m_expectingDataSize + 1];
40
+ m_device->read(buffer, m_expectingDataSize);
41
+ buffer[m_expectingDataSize] = 0;
42
+ processNext(buffer);
43
+ m_expectingDataSize = -1;
44
+ delete[] buffer;
45
+ }
46
+
47
+ void CommandParser::processNext(const char *data) {
48
+ if (m_commandName.isNull()) {
49
+ m_commandName = data;
50
+ m_argumentsExpected = -1;
51
+ } else {
52
+ processArgument(data);
53
+ }
54
+ }
55
+
56
+ void CommandParser::processArgument(const char *data) {
57
+ if (m_argumentsExpected == -1) {
58
+ m_argumentsExpected = QString(data).toInt();
59
+ } else if (m_expectingDataSize == -1) {
60
+ m_expectingDataSize = QString(data).toInt();
61
+ } else {
62
+ m_arguments.append(QString::fromUtf8(data));
63
+ }
64
+
65
+ if (m_arguments.length() == m_argumentsExpected) {
66
+ Command *command = m_commandFactory->createCommand(m_commandName.toAscii().constData(), m_arguments);
67
+ emit commandReady(command);
68
+ reset();
69
+ }
70
+ }
71
+
72
+ void CommandParser::reset() {
73
+ m_commandName = QString();
74
+ m_arguments.clear();
75
+ m_argumentsExpected = -1;
76
+ }
@@ -0,0 +1,33 @@
1
+ #include <QObject>
2
+ #include <QStringList>
3
+
4
+ class QIODevice;
5
+ class CommandFactory;
6
+ class Command;
7
+
8
+ class CommandParser : public QObject {
9
+ Q_OBJECT
10
+
11
+ public:
12
+ CommandParser(QIODevice *device, CommandFactory *commandFactory, QObject *parent = 0);
13
+
14
+ public slots:
15
+ void checkNext();
16
+
17
+ signals:
18
+ void commandReady(Command *command);
19
+
20
+ private:
21
+ void readLine();
22
+ void readDataBlock();
23
+ void processNext(const char *line);
24
+ void processArgument(const char *data);
25
+ void reset();
26
+ QIODevice *m_device;
27
+ QString m_commandName;
28
+ QStringList m_arguments;
29
+ int m_argumentsExpected;
30
+ int m_expectingDataSize;
31
+ CommandFactory *m_commandFactory;
32
+ };
33
+
@@ -0,0 +1,71 @@
1
+ #include "Connection.h"
2
+ #include "WebPage.h"
3
+ #include "CommandParser.h"
4
+ #include "CommandFactory.h"
5
+ #include "PageLoadingCommand.h"
6
+ #include "Command.h"
7
+
8
+ #include <QTcpSocket>
9
+
10
+ Connection::Connection(QTcpSocket *socket, WebPage *page, QObject *parent) :
11
+ QObject(parent) {
12
+ m_socket = socket;
13
+ m_page = page;
14
+ m_commandFactory = new CommandFactory(page, this);
15
+ m_commandParser = new CommandParser(socket, m_commandFactory, this);
16
+ m_pageSuccess = true;
17
+ m_commandWaiting = false;
18
+ connect(m_socket, SIGNAL(readyRead()), m_commandParser, SLOT(checkNext()));
19
+ connect(m_commandParser, SIGNAL(commandReady(Command *)), this, SLOT(commandReady(Command *)));
20
+ connect(m_page, SIGNAL(pageFinished(bool)), this, SLOT(pendingLoadFinished(bool)));
21
+ }
22
+
23
+ void Connection::commandReady(Command *command) {
24
+ m_queuedCommand = command;
25
+ if (m_page->isLoading())
26
+ m_commandWaiting = true;
27
+ else
28
+ startCommand();
29
+ }
30
+
31
+ void Connection::startCommand() {
32
+ m_commandWaiting = false;
33
+ if (m_pageSuccess) {
34
+ m_runningCommand = new PageLoadingCommand(m_queuedCommand, m_page, this);
35
+ connect(m_runningCommand, SIGNAL(finished(Response *)), this, SLOT(finishCommand(Response *)));
36
+ m_runningCommand->start();
37
+ } else {
38
+ writePageLoadFailure();
39
+ }
40
+ }
41
+
42
+ void Connection::pendingLoadFinished(bool success) {
43
+ m_pageSuccess = success;
44
+ if (m_commandWaiting)
45
+ startCommand();
46
+ }
47
+
48
+ void Connection::writePageLoadFailure() {
49
+ m_pageSuccess = true;
50
+ QString message = m_page->failureString();
51
+ writeResponse(new Response(false, message));
52
+ }
53
+
54
+ void Connection::finishCommand(Response *response) {
55
+ m_runningCommand->deleteLater();
56
+ writeResponse(response);
57
+ }
58
+
59
+ void Connection::writeResponse(Response *response) {
60
+ if (response->isSuccess())
61
+ m_socket->write("ok\n");
62
+ else
63
+ m_socket->write("failure\n");
64
+
65
+ QByteArray messageUtf8 = response->message();
66
+ QString messageLength = QString::number(messageUtf8.size()) + "\n";
67
+ m_socket->write(messageLength.toAscii());
68
+ m_socket->write(messageUtf8);
69
+ delete response;
70
+ }
71
+
data/src/Connection.h ADDED
@@ -0,0 +1,37 @@
1
+ #include <QObject>
2
+ #include <QStringList>
3
+
4
+ class QTcpSocket;
5
+ class WebPage;
6
+ class Command;
7
+ class Response;
8
+ class CommandParser;
9
+ class CommandFactory;
10
+ class PageLoadingCommand;
11
+
12
+ class Connection : public QObject {
13
+ Q_OBJECT
14
+
15
+ public:
16
+ Connection(QTcpSocket *socket, WebPage *page, QObject *parent = 0);
17
+
18
+ public slots:
19
+ void commandReady(Command *command);
20
+ void finishCommand(Response *response);
21
+ void pendingLoadFinished(bool success);
22
+
23
+ private:
24
+ void startCommand();
25
+ void writeResponse(Response *response);
26
+ void writePageLoadFailure();
27
+
28
+ QTcpSocket *m_socket;
29
+ Command *m_queuedCommand;
30
+ WebPage *m_page;
31
+ CommandParser *m_commandParser;
32
+ CommandFactory *m_commandFactory;
33
+ PageLoadingCommand *m_runningCommand;
34
+ bool m_pageSuccess;
35
+ bool m_commandWaiting;
36
+ };
37
+
@@ -0,0 +1,10 @@
1
+ #include "ConsoleMessages.h"
2
+ #include "WebPage.h"
3
+
4
+ ConsoleMessages::ConsoleMessages(WebPage *page, QStringList &arguments, QObject *parent) : Command(page, arguments, parent) {
5
+ }
6
+
7
+ void ConsoleMessages::start() {
8
+ emit finished(new Response(true, page()->consoleMessages()));
9
+ }
10
+
@@ -0,0 +1,12 @@
1
+ #include "Command.h"
2
+
3
+ class WebPage;
4
+
5
+ class ConsoleMessages : public Command {
6
+ Q_OBJECT
7
+
8
+ public:
9
+ ConsoleMessages(WebPage *page, QStringList &arguments, QObject *parent = 0);
10
+ virtual void start();
11
+ };
12
+