├── .gitignore
├── .rspec
├── .travis.yml
├── Appraisals
├── CONTRIBUTING.md
├── Gemfile
├── Gemfile.lock
├── LICENSE
├── NEWS.md
├── README.md
├── Rakefile
├── Vagrantfile
├── bin
└── Info.plist
├── capybara-webkit.gemspec
├── extconf.rb
├── gemfiles
├── 2.15.gemfile
└── master.gemfile
├── lib
├── capybara-webkit.rb
├── capybara
│ ├── webkit.rb
│ └── webkit
│ │ ├── browser.rb
│ │ ├── configuration.rb
│ │ ├── connection.rb
│ │ ├── cookie_jar.rb
│ │ ├── driver.rb
│ │ ├── errors.rb
│ │ ├── matchers.rb
│ │ ├── node.rb
│ │ ├── server.rb
│ │ └── version.rb
└── capybara_webkit_builder.rb
├── spec
├── browser_spec.rb
├── capybara_webkit_builder_spec.rb
├── configuration_spec.rb
├── connection_spec.rb
├── cookie_jar_spec.rb
├── driver_rendering_spec.rb
├── driver_resize_window_spec.rb
├── driver_spec.rb
├── errors_spec.rb
├── fixtures
│ ├── exit_text.rb
│ └── fake_server.sh
├── integration
│ └── session_spec.rb
├── node_spec.rb
├── selenium_compatibility_spec.rb
├── self_signed_ssl_cert.rb
├── server_spec.rb
├── spec_helper.rb
└── support
│ ├── app_runner.rb
│ ├── matchers
│ └── include_response.rb
│ └── output_writer.rb
├── src
├── AcceptAlert.cpp
├── AcceptAlert.h
├── AllowUrl.cpp
├── AllowUrl.h
├── Authenticate.cpp
├── Authenticate.h
├── BlacklistedRequestHandler.cpp
├── BlacklistedRequestHandler.h
├── BlockUrl.cpp
├── BlockUrl.h
├── Body.h
├── ClearCookies.cpp
├── ClearCookies.h
├── ClearPromptText.cpp
├── ClearPromptText.h
├── Command.cpp
├── Command.h
├── CommandFactory.cpp
├── CommandFactory.h
├── CommandParser.cpp
├── CommandParser.h
├── Connection.cpp
├── Connection.h
├── ConsoleMessages.cpp
├── ConsoleMessages.h
├── CurrentUrl.cpp
├── CurrentUrl.h
├── CustomHeadersRequestHandler.cpp
├── CustomHeadersRequestHandler.h
├── EnableLogging.cpp
├── EnableLogging.h
├── ErrorMessage.cpp
├── ErrorMessage.h
├── Evaluate.cpp
├── Evaluate.h
├── EvaluateAsync.cpp
├── EvaluateAsync.h
├── Execute.cpp
├── Execute.h
├── FindCss.cpp
├── FindCss.h
├── FindModal.cpp
├── FindModal.h
├── FindXpath.cpp
├── FindXpath.h
├── FrameFocus.cpp
├── FrameFocus.h
├── FrameTitle.cpp
├── FrameTitle.h
├── FrameUrl.cpp
├── FrameUrl.h
├── GetCookies.cpp
├── GetCookies.h
├── GetTimeout.cpp
├── GetTimeout.h
├── GetWindowHandle.cpp
├── GetWindowHandle.h
├── GetWindowHandles.cpp
├── GetWindowHandles.h
├── GoBack.cpp
├── GoBack.h
├── GoForward.cpp
├── GoForward.h
├── Header.cpp
├── Header.h
├── Headers.cpp
├── Headers.h
├── IgnoreDebugOutput.cpp
├── IgnoreDebugOutput.h
├── IgnoreSslErrors.cpp
├── IgnoreSslErrors.h
├── InvocationResult.cpp
├── InvocationResult.h
├── JavascriptAlertMessages.cpp
├── JavascriptAlertMessages.h
├── JavascriptCommand.cpp
├── JavascriptCommand.h
├── JavascriptConfirmMessages.cpp
├── JavascriptConfirmMessages.h
├── JavascriptInvocation.cpp
├── JavascriptInvocation.h
├── JavascriptPromptMessages.cpp
├── JavascriptPromptMessages.h
├── JsonSerializer.cpp
├── JsonSerializer.h
├── MissingContentHeaderRequestHandler.cpp
├── MissingContentHeaderRequestHandler.h
├── NetworkAccessManager.cpp
├── NetworkAccessManager.h
├── NetworkCookieJar.cpp
├── NetworkCookieJar.h
├── NetworkReplyProxy.cpp
├── NetworkReplyProxy.h
├── NetworkRequestFactory.cpp
├── NetworkRequestFactory.h
├── NoOpReply.cpp
├── NoOpReply.h
├── Node.cpp
├── Node.h
├── NullCommand.cpp
├── NullCommand.h
├── PageLoadingCommand.cpp
├── PageLoadingCommand.h
├── Refresh.cpp
├── Refresh.h
├── Render.cpp
├── Render.h
├── RequestHandler.cpp
├── RequestHandler.h
├── Reset.cpp
├── Reset.h
├── Response.cpp
├── Response.h
├── Server.cpp
├── Server.h
├── SetConfirmAction.cpp
├── SetConfirmAction.h
├── SetCookie.cpp
├── SetCookie.h
├── SetPromptAction.cpp
├── SetPromptAction.h
├── SetPromptText.cpp
├── SetPromptText.h
├── SetProxy.cpp
├── SetProxy.h
├── SetSkipImageLoading.cpp
├── SetSkipImageLoading.h
├── SetTimeout.cpp
├── SetTimeout.h
├── SetUnknownUrlMode.cpp
├── SetUnknownUrlMode.h
├── SetUrlBlacklist.cpp
├── SetUrlBlacklist.h
├── SocketCommand.cpp
├── SocketCommand.h
├── Status.cpp
├── Status.h
├── StdinNotifier.cpp
├── StdinNotifier.h
├── TimeoutCommand.cpp
├── TimeoutCommand.h
├── Title.cpp
├── Title.h
├── UnknownUrlHandler.cpp
├── UnknownUrlHandler.h
├── UnsupportedContentHandler.cpp
├── UnsupportedContentHandler.h
├── Version.cpp
├── Version.h
├── Visit.cpp
├── Visit.h
├── WebPage.cpp
├── WebPage.h
├── WebPageManager.cpp
├── WebPageManager.h
├── WindowClose.cpp
├── WindowClose.h
├── WindowCommand.cpp
├── WindowCommand.h
├── WindowFocus.cpp
├── WindowFocus.h
├── WindowMaximize.cpp
├── WindowMaximize.h
├── WindowOpen.cpp
├── WindowOpen.h
├── WindowResize.cpp
├── WindowResize.h
├── WindowSize.cpp
├── WindowSize.h
├── body.cpp
├── capybara.js
├── find_command.h
├── main.cpp
├── pointer.png
├── stable.h
├── webkit_server.pro
└── webkit_server.qrc
├── templates
├── Command.cpp
└── Command.h
├── test
├── testignoredebugoutput.cpp
└── testwebkitserver.pro
├── vagrant_setup.sh
└── webkit_server.pro
/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 | bin/webkit_server*
3 | test/testwebkitserver
4 | test/moc_predefs.h
5 | test/target_wrapper.sh
6 | *.swo
7 | *~
8 | *.o
9 | *.moc
10 | Makefile*
11 | qrc_*
12 | *.xcodeproj
13 | *.app
14 | moc_*.cpp
15 | .bundle
16 | pkg
17 | src/webkit_server
18 | src/webkit_server.exe
19 | .DS_Store
20 | tmp
21 | .rvmrc
22 | src/debug
23 | webkit_server.pro.user
24 | .vagrant
25 | *.gch
26 | .ruby-version
27 | .ruby-gemset
28 | .idea
29 | .qmake.stash
30 | gemfiles/*.lock
31 | /src/build/
32 | save_path_tmp/
--------------------------------------------------------------------------------
/.rspec:
--------------------------------------------------------------------------------
1 | --color
2 | --format documentation
3 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: required
2 | dist: trusty
3 | language: ruby
4 | rvm:
5 | - 2.2
6 | - 2.4
7 | - 2.5
8 | - jruby-9.1.16.0
9 | notifications:
10 | email: false
11 | script: xvfb-run bundle exec rake
12 | env:
13 | global:
14 | - BUNDLE_JOBS=4
15 | - NOKOGIRI_USE_SYSTEM_LIBRARIES=true
16 | - QMAKE=/usr/lib/x86_64-linux-gnu/qt5/bin/qmake
17 | - JAVA_OPTS=-Djava.security.egd=file:/dev/urandom
18 | addons:
19 | apt:
20 | sources:
21 | - ubuntu-sdk-team
22 | packages:
23 | - libqt5webkit5-dev
24 | - qtdeclarative5-dev
25 | matrix:
26 | include:
27 | - rvm: 1.9.3
28 | gemfile: gemfiles/2.15.gemfile
29 | env: QMAKE=/usr/lib/x86_64-linux-gnu/qt4/bin/qmake
30 | - rvm: jruby-19mode
31 | gemfile: gemfiles/2.15.gemfile
32 | - rvm: 2.3
33 | gemfile: gemfiles/2.15.gemfile
34 | - rvm: 2.5
35 | gemfile: gemfiles/master.gemfile
36 | allow_failures:
37 | - gemfile: gemfiles/master.gemfile
38 | gemfile:
39 | - Gemfile
40 | before_install:
41 | - gem install bundler
42 | install: bundle
43 |
--------------------------------------------------------------------------------
/Appraisals:
--------------------------------------------------------------------------------
1 | appraise "2.15" do
2 | gem "capybara", "~> 2.15.0"
3 | gem 'addressable', '< 2.5.0', :platforms=>[:ruby_19, :jruby_19] # 2.5 requires public_suffix which requires ruby 2.0
4 | gem 'nokogiri', '< 1.7.0', :platforms=>[:ruby_19, :jruby_19] # 1.7.0 requires ruby 2.1+
5 | end
6 |
7 | appraise "master" do
8 | gem "capybara", github: "jnicklas/capybara"
9 | gem "puma"
10 | end
11 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | We love pull requests from everyone. We expect users to follow our
2 | [code of conduct] while submitting code or comments.
3 |
4 | [code of conduct]: https://thoughtbot.com/open-source-code-of-conduct
5 |
6 | ## Dependencies
7 |
8 | Some of the tests depend on the `identify` command that comes with Imagemagick.
9 | Imagemagick can be installed via [homebrew](http://mxcl.github.com/homebrew/) on
10 | Mac OS X:
11 |
12 | brew install imagemagick
13 |
14 | If you prefer, you can use a [Vagrant](http://www.vagrantup.com/) virtual
15 | machine. The Vagrantfile in the capybara-webkit repository will get you up and
16 | running with all the development dependencies:
17 |
18 | gem install vagrant
19 | vagrant up
20 | vagrant ssh
21 | cd /vagrant
22 | rake
23 |
24 | ## Contributing
25 |
26 | 1. Fork the repo.
27 |
28 | 2. Run the tests. We only take pull requests with passing tests, and it's great
29 | to know that you have a clean slate: `bundle && bundle exec rake`
30 |
31 | 3. Add a test for your change. Only refactoring and documentation changes
32 | require no new tests. If you are adding functionality or fixing a bug, we need
33 | a test!
34 |
35 | 4. Make the test pass.
36 |
37 | 5. Push to your fork and submit a pull request.
38 |
39 |
40 | At this point you're waiting on us. We like to at least comment on, if not
41 | accept, pull requests within three business days (and, typically, one business
42 | day). We may suggest some changes or improvements or alternatives.
43 |
44 | Some things that will increase the chance that your pull request is accepted,
45 | taken straight from the Ruby on Rails guide:
46 |
47 | * Use Rails idioms and helpers
48 | * Include tests that fail without your code, and pass with it
49 | * Update the documentation, the surrounding one, examples elsewhere, guides,
50 | whatever is affected by your contribution
51 |
52 | Syntax:
53 |
54 | * Two spaces, no tabs.
55 | * No trailing whitespace. Blank lines should not have any space.
56 | * Prefer &&/|| over and/or.
57 | * MyClass.my_method(my_arg) not my_method( my_arg ) or my_method my_arg.
58 | * a = b and not a=b.
59 | * Follow the conventions you see used in the source already.
60 |
61 | And in case we didn't emphasize it enough: we love tests!
62 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 |
3 | gemspec
4 | gem "puma"
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | PATH
2 | remote: .
3 | specs:
4 | capybara-webkit (1.15.0)
5 | capybara (>= 2.3, < 4.0)
6 | json
7 |
8 | GEM
9 | remote: https://rubygems.org/
10 | specs:
11 | addressable (2.5.2)
12 | public_suffix (>= 2.0.2, < 4.0)
13 | appraisal (2.2.0)
14 | bundler
15 | rake
16 | thor (>= 0.14.0)
17 | capybara (3.0.1)
18 | addressable
19 | mini_mime (>= 0.1.3)
20 | nokogiri (~> 1.8)
21 | rack (>= 1.6.0)
22 | rack-test (>= 0.6.3)
23 | xpath (~> 3.0)
24 | diff-lcs (1.3)
25 | ffi (1.9.18)
26 | ffi (1.9.18-java)
27 | json (2.1.0)
28 | json (2.1.0-java)
29 | launchy (2.4.3)
30 | addressable (~> 2.3)
31 | launchy (2.4.3-java)
32 | addressable (~> 2.3)
33 | spoon (~> 0.0.1)
34 | mini_magick (4.8.0)
35 | mini_mime (1.0.0)
36 | mini_portile2 (2.3.0)
37 | mustermann (1.0.1)
38 | nokogiri (1.8.2)
39 | mini_portile2 (~> 2.3.0)
40 | nokogiri (1.8.2-java)
41 | nokogiri (1.8.2-x86-mingw32)
42 | mini_portile2 (~> 2.3.0)
43 | public_suffix (3.0.0)
44 | puma (3.11.3)
45 | puma (3.11.3-java)
46 | rack (2.0.3)
47 | rack-protection (2.0.0)
48 | rack
49 | rack-test (1.0.0)
50 | rack (>= 1.0, < 3)
51 | rake (11.3.0)
52 | rspec (3.7.0)
53 | rspec-core (~> 3.7.0)
54 | rspec-expectations (~> 3.7.0)
55 | rspec-mocks (~> 3.7.0)
56 | rspec-core (3.7.0)
57 | rspec-support (~> 3.7.0)
58 | rspec-expectations (3.7.0)
59 | diff-lcs (>= 1.2.0, < 2.0)
60 | rspec-support (~> 3.7.0)
61 | rspec-mocks (3.7.0)
62 | diff-lcs (>= 1.2.0, < 2.0)
63 | rspec-support (~> 3.7.0)
64 | rspec-support (3.7.0)
65 | sinatra (2.0.0)
66 | mustermann (~> 1.0)
67 | rack (~> 2.0)
68 | rack-protection (= 2.0.0)
69 | tilt (~> 2.0)
70 | spoon (0.0.6)
71 | ffi
72 | thor (0.20.0)
73 | tilt (2.0.8)
74 | xpath (3.0.0)
75 | nokogiri (~> 1.8)
76 |
77 | PLATFORMS
78 | java
79 | ruby
80 | x86-mingw32
81 |
82 | DEPENDENCIES
83 | appraisal
84 | capybara-webkit!
85 | launchy
86 | mini_magick
87 | puma
88 | rake (< 12.0.0)
89 | rspec (~> 3.5)
90 | sinatra
91 |
92 | BUNDLED WITH
93 | 1.16.1
94 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2010-2015 thoughtbot, inc.
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require 'bundler'
2 | require 'rspec/core/rake_task'
3 | require_relative './lib/capybara_webkit_builder'
4 | require 'appraisal'
5 |
6 | namespace :bundler do
7 | Bundler::GemHelper.install_tasks
8 | end
9 |
10 | desc "Generate a Makefile using qmake"
11 | file 'Makefile' do
12 | CapybaraWebkitBuilder.makefile('CONFIG+=test') or exit(1)
13 | end
14 |
15 | desc "Regenerate dependencies using qmake"
16 | task :qmake => 'Makefile' do
17 | CapybaraWebkitBuilder.qmake or exit(1)
18 | end
19 |
20 | desc "Build the webkit server"
21 | task :build => :qmake do
22 | CapybaraWebkitBuilder.build or exit(1)
23 | end
24 |
25 | desc "Run QtTest unit tests for webkit server"
26 | task :check => :build do
27 | sh("make check") or exit(1)
28 | end
29 |
30 | file 'bin/webkit_server' => :build
31 |
32 | RSpec::Core::RakeTask.new do |t|
33 | t.pattern = "spec/**/*_spec.rb"
34 | t.rspec_opts = "--format progress"
35 | end
36 |
37 | task :spec => :build
38 |
39 | desc "Default: build and run all specs"
40 | task :default => [:check, :spec]
41 |
42 | desc "Generate a new command called NAME"
43 | task :generate_command do
44 | name = ENV['NAME'] or raise "Provide a name with NAME="
45 |
46 | %w(h cpp).each do |extension|
47 | File.open("templates/Command.#{extension}", "r") do |source_file|
48 | contents = source_file.read
49 | contents.gsub!("NAME", name)
50 | File.open("src/#{name}.#{extension}", "w") do |target_file|
51 | target_file.write(contents)
52 | end
53 | end
54 | end
55 |
56 | Dir.glob("src/*.pro").each do |project_file_name|
57 | project = IO.read(project_file_name)
58 | project.gsub!(/^(HEADERS = .*)/, "\\1\n #{name}.h \\")
59 | project.gsub!(/^(SOURCES = .*)/, "\\1\n #{name}.cpp \\")
60 | File.open(project_file_name, "w") { |file| file.write(project) }
61 | end
62 |
63 | File.open("src/find_command.h", "a") do |file|
64 | file.write("CHECK_COMMAND(#{name})\n")
65 | end
66 |
67 | command_factory_file_name = "src/CommandFactory.cpp"
68 | command_factory = IO.read(command_factory_file_name)
69 | command_factory.sub!(/^$/, "#include \"#{name}.h\"\n")
70 | File.open(command_factory_file_name, "w") { |file| file.write(command_factory) }
71 | end
72 |
--------------------------------------------------------------------------------
/Vagrantfile:
--------------------------------------------------------------------------------
1 | # -*- mode: ruby -*-
2 | # vi: set ft=ruby :
3 |
4 | Vagrant::Config.run do |config|
5 | config.vm.box = "precise32"
6 | config.vm.provision :shell, :path => 'vagrant_setup.sh'
7 | end
8 |
--------------------------------------------------------------------------------
/bin/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleIconFile
6 |
7 | CFBundlePackageType
8 | APPL
9 | CFBundleGetInfoString
10 | Created by Qt/QMake
11 | CFBundleSignature
12 | ????
13 | CFBundleExecutable
14 | webkit_server
15 | CFBundleIdentifier
16 | com.yourcompany.webkit_server
17 | NOTE
18 | This file was generated by Qt/QMake.
19 | LSBackgroundOnly
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/capybara-webkit.gemspec:
--------------------------------------------------------------------------------
1 | $:.push File.expand_path("../lib", __FILE__)
2 | require "capybara/webkit/version"
3 |
4 | Gem::Specification.new do |s|
5 | s.name = "capybara-webkit"
6 | s.version = Capybara::Driver::Webkit::VERSION.dup
7 | s.authors = ["thoughtbot", "Joe Ferris", "Matt Horan", "Matt Mongeau",
8 | "Mike Burns", "Jason Morrison"]
9 | s.email = "support@thoughtbot.com"
10 | s.homepage = "http://github.com/thoughtbot/capybara-webkit"
11 | s.summary = "Headless Webkit driver for Capybara"
12 | s.license = 'MIT'
13 |
14 | s.files = `git ls-files`.split("\n")
15 | s.test_files = `git ls-files -- {spec,features}/*`.split("\n")
16 | s.require_path = "lib"
17 |
18 | s.extensions = "extconf.rb"
19 |
20 | s.required_ruby_version = ">= 1.9.0"
21 |
22 | s.requirements << "Qt >= 4.8"
23 |
24 | s.add_runtime_dependency("capybara", ">= 2.3", "< 4.0")
25 | s.add_runtime_dependency("json")
26 |
27 | s.add_development_dependency("rspec", "~> 3.5")
28 | # Sinatra is used by Capybara's TestApp
29 | s.add_development_dependency("sinatra")
30 | s.add_development_dependency("mini_magick")
31 | s.add_development_dependency("rake", "< 12.0.0")
32 | s.add_development_dependency("appraisal")
33 | s.add_development_dependency("launchy")
34 | end
35 |
36 |
--------------------------------------------------------------------------------
/extconf.rb:
--------------------------------------------------------------------------------
1 | require "mkmf"
2 |
3 | $CPPFLAGS = ""
4 |
5 | dir_config("gl")
6 | dir_config("zlib")
7 |
8 | include_path = $CPPFLAGS.gsub("-I", "").strip
9 | libs = $LIBPATH.map { |path| "-L#{path}"}.join(" ").strip
10 |
11 | unless include_path.empty?
12 | ENV["CAPYBARA_WEBKIT_INCLUDE_PATH"] = include_path
13 | end
14 |
15 | unless libs.empty?
16 | ENV["CAPYBARA_WEBKIT_LIBS"] = libs
17 | end
18 |
19 | require File.join(File.expand_path(File.dirname(__FILE__)), "lib", "capybara_webkit_builder")
20 | CapybaraWebkitBuilder.build_all
21 |
--------------------------------------------------------------------------------
/gemfiles/2.15.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source "https://rubygems.org"
4 |
5 | gem "json", "< 2.0", platforms: [:ruby_19, :jruby_19]
6 | gem "capybara", "~> 2.15.0"
7 | gem "addressable", "< 2.5.0", platforms: [:ruby_19, :jruby_19]
8 | gem "nokogiri", "< 1.7.0", platforms: [:ruby_19, :jruby_19]
9 |
10 | gemspec path: "../"
11 |
--------------------------------------------------------------------------------
/gemfiles/master.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source "https://rubygems.org"
4 |
5 | gem "capybara", github: "teamcapybara/capybara"
6 | gem "puma"
7 |
8 | gemspec path: "../"
9 |
--------------------------------------------------------------------------------
/lib/capybara-webkit.rb:
--------------------------------------------------------------------------------
1 | require "capybara/webkit"
2 |
--------------------------------------------------------------------------------
/lib/capybara/webkit.rb:
--------------------------------------------------------------------------------
1 | require "capybara"
2 |
3 | module Capybara
4 | module Webkit
5 | def self.configure(&block)
6 | Capybara::Webkit::Configuration.modify(&block)
7 | end
8 | end
9 | end
10 |
11 | require "capybara/webkit/driver"
12 | require "capybara/webkit/configuration"
13 |
14 | Capybara.register_driver :webkit do |app|
15 | Capybara::Webkit::Driver.new(app, Capybara::Webkit::Configuration.to_hash)
16 | end
17 |
18 | Capybara.register_driver :webkit_debug do |app|
19 | warn "[DEPRECATION] The webkit_debug driver is deprecated. " \
20 | "Please use Capybara::Webkit.configure instead:\n\n" \
21 | " Capybara::Webkit.configure do |config|\n" \
22 | " config.debug = true\n" \
23 | " end\n\n" \
24 | "This option is global and can be configured once" \
25 | " (not in a `before` or `setup` block)."
26 |
27 | Capybara::Webkit::Driver.new(
28 | app,
29 | Capybara::Webkit::Configuration.to_hash.merge(debug: true)
30 | )
31 | end
32 |
--------------------------------------------------------------------------------
/lib/capybara/webkit/configuration.rb:
--------------------------------------------------------------------------------
1 | module Capybara
2 | module Webkit
3 | class Configuration
4 | class << self
5 | private
6 |
7 | def instance
8 | @instance ||= new
9 | end
10 | end
11 |
12 | def self.to_hash
13 | instance.freeze.to_hash
14 | end
15 |
16 | def self.modify
17 | if instance.frozen?
18 | raise "All configuration must take place before the driver starts"
19 | else
20 | yield instance
21 | end
22 | end
23 |
24 | attr_accessor :allowed_urls
25 | attr_writer :block_unknown_urls
26 | attr_accessor :blocked_urls
27 | attr_accessor :debug
28 | attr_writer :ignore_ssl_errors
29 | attr_accessor :proxy
30 | attr_accessor :stderr
31 | attr_accessor :timeout
32 | attr_writer :skip_image_loading
33 | attr_accessor :raise_javascript_errors
34 |
35 | def initialize
36 | @allowed_urls = []
37 | @blocked_urls = []
38 | @block_unknown_urls = false
39 | @debug = false
40 | @ignore_ssl_errors = false
41 | @proxy = nil
42 | @skip_image_loading = false
43 | @stderr = $stderr
44 | @timeout = -1
45 | @raise_javascript_errors = false
46 | end
47 |
48 | def allow_url(url)
49 | @allowed_urls << url
50 | end
51 |
52 | def block_url(url)
53 | @blocked_urls << url
54 | end
55 |
56 | def block_unknown_urls
57 | @block_unknown_urls = true
58 | end
59 |
60 | def block_unknown_urls?
61 | @block_unknown_urls
62 | end
63 |
64 | def allow_unknown_urls
65 | allow_url("*")
66 | end
67 |
68 | def ignore_ssl_errors
69 | @ignore_ssl_errors = true
70 | end
71 |
72 | def ignore_ssl_errors?
73 | @ignore_ssl_errors
74 | end
75 |
76 | def skip_image_loading
77 | @skip_image_loading = true
78 | end
79 |
80 | def skip_image_loading?
81 | @skip_image_loading
82 | end
83 |
84 | def use_proxy(proxy)
85 | @proxy = proxy
86 | end
87 |
88 | def to_hash
89 | {
90 | allowed_urls: allowed_urls,
91 | block_unknown_urls: block_unknown_urls?,
92 | blocked_urls: blocked_urls,
93 | debug: debug,
94 | ignore_ssl_errors: ignore_ssl_errors?,
95 | proxy: proxy,
96 | skip_image_loading: skip_image_loading?,
97 | stderr: stderr,
98 | timeout: timeout,
99 | raise_javascript_errors: raise_javascript_errors,
100 | }
101 | end
102 | end
103 | end
104 | end
105 |
--------------------------------------------------------------------------------
/lib/capybara/webkit/connection.rb:
--------------------------------------------------------------------------------
1 | require 'socket'
2 | require 'timeout'
3 | require 'thread'
4 |
5 | module Capybara::Webkit
6 | class Connection
7 | def initialize(options = {})
8 | @socket = nil
9 | if options.has_key?(:socket_class)
10 | warn '[DEPRECATION] The Capybara::Webkit::Connection `socket_class` ' \
11 | 'option is deprecated without replacement.'
12 | @socket_class = options[:socket_class]
13 | else
14 | @socket_class = TCPSocket
15 | end
16 | @server = options[:server]
17 | start_server
18 | connect
19 | end
20 |
21 | def puts(string)
22 | @socket.puts string
23 | end
24 |
25 | def print(string)
26 | @socket.print string
27 | end
28 |
29 | def gets
30 | response = ""
31 | until response.match(/\n/) do
32 | response += read(1)
33 | end
34 | response
35 | end
36 |
37 | def read(length)
38 | response = ""
39 | begin
40 | while response.length < length do
41 | response += @socket.read_nonblock(length - response.length)
42 | end
43 | rescue IO::WaitReadable
44 | Thread.new { IO.select([@socket]) }.join
45 | retry
46 | end
47 | response
48 | end
49 |
50 | def restart
51 | @socket = nil
52 | start_server
53 | connect
54 | end
55 |
56 | def port
57 | @server.port
58 | end
59 |
60 | def pid
61 | @server.pid
62 | end
63 |
64 | private
65 |
66 | def start_server
67 | @server.start
68 | end
69 |
70 | def connect
71 | Timeout.timeout(5) do
72 | while @socket.nil?
73 | attempt_connect
74 | end
75 | end
76 | end
77 |
78 | def attempt_connect
79 | @socket = @socket_class.open("127.0.0.1", port)
80 | if defined?(Socket::TCP_NODELAY)
81 | @socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, true)
82 | end
83 | rescue Errno::ECONNREFUSED
84 | end
85 | end
86 | end
87 |
--------------------------------------------------------------------------------
/lib/capybara/webkit/cookie_jar.rb:
--------------------------------------------------------------------------------
1 | require 'webrick/cookie'
2 |
3 | # A simple cookie jar implementation.
4 | # Does not take special cookie attributes
5 | # into account like expire, max-age, httponly, secure
6 | class Capybara::Webkit::CookieJar
7 | attr_reader :browser
8 |
9 | def initialize(browser)
10 | @browser = browser
11 | end
12 |
13 | def [](*args)
14 | cookie = find(*args)
15 | cookie && cookie.value
16 | end
17 |
18 | def find(name, domain = nil, path = "/")
19 | # we are sorting by path size because more specific paths take
20 | # precendence
21 | cookies.sort_by { |c| -c.path.size }.find { |c|
22 | c.name.downcase == name.downcase &&
23 | (!domain || valid_domain?(c, domain)) &&
24 | (!path || valid_path?(c, path))
25 | }
26 | end
27 |
28 | protected
29 |
30 | def valid_domain?(cookie, domain)
31 | ends_with?(("." + domain).downcase,
32 | normalize_domain(cookie.domain).downcase)
33 | end
34 |
35 | def normalize_domain(domain)
36 | domain = "." + domain unless domain[0,1] == "."
37 | domain
38 | end
39 |
40 | def valid_path?(cookie, path)
41 | starts_with?(path, cookie.path)
42 | end
43 |
44 | def ends_with?(str, suffix)
45 | str[-suffix.size..-1] == suffix
46 | end
47 |
48 | def starts_with?(str, prefix)
49 | str[0, prefix.size] == prefix
50 | end
51 |
52 | def cookies
53 | browser.get_cookies.map { |c| WEBrick::Cookie.parse_set_cookie(c) }
54 | end
55 | end
56 |
--------------------------------------------------------------------------------
/lib/capybara/webkit/errors.rb:
--------------------------------------------------------------------------------
1 | module Capybara::Webkit
2 | class InvalidResponseError < StandardError
3 | end
4 |
5 | class NoResponseError < StandardError
6 | end
7 |
8 | class NodeNotAttachedError < Capybara::ElementNotFound
9 | end
10 |
11 | class ClickFailed < StandardError
12 | end
13 |
14 | class TimeoutError < Timeout::Error
15 | end
16 |
17 | class NoSuchWindowError < StandardError
18 | end
19 |
20 | class ConnectionError < StandardError
21 | end
22 |
23 | class ModalNotFound < StandardError
24 | end
25 |
26 | class CrashError < StandardError
27 | end
28 |
29 | class JsonError
30 | def initialize(response)
31 | error = JSON.parse response
32 |
33 | @class_name = error['class']
34 | @message = error['message']
35 | end
36 |
37 | def exception
38 | error_class.new @message
39 | end
40 |
41 | private
42 |
43 | def error_class
44 | Capybara::Webkit.const_get @class_name
45 | end
46 | end
47 |
48 | class JavaScriptError < StandardError
49 | def initialize(errors)
50 | @javascript_errors = errors
51 | super(errors.join(","))
52 | end
53 |
54 | attr_reader :javascript_errors
55 | end
56 | end
57 |
--------------------------------------------------------------------------------
/lib/capybara/webkit/matchers.rb:
--------------------------------------------------------------------------------
1 | module Capybara
2 | module Webkit
3 | module RspecMatchers
4 | RSpec::Matchers.define :have_errors do |expected|
5 | match do |actual|
6 | actual = resolve(actual)
7 | actual.error_messages.any?
8 | end
9 |
10 | #RSpec 2 compatability
11 | send(respond_to?(:failure_message) ? :failure_message : :failure_message_for_should) do |_actual|
12 | "Expected Javascript errors, but there were none."
13 | end
14 |
15 | #RSpec 2 compatability
16 | send(respond_to?(:failure_message_when_negated) ? :failure_message_when_negated : :failure_message_for_should_not) do |actual|
17 | actual = resolve(actual)
18 | "Expected no Javascript errors, got:\n#{error_messages_for(actual)}"
19 | end
20 |
21 | def error_messages_for(obj)
22 | obj.error_messages.map do |m|
23 | " - #{m[:message]}"
24 | end.join("\n")
25 | end
26 |
27 | def resolve(actual)
28 | if actual.respond_to? :page
29 | actual.page.driver
30 | elsif actual.respond_to? :driver
31 | actual.driver
32 | else
33 | actual
34 | end
35 | end
36 | end
37 | end
38 | end
39 | end
40 |
--------------------------------------------------------------------------------
/lib/capybara/webkit/server.rb:
--------------------------------------------------------------------------------
1 | require "open3"
2 | require "thread"
3 |
4 | module Capybara
5 | module Webkit
6 | class Server
7 | SERVER_PATH = File.expand_path("../../../../bin/webkit_server", __FILE__)
8 | WEBKIT_SERVER_START_TIMEOUT = 15
9 |
10 | attr_reader :port, :pid
11 |
12 | def initialize(options = {})
13 | if options.has_key?(:stderr)
14 | @output_target = options[:stderr]
15 | elsif options.has_key?(:stdout)
16 | warn "[DEPRECATION] The Capybara::Webkit::Connection `stdout` " \
17 | "option is deprecated. Please use `stderr` instead."
18 | @output_target = options[:stdout]
19 | else
20 | @output_target = $stderr
21 | end
22 | end
23 |
24 | def start
25 | open_pipe
26 | discover_port
27 | discover_pid
28 | forward_output_in_background_thread
29 | register_shutdown_hook
30 | end
31 |
32 | private
33 |
34 | def open_pipe
35 | if IO.respond_to?(:popen4)
36 | @pid,
37 | @pipe_stdin,
38 | @pipe_stdout,
39 | @pipe_stderr = IO.popen4(SERVER_PATH)
40 | else
41 | @pipe_stdin,
42 | @pipe_stdout,
43 | @pipe_stderr,
44 | @wait_thr = Open3.popen3(SERVER_PATH)
45 | end
46 | end
47 |
48 | def discover_port
49 | if IO.select([@pipe_stdout], nil, nil, WEBKIT_SERVER_START_TIMEOUT)
50 | @port = parse_port(@pipe_stdout.first)
51 | else
52 | raise(
53 | ConnectionError,
54 | "#{SERVER_PATH} failed to start after " \
55 | "#{WEBKIT_SERVER_START_TIMEOUT} seconds.",
56 | )
57 | end
58 | end
59 |
60 | def parse_port(line)
61 | if match = line.to_s.match(/listening on port: (\d+)/)
62 | match[1].to_i
63 | else
64 | raise ConnectionError, "#{SERVER_PATH} failed to start."
65 | end
66 | end
67 |
68 | def discover_pid
69 | @pid ||= @wait_thr[:pid]
70 | end
71 |
72 | def forward_output_in_background_thread
73 | Thread.new do
74 | Thread.current.abort_on_exception = true
75 | IO.copy_stream(@pipe_stderr, @output_target) if @output_target
76 | end
77 | end
78 |
79 | def register_shutdown_hook
80 | @owner_pid = Process.pid
81 | at_exit do
82 | if Process.pid == @owner_pid
83 | kill_process
84 | end
85 | end
86 | end
87 |
88 | def kill_process
89 | if @pid
90 | if RUBY_PLATFORM =~ /mingw32/
91 | Process.kill(9, @pid)
92 | else
93 | Process.kill("INT", @pid)
94 | end
95 | Process.wait(@pid)
96 | @wait_thr.join if @wait_thr
97 | end
98 | rescue Errno::ESRCH, Errno::ECHILD
99 | # This just means that the webkit_server process has already ended
100 | ensure
101 | @pid = @wait_thr = nil
102 | end
103 | end
104 | end
105 | end
106 |
--------------------------------------------------------------------------------
/lib/capybara/webkit/version.rb:
--------------------------------------------------------------------------------
1 | module Capybara
2 | module Driver
3 | class Webkit
4 | VERSION = "1.15.1".freeze
5 | end
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/lib/capybara_webkit_builder.rb:
--------------------------------------------------------------------------------
1 | require 'etc'
2 | require "fileutils"
3 | require "rbconfig"
4 | require "shellwords"
5 |
6 | module CapybaraWebkitBuilder
7 | extend self
8 |
9 | SUCCESS_STATUS = 0
10 | COMMAND_NOT_FOUND_STATUS = 127
11 |
12 | def make_bin
13 | ENV['MAKE'] || 'make'
14 | end
15 |
16 | def qmake_bin
17 | ENV['QMAKE'] || default_qmake_binary
18 | end
19 |
20 | def default_qmake_binary
21 | case RbConfig::CONFIG['host_os']
22 | when /freebsd/
23 | "qmake-qt4"
24 | when /openbsd/
25 | "qmake-qt5"
26 | else
27 | "qmake"
28 | end
29 | end
30 |
31 | def sh(command)
32 | system(command)
33 | success = $?.exitstatus == SUCCESS_STATUS
34 | if $?.exitstatus == COMMAND_NOT_FOUND_STATUS
35 | puts "Command '#{command}' not available"
36 | elsif !success
37 | puts "Command '#{command}' failed"
38 | end
39 | success
40 | end
41 |
42 | def makefile(*configs)
43 | configs += default_configs
44 | configs = configs.map { |config| config.shellescape}.join(" ")
45 | sh("#{qmake_bin} #{configs}")
46 | end
47 |
48 | def qmake
49 | make "qmake"
50 | end
51 |
52 | def path_to_binary
53 | if Gem.win_platform?
54 | "src/debug/webkit_server.exe"
55 | else
56 | "src/webkit_server"
57 | end
58 | end
59 |
60 | def build
61 | make or return false
62 |
63 | FileUtils.mkdir("bin") unless File.directory?("bin")
64 | FileUtils.cp(path_to_binary, "bin", :preserve => true)
65 | end
66 |
67 | def clean
68 | File.open("Makefile", "w") do |file|
69 | file.print "all:\n\t@echo ok\ninstall:\n\t@echo ok"
70 | end
71 | end
72 |
73 | def default_configs
74 | configs = []
75 | libpath = ENV["CAPYBARA_WEBKIT_LIBS"]
76 | cppflags = ENV["CAPYBARA_WEBKIT_INCLUDE_PATH"]
77 | if libpath
78 | configs << "LIBS += #{libpath}"
79 | end
80 | if cppflags
81 | configs << "INCLUDEPATH += #{cppflags}"
82 | end
83 | configs
84 | end
85 |
86 | def build_all
87 | makefile &&
88 | qmake &&
89 | build &&
90 | clean
91 | end
92 |
93 | def make(target = "")
94 | job_count = Etc.respond_to?(:nprocessors) ? Etc.nprocessors : 1
95 | env_hide('CDPATH') { sh("#{make_bin} #{target} --jobs=#{job_count}") }
96 | end
97 |
98 | def env_hide(name)
99 | @stored_env ||= {}
100 | @stored_env[name] = ENV.delete(name)
101 |
102 | yield
103 | ensure
104 | ENV[name] = @stored_env[name]
105 | end
106 | end
107 |
--------------------------------------------------------------------------------
/spec/browser_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'self_signed_ssl_cert'
3 | require 'stringio'
4 | require 'capybara/webkit/driver'
5 | require 'socket'
6 | require 'base64'
7 |
8 | describe Capybara::Webkit::Browser do
9 |
10 | let(:server) { Capybara::Webkit::Server.new }
11 | let(:connection) { Capybara::Webkit::Connection.new(server: server) }
12 | let(:browser) { Capybara::Webkit::Browser.new(connection) }
13 |
14 | describe "forking", skip_on_windows: true, skip_on_jruby: true do
15 | it "only shuts down the server from the main process" do
16 | browser.reset!
17 | pid = fork {}
18 | Process.wait(pid)
19 | expect { browser.reset! }.not_to raise_error
20 | end
21 | end
22 |
23 | it "doesn't try to read an empty response" do
24 | connection = double("connection", puts: nil, print: nil)
25 | allow(connection).to receive(:gets).and_return("ok\n", "0\n")
26 | allow(connection).to receive(:read).and_raise(StandardError.new("tried to read empty response"))
27 |
28 | browser = Capybara::Webkit::Browser.new(connection)
29 |
30 | expect { browser.visit("/") }.not_to raise_error
31 | end
32 |
33 | describe '#command' do
34 | context 'non-ok response' do
35 | it 'raises an error of given class' do
36 | error_json = '{"class": "ClickFailed"}'
37 |
38 | expect(connection).to receive(:gets).ordered.and_return 'error'
39 | expect(connection).to receive(:gets).ordered.and_return error_json.bytesize
40 | allow(connection).to receive(:read).and_return(error_json)
41 |
42 | expect { browser.command 'blah', 'meh' }.to raise_error(Capybara::Webkit::ClickFailed)
43 | end
44 | end
45 | end
46 |
47 | describe "#reset!" do
48 | it "resets to the default state" do
49 | connection = double("connection", puts: nil, print: nil)
50 | allow(connection).to receive(:gets).and_return("ok\n", "{}\n")
51 |
52 | browser = Capybara::Webkit::Browser.new(connection)
53 | browser.set_raise_javascript_errors(true)
54 |
55 | expect(browser.raise_javascript_errors?).to be true
56 |
57 | browser.reset!
58 |
59 | expect(browser.raise_javascript_errors?).to be false
60 | end
61 | end
62 | end
63 |
--------------------------------------------------------------------------------
/spec/capybara_webkit_builder_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'capybara_webkit_builder'
3 |
4 | describe CapybaraWebkitBuilder do
5 | let(:builder) { CapybaraWebkitBuilder }
6 |
7 | it "will use the env variable for #make_bin" do
8 | with_env_vars("MAKE" => "fake_make") do
9 | expect(builder.make_bin).to eq "fake_make"
10 | end
11 | end
12 |
13 | it "will use the env variable for #qmake_bin" do
14 | with_env_vars("QMAKE" => "fake_qmake") do
15 | expect(builder.qmake_bin).to eq "fake_qmake"
16 | end
17 | end
18 |
19 | it "defaults the #make_bin" do
20 | with_env_vars("MAKE_BIN" => nil) do
21 | expect(builder.make_bin).to eq 'make'
22 | end
23 | end
24 |
25 | it "defaults the #qmake_bin" do
26 | with_env_vars("QMAKE" => nil) do
27 | expect(builder.qmake_bin).to eq 'qmake'
28 | end
29 | end
30 | end
31 |
32 |
--------------------------------------------------------------------------------
/spec/configuration_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 |
3 | describe Capybara::Webkit::Configuration do
4 | it "returns a hash and then prevents future modification" do
5 | Capybara::Webkit.configure do |config|
6 | config.debug = true
7 | end
8 |
9 | result = Capybara::Webkit::Configuration.to_hash
10 |
11 | expect(result).to include(debug: true)
12 | expect { Capybara::Webkit.configure {} }.to raise_error(
13 | "All configuration must take place before the driver starts"
14 | )
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/spec/connection_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'capybara/webkit/connection'
3 |
4 | describe Capybara::Webkit::Connection do
5 | it "sets appropriate options on its socket" do
6 | socket = double("socket")
7 | server = double(:Server, start: nil, port: 123)
8 | allow(TCPSocket).to receive(:open).and_return(socket)
9 | if defined?(Socket::TCP_NODELAY)
10 | expect(socket).to receive(:setsockopt).
11 | with(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, true)
12 | else
13 | expect(socket).not_to receive(:setsockopt)
14 | end
15 |
16 | Capybara::Webkit::Connection.new(server: server)
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/spec/cookie_jar_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'capybara/webkit/cookie_jar'
3 |
4 | describe Capybara::Webkit::CookieJar do
5 | let(:browser) {
6 | browser = double("Browser")
7 | allow(browser).to receive(:get_cookies) { [
8 | "cookie1=1; domain=.example.org; path=/",
9 | "cookie1=2; domain=.example.org; path=/dir1/",
10 | "cookie1=3; domain=.facebook.com; path=/",
11 | "cookie2=4; domain=.sub1.example.org; path=/",
12 | ] }
13 | browser
14 | }
15 |
16 | subject { Capybara::Webkit::CookieJar.new(browser) }
17 |
18 | describe "#find" do
19 | it "returns a cookie object" do
20 | expect(subject.find("cookie1", "www.facebook.com").domain).to eq ".facebook.com"
21 | end
22 |
23 | it "returns the right cookie for every given domain/path" do
24 | expect(subject.find("cookie1", "example.org").value).to eq "1"
25 | expect(subject.find("cookie1", "www.facebook.com").value).to eq "3"
26 | expect(subject.find("cookie2", "sub1.example.org").value).to eq "4"
27 | end
28 |
29 | it "does not return a cookie from other domain" do
30 | expect(subject.find("cookie2", "www.example.org")).to eq nil
31 | end
32 |
33 | it "respects path precedence rules" do
34 | expect(subject.find("cookie1", "www.example.org").value).to eq "1"
35 | expect(subject.find("cookie1", "www.example.org", "/dir1/123").value).to eq "2"
36 | end
37 | end
38 |
39 | describe "#[]" do
40 | it "returns the first matching cookie's value" do
41 | expect(subject["cookie1", "example.org"]).to eq "1"
42 | end
43 |
44 | it "returns nil if no cookie is found" do
45 | expect(subject["notexisting"]).to eq nil
46 | end
47 | end
48 | end
49 |
--------------------------------------------------------------------------------
/spec/driver_rendering_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'capybara/webkit/driver'
3 | require 'mini_magick'
4 |
5 | describe Capybara::Webkit::Driver, "rendering an image" do
6 | include AppRunner
7 |
8 | let(:driver) do
9 | driver_for_html(<<-HTML)
10 |
11 |
12 | Hello World
13 |
14 |
15 | HTML
16 | end
17 |
18 | before(:each) do
19 | # Set up the tmp directory and file name
20 | tmp_dir = File.join(PROJECT_ROOT, 'tmp')
21 | FileUtils.mkdir_p tmp_dir
22 | @file_name = File.join(tmp_dir, 'render-test.png')
23 | driver.visit("#{AppRunner.app_host}/")
24 | end
25 |
26 | def render(options)
27 | FileUtils.rm_f @file_name
28 | driver.save_screenshot @file_name, options
29 |
30 | @image = MiniMagick::Image.open @file_name
31 | end
32 |
33 | context "with default options" do
34 | before { render({}) }
35 |
36 | it "should be a PNG" do
37 | expect(@image[:format]).to eq "PNG"
38 | end
39 |
40 | it "width default to 1000px (with 15px less for the scrollbar)" do
41 | expect(@image[:width]).to be < 1001
42 | expect(@image[:width]).to be > 1000-17
43 | end
44 |
45 | it "height should be at least 10px" do
46 | expect(@image[:height]).to be >= 10
47 | end
48 | end
49 |
50 | context "with dimensions set larger than necessary" do
51 | before { render(:width => 500, :height => 400) }
52 |
53 | it "width should match the width given" do
54 | expect(@image[:width]).to eq 500
55 | end
56 |
57 | it "height should match the height given" do
58 | expect(@image[:height]).to eq 400
59 | end
60 |
61 | it "should reset window dimensions to their default value" do
62 | expect(driver.evaluate_script('window.innerWidth')).to eq 1680
63 | expect(driver.evaluate_script('window.innerHeight')).to eq 1050
64 | end
65 | end
66 |
67 | context "with dimensions set smaller than the document's default" do
68 | before { render(:width => 50, :height => 10) }
69 |
70 | it "width should be greater than the width given" do
71 | expect(@image[:width]).to be > 50
72 | end
73 |
74 | it "height should be greater than the height given" do
75 | expect(@image[:height]).to be > 10
76 | end
77 |
78 | it "should restore viewport dimensions after rendering" do
79 | expect(driver.evaluate_script('window.innerWidth')).to eq 1680
80 | expect(driver.evaluate_script('window.innerHeight')).to eq 1050
81 | end
82 | end
83 |
84 | context "with a custom viewport size" do
85 | before { driver.resize_window(800, 600) }
86 |
87 | it "should restore viewport dimensions after rendering" do
88 | render({})
89 | expect(driver.evaluate_script('window.innerWidth')).to eq 800
90 | expect(driver.evaluate_script('window.innerHeight')).to eq 600
91 | end
92 | end
93 |
94 | context "with invalid filepath" do
95 | before do
96 | @file_name = File.dirname(@file_name)
97 | end
98 |
99 | it "raises an InvalidResponseError" do
100 | expect { render({}) }.to raise_error(Capybara::Webkit::InvalidResponseError)
101 | end
102 | end
103 | end
104 |
--------------------------------------------------------------------------------
/spec/driver_resize_window_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'capybara/webkit/driver'
3 |
4 | describe Capybara::Webkit::Driver, "#resize_window(width, height)" do
5 | include AppRunner
6 |
7 | let(:driver) do
8 | driver_for_html(<<-HTML)
9 |
10 |
11 | UNKNOWN
12 |
13 |
18 |
19 |
20 |
21 | HTML
22 | end
23 |
24 | DEFAULT_DIMENTIONS = "[1680x1050]"
25 |
26 | it "resizes the window to the specified size" do
27 | driver.visit("#{AppRunner.app_host}/")
28 |
29 | driver.resize_window(800, 600)
30 | expect(driver.html).to include("[800x600]")
31 |
32 | driver.resize_window(300, 100)
33 | expect(driver.html).to include("[300x100]")
34 | end
35 |
36 | it "resizes the window to the specified size even before the document has loaded" do
37 | driver.resize_window(800, 600)
38 | driver.visit("#{AppRunner.app_host}/")
39 | expect(driver.html).to include("[800x600]")
40 | end
41 |
42 | it "resets the window to the default size when the driver is reset" do
43 | driver.resize_window(800, 600)
44 | driver.reset!
45 | driver.visit("#{AppRunner.app_host}/")
46 | expect(driver.html).to include(DEFAULT_DIMENTIONS)
47 | end
48 |
49 | it "resizes windows by handle" do
50 | driver.visit("#{AppRunner.app_host}/")
51 | driver.open_new_window
52 | driver.visit("#{AppRunner.app_host}/")
53 |
54 | driver.resize_window_to(driver.window_handles.first, 800, 600)
55 | driver.resize_window_to(driver.window_handles.last, 400, 300)
56 |
57 | expect(driver.window_size(driver.window_handles.first)).to eq [800, 600]
58 | expect(driver.window_size(driver.window_handles.last)).to eq [400, 300]
59 | end
60 |
61 | it "maximizes a window" do
62 | driver.visit("#{AppRunner.app_host}/")
63 | driver.resize_window(400, 300)
64 | driver.maximize_window(driver.current_window_handle)
65 | width, height = *driver.window_size(driver.current_window_handle)
66 |
67 | expect(width).to be > 400
68 | expect(height).to be > 300
69 | end
70 | end
71 |
--------------------------------------------------------------------------------
/spec/errors_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe Capybara::Webkit::JsonError do
4 | let(:error) { described_class.new '{"class": "ClickFailed", "message": "Error clicking this element"}' }
5 |
6 | subject { error.exception }
7 |
8 | it { should be_an_instance_of Capybara::Webkit::ClickFailed }
9 |
10 | it { expect(subject.message).to eq 'Error clicking this element' }
11 | end
12 |
--------------------------------------------------------------------------------
/spec/fixtures/exit_text.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | # require 'selenium-webdriver'
3 |
4 | RSpec.describe Capybara::Webkit::Driver do
5 | it "should exit with a zero exit status" do
6 | browser = Capybara::Webkit::Driver.new(TestApp).browser
7 | expect(true).to eq(true)
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/spec/fixtures/fake_server.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | sleep 1
4 |
--------------------------------------------------------------------------------
/spec/node_spec.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding: UTF-8 -*-
2 |
3 | require "spec_helper"
4 | require "capybara/webkit/driver"
5 | require "base64"
6 | require "self_signed_ssl_cert"
7 |
8 | describe Capybara::Webkit::Node do
9 | include AppRunner
10 |
11 | def visit(path, driver = self.driver)
12 | driver.visit(url(path))
13 | end
14 |
15 | def url(path)
16 | "#{AppRunner.app_host}#{path}"
17 | end
18 |
19 | context "html app" do
20 | let(:driver) do
21 | driver_for_html(<<-HTML)
22 |
23 |
24 | Hello HTML
25 |
26 |
27 |
34 |
35 |
36 |
Hidden
37 |
Visibile
38 |
39 |
Nested Visibile
40 |
41 |
42 |
43 |
44 |
Should not be displayed
45 |
46 |
47 |
48 | HTML
49 | end
50 |
51 | before { visit("/") }
52 |
53 | context "Node#[]" do
54 | it "gets boolean properties" do
55 | box1 = driver.find_css('input[name="checkedbox"]').first
56 | box2 = driver.find_css('input[name="uncheckedbox"]').first
57 | box3 = driver.find_css('input[name="falsecheckedbox"]').first
58 | expect(box1["checked"]).to eq true
59 | expect(box2["checked"]).to eq false
60 | expect(box3["checked"]).to eq true
61 | box1.set(false)
62 | expect(box1["checked"]).to eq false
63 | end
64 |
65 | it "prefers property over attribute" do
66 | input = driver.find_css('input[name="foo"]').first
67 | expect(input["value"]).to eq "bar"
68 | input.set("new value")
69 | expect(input["value"]).to eq "new value"
70 | end
71 |
72 | it "returns attribute when property is an object" do
73 | input = driver.find_css('input[name="styled"]').first
74 | expect(input["style"]).to eq "font-size: 150%;"
75 | end
76 | end
77 |
78 | context "Node#visible" do
79 | it "correctly analyzes visibility CSS" do
80 | expect(driver.find_css('#hidden').first.visible?).to be false
81 | expect(driver.find_css('#visible').first.visible?).to be true
82 | expect(driver.find_css('#nested_visible').first.visible?).to be true
83 | end
84 |
85 | it "correctly analyzes display: none CSS" do
86 | expect(driver.find_css('#not_displayed').first.visible?).to be false
87 | end
88 | end
89 | end
90 | end
91 |
--------------------------------------------------------------------------------
/spec/selenium_compatibility_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe Capybara::Webkit, 'compatibility with selenium' do
4 | include AppRunner
5 |
6 | it 'generates the same events as selenium when filling out forms', selenium_compatibility: true do
7 | run_application_for_html(<<-HTML)
8 |
9 |
21 |
35 |
36 | HTML
37 |
38 | compare_events_for_drivers(:reusable_webkit, :selenium) do
39 | visit "/"
40 | fill_in "One", :with => "some value"
41 | fill_in "One", :with => "a new value"
42 | fill_in "Two", :with => "other value"
43 | fill_in "Three", :with => "readonly value"
44 | fill_in "Textarea", :with => "last value"
45 | select "some option", :from => "five"
46 | click_button "Submit"
47 | end
48 | end
49 |
50 | it 'generates the same requests and responses as selenium', selenium_compatibility: true do
51 | requests = []
52 |
53 | app = Class.new(ExampleApp) do
54 | before do
55 | unless request.path_info =~ /favicon\.ico/
56 | requests << request.path_info
57 | end
58 | end
59 |
60 | get '/' do
61 | <<-HTML
62 |
63 |
64 |
65 |
66 |
67 | Original
68 |
69 | HTML
70 | end
71 |
72 | get '/:script.js' do
73 | ''
74 | end
75 |
76 | get '/requests' do
77 | <<-HTML
78 |
79 |
80 | #{requests.sort.join("\n")}
81 |
82 |
83 | HTML
84 | end
85 | end
86 |
87 | run_application app
88 |
89 | compare_for_drivers(:reusable_webkit, :selenium) do |session|
90 | responses = []
91 | session.visit "/"
92 | responses << record_response(session)
93 | session.visit "/"
94 | responses << record_response(session)
95 | session.visit "/requests"
96 | responses << record_response(session)
97 | requests.clear
98 | responses.join("\n\n")
99 | end
100 | end
101 |
102 | def compare_events_for_drivers(first, second, &block)
103 | compare_for_drivers(first, second) do |session|
104 | session.instance_eval(&block)
105 | session.evaluate_script("window.log")
106 | end
107 | end
108 |
109 | def compare_for_drivers(first, second, &block)
110 | expect(for_driver(first, &block)).to eq for_driver(second, &block)
111 | end
112 |
113 | def for_driver(name, &block)
114 | session = Capybara::Session.new(name, AppRunner.app)
115 | result = yield session
116 | result
117 | end
118 |
119 | def record_response(session)
120 | [
121 | session.current_url,
122 | normalize_body(session.body)
123 | ].join("\n")
124 | end
125 |
126 | def normalize_body(body)
127 | if body.length > 0
128 | Nokogiri::HTML.parse(body).at("//body").text.strip
129 | else
130 | "(empty)"
131 | end
132 | end
133 | end
134 |
--------------------------------------------------------------------------------
/spec/self_signed_ssl_cert.rb:
--------------------------------------------------------------------------------
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
43 |
--------------------------------------------------------------------------------
/spec/server_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 | require "capybara/webkit/server"
3 |
4 | describe Capybara::Webkit::Connection do
5 | it "ensures the process ends when the parent process ends", skip_on_windows: true, skip_on_jruby: true do
6 | sleep 1 # Without this sleep popen3 hangs on OSX when running the tests - not really sure why
7 | read_io, write_io = IO.pipe
8 |
9 | fork_pid = fork do
10 | read_io.close
11 | server = start_server
12 | write_io.write(server.pid)
13 | write_io.close
14 | Process.wait(server.pid)
15 | end
16 |
17 | write_io.close
18 |
19 | webkit_pid = read_io.read.to_i
20 | expect(webkit_pid).to be > 1
21 | read_io.close
22 | Process.kill(9, fork_pid)
23 |
24 | eventually do
25 | expect { Process.getpgid(webkit_pid) }.to raise_error Errno::ESRCH
26 | end
27 | end
28 |
29 | it "shouldn't output extraneous warnings when exiting", skip_on_windows: true do
30 | output_str, status = Open3.capture2e("rspec spec/fixtures/exit_text.rb")
31 | expect(status.exitstatus).to eq(0)
32 | expect(output_str).not_to include("AsynchronousCloseException")
33 | end
34 |
35 | it "raises an error if the server has stopped", skip_on_windows: true do
36 | path = "false"
37 | stub_const("Capybara::Webkit::Server::SERVER_PATH", path)
38 |
39 | expect { start_server }.
40 | to raise_error(
41 | Capybara::Webkit::ConnectionError,
42 | "#{path} failed to start.")
43 | end
44 |
45 | it "raises an error if the server is not ready", skip_on_windows: true do
46 | server_path = File.expand_path(File.join(__FILE__, "..", "fixtures", "fake_server.sh"))
47 | stub_const("Capybara::Webkit::Server::SERVER_PATH", server_path)
48 | start_timeout = 0.5
49 | stub_const(
50 | "Capybara::Webkit::Server::WEBKIT_SERVER_START_TIMEOUT",
51 | start_timeout,
52 | )
53 |
54 | error_string =
55 | "#{server_path} failed to start after #{start_timeout} seconds."
56 |
57 | expect { start_server }.
58 | to raise_error(Capybara::Webkit::ConnectionError, error_string)
59 | end
60 |
61 | it "boots a server to talk to" do
62 | url = "http://#{@rack_server.host}:#{@rack_server.port}/"
63 | server = start_server
64 | socket = connect_to(server)
65 | socket.puts "Visit"
66 | socket.puts 1
67 | socket.puts url.to_s.bytesize
68 | socket.print url
69 | expect(socket.gets).to eq "ok\n"
70 | expect(socket.gets).to eq "0\n"
71 | socket.puts "Body"
72 | socket.puts 0
73 | expect(socket.gets).to eq "ok\n"
74 | response_length = socket.gets.to_i
75 | response = socket.read(response_length)
76 | expect(response).to include("Hey there")
77 | end
78 |
79 | it "forwards stderr to the given IO object" do
80 | read_io, write_io = IO.pipe
81 | server = start_server(stderr: write_io)
82 | socket = connect_to(server)
83 | socket.puts "EnableLogging"
84 | socket.puts 0
85 |
86 | script = "console.log('hello world')"
87 | socket.puts "Execute"
88 | socket.puts 1
89 | socket.puts script.to_s.bytesize
90 | socket.print script
91 |
92 | expect(read_io).to include_response /\n\d{2}:\d{2}:\d{2}\.\d{3} hello world/
93 | end
94 |
95 | it "does not forward stderr to nil" do
96 | expect(IO).not_to receive(:copy_stream)
97 | start_server(stderr: nil)
98 | end
99 |
100 | it "prints a deprecation warning if the stdout option is used" do
101 | expect_any_instance_of(Capybara::Webkit::Server).to receive(:warn)
102 | start_server(stdout: nil)
103 | end
104 |
105 | it "does not forward stdout to nil if the stdout option is used" do
106 | allow_any_instance_of(Capybara::Webkit::Server).to receive(:warn)
107 | expect(IO).not_to receive(:copy_stream)
108 | start_server(stdout: nil)
109 | end
110 |
111 | it "returns the server port" do
112 | expect(start_server.port).to be_between 0x400, 0xffff
113 | end
114 |
115 | it "chooses a new port number for a new connection" do
116 | expect(start_server.port).not_to eq start_server.port
117 | end
118 |
119 | before(:all) do
120 | @app = lambda do |_|
121 | body = "Hey there"
122 | [
123 | 200,
124 | { "Content-Type" => "text/html", "Content-Length" => body.size.to_s },
125 | [body],
126 | ]
127 | end
128 | @rack_server = Capybara::Server.new(@app)
129 | @rack_server.boot
130 | end
131 |
132 | def start_server(options = {})
133 | Capybara::Webkit::Server.new(options).tap(&:start)
134 | end
135 |
136 | def connect_to(server)
137 | TCPSocket.open("127.0.0.1", server.port)
138 | end
139 |
140 | def eventually
141 | polling_interval = 0.1
142 | time_limit = Time.now + 3
143 | loop do
144 | begin
145 | yield
146 | return
147 | rescue RSpec::Expectations::ExpectationNotMetError => error
148 | raise error if Time.now >= time_limit
149 | sleep polling_interval
150 | end
151 | end
152 | end
153 | end
154 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | require 'rspec'
2 | require 'rbconfig'
3 | require 'capybara'
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 | require 'capybara/webkit'
12 | $webkit_server = Capybara::Webkit::Server.new
13 | $webkit_connection = Capybara::Webkit::Connection.new(server: $webkit_server)
14 | $webkit_browser = Capybara::Webkit::Browser.new($webkit_connection)
15 |
16 | if ENV["DEBUG"]
17 | $webkit_browser.enable_logging
18 | end
19 |
20 | require 'capybara/spec/spec_helper'
21 |
22 | Capybara.register_driver :reusable_webkit do |app|
23 | Capybara::Webkit::Driver.new(app, :browser => $webkit_browser)
24 | end
25 |
26 | def has_internet?
27 | require 'resolv'
28 | dns_resolver = Resolv::DNS.new
29 | begin
30 | dns_resolver.getaddress("example.com")
31 | true
32 | rescue Resolv::ResolvError
33 | false
34 | end
35 | end
36 |
37 | RSpec.configure do |c|
38 | Capybara::SpecHelper.configure(c)
39 |
40 | c.filter_run_excluding :skip_on_windows => !(RbConfig::CONFIG['host_os'] =~ /mingw32/).nil?
41 | c.filter_run_excluding :skip_on_jruby => !defined?(::JRUBY_VERSION).nil?
42 | c.filter_run_excluding :selenium_compatibility => (Capybara::VERSION =~ /^2\.4\./).nil?
43 | c.filter_run_excluding :skip_if_offline => !has_internet?
44 |
45 | #Check for QT version is 4 to skip QT5 required specs
46 | #This should be removed once support for QT4 is dropped
47 | require 'capybara_webkit_builder'
48 | c.filter_run_excluding :skip_on_qt4 => !(%x(#{CapybaraWebkitBuilder.qmake_bin} -v).match(/Using Qt version 4/)).nil?
49 |
50 | c.filter_run_excluding :full_description => lambda { |description, metadata|
51 | patterns = [
52 | # Accessing unattached nodes is allowed when reload is disabled - Legacy behavior
53 | /^Capybara::Session webkit node #reload without automatic reload should not automatically reload/,
54 | # QtWebkit doesn't support datalist
55 | /^Capybara::Session webkit #select input with datalist should select an option/,
56 | # We focus the next window instead of failing when closing windows.
57 | /^Capybara::Session webkit Capybara::Window\s*#close.*no_such_window_error/,
58 | # QtWebkit doesn't support HTTP 308 response status
59 | /^Capybara::Session webkit #click_button should follow permanent redirects that maintain method/,
60 | # No support for fullscreen window
61 | /^Capybara::Session webkit Capybara::Window#fullscreen should be able to fullscreen the window/,
62 | ]
63 | # These tests were implemented in a non-compatible manner in Capybara < 2.12.0 (outerWidth, outerHeight)
64 | if Gem::Version.new(Capybara::VERSION) < Gem::Version.new("2.12.0")
65 | patterns << /Capybara::Session webkit Capybara::Window\s*#(size|resize_to|maximize)/ <<
66 | /Capybara::Session webkit node\s*#set should allow me to change the contents of a contenteditable elements child/
67 | end
68 | patterns.any? { |pattern| description =~ pattern }
69 | }
70 |
71 | c.filter_run :focus unless ENV["TRAVIS"]
72 | c.run_all_when_everything_filtered = true
73 | end
74 |
75 | def with_env_vars(vars)
76 | old_env_variables = {}
77 | vars.each do |key, value|
78 | old_env_variables[key] = ENV[key]
79 | ENV[key] = value
80 | end
81 |
82 | yield
83 |
84 | old_env_variables.each do |key, value|
85 | ENV[key] = value
86 | end
87 | end
88 |
--------------------------------------------------------------------------------
/spec/support/app_runner.rb:
--------------------------------------------------------------------------------
1 | # Boots a single Capybara::Server for a Rack application that delegates to another, singleton Rack
2 | # application that can be configured for each spec.
3 |
4 | require 'sinatra/base'
5 |
6 | module AppRunner
7 | class << self
8 | attr_accessor :app, :app_host, :browser, :configuration
9 | end
10 |
11 | def self.boot
12 | app_container = lambda { |env| AppRunner.app.call(env) }
13 | server = Capybara::Server.new(app_container)
14 | server.boot
15 | self.app_host = "http://127.0.0.1:#{server.port}"
16 | end
17 |
18 | def self.reset
19 | self.app = lambda do |env|
20 | [200, { 'Content-Type' => 'html', 'Content-Length' => 0 }, []]
21 | end
22 |
23 | self.browser = $webkit_browser
24 | self.browser.reset!
25 |
26 | self.configuration = Capybara::Webkit::Configuration.new
27 | end
28 |
29 | def run_application(app)
30 | AppRunner.app = app
31 | end
32 |
33 | def configure
34 | yield AppRunner.configuration
35 | end
36 |
37 | def fork_connection
38 | AppRunner.fork_connection
39 | end
40 |
41 | def self.fork_connection
42 | server = Capybara::Webkit::Server.new(options)
43 | connection = Capybara::Webkit::Connection.new(server: server)
44 | AppRunner.browser = Capybara::Webkit::Browser.new(connection)
45 | connection
46 | end
47 |
48 | def driver_for_app(&body)
49 | app = Class.new(ExampleApp, &body)
50 | run_application app
51 | AppRunner.build_driver
52 | end
53 |
54 | def driver_for_html(html)
55 | run_application_for_html html
56 | AppRunner.build_driver
57 | end
58 |
59 | def session_for_app(&body)
60 | app = Class.new(ExampleApp, &body)
61 | run_application app
62 | Capybara::Session.new(:reusable_webkit, AppRunner.app)
63 | end
64 |
65 | def run_application_for_html(html)
66 | run_application lambda { |env|
67 | [200, { 'Content-Type' => 'text/html', 'Content-Length' => html.size.to_s }, [html]]
68 | }
69 | end
70 |
71 | def self.build_driver
72 | Capybara::Webkit::Driver.new(app, options.merge(browser: browser))
73 | end
74 |
75 | def self.options
76 | configuration.to_hash
77 | end
78 | private_class_method :options
79 |
80 | def self.included(example_group)
81 | example_group.class_eval do
82 | before { AppRunner.reset }
83 | after { $webkit_browser.reset! }
84 | end
85 | end
86 | end
87 |
88 | class ExampleApp < Sinatra::Base
89 | # Sinatra fixes invalid responses that would break QWebPage, so this middleware breaks them again
90 | # for testing purposes.
91 | class ResponseInvalidator
92 | def initialize(app)
93 | @app = app
94 | end
95 |
96 | def call(env)
97 | response = @app.call(env)
98 | if response.to_a[1]['X-Response-Invalid']
99 | [404, {}, []]
100 | else
101 | response
102 | end
103 | end
104 | end
105 |
106 | use ResponseInvalidator
107 |
108 | def invalid_response
109 | [200, { 'X-Response-Invalid' => 'TRUE' }, []]
110 | end
111 | end
112 |
113 | AppRunner.boot
114 |
--------------------------------------------------------------------------------
/spec/support/matchers/include_response.rb:
--------------------------------------------------------------------------------
1 | RSpec::Matchers.define :include_response do |expected_response|
2 | read_timeout = 2
3 | read_bytes = 4096
4 | response = ""
5 |
6 | match do |read_io|
7 | found_response = false
8 |
9 | while !found_response && IO.select([read_io], nil, nil, read_timeout) do
10 | response += read_io.read_nonblock(read_bytes)
11 | found_response = if expected_response.is_a? Regexp
12 | response.match(expected_response)
13 | else
14 | response.include?(expected_response)
15 | end
16 | end
17 |
18 | found_response
19 | end
20 |
21 | send(respond_to?(:failure_message) ? :failure_message : :failure_message_for_should) do |actual|
22 | "expected #{response} to include #{expected_response}"
23 | end
24 |
25 | send(respond_to?(:failure_message_when_negated) ? :failure_message_when_negated : :failure_message_for_should_not) do |actual|
26 | "expected #{response} to not include #{expected_response}"
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/spec/support/output_writer.rb:
--------------------------------------------------------------------------------
1 | shared_examples_for "output writer" do
2 | before do
3 | @read, @write = IO.pipe
4 | configure { |config| config.stderr = @write }
5 | fork_connection
6 | end
7 |
8 | let(:stderr) do
9 | @write.close
10 | @read.read
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/src/AcceptAlert.cpp:
--------------------------------------------------------------------------------
1 | #include "AcceptAlert.h"
2 | #include "SocketCommand.h"
3 | #include "WebPage.h"
4 | #include "WebPageManager.h"
5 |
6 | AcceptAlert::AcceptAlert(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
7 | }
8 |
9 | void AcceptAlert::start() {
10 | finish(true, page()->acceptAlert(arguments()[0]));
11 | }
12 |
--------------------------------------------------------------------------------
/src/AcceptAlert.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 |
3 | class AcceptAlert : public SocketCommand {
4 | Q_OBJECT
5 |
6 | public:
7 | AcceptAlert(WebPageManager *, QStringList &arguments, QObject *parent = 0);
8 | virtual void start();
9 | };
10 |
11 |
--------------------------------------------------------------------------------
/src/AllowUrl.cpp:
--------------------------------------------------------------------------------
1 | #include "AllowUrl.h"
2 | #include "SocketCommand.h"
3 | #include "WebPage.h"
4 | #include "WebPageManager.h"
5 |
6 | AllowUrl::AllowUrl(
7 | WebPageManager *manager,
8 | QStringList &arguments,
9 | QObject *parent
10 | ) : SocketCommand(manager, arguments, parent) {
11 | }
12 |
13 | void AllowUrl::start() {
14 | manager()->allowUrl(arguments()[0]);
15 | finish(true);
16 | }
17 |
--------------------------------------------------------------------------------
/src/AllowUrl.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 |
3 | class AllowUrl : public SocketCommand {
4 | Q_OBJECT
5 |
6 | public:
7 | AllowUrl(WebPageManager *, QStringList &arguments, QObject *parent = 0);
8 | virtual void start();
9 | };
10 |
11 |
--------------------------------------------------------------------------------
/src/Authenticate.cpp:
--------------------------------------------------------------------------------
1 | #include "Authenticate.h"
2 | #include "WebPage.h"
3 | #include "NetworkAccessManager.h"
4 | #include "WebPageManager.h"
5 |
6 | Authenticate::Authenticate(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
7 | }
8 |
9 | void Authenticate::start() {
10 | QString username = arguments()[0];
11 | QString password = arguments()[1];
12 |
13 | NetworkAccessManager* networkAccessManager = manager()->networkAccessManager();
14 | #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
15 | //Reset Authentication cache
16 | networkAccessManager->clearAccessCache();
17 | #endif
18 | networkAccessManager->setUserName(username);
19 | networkAccessManager->setPassword(password);
20 |
21 | finish(true);
22 | }
23 |
24 |
--------------------------------------------------------------------------------
/src/Authenticate.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 |
3 | class WebPage;
4 |
5 | class Authenticate : public SocketCommand {
6 | Q_OBJECT
7 |
8 | public:
9 | Authenticate(WebPageManager *manager, QStringList &arguments, QObject *parent = 0);
10 | virtual void start();
11 | };
12 |
13 |
--------------------------------------------------------------------------------
/src/BlacklistedRequestHandler.cpp:
--------------------------------------------------------------------------------
1 | #include "BlacklistedRequestHandler.h"
2 | #include "NetworkReplyProxy.h"
3 | #include "NoOpReply.h"
4 |
5 | BlacklistedRequestHandler::BlacklistedRequestHandler(
6 | RequestHandler *next,
7 | QObject *parent
8 | ) : RequestHandler(parent) {
9 | m_next = next;
10 | }
11 |
12 | QNetworkReply* BlacklistedRequestHandler::handleRequest(
13 | NetworkAccessManager *manager,
14 | QNetworkAccessManager::Operation operation,
15 | QNetworkRequest &request,
16 | QIODevice *outgoingData
17 | ) {
18 | if (this->isBlacklisted(request.url())) {
19 | return new NetworkReplyProxy(new NoOpReply(request), this);
20 | } else {
21 | return m_next->handleRequest(manager, operation, request, outgoingData);
22 | }
23 | }
24 |
25 | void BlacklistedRequestHandler::setUrlBlacklist(QStringList urlBlacklist) {
26 | m_urlBlacklist.clear();
27 |
28 | QStringListIterator iter(urlBlacklist);
29 | while (iter.hasNext()) {
30 | m_urlBlacklist << iter.next();
31 | }
32 | }
33 |
34 | bool BlacklistedRequestHandler::isBlacklisted(QUrl url) {
35 | QString urlString = url.toString();
36 | QStringListIterator iter(m_urlBlacklist);
37 |
38 | while (iter.hasNext()) {
39 | QRegExp blacklisted = QRegExp(iter.next());
40 | blacklisted.setPatternSyntax(QRegExp::Wildcard);
41 |
42 | if(urlString.contains(blacklisted)) {
43 | return true;
44 | }
45 | }
46 |
47 | return false;
48 | }
49 |
50 | void BlacklistedRequestHandler::blockUrl(const QString &url) {
51 | m_urlBlacklist.append(url);
52 | }
53 |
54 | void BlacklistedRequestHandler::reset() {
55 | m_urlBlacklist.clear();
56 | }
57 |
--------------------------------------------------------------------------------
/src/BlacklistedRequestHandler.h:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include "RequestHandler.h"
4 |
5 | class BlacklistedRequestHandler : public RequestHandler {
6 | public:
7 | BlacklistedRequestHandler(RequestHandler *next, QObject *parent = 0);
8 | virtual QNetworkReply* handleRequest(
9 | NetworkAccessManager *,
10 | QNetworkAccessManager::Operation,
11 | QNetworkRequest &,
12 | QIODevice *
13 | );
14 | void setUrlBlacklist(QStringList urlBlacklist);
15 | void blockUrl(const QString &);
16 | void reset();
17 |
18 | private:
19 | RequestHandler *m_next;
20 | QStringList m_urlBlacklist;
21 | bool isBlacklisted(QUrl url);
22 | };
23 |
--------------------------------------------------------------------------------
/src/BlockUrl.cpp:
--------------------------------------------------------------------------------
1 | #include "BlockUrl.h"
2 | #include "SocketCommand.h"
3 | #include "WebPage.h"
4 | #include "WebPageManager.h"
5 |
6 | BlockUrl::BlockUrl(
7 | WebPageManager *manager,
8 | QStringList &arguments,
9 | QObject *parent
10 | ) : SocketCommand(manager, arguments, parent) {
11 | }
12 |
13 | void BlockUrl::start() {
14 | manager()->blockUrl(arguments()[0]);
15 | finish(true);
16 | }
17 |
--------------------------------------------------------------------------------
/src/BlockUrl.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 |
3 | class BlockUrl : public SocketCommand {
4 | Q_OBJECT
5 |
6 | public:
7 | BlockUrl(WebPageManager *, QStringList &arguments, QObject *parent = 0);
8 | virtual void start();
9 | };
10 |
11 |
--------------------------------------------------------------------------------
/src/Body.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 |
3 | class Body : public SocketCommand {
4 | Q_OBJECT
5 |
6 | public:
7 | Body(WebPageManager *, QStringList &arguments, QObject *parent = 0);
8 | virtual void start();
9 | };
10 |
11 |
--------------------------------------------------------------------------------
/src/ClearCookies.cpp:
--------------------------------------------------------------------------------
1 | #include "ClearCookies.h"
2 | #include "WebPage.h"
3 | #include "WebPageManager.h"
4 | #include "NetworkCookieJar.h"
5 | #include
6 |
7 | ClearCookies::ClearCookies(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {}
8 |
9 | void ClearCookies::start()
10 | {
11 | manager()->cookieJar()->clearCookies();
12 | finish(true);
13 | }
14 |
--------------------------------------------------------------------------------
/src/ClearCookies.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 |
3 | class ClearCookies : public SocketCommand {
4 | Q_OBJECT;
5 |
6 | public:
7 | ClearCookies(WebPageManager *, QStringList &arguments, QObject *parent = 0);
8 | virtual void start();
9 | };
10 |
--------------------------------------------------------------------------------
/src/ClearPromptText.cpp:
--------------------------------------------------------------------------------
1 | #include "ClearPromptText.h"
2 | #include "WebPage.h"
3 | #include "WebPageManager.h"
4 |
5 | ClearPromptText::ClearPromptText(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {}
6 |
7 | void ClearPromptText::start()
8 | {
9 | page()->setPromptText(QString());
10 | finish(true);
11 | }
12 |
--------------------------------------------------------------------------------
/src/ClearPromptText.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 |
3 | class ClearPromptText : public SocketCommand {
4 | Q_OBJECT;
5 |
6 | public:
7 | ClearPromptText(WebPageManager *manager, QStringList &arguments, QObject *parent = 0);
8 | virtual void start();
9 | };
10 |
--------------------------------------------------------------------------------
/src/Command.cpp:
--------------------------------------------------------------------------------
1 | #include "Command.h"
2 | #include "ErrorMessage.h"
3 |
4 | Command::Command(QObject *parent) : QObject(parent) {
5 | }
6 |
7 | QString Command::toString() const {
8 | return metaObject()->className();
9 | }
10 |
11 | void Command::finish(bool success) {
12 | emit finished(new Response(success, this));
13 | }
14 |
15 | void Command::finish(bool success, QString message) {
16 | emit finished(new Response(success, message, this));
17 | }
18 |
19 | void Command::finish(bool success, QByteArray message) {
20 | emit finished(new Response(success, message, this));
21 | }
22 |
23 | void Command::finish(bool success, ErrorMessage *message) {
24 | emit finished(new Response(success, message, this));
25 | }
26 |
--------------------------------------------------------------------------------
/src/Command.h:
--------------------------------------------------------------------------------
1 | #ifndef COMMAND_H
2 | #define COMMAND_H
3 |
4 | #include "Response.h"
5 | #include
6 | #include
7 |
8 | class ErrorMessage;
9 |
10 | class Command : public QObject {
11 | Q_OBJECT
12 |
13 | public:
14 | Command(QObject *parent = 0);
15 | virtual void start() = 0;
16 | virtual QString toString() const;
17 |
18 | protected:
19 | void finish(bool success);
20 | void finish(bool success, QString message);
21 | void finish(bool success, QByteArray message);
22 | void finish(bool success, ErrorMessage *message);
23 |
24 | signals:
25 | void finished(Response *response);
26 | };
27 |
28 | #endif
29 |
30 |
--------------------------------------------------------------------------------
/src/CommandFactory.cpp:
--------------------------------------------------------------------------------
1 | #include "CommandFactory.h"
2 | #include "NullCommand.h"
3 | #include "Visit.h"
4 | #include "FindXpath.h"
5 | #include "Reset.h"
6 | #include "Node.h"
7 | #include "Evaluate.h"
8 | #include "EvaluateAsync.h"
9 | #include "Execute.h"
10 | #include "FrameFocus.h"
11 | #include "Header.h"
12 | #include "Render.h"
13 | #include "Body.h"
14 | #include "Status.h"
15 | #include "Headers.h"
16 | #include "SetCookie.h"
17 | #include "ClearCookies.h"
18 | #include "GetCookies.h"
19 | #include "SetProxy.h"
20 | #include "ConsoleMessages.h"
21 | #include "CurrentUrl.h"
22 | #include "SetTimeout.h"
23 | #include "GetTimeout.h"
24 | #include "WindowResize.h"
25 | #include "IgnoreSslErrors.h"
26 | #include "SetSkipImageLoading.h"
27 | #include "WindowFocus.h"
28 | #include "GetWindowHandles.h"
29 | #include "GetWindowHandle.h"
30 | #include "WebPageManager.h"
31 | #include "Authenticate.h"
32 | #include "EnableLogging.h"
33 | #include "SetConfirmAction.h"
34 | #include "SetPromptAction.h"
35 | #include "SetPromptText.h"
36 | #include "ClearPromptText.h"
37 | #include "JavascriptAlertMessages.h"
38 | #include "JavascriptConfirmMessages.h"
39 | #include "JavascriptPromptMessages.h"
40 | #include "SetUrlBlacklist.h"
41 | #include "Version.h"
42 | #include "Title.h"
43 | #include "FindCss.h"
44 | #include "WindowClose.h"
45 | #include "WindowOpen.h"
46 | #include "WindowSize.h"
47 | #include "WindowMaximize.h"
48 | #include "GoBack.h"
49 | #include "GoForward.h"
50 | #include "Refresh.h"
51 | #include "AcceptAlert.h"
52 | #include "FindModal.h"
53 | #include "SetUnknownUrlMode.h"
54 | #include "AllowUrl.h"
55 | #include "BlockUrl.h"
56 | #include "FrameTitle.h"
57 | #include "FrameUrl.h"
58 |
59 | CommandFactory::CommandFactory(WebPageManager *manager, QObject *parent) : QObject(parent) {
60 | m_manager = manager;
61 | }
62 |
63 | Command *CommandFactory::createCommand(const char *name, QStringList &arguments) {
64 | #include "find_command.h"
65 | return new NullCommand(QString(name));
66 | }
67 |
--------------------------------------------------------------------------------
/src/CommandFactory.h:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | class Command;
4 | class WebPage;
5 | class WebPageManager;
6 |
7 | class CommandFactory : public QObject {
8 | Q_OBJECT
9 |
10 | public:
11 | CommandFactory(WebPageManager *, QObject *parent = 0);
12 | Command *createCommand(const char *name, QStringList &arguments);
13 |
14 | private:
15 | WebPageManager *m_manager;
16 | };
17 |
18 |
--------------------------------------------------------------------------------
/src/CommandParser.cpp:
--------------------------------------------------------------------------------
1 | #include "CommandParser.h"
2 | #include "CommandFactory.h"
3 | #include "SocketCommand.h"
4 |
5 | #include
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.toLatin1().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 | }
77 |
--------------------------------------------------------------------------------
/src/CommandParser.h:
--------------------------------------------------------------------------------
1 | #include
2 | #include
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 |
34 |
--------------------------------------------------------------------------------
/src/Connection.cpp:
--------------------------------------------------------------------------------
1 | #include "Connection.h"
2 | #include "WebPage.h"
3 | #include "WebPageManager.h"
4 | #include "CommandParser.h"
5 | #include "CommandFactory.h"
6 | #include "PageLoadingCommand.h"
7 | #include "TimeoutCommand.h"
8 | #include "SocketCommand.h"
9 | #include "ErrorMessage.h"
10 |
11 | #include
12 |
13 | Connection::Connection(QTcpSocket *socket, WebPageManager *manager, QObject *parent) :
14 | QObject(parent) {
15 | m_socket = socket;
16 | m_manager = manager;
17 | m_commandFactory = new CommandFactory(m_manager, this);
18 | m_commandParser = new CommandParser(socket, m_commandFactory, this);
19 | m_pageSuccess = true;
20 | m_pendingCommand = NULL;
21 | connect(m_socket, SIGNAL(readyRead()), m_commandParser, SLOT(checkNext()));
22 | connect(m_commandParser, SIGNAL(commandReady(Command *)), this, SLOT(commandReady(Command *)));
23 | connect(m_manager, SIGNAL(pageFinished(bool)), this, SLOT(pendingLoadFinished(bool)));
24 | }
25 |
26 | void Connection::commandReady(Command *command) {
27 | m_manager->log() << "Received" << command->toString();
28 | startCommand(command);
29 | }
30 |
31 | void Connection::startCommand(Command *command) {
32 | if (m_pendingCommand) {
33 | m_pendingCommand->deleteLater();
34 | }
35 | if (m_pageSuccess) {
36 | m_pendingCommand = new TimeoutCommand(new PageLoadingCommand(command, m_manager, this), m_manager, this);
37 | connect(m_pendingCommand, SIGNAL(finished(Response *)), this, SLOT(finishCommand(Response *)));
38 | m_pendingCommand->start();
39 | } else {
40 | writePageLoadFailure();
41 | }
42 | }
43 |
44 | void Connection::pendingLoadFinished(bool success) {
45 | m_pageSuccess = m_pageSuccess && success;
46 | }
47 |
48 | void Connection::writePageLoadFailure() {
49 | m_pageSuccess = true;
50 | QString message = currentPage()->failureString();
51 | Response response(false, new ErrorMessage(message));
52 | writeResponse(&response);
53 | }
54 |
55 | void Connection::finishCommand(Response *response) {
56 | m_pageSuccess = true;
57 | writeResponse(response);
58 | sender()->deleteLater();
59 | m_pendingCommand = NULL;
60 | }
61 |
62 | void Connection::writeResponse(Response *response) {
63 | if (response->isSuccess())
64 | m_socket->write("ok\n");
65 | else
66 | m_socket->write("failure\n");
67 |
68 | m_manager->log() << "Wrote response" << response->isSuccess() << response->message();
69 |
70 | QByteArray messageUtf8 = response->message();
71 | QString messageLength = QString::number(messageUtf8.size()) + "\n";
72 | m_socket->write(messageLength.toLatin1());
73 | m_socket->write(messageUtf8);
74 | }
75 |
76 | WebPage *Connection::currentPage() {
77 | return m_manager->currentPage();
78 | }
79 |
--------------------------------------------------------------------------------
/src/Connection.h:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | class QTcpSocket;
5 | class WebPage;
6 | class Command;
7 | class Response;
8 | class CommandParser;
9 | class CommandFactory;
10 | class PageLoadingCommand;
11 | class WebPageManager;
12 |
13 | class Connection : public QObject {
14 | Q_OBJECT
15 |
16 | public:
17 | Connection(QTcpSocket *socket, WebPageManager *manager, QObject *parent = 0);
18 |
19 | public slots:
20 | void commandReady(Command *command);
21 | void finishCommand(Response *response);
22 | void pendingLoadFinished(bool success);
23 |
24 | private:
25 | void startCommand(Command *);
26 | void writeResponse(Response *response);
27 | void writePageLoadFailure();
28 |
29 | QTcpSocket *m_socket;
30 | WebPageManager *m_manager;
31 | CommandParser *m_commandParser;
32 | CommandFactory *m_commandFactory;
33 | bool m_pageSuccess;
34 | WebPage *currentPage();
35 | Command *m_pendingCommand;
36 | };
37 |
38 |
--------------------------------------------------------------------------------
/src/ConsoleMessages.cpp:
--------------------------------------------------------------------------------
1 | #include "ConsoleMessages.h"
2 | #include "WebPage.h"
3 | #include "WebPageManager.h"
4 | #include "JsonSerializer.h"
5 |
6 | ConsoleMessages::ConsoleMessages(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
7 | }
8 |
9 | void ConsoleMessages::start() {
10 | JsonSerializer serializer;
11 | QByteArray json = serializer.serialize(page()->consoleMessages());
12 | finish(true, json);
13 | }
14 |
15 |
--------------------------------------------------------------------------------
/src/ConsoleMessages.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 |
3 | class ConsoleMessages : public SocketCommand {
4 | Q_OBJECT
5 |
6 | public:
7 | ConsoleMessages(WebPageManager *, QStringList &arguments, QObject *parent = 0);
8 | virtual void start();
9 | };
10 |
11 |
--------------------------------------------------------------------------------
/src/CurrentUrl.cpp:
--------------------------------------------------------------------------------
1 | #include "CurrentUrl.h"
2 | #include "WebPage.h"
3 | #include "WebPageManager.h"
4 |
5 | CurrentUrl::CurrentUrl(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
6 | }
7 |
8 | void CurrentUrl::start() {
9 | QStringList arguments;
10 | QVariant result = page()->mainFrame()->evaluateJavaScript("window.location.toString()");
11 | QString url = result.toString();
12 | finish(true, url);
13 | }
14 |
15 |
--------------------------------------------------------------------------------
/src/CurrentUrl.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 |
3 | class CurrentUrl : public SocketCommand {
4 | Q_OBJECT
5 |
6 | public:
7 | CurrentUrl(WebPageManager *, QStringList &arguments, QObject *parent = 0);
8 | virtual void start();
9 | };
10 |
11 |
--------------------------------------------------------------------------------
/src/CustomHeadersRequestHandler.cpp:
--------------------------------------------------------------------------------
1 | #include "CustomHeadersRequestHandler.h"
2 | #include "NetworkReplyProxy.h"
3 | #include "NoOpReply.h"
4 |
5 | CustomHeadersRequestHandler::CustomHeadersRequestHandler(
6 | RequestHandler *next,
7 | QObject *parent
8 | ) : RequestHandler(parent) {
9 | m_next = next;
10 | }
11 |
12 | QNetworkReply* CustomHeadersRequestHandler::handleRequest(
13 | NetworkAccessManager *manager,
14 | QNetworkAccessManager::Operation operation,
15 | QNetworkRequest &request,
16 | QIODevice *outgoingData
17 | ) {
18 | Q_UNUSED(manager)
19 | Q_UNUSED(operation)
20 | Q_UNUSED(outgoingData)
21 |
22 | QHashIterator item(m_headers);
23 | while (item.hasNext()) {
24 | item.next();
25 | request.setRawHeader(item.key().toLatin1(), item.value().toLatin1());
26 | }
27 |
28 | return m_next->handleRequest(manager, operation, request, outgoingData);
29 | }
30 |
31 | void CustomHeadersRequestHandler::addHeader(QString key, QString value) {
32 | m_headers.insert(key, value);
33 | }
34 |
35 | void CustomHeadersRequestHandler::reset() {
36 | m_headers.clear();
37 | }
38 |
--------------------------------------------------------------------------------
/src/CustomHeadersRequestHandler.h:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #include "RequestHandler.h"
5 |
6 | class CustomHeadersRequestHandler : public RequestHandler {
7 | public:
8 | CustomHeadersRequestHandler(RequestHandler *next, QObject *parent = 0);
9 | virtual QNetworkReply* handleRequest(
10 | NetworkAccessManager *,
11 | QNetworkAccessManager::Operation,
12 | QNetworkRequest &,
13 | QIODevice *
14 | );
15 | void addHeader(QString, QString);
16 | virtual void reset();
17 |
18 | private:
19 | RequestHandler *m_next;
20 | QHash m_headers;
21 | };
22 |
--------------------------------------------------------------------------------
/src/EnableLogging.cpp:
--------------------------------------------------------------------------------
1 | #include "EnableLogging.h"
2 | #include "WebPageManager.h"
3 |
4 | EnableLogging::EnableLogging(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
5 | }
6 |
7 | void EnableLogging::start() {
8 | manager()->enableLogging();
9 | finish(true);
10 | }
11 |
--------------------------------------------------------------------------------
/src/EnableLogging.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 |
3 | class WebPageManager;
4 |
5 | class EnableLogging : public SocketCommand {
6 | Q_OBJECT
7 |
8 | public:
9 | EnableLogging(WebPageManager *, QStringList &arguments, QObject *parent = 0);
10 | virtual void start();
11 | };
12 |
13 |
--------------------------------------------------------------------------------
/src/ErrorMessage.cpp:
--------------------------------------------------------------------------------
1 | #include "ErrorMessage.h"
2 | #include "JsonSerializer.h"
3 |
4 | ErrorMessage::ErrorMessage(QString message, QObject *parent) : QObject(parent) {
5 | m_message = message;
6 | }
7 |
8 | ErrorMessage::ErrorMessage(QString type, QString message, QObject *parent) : QObject(parent) {
9 | m_type = type;
10 | m_message = message;
11 | }
12 |
13 | QByteArray ErrorMessage::toString() {
14 | JsonSerializer serializer;
15 |
16 | QVariantMap map;
17 |
18 | if (m_type.isNull())
19 | map["class"] = "InvalidResponseError";
20 | else
21 | map["class"] = m_type;
22 |
23 | map["message"] = m_message;
24 |
25 | return serializer.serialize(map);
26 | }
27 |
--------------------------------------------------------------------------------
/src/ErrorMessage.h:
--------------------------------------------------------------------------------
1 | #ifndef __ERROR_MESSAGE_H
2 | #define __ERROR_MESSAGE_H
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | class ErrorMessage : public QObject {
9 | Q_OBJECT
10 |
11 | public:
12 | ErrorMessage(QString message, QObject *parent = 0);
13 | ErrorMessage(QString type, QString message, QObject *parent = 0);
14 | QByteArray toString();
15 |
16 | private:
17 | QString m_type;
18 | QString m_message;
19 | };
20 |
21 | #endif
22 |
--------------------------------------------------------------------------------
/src/Evaluate.cpp:
--------------------------------------------------------------------------------
1 | #include "Evaluate.h"
2 | #include "WebPage.h"
3 | #include "WebPageManager.h"
4 | #include "JsonSerializer.h"
5 | #include
6 |
7 | Evaluate::Evaluate(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
8 | }
9 |
10 | void Evaluate::start() {
11 | QString script = arguments()[0];
12 | QString jsonArgs;
13 | if (arguments().length()>1){
14 | jsonArgs = arguments()[1];
15 | } else {
16 | jsonArgs ="[]";
17 | }
18 | QString eval_script = QString("(function(){"
19 | " for(var i=0; icurrentFrame()->addToJavaScriptWindowObject("CapybaraInvocation", &invocation_stub);
32 | QVariant result = page()->currentFrame()->evaluateJavaScript(eval_script);
33 | JsonSerializer serializer;
34 | finish(true, serializer.serialize(result));
35 | }
36 |
--------------------------------------------------------------------------------
/src/Evaluate.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 |
3 | #include
4 |
5 | class Evaluate : public SocketCommand {
6 | Q_OBJECT
7 |
8 | public:
9 | Evaluate(WebPageManager *, QStringList &arguments, QObject *parent = 0);
10 | virtual void start();
11 | };
12 |
13 |
--------------------------------------------------------------------------------
/src/EvaluateAsync.cpp:
--------------------------------------------------------------------------------
1 | #include "EvaluateAsync.h"
2 | #include "WebPage.h"
3 | #include "WebPageManager.h"
4 | #include "JsonSerializer.h"
5 | #include
6 |
7 | EvaluateAsync::EvaluateAsync(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
8 | }
9 |
10 | void EvaluateAsync::start() {
11 | QString script = arguments()[0];
12 | QString jsonArgs;
13 | if (arguments().length()>1){
14 | jsonArgs = arguments()[1];
15 | } else {
16 | jsonArgs ="[]";
17 | }
18 | QString eval_script = QString("(function(){"
19 | " for(var i=0; icurrentFrame()->addToJavaScriptWindowObject("CapybaraInvocation", &invocation_stub);
40 | page()->currentFrame()->addToJavaScriptWindowObject("CapybaraAsync", this);
41 | page()->currentFrame()->evaluateJavaScript(eval_script);
42 | }
43 |
44 | void EvaluateAsync::asyncResult(QVariantList result) {
45 | JsonSerializer serializer;
46 | finish(true, serializer.serialize(result));
47 | }
48 |
49 | void EvaluateAsync::asyncResult(QVariant result) {
50 | JsonSerializer serializer;
51 | finish(true, serializer.serialize(result));
52 | }
53 |
54 |
55 |
--------------------------------------------------------------------------------
/src/EvaluateAsync.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 |
3 | #include
4 |
5 | class EvaluateAsync : public SocketCommand {
6 | Q_OBJECT
7 |
8 | public:
9 | EvaluateAsync(WebPageManager *, QStringList &arguments, QObject *parent = 0);
10 | virtual void start();
11 | Q_INVOKABLE void asyncResult(QVariant result);
12 | Q_INVOKABLE void asyncResult(QVariantList result);
13 | };
14 |
15 |
--------------------------------------------------------------------------------
/src/Execute.cpp:
--------------------------------------------------------------------------------
1 | #include "Execute.h"
2 | #include "WebPage.h"
3 | #include "WebPageManager.h"
4 | #include "ErrorMessage.h"
5 |
6 | Execute::Execute(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
7 | }
8 |
9 | void Execute::start() {
10 | QString jsonArgs;
11 | if (arguments().length()>1){
12 | jsonArgs = arguments()[1];
13 | } else {
14 | jsonArgs ="[]";
15 | }
16 | QString script = QString("(function(){"
17 | " for(var i=0; icurrentFrame()->addToJavaScriptWindowObject("CapybaraInvocation", &invocation_stub);
29 | QVariant result = page()->currentFrame()->evaluateJavaScript(script);
30 | if (result.isValid()) {
31 | finish(true);
32 | } else {
33 | finish(false, new ErrorMessage("Javascript failed to execute"));
34 | }
35 | }
36 |
37 |
--------------------------------------------------------------------------------
/src/Execute.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 |
3 | class Execute : public SocketCommand {
4 | Q_OBJECT
5 |
6 | public:
7 | Execute(WebPageManager *, QStringList &arguments, QObject *parent = 0);
8 | virtual void start();
9 | };
10 |
11 |
--------------------------------------------------------------------------------
/src/FindCss.cpp:
--------------------------------------------------------------------------------
1 | #include "FindCss.h"
2 | #include "WebPage.h"
3 | #include "WebPageManager.h"
4 | #include "InvocationResult.h"
5 |
6 | FindCss::FindCss(WebPageManager *manager, QStringList &arguments, QObject *parent) : JavascriptCommand(manager, arguments, parent) {
7 | }
8 |
9 | void FindCss::start() {
10 | InvocationResult result = page()->invokeCapybaraFunction("findCss", true, arguments());
11 | finish(&result);
12 | }
13 |
14 |
--------------------------------------------------------------------------------
/src/FindCss.h:
--------------------------------------------------------------------------------
1 | #include "JavascriptCommand.h"
2 |
3 | class FindCss : public JavascriptCommand {
4 | Q_OBJECT
5 |
6 | public:
7 | FindCss(WebPageManager *, QStringList &arguments, QObject *parent = 0);
8 | virtual void start();
9 | };
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/FindModal.cpp:
--------------------------------------------------------------------------------
1 | #include "FindModal.h"
2 | #include "SocketCommand.h"
3 | #include "WebPage.h"
4 | #include "WebPageManager.h"
5 | #include "ErrorMessage.h"
6 |
7 | FindModal::FindModal(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
8 | }
9 |
10 | void FindModal::start() {
11 | if (page()->modalCount() == 0) {
12 | connect(page(), SIGNAL(modalReady()), SLOT(handleModalReady()));
13 | } else {
14 | handleModalReady();
15 | }
16 | }
17 |
18 | void FindModal::handleModalReady() {
19 | sender()->disconnect(SIGNAL(modalReady()), this, SLOT(handleModalReady()));
20 | QString message = page()->modalMessage();
21 | if (message.isNull()) {
22 | finish(false, new ErrorMessage("ModalNotFound", ""));
23 | } else {
24 | finish(true, message);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/FindModal.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 |
3 | class FindModal : public SocketCommand {
4 | Q_OBJECT
5 |
6 | public:
7 | FindModal(WebPageManager *, QStringList &arguments, QObject *parent = 0);
8 | virtual void start();
9 |
10 | public slots:
11 | void handleModalReady();
12 | };
13 |
14 |
--------------------------------------------------------------------------------
/src/FindXpath.cpp:
--------------------------------------------------------------------------------
1 | #include "FindXpath.h"
2 | #include "WebPage.h"
3 | #include "WebPageManager.h"
4 | #include "InvocationResult.h"
5 |
6 | FindXpath::FindXpath(WebPageManager *manager, QStringList &arguments, QObject *parent) : JavascriptCommand(manager, arguments, parent) {
7 | }
8 |
9 | void FindXpath::start() {
10 | InvocationResult result = page()->invokeCapybaraFunction("findXpath", true, arguments());
11 | finish(&result);
12 | }
13 |
14 |
--------------------------------------------------------------------------------
/src/FindXpath.h:
--------------------------------------------------------------------------------
1 | #include "JavascriptCommand.h"
2 |
3 | class FindXpath : public JavascriptCommand {
4 | Q_OBJECT
5 |
6 | public:
7 | FindXpath(WebPageManager *, QStringList &arguments, QObject *parent = 0);
8 | virtual void start();
9 | };
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/FrameFocus.cpp:
--------------------------------------------------------------------------------
1 | #include "FrameFocus.h"
2 | #include "SocketCommand.h"
3 | #include "WebPage.h"
4 | #include "WebPageManager.h"
5 | #include "ErrorMessage.h"
6 |
7 | FrameFocus::FrameFocus(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
8 | }
9 |
10 | void FrameFocus::start() {
11 | switch(arguments().length()) {
12 | case 1:
13 | focusId(arguments()[0]);
14 | break;
15 | case 2:
16 | focusIndex(arguments()[1].toInt());
17 | break;
18 | default:
19 | focusParent();
20 | }
21 | }
22 |
23 | void FrameFocus::findFrames() {
24 | frames = page()->currentFrame()->childFrames();
25 | }
26 |
27 | void FrameFocus::focusIndex(int index) {
28 | findFrames();
29 | if (isFrameAtIndex(index)) {
30 | frames[index]->setFocus();
31 | page()->setCurrentFrameParent(frames[index]->parentFrame());
32 | success();
33 | } else {
34 | frameNotFound();
35 | }
36 | }
37 |
38 | bool FrameFocus::isFrameAtIndex(int index) {
39 | return 0 <= index && index < frames.length();
40 | }
41 |
42 | void FrameFocus::focusId(QString name) {
43 | findFrames();
44 | for (int i = 0; i < frames.length(); i++) {
45 | if (frames[i]->frameName().compare(name) == 0) {
46 | frames[i]->setFocus();
47 | page()->setCurrentFrameParent(frames[i]->parentFrame());
48 | success();
49 | return;
50 | }
51 | }
52 |
53 | frameNotFound();
54 | }
55 |
56 | void FrameFocus::focusParent() {
57 | // if (page()->currentFrame()->parentFrame() == 0) {
58 | if (page()->currentFrameParent() == 0) {
59 | finish(false, new ErrorMessage("Already at parent frame."));
60 | } else {
61 | // page()->currentFrame()->parentFrame()->setFocus();
62 | page()->currentFrameParent()->setFocus();
63 | page()->setCurrentFrameParent(page()->currentFrameParent()->parentFrame());
64 | success();
65 | }
66 | }
67 |
68 | void FrameFocus::frameNotFound() {
69 | finish(false, new ErrorMessage("Unable to locate frame."));
70 | }
71 |
72 | void FrameFocus::success() {
73 | finish(true);
74 | }
75 |
--------------------------------------------------------------------------------
/src/FrameFocus.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 |
3 | class QWebFrame;
4 |
5 | class FrameFocus : public SocketCommand {
6 | Q_OBJECT
7 |
8 | public:
9 | FrameFocus(WebPageManager *, QStringList &arguments, QObject *parent = 0);
10 | virtual void start();
11 |
12 | private:
13 | void findFrames();
14 |
15 | void focusParent();
16 |
17 | void focusIndex(int index);
18 | bool isFrameAtIndex(int index);
19 |
20 | void focusId(QString id);
21 |
22 | void success();
23 | void frameNotFound();
24 |
25 | QList frames;
26 | };
27 |
28 |
--------------------------------------------------------------------------------
/src/FrameTitle.cpp:
--------------------------------------------------------------------------------
1 | #include "FrameTitle.h"
2 | #include "SocketCommand.h"
3 | #include "WebPage.h"
4 | #include "WebPageManager.h"
5 |
6 | FrameTitle::FrameTitle(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
7 | }
8 |
9 | void FrameTitle::start() {
10 | finish(true, page()->currentFrame()->title());
11 | }
12 |
--------------------------------------------------------------------------------
/src/FrameTitle.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 |
3 | class FrameTitle : public SocketCommand {
4 | Q_OBJECT
5 |
6 | public:
7 | FrameTitle(WebPageManager *, QStringList &arguments, QObject *parent = 0);
8 | virtual void start();
9 | };
10 |
11 |
--------------------------------------------------------------------------------
/src/FrameUrl.cpp:
--------------------------------------------------------------------------------
1 | #include "FrameUrl.h"
2 | #include "SocketCommand.h"
3 | #include "WebPage.h"
4 | #include "WebPageManager.h"
5 |
6 | FrameUrl::FrameUrl(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
7 | }
8 |
9 | void FrameUrl::start() {
10 | QVariant result = page()->currentFrame()->evaluateJavaScript("window.location.toString()");
11 | QString url = result.toString();
12 | finish(true, url);
13 | }
14 |
--------------------------------------------------------------------------------
/src/FrameUrl.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 |
3 | class FrameUrl : public SocketCommand {
4 | Q_OBJECT
5 |
6 | public:
7 | FrameUrl(WebPageManager *, QStringList &arguments, QObject *parent = 0);
8 | virtual void start();
9 | };
10 |
11 |
--------------------------------------------------------------------------------
/src/GetCookies.cpp:
--------------------------------------------------------------------------------
1 | #include "GetCookies.h"
2 | #include "WebPage.h"
3 | #include "WebPageManager.h"
4 | #include "NetworkCookieJar.h"
5 |
6 | GetCookies::GetCookies(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent)
7 | {
8 | m_buffer = "";
9 | }
10 |
11 | void GetCookies::start()
12 | {
13 | NetworkCookieJar *jar = manager()->cookieJar();
14 | foreach (QNetworkCookie cookie, jar->getAllCookies()) {
15 | m_buffer.append(cookie.toRawForm());
16 | m_buffer.append("\n");
17 | }
18 | finish(true, m_buffer);
19 | }
20 |
--------------------------------------------------------------------------------
/src/GetCookies.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 |
3 | class GetCookies : public SocketCommand {
4 | Q_OBJECT;
5 |
6 | public:
7 | GetCookies(WebPageManager *, QStringList &arguments, QObject *parent = 0);
8 | virtual void start();
9 |
10 | private:
11 | QString m_buffer;
12 | };
13 |
--------------------------------------------------------------------------------
/src/GetTimeout.cpp:
--------------------------------------------------------------------------------
1 | #include "GetTimeout.h"
2 | #include "WebPageManager.h"
3 |
4 | GetTimeout::GetTimeout(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
5 | }
6 |
7 | void GetTimeout::start() {
8 | finish(true, QString::number(manager()->getTimeout()));
9 | }
10 |
--------------------------------------------------------------------------------
/src/GetTimeout.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 |
3 | class WebPageManager;
4 |
5 | class GetTimeout : public SocketCommand {
6 | Q_OBJECT;
7 |
8 | public:
9 | GetTimeout(WebPageManager *page, QStringList &arguments, QObject *parent = 0);
10 | virtual void start();
11 | };
12 |
--------------------------------------------------------------------------------
/src/GetWindowHandle.cpp:
--------------------------------------------------------------------------------
1 | #include "GetWindowHandle.h"
2 | #include "WebPage.h"
3 | #include "WebPageManager.h"
4 | #include
5 |
6 | GetWindowHandle::GetWindowHandle(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
7 | }
8 |
9 | void GetWindowHandle::start() {
10 | finish(true, page()->uuid());
11 | }
12 |
--------------------------------------------------------------------------------
/src/GetWindowHandle.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 |
3 | class GetWindowHandle : public SocketCommand {
4 | Q_OBJECT
5 |
6 | public:
7 | GetWindowHandle(WebPageManager *, QStringList &arguments, QObject *parent = 0);
8 | virtual void start();
9 | };
10 |
11 |
--------------------------------------------------------------------------------
/src/GetWindowHandles.cpp:
--------------------------------------------------------------------------------
1 | #include "GetWindowHandles.h"
2 | #include "WebPageManager.h"
3 | #include "CommandFactory.h"
4 | #include "WebPage.h"
5 | #include "JsonSerializer.h"
6 | #include
7 |
8 | GetWindowHandles::GetWindowHandles(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
9 | }
10 |
11 | void GetWindowHandles::start() {
12 | QVariantList handles;
13 |
14 | foreach(WebPage *page, manager()->pages())
15 | handles << page->uuid();
16 |
17 | JsonSerializer serializer;
18 | QByteArray json = serializer.serialize(handles);
19 |
20 | finish(true, json);
21 | }
22 |
--------------------------------------------------------------------------------
/src/GetWindowHandles.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 |
3 | class GetWindowHandles : public SocketCommand {
4 | Q_OBJECT
5 |
6 | public:
7 | GetWindowHandles(WebPageManager *, QStringList &arguments, QObject *parent = 0);
8 | virtual void start();
9 | };
10 |
11 |
--------------------------------------------------------------------------------
/src/GoBack.cpp:
--------------------------------------------------------------------------------
1 | #include "GoBack.h"
2 | #include "SocketCommand.h"
3 | #include "WebPage.h"
4 | #include "WebPageManager.h"
5 |
6 | GoBack::GoBack(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
7 | }
8 |
9 | void GoBack::start() {
10 | page()->triggerAction(QWebPage::Back);
11 | finish(true);
12 | }
13 |
--------------------------------------------------------------------------------
/src/GoBack.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 |
3 | class GoBack : public SocketCommand {
4 | Q_OBJECT
5 |
6 | public:
7 | GoBack(WebPageManager *, QStringList &arguments, QObject *parent = 0);
8 | virtual void start();
9 | };
10 |
11 |
--------------------------------------------------------------------------------
/src/GoForward.cpp:
--------------------------------------------------------------------------------
1 | #include "GoForward.h"
2 | #include "SocketCommand.h"
3 | #include "WebPage.h"
4 | #include "WebPageManager.h"
5 |
6 | GoForward::GoForward(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
7 | }
8 |
9 | void GoForward::start() {
10 | page()->triggerAction(QWebPage::Forward);
11 | finish(true);
12 | }
13 |
--------------------------------------------------------------------------------
/src/GoForward.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 |
3 | class GoForward : public SocketCommand {
4 | Q_OBJECT
5 |
6 | public:
7 | GoForward(WebPageManager *, QStringList &arguments, QObject *parent = 0);
8 | virtual void start();
9 | };
10 |
11 |
--------------------------------------------------------------------------------
/src/Header.cpp:
--------------------------------------------------------------------------------
1 | #include "Header.h"
2 | #include "WebPage.h"
3 | #include "WebPageManager.h"
4 | #include "NetworkAccessManager.h"
5 |
6 | Header::Header(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
7 | }
8 |
9 | void Header::start() {
10 | QString key = arguments()[0];
11 | QString value = arguments()[1];
12 | if (key.toLower().replace("-", "_") == "user_agent") {
13 | page()->setUserAgent(value);
14 | } else {
15 | manager()->addHeader(key, value);
16 | }
17 | finish(true);
18 | }
19 |
--------------------------------------------------------------------------------
/src/Header.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 |
3 | class Header : public SocketCommand {
4 | Q_OBJECT
5 |
6 | public:
7 | Header(WebPageManager *, QStringList &arguments, QObject *parent = 0);
8 | virtual void start();
9 | };
10 |
--------------------------------------------------------------------------------
/src/Headers.cpp:
--------------------------------------------------------------------------------
1 | #include "Headers.h"
2 | #include "WebPage.h"
3 | #include "WebPageManager.h"
4 | #include "JsonSerializer.h"
5 |
6 | Headers::Headers(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
7 | }
8 |
9 | void Headers::start() {
10 | JsonSerializer serializer;
11 | QByteArray json = serializer.serialize(page()->pageHeaders());
12 | finish(true, json);
13 | }
14 |
15 |
--------------------------------------------------------------------------------
/src/Headers.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 |
3 | class Headers : public SocketCommand {
4 | Q_OBJECT
5 |
6 | public:
7 | Headers(WebPageManager *, QStringList &arguments, QObject *parent = 0);
8 | virtual void start();
9 | };
10 |
11 |
--------------------------------------------------------------------------------
/src/IgnoreDebugOutput.cpp:
--------------------------------------------------------------------------------
1 | #include "IgnoreDebugOutput.h"
2 |
3 | #include
4 | #include
5 | #include
6 |
7 | void debugIgnoringMessageHandler(QtMsgType type, const char *msg);
8 |
9 | #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
10 | void debugIgnoringMessageHandlerQt5(QtMsgType type, const QMessageLogContext &context, const QString &message);
11 | #endif
12 |
13 | void debugIgnoringMessageHandler(QtMsgType type, const char *msg) {
14 | switch (type) {
15 | case QtDebugMsg:
16 | case QtWarningMsg:
17 | break;
18 | default:
19 | fprintf(stderr, "%s\n", msg);
20 | break;
21 | }
22 | }
23 |
24 | #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
25 | void debugIgnoringMessageHandlerQt5(QtMsgType type, const QMessageLogContext &context, const QString &message) {
26 | Q_UNUSED(context);
27 | debugIgnoringMessageHandler(type, message.toLocal8Bit().data());
28 | }
29 | #endif
30 |
31 | void ignoreDebugOutput(void) {
32 | #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
33 | qInstallMessageHandler(debugIgnoringMessageHandlerQt5);
34 | #else
35 | qInstallMsgHandler(debugIgnoringMessageHandler);
36 | #endif
37 | }
38 |
--------------------------------------------------------------------------------
/src/IgnoreDebugOutput.h:
--------------------------------------------------------------------------------
1 | void ignoreDebugOutput(void);
2 |
--------------------------------------------------------------------------------
/src/IgnoreSslErrors.cpp:
--------------------------------------------------------------------------------
1 | #include "IgnoreSslErrors.h"
2 | #include "WebPage.h"
3 | #include "WebPageManager.h"
4 |
5 | IgnoreSslErrors::IgnoreSslErrors(WebPageManager *manager, QStringList &arguments, QObject *parent) :
6 | SocketCommand(manager, arguments, parent) {
7 | }
8 |
9 | void IgnoreSslErrors::start() {
10 | manager()->setIgnoreSslErrors(true);
11 | finish(true);
12 | }
13 |
14 |
--------------------------------------------------------------------------------
/src/IgnoreSslErrors.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 |
3 | class IgnoreSslErrors : public SocketCommand {
4 | Q_OBJECT
5 |
6 | public:
7 | IgnoreSslErrors(WebPageManager *manager, QStringList &arguments, QObject *parent = 0);
8 | virtual void start();
9 | };
10 |
11 |
--------------------------------------------------------------------------------
/src/InvocationResult.cpp:
--------------------------------------------------------------------------------
1 | #include "InvocationResult.h"
2 | #include "ErrorMessage.h"
3 |
4 | InvocationResult::InvocationResult(QVariant result, bool error) {
5 | m_result = result;
6 | m_error = error;
7 | }
8 |
9 | const QVariant &InvocationResult::result() const {
10 | return m_result;
11 | }
12 |
13 | bool InvocationResult::hasError() {
14 | return m_error;
15 | }
16 |
17 | ErrorMessage *InvocationResult::errorMessage() {
18 | if (!m_result.canConvert())
19 | return new ErrorMessage(m_result.toString());
20 |
21 | QVariantMap error = m_result.toMap();
22 |
23 | QString message = error["message"].toString();
24 |
25 | if (error["name"] == "Capybara.ClickFailed")
26 | return new ErrorMessage("ClickFailed", message);
27 | else if (error["name"] == "Capybara.NodeNotAttachedError")
28 | return new ErrorMessage("NodeNotAttachedError", message);
29 | else
30 | return new ErrorMessage(message);
31 | }
32 |
--------------------------------------------------------------------------------
/src/InvocationResult.h:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | class ErrorMessage;
4 |
5 | class InvocationResult {
6 | public:
7 | InvocationResult(QVariant result, bool error = false);
8 | const QVariant &result() const;
9 | bool hasError();
10 | ErrorMessage *errorMessage();
11 |
12 | private:
13 | QVariant m_result;
14 | bool m_error;
15 | };
16 |
17 |
--------------------------------------------------------------------------------
/src/JavascriptAlertMessages.cpp:
--------------------------------------------------------------------------------
1 | #include "JavascriptAlertMessages.h"
2 | #include "WebPage.h"
3 | #include "WebPageManager.h"
4 | #include "JsonSerializer.h"
5 |
6 | JavascriptAlertMessages::JavascriptAlertMessages(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {}
7 |
8 | void JavascriptAlertMessages::start()
9 | {
10 | JsonSerializer serializer;
11 | QByteArray json = serializer.serialize(page()->alertMessages());
12 | finish(true, json);
13 | }
14 |
--------------------------------------------------------------------------------
/src/JavascriptAlertMessages.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 |
3 | class JavascriptAlertMessages : public SocketCommand {
4 | Q_OBJECT
5 |
6 | public:
7 | JavascriptAlertMessages(WebPageManager *manager, QStringList &arguments, QObject *parent = 0);
8 | virtual void start();
9 | };
10 |
--------------------------------------------------------------------------------
/src/JavascriptCommand.cpp:
--------------------------------------------------------------------------------
1 | #include "JavascriptCommand.h"
2 | #include "WebPageManager.h"
3 | #include "InvocationResult.h"
4 |
5 | JavascriptCommand::JavascriptCommand(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
6 | }
7 |
8 | void JavascriptCommand::finish(InvocationResult *result) {
9 | if (result->hasError())
10 | SocketCommand::finish(false, result->errorMessage());
11 | else {
12 | QString message = result->result().toString();
13 | SocketCommand::finish(true, message);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/JavascriptCommand.h:
--------------------------------------------------------------------------------
1 | #ifndef JAVASCRIPT_COMMAND_H
2 | #define JAVASCRIPT_COMMAND_H
3 |
4 | #include
5 | #include
6 | #include "SocketCommand.h"
7 |
8 | class WebPage;
9 | class WebPageManager;
10 | class InvocationResult;
11 |
12 | class JavascriptCommand : public SocketCommand {
13 | Q_OBJECT
14 |
15 | public:
16 | JavascriptCommand(WebPageManager *, QStringList &arguments, QObject *parent = 0);
17 | void finish(InvocationResult *result);
18 | };
19 |
20 | #endif
21 |
--------------------------------------------------------------------------------
/src/JavascriptConfirmMessages.cpp:
--------------------------------------------------------------------------------
1 | #include "JavascriptConfirmMessages.h"
2 | #include "WebPage.h"
3 | #include "WebPageManager.h"
4 | #include "JsonSerializer.h"
5 |
6 | JavascriptConfirmMessages::JavascriptConfirmMessages(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {}
7 |
8 | void JavascriptConfirmMessages::start()
9 | {
10 | JsonSerializer serializer;
11 | QByteArray json = serializer.serialize(page()->confirmMessages());
12 | finish(true, json);
13 | }
14 |
--------------------------------------------------------------------------------
/src/JavascriptConfirmMessages.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 |
3 | class JavascriptConfirmMessages : public SocketCommand {
4 | Q_OBJECT
5 |
6 | public:
7 | JavascriptConfirmMessages(WebPageManager *manager, QStringList &arguments, QObject *parent = 0);
8 | virtual void start();
9 | };
10 |
--------------------------------------------------------------------------------
/src/JavascriptInvocation.h:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 |
7 | class WebPage;
8 | class InvocationResult;
9 |
10 | class JavascriptInvocation : public QObject {
11 | Q_OBJECT
12 | Q_PROPERTY(QString functionName READ functionName)
13 | Q_PROPERTY(bool allowUnattached READ allowUnattached)
14 | Q_PROPERTY(QStringList arguments READ arguments)
15 | Q_PROPERTY(QVariant error READ getError WRITE setError)
16 | Q_PROPERTY(Qt::Key key_enum)
17 |
18 | public:
19 | JavascriptInvocation(const QString &functionName, bool allowUnattached, const QStringList &arguments, WebPage *page, QObject *parent = 0);
20 | QString &functionName();
21 | bool allowUnattached();
22 | QStringList &arguments();
23 | Q_INVOKABLE void leftClick(int x, int y, QVariantList keys);
24 | Q_INVOKABLE void rightClick(int x, int y, QVariantList keys);
25 | Q_INVOKABLE void doubleClick(int x, int y, QVariantList keys);
26 | Q_INVOKABLE bool clickTest(QWebElement element, int absoluteX, int absoluteY);
27 | Q_INVOKABLE QVariantMap clickPosition(QWebElement element, int left, int top, int width, int height);
28 | Q_INVOKABLE void hover(int absoluteX, int absoluteY);
29 | Q_INVOKABLE void keypress(QChar);
30 | Q_INVOKABLE void namedKeydown(QString keyName);
31 | Q_INVOKABLE void namedKeyup(QString keyName);
32 | Q_INVOKABLE void namedKeypress(QString keyName, QString modifiers);
33 | Q_INVOKABLE const QString render(void);
34 | QVariant getError();
35 | void setError(QVariant error);
36 | InvocationResult invoke(QWebFrame *);
37 |
38 | private:
39 | QString m_functionName;
40 | bool m_allowUnattached;
41 | QStringList m_arguments;
42 | WebPage *m_page;
43 | QVariant m_error;
44 | void hover(const QPoint &);
45 | int keyCodeFor(const QChar &);
46 | int keyCodeForName(const QString &);
47 | Qt::Key key_enum;
48 | Qt::KeyboardModifiers m_currentModifiers;
49 | Qt::KeyboardModifiers modifiers(const QVariantList& keys);
50 | static QMap makeModifiersMap();
51 | static QMap m_modifiersMap;
52 | };
53 |
54 |
--------------------------------------------------------------------------------
/src/JavascriptPromptMessages.cpp:
--------------------------------------------------------------------------------
1 | #include "JavascriptPromptMessages.h"
2 | #include "WebPage.h"
3 | #include "WebPageManager.h"
4 | #include "JsonSerializer.h"
5 |
6 | JavascriptPromptMessages::JavascriptPromptMessages(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {}
7 |
8 | void JavascriptPromptMessages::start()
9 | {
10 | JsonSerializer serializer;
11 | QByteArray json = serializer.serialize(page()->promptMessages());
12 | finish(true, json);
13 | }
14 |
--------------------------------------------------------------------------------
/src/JavascriptPromptMessages.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 |
3 | class JavascriptPromptMessages : public SocketCommand {
4 | Q_OBJECT
5 |
6 | public:
7 | JavascriptPromptMessages(WebPageManager *manager, QStringList &arguments, QObject *parent = 0);
8 | virtual void start();
9 | };
10 |
--------------------------------------------------------------------------------
/src/JsonSerializer.cpp:
--------------------------------------------------------------------------------
1 | #include "JsonSerializer.h"
2 | #include
3 |
4 | JsonSerializer::JsonSerializer(QObject *parent) : QObject(parent) {
5 | }
6 |
7 | QByteArray JsonSerializer::serialize(const QVariant &object) {
8 | addVariant(object);
9 | return m_buffer;
10 | }
11 |
12 | void JsonSerializer::addVariant(const QVariant &object) {
13 | if (object.isValid()) {
14 | switch(object.type()) {
15 | case QMetaType::QString:
16 | case QMetaType::QDateTime:
17 | {
18 | addString(object.toString());
19 | }
20 | break;
21 | case QMetaType::QVariantList:
22 | {
23 | QVariantList list = object.toList();
24 | addArray(list);
25 | }
26 | break;
27 | case QMetaType::Double:
28 | if (std::isinf(object.toDouble()))
29 | m_buffer.append("null");
30 | else
31 | m_buffer.append(object.toString());
32 | break;
33 | case QMetaType::QVariantMap:
34 | {
35 | QVariantMap map = object.toMap();
36 | addMap(map);
37 | break;
38 | }
39 | case QMetaType::Bool:
40 | {
41 | m_buffer.append(object.toString());
42 | break;
43 | }
44 | case QMetaType::Int:
45 | {
46 | m_buffer.append(object.toString());
47 | break;
48 | }
49 | default:
50 | m_buffer.append("null");
51 | }
52 | } else {
53 | m_buffer.append("null");
54 | }
55 | }
56 |
57 | void JsonSerializer::addString(const QString &string) {
58 | m_buffer.append("\"");
59 | m_buffer.append(sanitizeString(string));
60 | m_buffer.append("\"");
61 | }
62 |
63 | void JsonSerializer::addArray(const QVariantList &list) {
64 | m_buffer.append("[");
65 | for (int i = 0; i < list.length(); i++) {
66 | if (i > 0)
67 | m_buffer.append(",");
68 | addVariant(list[i]);
69 | }
70 | m_buffer.append("]");
71 | }
72 |
73 | void JsonSerializer::addMap(const QVariantMap &map) {
74 | m_buffer.append("{");
75 | QMapIterator iterator(map);
76 | while (iterator.hasNext()) {
77 | iterator.next();
78 | QString key = iterator.key();
79 | QVariant value = iterator.value();
80 | addString(key);
81 | m_buffer.append(":");
82 | addVariant(value);
83 | if (iterator.hasNext())
84 | m_buffer.append(",");
85 | }
86 | m_buffer.append("}");
87 | }
88 |
89 | QByteArray JsonSerializer::sanitizeString(QString str) {
90 | str.replace("\\", "\\\\");
91 | str.replace("\"", "\\\"");
92 | str.replace("\b", "\\b");
93 | str.replace("\f", "\\f");
94 | str.replace("\n", "\\n");
95 | str.replace("\r", "\\r");
96 | str.replace("\t", "\\t");
97 |
98 | QByteArray result;
99 | const ushort* unicode = str.utf16();
100 | unsigned int i = 0;
101 |
102 | while (unicode[i]) {
103 | if (unicode[i] > 31 && unicode[i] < 128) {
104 | result.append(unicode[i]);
105 | }
106 | else {
107 | QString hexCode = QString::number(unicode[i], 16).rightJustified(4, '0');
108 |
109 | result.append("\\u").append(hexCode);
110 | }
111 | ++i;
112 | }
113 |
114 | return result;
115 | }
116 |
117 |
--------------------------------------------------------------------------------
/src/JsonSerializer.h:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | class JsonSerializer : public QObject {
5 | Q_OBJECT
6 |
7 | public:
8 | JsonSerializer(QObject *parent = 0);
9 | QByteArray serialize(const QVariant &object);
10 |
11 | private:
12 | void addVariant(const QVariant &object);
13 | void addString(const QString &string);
14 | void addArray(const QVariantList &list);
15 | void addMap(const QVariantMap &map);
16 | QByteArray sanitizeString(QString string);
17 |
18 | QByteArray m_buffer;
19 | };
20 |
21 |
--------------------------------------------------------------------------------
/src/MissingContentHeaderRequestHandler.cpp:
--------------------------------------------------------------------------------
1 | #include "MissingContentHeaderRequestHandler.h"
2 | #include "NetworkReplyProxy.h"
3 | #include "NoOpReply.h"
4 |
5 | MissingContentHeaderRequestHandler::MissingContentHeaderRequestHandler(
6 | RequestHandler *next,
7 | QObject *parent
8 | ) : RequestHandler(parent) {
9 | m_next = next;
10 | }
11 |
12 | QNetworkReply* MissingContentHeaderRequestHandler::handleRequest(
13 | NetworkAccessManager *manager,
14 | QNetworkAccessManager::Operation operation,
15 | QNetworkRequest &request,
16 | QIODevice *outgoingData
17 | ) {
18 | if (operation != QNetworkAccessManager::PostOperation && operation != QNetworkAccessManager::PutOperation) {
19 | request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant());
20 | }
21 |
22 | return m_next->handleRequest(manager, operation, request, outgoingData);
23 | }
24 |
--------------------------------------------------------------------------------
/src/MissingContentHeaderRequestHandler.h:
--------------------------------------------------------------------------------
1 | #include "RequestHandler.h"
2 |
3 | class MissingContentHeaderRequestHandler : public RequestHandler {
4 | public:
5 | MissingContentHeaderRequestHandler(RequestHandler *next, QObject *parent = 0);
6 | virtual QNetworkReply* handleRequest(
7 | NetworkAccessManager *,
8 | QNetworkAccessManager::Operation,
9 | QNetworkRequest &,
10 | QIODevice *
11 | );
12 |
13 | private:
14 | RequestHandler *m_next;
15 | };
16 |
--------------------------------------------------------------------------------
/src/NetworkAccessManager.cpp:
--------------------------------------------------------------------------------
1 | #include "NetworkAccessManager.h"
2 | #include "WebPage.h"
3 | #include "NetworkReplyProxy.h"
4 | #include "RequestHandler.h"
5 |
6 | NetworkAccessManager::NetworkAccessManager(
7 | RequestHandler * requestHandler,
8 | QObject *parent
9 | ) : QNetworkAccessManager(parent) {
10 | m_requestHandler = requestHandler;
11 | connect(this, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)), SLOT(provideAuthentication(QNetworkReply*,QAuthenticator*)));
12 | connect(this, SIGNAL(finished(QNetworkReply *)), this, SLOT(finished(QNetworkReply *)));
13 | disableKeyChainLookup();
14 | }
15 |
16 | QNetworkReply* NetworkAccessManager::sendRequest(
17 | QNetworkAccessManager::Operation operation,
18 | const QNetworkRequest &request,
19 | QIODevice * outgoingData
20 | ) {
21 | QNetworkReply *reply = new NetworkReplyProxy(
22 | QNetworkAccessManager::createRequest(operation,
23 | request,
24 | outgoingData
25 | ),
26 | this
27 | );
28 |
29 | QByteArray url = reply->request().url().toEncoded();
30 | emit requestCreated(url, reply);
31 |
32 | return reply;
33 | }
34 |
35 | QNetworkReply* NetworkAccessManager::createRequest(
36 | QNetworkAccessManager::Operation operation,
37 | const QNetworkRequest &unsafeRequest,
38 | QIODevice * outgoingData = 0
39 | ) {
40 | QNetworkRequest request(unsafeRequest);
41 | QNetworkReply *reply =
42 | m_requestHandler->handleRequest(this, operation, request, outgoingData);
43 | return reply;
44 | };
45 |
46 | void NetworkAccessManager::finished(QNetworkReply *reply) {
47 | QUrl redirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
48 | if (redirectUrl.isValid())
49 | m_redirectMappings[reply->url().resolved(redirectUrl)] = reply->url();
50 | else {
51 | QUrl requestedUrl = reply->url();
52 | while (m_redirectMappings.contains(requestedUrl))
53 | requestedUrl = m_redirectMappings.take(requestedUrl);
54 | emit finished(requestedUrl, reply);
55 | }
56 | }
57 |
58 | void NetworkAccessManager::reset() {
59 | m_userName = QString();
60 | m_password = QString();
61 | }
62 |
63 | void NetworkAccessManager::setUserName(const QString &userName) {
64 | m_userName = userName;
65 | }
66 |
67 | void NetworkAccessManager::setPassword(const QString &password) {
68 | m_password = password;
69 | }
70 |
71 | void NetworkAccessManager::provideAuthentication(QNetworkReply *reply, QAuthenticator *authenticator) {
72 | Q_UNUSED(reply);
73 | if (m_userName != authenticator->user())
74 | authenticator->setUser(m_userName);
75 | if (m_password != authenticator->password())
76 | authenticator->setPassword(m_password);
77 | }
78 |
79 | /*
80 | * This is a workaround for a Qt 5/OS X bug:
81 | * https://bugreports.qt-project.org/browse/QTBUG-30434
82 | */
83 | void NetworkAccessManager::disableKeyChainLookup() {
84 | QNetworkProxy fixedProxy = proxy();
85 | fixedProxy.setHostName(" ");
86 | setProxy(fixedProxy);
87 | }
88 |
--------------------------------------------------------------------------------
/src/NetworkAccessManager.h:
--------------------------------------------------------------------------------
1 | #ifndef __NETWORKACCESSMANAGER_H
2 | #define __NETWORKACCESSMANAGER_H
3 | #include
4 | #include
5 | #include
6 |
7 | class RequestHandler;
8 |
9 | class NetworkAccessManager : public QNetworkAccessManager {
10 | Q_OBJECT
11 |
12 | public:
13 | NetworkAccessManager(RequestHandler *, QObject *parent = 0);
14 | void reset();
15 | void setUserName(const QString &userName);
16 | void setPassword(const QString &password);
17 | QNetworkReply* sendRequest(
18 | QNetworkAccessManager::Operation,
19 | const QNetworkRequest &,
20 | QIODevice *
21 | );
22 |
23 | protected:
24 | QNetworkReply* createRequest(
25 | QNetworkAccessManager::Operation,
26 | const QNetworkRequest &,
27 | QIODevice *
28 | );
29 | QString m_userName;
30 | QString m_password;
31 |
32 | private:
33 | void disableKeyChainLookup();
34 |
35 | QHash m_redirectMappings;
36 | RequestHandler * m_requestHandler;
37 |
38 | private slots:
39 | void provideAuthentication(QNetworkReply *reply, QAuthenticator *authenticator);
40 | void finished(QNetworkReply *);
41 |
42 | signals:
43 | void requestCreated(QByteArray &url, QNetworkReply *reply);
44 | void finished(QUrl &, QNetworkReply *);
45 | };
46 | #endif
47 |
--------------------------------------------------------------------------------
/src/NetworkCookieJar.cpp:
--------------------------------------------------------------------------------
1 | #include "NetworkCookieJar.h"
2 | #include "QtCore/qdatetime.h"
3 |
4 | NetworkCookieJar::NetworkCookieJar(QObject *parent)
5 | : QNetworkCookieJar(parent)
6 | { }
7 |
8 | QList NetworkCookieJar::getAllCookies() const
9 | {
10 | return allCookies();
11 | }
12 |
13 | void NetworkCookieJar::clearCookies()
14 | {
15 | setAllCookies(QList());
16 | }
17 |
18 | static inline bool isParentDomain(QString domain, QString reference)
19 | {
20 | if (!reference.startsWith(QLatin1Char('.')))
21 | return domain == reference;
22 |
23 | return domain.endsWith(reference) || domain == reference.mid(1);
24 | }
25 |
26 | void NetworkCookieJar::overwriteCookies(const QList& cookieList)
27 | {
28 | /* this function is basically a copy-and-paste of the original
29 | QNetworkCookieJar::setCookiesFromUrl with the domain and
30 | path validations removed */
31 |
32 | QString defaultPath(QLatin1Char('/'));
33 | QDateTime now = QDateTime::currentDateTime();
34 | QList newCookies = allCookies();
35 |
36 | foreach (QNetworkCookie cookie, cookieList) {
37 | bool isDeletion = (!cookie.isSessionCookie() &&
38 | cookie.expirationDate() < now);
39 |
40 | // validate the cookie & set the defaults if unset
41 | if (cookie.path().isEmpty())
42 | cookie.setPath(defaultPath);
43 |
44 | // don't do path checking. See http://bugreports.qt.nokia.com/browse/QTBUG-5815
45 | // else if (!isParentPath(pathAndFileName, cookie.path())) {
46 | // continue; // not accepted
47 | // }
48 |
49 | if (cookie.domain().isEmpty()) {
50 | continue;
51 | } else {
52 | // Ensure the domain starts with a dot if its field was not empty
53 | // in the HTTP header. There are some servers that forget the
54 | // leading dot and this is actually forbidden according to RFC 2109,
55 | // but all browsers accept it anyway so we do that as well.
56 | if (!cookie.domain().startsWith(QLatin1Char('.')))
57 | cookie.setDomain(QLatin1Char('.') + cookie.domain());
58 |
59 | QString domain = cookie.domain();
60 |
61 | // the check for effective TLDs makes the "embedded dot" rule from RFC 2109 section 4.3.2
62 | // redundant; the "leading dot" rule has been relaxed anyway, see above
63 | // we remove the leading dot for this check
64 | /*
65 | if (QNetworkCookieJarPrivate::isEffectiveTLD(domain.remove(0, 1)))
66 | continue; // not accepted
67 | */
68 | }
69 |
70 | for (int i = 0; i < newCookies.size(); ++i) {
71 | // does this cookie already exist?
72 | const QNetworkCookie ¤t = newCookies.at(i);
73 | if (cookie.name() == current.name() &&
74 | cookie.domain() == current.domain() &&
75 | cookie.path() == current.path()) {
76 | // found a match
77 | newCookies.removeAt(i);
78 | break;
79 | }
80 | }
81 |
82 | // did not find a match
83 | if (!isDeletion) {
84 | int countForDomain = 0;
85 | for (int i = newCookies.size() - 1; i >= 0; --i) {
86 | // Start from the end and delete the oldest cookies to keep a maximum count of 50.
87 | const QNetworkCookie ¤t = newCookies.at(i);
88 | if (isParentDomain(cookie.domain(), current.domain())
89 | || isParentDomain(current.domain(), cookie.domain())) {
90 | if (countForDomain >= 49)
91 | newCookies.removeAt(i);
92 | else
93 | ++countForDomain;
94 | }
95 | }
96 |
97 | newCookies += cookie;
98 | }
99 | }
100 | setAllCookies(newCookies);
101 | }
102 |
--------------------------------------------------------------------------------
/src/NetworkCookieJar.h:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | class NetworkCookieJar : public QNetworkCookieJar {
5 |
6 | Q_OBJECT;
7 |
8 | public:
9 |
10 | NetworkCookieJar(QObject *parent = 0);
11 |
12 | QList getAllCookies() const;
13 | void clearCookies();
14 | void overwriteCookies(const QList& cookieList);
15 | };
16 |
--------------------------------------------------------------------------------
/src/NetworkReplyProxy.cpp:
--------------------------------------------------------------------------------
1 | #include "NetworkReplyProxy.h"
2 |
3 | NetworkReplyProxy::NetworkReplyProxy(QNetworkReply* reply, QObject* parent)
4 | : QNetworkReply(parent)
5 | , m_reply(reply)
6 | {
7 | m_reply->setParent(this);
8 |
9 | setOperation(m_reply->operation());
10 | setRequest(m_reply->request());
11 | setUrl(m_reply->url());
12 |
13 | if (m_reply->isFinished()) {
14 | readInternal();
15 | setFinished(true);
16 | }
17 |
18 | applyMetaData();
19 |
20 | connect(m_reply, SIGNAL(metaDataChanged()), SLOT(applyMetaData()));
21 | connect(m_reply, SIGNAL(readyRead()), SLOT(handleReadyRead()));
22 | connect(m_reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(errorInternal(QNetworkReply::NetworkError)));
23 | connect(m_reply, SIGNAL(finished()), SLOT(handleFinished()));
24 |
25 | connect(m_reply, SIGNAL(uploadProgress(qint64,qint64)), SIGNAL(uploadProgress(qint64,qint64)));
26 | connect(m_reply, SIGNAL(downloadProgress(qint64,qint64)), SIGNAL(downloadProgress(qint64,qint64)));
27 | connect(m_reply, SIGNAL(sslErrors(const QList &)), SIGNAL(sslErrors(const QList &)));
28 |
29 | setOpenMode(ReadOnly);
30 | }
31 |
32 | void NetworkReplyProxy::abort() { m_reply->abort(); }
33 | void NetworkReplyProxy::close() { m_reply->close(); }
34 | bool NetworkReplyProxy::isSequential() const { return m_reply->isSequential(); }
35 |
36 | void NetworkReplyProxy::handleFinished() {
37 | setFinished(true);
38 | emit finished();
39 | }
40 |
41 | qint64 NetworkReplyProxy::bytesAvailable() const
42 | {
43 | return m_buffer.size() + QIODevice::bytesAvailable();
44 | }
45 |
46 | qint64 NetworkReplyProxy::readData(char* data, qint64 maxlen)
47 | {
48 | qint64 size = qMin(maxlen, qint64(m_buffer.size()));
49 | memcpy(data, m_buffer.constData(), size);
50 | m_buffer.remove(0, size);
51 | return size;
52 | }
53 |
54 | void NetworkReplyProxy::ignoreSslErrors() { m_reply->ignoreSslErrors(); }
55 | void NetworkReplyProxy::applyMetaData() {
56 | foreach(QNetworkReply::RawHeaderPair header, m_reply->rawHeaderPairs())
57 | setRawHeader(header.first, header.second);
58 |
59 | setAttribute(QNetworkRequest::HttpStatusCodeAttribute, m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute));
60 | setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, m_reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute));
61 | setAttribute(QNetworkRequest::RedirectionTargetAttribute, m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute));
62 | setAttribute(QNetworkRequest::ConnectionEncryptedAttribute, m_reply->attribute(QNetworkRequest::ConnectionEncryptedAttribute));
63 | setAttribute(QNetworkRequest::CacheLoadControlAttribute, m_reply->attribute(QNetworkRequest::CacheLoadControlAttribute));
64 | setAttribute(QNetworkRequest::CacheSaveControlAttribute, m_reply->attribute(QNetworkRequest::CacheSaveControlAttribute));
65 | setAttribute(QNetworkRequest::SourceIsFromCacheAttribute, m_reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute));
66 | setAttribute(QNetworkRequest::DoNotBufferUploadDataAttribute, m_reply->attribute(QNetworkRequest::DoNotBufferUploadDataAttribute));
67 | emit metaDataChanged();
68 | }
69 |
70 | void NetworkReplyProxy::errorInternal(QNetworkReply::NetworkError _error)
71 | {
72 | setError(_error, errorString());
73 | emit error(_error);
74 | }
75 |
76 | void NetworkReplyProxy::readInternal() {
77 | QByteArray data = m_reply->readAll();
78 | m_data += data;
79 | m_buffer += data;
80 | }
81 |
82 | void NetworkReplyProxy::handleReadyRead()
83 | {
84 | readInternal();
85 | emit readyRead();
86 | }
87 |
88 | QByteArray NetworkReplyProxy::data() {
89 | return m_data;
90 | }
91 |
92 |
--------------------------------------------------------------------------------
/src/NetworkReplyProxy.h:
--------------------------------------------------------------------------------
1 | #ifndef _NETWORKREPLYPROXY_H
2 | #define _NETWORKREPLYPROXY_H
3 | /*
4 | * Copyright (C) 2009, 2010 Nokia Corporation and/or its subsidiary(-ies)
5 | * Copyright (C) 2009, 2010 Holger Hans Peter Freyther
6 | *
7 | * All rights reserved.
8 | *
9 | * Redistribution and use in source and binary forms, with or without
10 | * modification, are permitted provided that the following conditions
11 | * are met:
12 | * 1. Redistributions of source code must retain the above copyright
13 | * notice, this list of conditions and the following disclaimer.
14 | * 2. Redistributions in binary form must reproduce the above copyright
15 | * notice, this list of conditions and the following disclaimer in the
16 | * documentation and/or other materials provided with the distribution.
17 | *
18 | * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
19 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
22 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
26 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 | */
30 |
31 | #include
32 | #include
33 |
34 | class NetworkReplyProxy : public QNetworkReply {
35 | Q_OBJECT
36 |
37 | public:
38 | NetworkReplyProxy(QNetworkReply* reply, QObject* parent);
39 |
40 | virtual void abort();
41 | virtual void close();
42 | virtual bool isSequential() const;
43 |
44 | virtual qint64 bytesAvailable() const;
45 |
46 | virtual qint64 readData(char* data, qint64 maxlen);
47 |
48 | QByteArray data();
49 |
50 | public slots:
51 | void ignoreSslErrors();
52 | void applyMetaData();
53 | void errorInternal(QNetworkReply::NetworkError _error);
54 | void handleReadyRead();
55 | void handleFinished();
56 |
57 | private:
58 | void readInternal();
59 |
60 | QNetworkReply* m_reply;
61 | QByteArray m_data;
62 | QByteArray m_buffer;
63 | };
64 |
65 | #endif
66 |
--------------------------------------------------------------------------------
/src/NetworkRequestFactory.cpp:
--------------------------------------------------------------------------------
1 | #include "NetworkRequestFactory.h"
2 | #include "NetworkAccessManager.h"
3 |
4 | NetworkRequestFactory::NetworkRequestFactory(QObject *parent) :
5 | RequestHandler(parent) {
6 | }
7 |
8 | QNetworkReply* NetworkRequestFactory::handleRequest(
9 | NetworkAccessManager *manager,
10 | QNetworkAccessManager::Operation operation,
11 | QNetworkRequest &request,
12 | QIODevice *outgoingData
13 | ) {
14 | return manager->sendRequest(operation, request, outgoingData);
15 | }
16 |
--------------------------------------------------------------------------------
/src/NetworkRequestFactory.h:
--------------------------------------------------------------------------------
1 | #include "RequestHandler.h"
2 |
3 | class NetworkRequestFactory : public RequestHandler {
4 | public:
5 | NetworkRequestFactory(QObject *parent = 0);
6 | virtual QNetworkReply* handleRequest(
7 | NetworkAccessManager *,
8 | QNetworkAccessManager::Operation,
9 | QNetworkRequest &,
10 | QIODevice *
11 | );
12 | };
13 |
--------------------------------------------------------------------------------
/src/NoOpReply.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include "NoOpReply.h"
3 |
4 | NoOpReply::NoOpReply(const QNetworkRequest &request, QObject *parent) : QNetworkReply(parent) {
5 | open(ReadOnly | Unbuffered);
6 | setAttribute(QNetworkRequest::HttpStatusCodeAttribute, 200);
7 | setHeader(QNetworkRequest::ContentLengthHeader, QVariant(0));
8 | setHeader(QNetworkRequest::ContentTypeHeader, QVariant(QString("text/plain")));
9 | setUrl(request.url());
10 |
11 | QTimer::singleShot( 0, this, SIGNAL(readyRead()) );
12 | QTimer::singleShot( 0, this, SIGNAL(finished()) );
13 | }
14 |
15 | void NoOpReply::abort() {
16 | // NO-OP
17 | }
18 |
19 | qint64 NoOpReply::bytesAvailable() const {
20 | return 0;
21 | }
22 |
23 | bool NoOpReply::isSequential() const {
24 | return true;
25 | }
26 |
27 | qint64 NoOpReply::readData(char *data, qint64 maxSize) {
28 | Q_UNUSED(data);
29 | Q_UNUSED(maxSize);
30 | return 0;
31 | }
32 |
33 |
--------------------------------------------------------------------------------
/src/NoOpReply.h:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | class NoOpReply : public QNetworkReply {
5 | Q_OBJECT
6 |
7 | public:
8 | NoOpReply(const QNetworkRequest &request, QObject *parent = 0);
9 |
10 | void abort();
11 | qint64 bytesAvailable() const;
12 | bool isSequential() const;
13 |
14 | protected:
15 | qint64 readData(char *data, qint64 maxSize);
16 |
17 | };
18 |
19 |
--------------------------------------------------------------------------------
/src/Node.cpp:
--------------------------------------------------------------------------------
1 | #include "Node.h"
2 | #include "WebPage.h"
3 | #include "WebPageManager.h"
4 | #include "JsonSerializer.h"
5 | #include "InvocationResult.h"
6 |
7 | Node::Node(WebPageManager *manager, QStringList &arguments, QObject *parent) : JavascriptCommand(manager, arguments, parent) {
8 | }
9 |
10 | void Node::start() {
11 | QStringList functionArguments(arguments());
12 | QString functionName = functionArguments.takeFirst();
13 | QString allowUnattached = functionArguments.takeFirst();
14 | InvocationResult result = page()->invokeCapybaraFunction(functionName, allowUnattached == "true", functionArguments);
15 | if (functionName == "focus_frame") {
16 | page()->setCurrentFrameParent(page()->currentFrame()->parentFrame());
17 | }
18 |
19 | if (result.hasError()) {
20 | finish(&result);
21 | } else {
22 | JsonSerializer serializer;
23 | InvocationResult jsonResult = InvocationResult(serializer.serialize(result.result()));
24 | finish(&jsonResult);
25 | }
26 | }
27 |
28 | QString Node::toString() const {
29 | QStringList functionArguments(arguments());
30 | return QString("Node.") + functionArguments.takeFirst();
31 | }
32 |
--------------------------------------------------------------------------------
/src/Node.h:
--------------------------------------------------------------------------------
1 | #include "JavascriptCommand.h"
2 | #include
3 |
4 | class Node : public JavascriptCommand {
5 | Q_OBJECT
6 |
7 | public:
8 | Node(WebPageManager *manager, QStringList &arguments, QObject *parent = 0);
9 | virtual void start();
10 | virtual QString toString() const;
11 | };
12 |
13 |
--------------------------------------------------------------------------------
/src/NullCommand.cpp:
--------------------------------------------------------------------------------
1 | #include "NullCommand.h"
2 | #include "WebPage.h"
3 | #include "WebPageManager.h"
4 | #include "ErrorMessage.h"
5 |
6 | NullCommand::NullCommand(QString name, QObject *parent) : Command(parent) {
7 | m_name = name;
8 | }
9 |
10 | void NullCommand::start() {
11 | QString failure = QString("[Capybara WebKit] Unknown command: ") + m_name + "\n";
12 | finish(false, new ErrorMessage(failure));
13 | }
14 |
15 |
--------------------------------------------------------------------------------
/src/NullCommand.h:
--------------------------------------------------------------------------------
1 | #include "Command.h"
2 |
3 | class NullCommand : public Command {
4 | Q_OBJECT
5 |
6 | public:
7 | NullCommand(QString name, QObject *parent = 0);
8 | virtual void start();
9 |
10 | private:
11 | QString m_name;
12 | };
13 |
--------------------------------------------------------------------------------
/src/PageLoadingCommand.cpp:
--------------------------------------------------------------------------------
1 | #include "PageLoadingCommand.h"
2 | #include "SocketCommand.h"
3 | #include "WebPage.h"
4 | #include "WebPageManager.h"
5 | #include "ErrorMessage.h"
6 |
7 | PageLoadingCommand::PageLoadingCommand(Command *command, WebPageManager *manager, QObject *parent) : Command(parent) {
8 | m_manager = manager;
9 | m_command = command;
10 | m_pageLoadingFromCommand = false;
11 | m_pageSuccess = true;
12 | m_pendingResponse = NULL;
13 | m_command->setParent(this);
14 | }
15 |
16 | void PageLoadingCommand::start() {
17 | m_manager->log() << "Started" << m_command->toString();
18 | connect(m_command, SIGNAL(finished(Response *)), this, SLOT(commandFinished(Response *)));
19 | connect(m_manager, SIGNAL(loadStarted()), this, SLOT(pageLoadingFromCommand()));
20 | connect(m_manager, SIGNAL(pageFinished(bool)), this, SLOT(pendingLoadFinished(bool)));
21 | m_command->start();
22 | };
23 |
24 | void PageLoadingCommand::pendingLoadFinished(bool success) {
25 | m_pageSuccess = success;
26 | if (m_pageLoadingFromCommand) {
27 | m_pageLoadingFromCommand = false;
28 | if (m_pendingResponse) {
29 | m_manager->log() << "Page load from command finished";
30 | if (m_pageSuccess) {
31 | emit finished(m_pendingResponse);
32 | } else {
33 | QString message = m_manager->currentPage()->failureString();
34 | finish(false, new ErrorMessage(message));
35 | }
36 | }
37 | }
38 | }
39 |
40 | void PageLoadingCommand::pageLoadingFromCommand() {
41 | m_manager->log() << m_command->toString() << "started page load";
42 | m_pageLoadingFromCommand = true;
43 | }
44 |
45 | void PageLoadingCommand::commandFinished(Response *response) {
46 | disconnect(m_manager, SIGNAL(loadStarted()), this, SLOT(pageLoadingFromCommand()));
47 | m_manager->log() << "Finished" << m_command->toString() << "with response" << response->toString();
48 |
49 | if (m_pageLoadingFromCommand)
50 | m_pendingResponse = response;
51 | else
52 | emit finished(response);
53 | }
54 |
--------------------------------------------------------------------------------
/src/PageLoadingCommand.h:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include "Command.h"
4 |
5 | class Response;
6 | class WebPageManager;
7 |
8 | /*
9 | * Decorates a Command by deferring the finished() signal until any pending
10 | * page loads are complete.
11 | *
12 | * If a Command starts a page load, no signal will be emitted until the page
13 | * load is finished.
14 | *
15 | * If a pending page load fails, the command's response will be discarded and a
16 | * failure response will be emitted instead.
17 | */
18 | class PageLoadingCommand : public Command {
19 | Q_OBJECT
20 |
21 | public:
22 | PageLoadingCommand(Command *command, WebPageManager *page, QObject *parent = 0);
23 | virtual void start();
24 |
25 | public slots:
26 | void pageLoadingFromCommand();
27 | void pendingLoadFinished(bool success);
28 | void commandFinished(Response *response);
29 |
30 | private:
31 | WebPageManager *m_manager;
32 | Command *m_command;
33 | Response *m_pendingResponse;
34 | bool m_pageSuccess;
35 | bool m_pageLoadingFromCommand;
36 | };
37 |
38 |
--------------------------------------------------------------------------------
/src/Refresh.cpp:
--------------------------------------------------------------------------------
1 | #include "Refresh.h"
2 | #include "SocketCommand.h"
3 | #include "WebPage.h"
4 | #include "WebPageManager.h"
5 |
6 | Refresh::Refresh(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
7 | }
8 |
9 | void Refresh::start() {
10 | page()->triggerAction(QWebPage::Reload);
11 | finish(true);
12 | }
13 |
--------------------------------------------------------------------------------
/src/Refresh.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 |
3 | class Refresh : public SocketCommand {
4 | Q_OBJECT
5 |
6 | public:
7 | Refresh(WebPageManager *, QStringList &arguments, QObject *parent = 0);
8 | virtual void start();
9 | };
10 |
11 |
--------------------------------------------------------------------------------
/src/Render.cpp:
--------------------------------------------------------------------------------
1 | #include "Render.h"
2 | #include "WebPage.h"
3 | #include "WebPageManager.h"
4 | #include "ErrorMessage.h"
5 |
6 | Render::Render(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
7 | }
8 |
9 | void Render::start() {
10 | QString imagePath = arguments()[0];
11 | int width = arguments()[1].toInt();
12 | int height = arguments()[2].toInt();
13 |
14 | QSize size(width, height);
15 |
16 | bool result = page()->render( imagePath, size );
17 |
18 | if (result) {
19 | finish(true);
20 | } else {
21 | const QString failure = QString("Unable to save %1x%2 image to %3").
22 | arg(width).
23 | arg(height).
24 | arg(imagePath);
25 | finish(false, new ErrorMessage(failure));
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/Render.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 | #include
3 |
4 | class Render : public SocketCommand {
5 | Q_OBJECT
6 |
7 | public:
8 | Render(WebPageManager *page, QStringList &arguments, QObject *parent = 0);
9 | virtual void start();
10 | };
11 |
--------------------------------------------------------------------------------
/src/RequestHandler.cpp:
--------------------------------------------------------------------------------
1 | #include "RequestHandler.h"
2 |
3 | RequestHandler::RequestHandler(QObject *parent) : QObject(parent) {
4 | }
5 |
--------------------------------------------------------------------------------
/src/RequestHandler.h:
--------------------------------------------------------------------------------
1 | #ifndef __REQUESTHANDLER_H
2 | #define __REQUESTHANDLER_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | class NetworkAccessManager;
10 |
11 | class RequestHandler : public QObject {
12 | Q_OBJECT
13 |
14 | public:
15 | RequestHandler(QObject *parent = 0);
16 |
17 | virtual QNetworkReply* handleRequest(
18 | NetworkAccessManager *,
19 | QNetworkAccessManager::Operation,
20 | QNetworkRequest &,
21 | QIODevice *
22 | ) = 0;
23 | };
24 |
25 | #endif
26 |
--------------------------------------------------------------------------------
/src/Reset.cpp:
--------------------------------------------------------------------------------
1 | #include "Reset.h"
2 | #include "WebPage.h"
3 | #include "WebPageManager.h"
4 |
5 | Reset::Reset(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
6 | }
7 |
8 | void Reset::start() {
9 | manager()->reset();
10 |
11 | finish(true);
12 | }
13 |
14 |
--------------------------------------------------------------------------------
/src/Reset.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 |
3 | class Reset : public SocketCommand {
4 | Q_OBJECT
5 |
6 | public:
7 | Reset(WebPageManager *, QStringList &arguments, QObject *parent = 0);
8 | virtual void start();
9 | };
10 |
11 |
--------------------------------------------------------------------------------
/src/Response.cpp:
--------------------------------------------------------------------------------
1 | #include "Response.h"
2 | #include "ErrorMessage.h"
3 | #include
4 |
5 | Response::Response(bool success, QString message, QObject *parent) : QObject(parent) {
6 | m_success = success;
7 | m_message = message.toUtf8();
8 | }
9 |
10 | Response::Response(bool success, QByteArray message, QObject *parent) : QObject(parent) {
11 | m_success = success;
12 | m_message = message;
13 | }
14 |
15 | Response::Response(bool success, ErrorMessage *message, QObject *parent) : QObject(parent) {
16 | m_success = success;
17 | m_message = message->toString();
18 | message->deleteLater();
19 | }
20 |
21 | Response::Response(bool success, QObject *parent) : QObject(parent) {
22 | m_success = success;
23 | }
24 |
25 | bool Response::isSuccess() const {
26 | return m_success;
27 | }
28 |
29 | QByteArray Response::message() const {
30 | return m_message;
31 | }
32 |
33 | QString Response::toString() const {
34 | return QString(m_success ? "Success(" : "Failure(") + m_message + ")";
35 | }
36 |
--------------------------------------------------------------------------------
/src/Response.h:
--------------------------------------------------------------------------------
1 | #ifndef RESPONSE_H
2 | #define RESPONSE_H
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | class ErrorMessage;
9 |
10 | class Response : public QObject {
11 | Q_OBJECT
12 |
13 | public:
14 | Response(bool success, QString message, QObject *parent = 0);
15 | Response(bool success, QByteArray message, QObject *parent = 0);
16 | Response(bool success, ErrorMessage *message, QObject *parent = 0);
17 | Response(bool success, QObject *parent);
18 | bool isSuccess() const;
19 | QByteArray message() const;
20 | QString toString() const;
21 |
22 | protected:
23 | QByteArray m_message;
24 |
25 | private:
26 | bool m_success;
27 | };
28 |
29 | #endif
30 |
31 |
--------------------------------------------------------------------------------
/src/Server.cpp:
--------------------------------------------------------------------------------
1 | #include "Server.h"
2 | #include "Connection.h"
3 | #include "WebPageManager.h"
4 |
5 | #include
6 |
7 | Server::Server(QObject *parent) : QObject(parent) {
8 | m_tcp_server = new QTcpServer(this);
9 | }
10 |
11 | bool Server::start() {
12 | #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
13 | QTextStream(stderr) <<
14 | "WARNING: The next major version of capybara-webkit " <<
15 | "will require at least version 5.0 of Qt. " <<
16 | "You're using version " << QT_VERSION_STR << "." << endl;
17 | #endif
18 |
19 | connect(m_tcp_server, SIGNAL(newConnection()), this, SLOT(handleConnection()));
20 | return m_tcp_server->listen(QHostAddress::LocalHost, 0);
21 | }
22 |
23 | quint16 Server::server_port() const {
24 | return m_tcp_server->serverPort();
25 | }
26 |
27 | void Server::handleConnection() {
28 | QTcpSocket *socket = m_tcp_server->nextPendingConnection();
29 | new Connection(socket, new WebPageManager(this), this);
30 | }
31 |
--------------------------------------------------------------------------------
/src/Server.h:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | class QTcpServer;
4 |
5 | class Server : public QObject {
6 | Q_OBJECT
7 |
8 | public:
9 | Server(QObject *parent);
10 | bool start();
11 | quint16 server_port() const;
12 |
13 | public slots:
14 | void handleConnection();
15 |
16 | private:
17 | QTcpServer *m_tcp_server;
18 | };
19 |
20 |
--------------------------------------------------------------------------------
/src/SetConfirmAction.cpp:
--------------------------------------------------------------------------------
1 | #include "SetConfirmAction.h"
2 | #include "WebPage.h"
3 | #include "WebPageManager.h"
4 |
5 | SetConfirmAction::SetConfirmAction(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {}
6 |
7 | void SetConfirmAction::start()
8 | {
9 | QString index;
10 | switch (arguments().length()) {
11 | case 2:
12 | index = page()->setConfirmAction(arguments()[0], arguments()[1]);
13 | break;
14 | default:
15 | page()->setConfirmAction(arguments()[0]);
16 | }
17 |
18 | finish(true, index);
19 | }
20 |
--------------------------------------------------------------------------------
/src/SetConfirmAction.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 |
3 | class SetConfirmAction : public SocketCommand {
4 | Q_OBJECT;
5 |
6 | public:
7 | SetConfirmAction(WebPageManager *manager, QStringList &arguments, QObject *parent = 0);
8 | virtual void start();
9 | };
10 |
--------------------------------------------------------------------------------
/src/SetCookie.cpp:
--------------------------------------------------------------------------------
1 | #include "SetCookie.h"
2 | #include "WebPage.h"
3 | #include "WebPageManager.h"
4 | #include "NetworkCookieJar.h"
5 | #include
6 |
7 | SetCookie::SetCookie(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {}
8 |
9 | void SetCookie::start()
10 | {
11 | QList cookies = QNetworkCookie::parseCookies(arguments()[0].toLatin1());
12 | NetworkCookieJar *jar = manager()->cookieJar();
13 | jar->overwriteCookies(cookies);
14 | finish(true);
15 | }
16 |
--------------------------------------------------------------------------------
/src/SetCookie.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 |
3 | class SetCookie : public SocketCommand {
4 | Q_OBJECT;
5 |
6 | public:
7 | SetCookie(WebPageManager *, QStringList &arguments, QObject *parent = 0);
8 | virtual void start();
9 | };
10 |
--------------------------------------------------------------------------------
/src/SetPromptAction.cpp:
--------------------------------------------------------------------------------
1 | #include "SetPromptAction.h"
2 | #include "WebPage.h"
3 | #include "WebPageManager.h"
4 |
5 | SetPromptAction::SetPromptAction(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {}
6 |
7 | void SetPromptAction::start()
8 | {
9 | QString index;
10 | switch (arguments().length()) {
11 | case 3:
12 | index = page()->setPromptAction(arguments()[0], arguments()[1], arguments()[2]);
13 | break;
14 | case 2:
15 | index = page()->setPromptAction(arguments()[0], arguments()[1]);
16 | break;
17 | default:
18 | page()->setPromptAction(arguments()[0]);
19 | }
20 |
21 | finish(true, index);
22 | }
23 |
--------------------------------------------------------------------------------
/src/SetPromptAction.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 |
3 | class SetPromptAction : public SocketCommand {
4 | Q_OBJECT;
5 |
6 | public:
7 | SetPromptAction(WebPageManager *manager, QStringList &arguments, QObject *parent = 0);
8 | virtual void start();
9 | };
10 |
--------------------------------------------------------------------------------
/src/SetPromptText.cpp:
--------------------------------------------------------------------------------
1 | #include "SetPromptText.h"
2 | #include "WebPage.h"
3 | #include "WebPageManager.h"
4 |
5 | SetPromptText::SetPromptText(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {}
6 |
7 | void SetPromptText::start()
8 | {
9 | page()->setPromptText(arguments()[0]);
10 | finish(true);
11 | }
12 |
--------------------------------------------------------------------------------
/src/SetPromptText.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 |
3 | class SetPromptText : public SocketCommand {
4 | Q_OBJECT;
5 |
6 | public:
7 | SetPromptText(WebPageManager *manager, QStringList &arguments, QObject *parent = 0);
8 | virtual void start();
9 | };
10 |
--------------------------------------------------------------------------------
/src/SetProxy.cpp:
--------------------------------------------------------------------------------
1 | #include "SetProxy.h"
2 | #include "WebPage.h"
3 | #include "WebPageManager.h"
4 | #include "NetworkAccessManager.h"
5 | #include
6 |
7 | SetProxy::SetProxy(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {}
8 |
9 | void SetProxy::start()
10 | {
11 | // default to empty proxy
12 | QNetworkProxy proxy;
13 |
14 | if (arguments().size() > 0)
15 | proxy = QNetworkProxy(QNetworkProxy::HttpProxy,
16 | arguments()[0],
17 | (quint16)(arguments()[1].toInt()),
18 | arguments()[2],
19 | arguments()[3]);
20 |
21 | manager()->networkAccessManager()->setProxy(proxy);
22 | finish(true);
23 | }
24 |
--------------------------------------------------------------------------------
/src/SetProxy.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 |
3 | class SetProxy : public SocketCommand {
4 | Q_OBJECT;
5 |
6 | public:
7 | SetProxy(WebPageManager *, QStringList &arguments, QObject *parent = 0);
8 | virtual void start();
9 | };
10 |
--------------------------------------------------------------------------------
/src/SetSkipImageLoading.cpp:
--------------------------------------------------------------------------------
1 | #include "SetSkipImageLoading.h"
2 | #include "WebPage.h"
3 | #include "WebPageManager.h"
4 |
5 | SetSkipImageLoading::SetSkipImageLoading(WebPageManager *manager, QStringList &arguments, QObject *parent) :
6 | SocketCommand(manager, arguments, parent) {
7 | }
8 |
9 | void SetSkipImageLoading::start() {
10 | page()->setSkipImageLoading(arguments().contains("true"));
11 | finish(true);
12 | }
13 |
--------------------------------------------------------------------------------
/src/SetSkipImageLoading.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 |
3 | class SetSkipImageLoading : public SocketCommand {
4 | Q_OBJECT
5 |
6 | public:
7 | SetSkipImageLoading(WebPageManager *manager, QStringList &arguments, QObject *parent = 0);
8 | virtual void start();
9 | };
10 |
--------------------------------------------------------------------------------
/src/SetTimeout.cpp:
--------------------------------------------------------------------------------
1 | #include "SetTimeout.h"
2 | #include "WebPageManager.h"
3 | #include "ErrorMessage.h"
4 |
5 | SetTimeout::SetTimeout(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
6 | }
7 |
8 | void SetTimeout::start() {
9 | QString timeoutString = arguments()[0];
10 | bool ok;
11 | int timeout = timeoutString.toInt(&ok);
12 |
13 | if (ok) {
14 | manager()->setTimeout(timeout);
15 | finish(true);
16 | } else {
17 | finish(false, new ErrorMessage("Invalid value for timeout"));
18 | }
19 | }
20 |
21 |
--------------------------------------------------------------------------------
/src/SetTimeout.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 |
3 | class SetTimeout : public SocketCommand {
4 | Q_OBJECT
5 |
6 | public:
7 | SetTimeout(WebPageManager *manager, QStringList &arguments, QObject *parent = 0);
8 | virtual void start();
9 | };
10 |
--------------------------------------------------------------------------------
/src/SetUnknownUrlMode.cpp:
--------------------------------------------------------------------------------
1 | #include "SetUnknownUrlMode.h"
2 | #include "WebPage.h"
3 | #include "WebPageManager.h"
4 | #include "ErrorMessage.h"
5 |
6 | SetUnknownUrlMode::SetUnknownUrlMode(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
7 | }
8 |
9 | void SetUnknownUrlMode::start() {
10 | QString modeString = arguments()[0];
11 | QStringList modes;
12 | modes << "warn" << "block";
13 |
14 | switch(modes.indexOf(modeString)) {
15 | case 0:
16 | manager()->setUnknownUrlMode(UnknownUrlHandler::WARN);
17 | finish(true);
18 | break;
19 | case 1:
20 | manager()->setUnknownUrlMode(UnknownUrlHandler::BLOCK);
21 | finish(true);
22 | break;
23 | default:
24 | QString error = QString("Invalid mode string:") + modeString;
25 | finish(false, new ErrorMessage(error));
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/SetUnknownUrlMode.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 | #include "UnknownUrlHandler.h"
3 |
4 | class SetUnknownUrlMode : public SocketCommand {
5 | Q_OBJECT
6 |
7 | public:
8 | SetUnknownUrlMode(WebPageManager *, QStringList &arguments, QObject *parent = 0);
9 | virtual void start();
10 | };
11 |
--------------------------------------------------------------------------------
/src/SetUrlBlacklist.cpp:
--------------------------------------------------------------------------------
1 |
2 | #include "SetUrlBlacklist.h"
3 | #include "WebPageManager.h"
4 | #include "WebPage.h"
5 | #include "NetworkAccessManager.h"
6 |
7 | SetUrlBlacklist::SetUrlBlacklist(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
8 | }
9 |
10 | void SetUrlBlacklist::start() {
11 | manager()->setUrlBlacklist(arguments());
12 | finish(true);
13 | }
14 |
15 |
--------------------------------------------------------------------------------
/src/SetUrlBlacklist.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 |
3 | class SetUrlBlacklist : public SocketCommand {
4 | Q_OBJECT
5 |
6 | public:
7 | SetUrlBlacklist(WebPageManager *manager, QStringList &arguments, QObject *parent = 0);
8 | virtual void start();
9 | };
10 |
--------------------------------------------------------------------------------
/src/SocketCommand.cpp:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 | #include "WebPage.h"
3 | #include "WebPageManager.h"
4 |
5 | SocketCommand::SocketCommand(WebPageManager *manager, QStringList &arguments, QObject *parent) : Command(parent) {
6 | m_manager = manager;
7 | m_arguments = arguments;
8 | }
9 |
10 | WebPage *SocketCommand::page() const {
11 | return m_manager->currentPage();
12 | }
13 |
14 | const QStringList &SocketCommand::arguments() const {
15 | return m_arguments;
16 | }
17 |
18 | WebPageManager *SocketCommand::manager() const {
19 | return m_manager;
20 | }
21 |
22 | QString SocketCommand::toString() const {
23 | QString result;
24 | QTextStream(&result) << metaObject()->className() << QString("(") << m_arguments.join(", ") << QString(")");
25 | return result;
26 | }
27 |
28 |
--------------------------------------------------------------------------------
/src/SocketCommand.h:
--------------------------------------------------------------------------------
1 | #ifndef SOCKET_COMMAND_H
2 | #define SOCKET_COMMAND_H
3 |
4 | #include
5 | #include
6 | #include "Command.h"
7 |
8 | class WebPage;
9 | class WebPageManager;
10 | class Response;
11 |
12 | class SocketCommand : public Command {
13 | Q_OBJECT
14 |
15 | public:
16 | SocketCommand(WebPageManager *, QStringList &arguments, QObject *parent = 0);
17 | virtual QString toString() const;
18 |
19 | protected:
20 | WebPage *page() const;
21 | const QStringList &arguments() const;
22 | WebPageManager *manager() const;
23 |
24 | private:
25 | QStringList m_arguments;
26 | WebPageManager *m_manager;
27 |
28 | };
29 |
30 | #endif
31 |
--------------------------------------------------------------------------------
/src/Status.cpp:
--------------------------------------------------------------------------------
1 | #include "Status.h"
2 | #include "WebPage.h"
3 | #include "WebPageManager.h"
4 | #include
5 |
6 | Status::Status(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
7 | }
8 |
9 | void Status::start() {
10 | int status = page()->getLastStatus();
11 | finish(true, QString::number(status));
12 | }
13 |
14 |
--------------------------------------------------------------------------------
/src/Status.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 |
3 | class Status : public SocketCommand {
4 | Q_OBJECT
5 |
6 | public:
7 | Status(WebPageManager *, QStringList &arguments, QObject *parent = 0);
8 | virtual void start();
9 | };
10 |
11 |
--------------------------------------------------------------------------------
/src/StdinNotifier.cpp:
--------------------------------------------------------------------------------
1 | #include "StdinNotifier.h"
2 |
3 | #include
4 | #include
5 | #include
6 |
7 | StdinNotifier::StdinNotifier(QObject *parent) : QObject(parent) {
8 | m_notifier = new QSocketNotifier(fileno(stdin), QSocketNotifier::Read, this);
9 | connect(m_notifier, SIGNAL(activated(int)), this, SLOT(notifierActivated()));
10 | }
11 |
12 | void StdinNotifier::notifierActivated() {
13 | std::string line;
14 | std::getline(std::cin, line);
15 | if (std::cin.eof()) {
16 | emit eof();
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/StdinNotifier.h:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | class QSocketNotifier;
4 |
5 | class StdinNotifier : public QObject {
6 | Q_OBJECT
7 |
8 | public:
9 | StdinNotifier(QObject *parent = 0);
10 |
11 | public slots:
12 | void notifierActivated();
13 |
14 | signals:
15 | void eof();
16 |
17 | private:
18 | QSocketNotifier *m_notifier;
19 | };
20 |
21 |
--------------------------------------------------------------------------------
/src/TimeoutCommand.cpp:
--------------------------------------------------------------------------------
1 | #include "TimeoutCommand.h"
2 | #include "Command.h"
3 | #include "WebPageManager.h"
4 | #include "WebPage.h"
5 | #include "ErrorMessage.h"
6 | #include
7 | #include
8 |
9 | TimeoutCommand::TimeoutCommand(Command *command, WebPageManager *manager, QObject *parent) : Command(parent) {
10 | m_command = command;
11 | m_manager = manager;
12 | m_timer = new QTimer(this);
13 | m_timer->setSingleShot(true);
14 | m_command->setParent(this);
15 | connect(m_timer, SIGNAL(timeout()), this, SLOT(commandTimeout()));
16 | connect(m_manager, SIGNAL(loadStarted()), this, SLOT(pageLoadingFromCommand()));
17 | }
18 |
19 | void TimeoutCommand::start() {
20 | QApplication::processEvents();
21 | if (m_manager->isLoading()) {
22 | m_manager->log() << this->toString() << "waiting for load to finish";
23 | connect(m_manager, SIGNAL(pageFinished(bool)), this, SLOT(pendingLoadFinished(bool)));
24 | startTimeout();
25 | } else {
26 | startCommand();
27 | }
28 | }
29 |
30 | void TimeoutCommand::startCommand() {
31 | connect(m_command, SIGNAL(finished(Response *)), this, SLOT(commandFinished(Response *)));
32 | m_command->start();
33 | }
34 |
35 | void TimeoutCommand::startTimeout() {
36 | int timeout = m_manager->getTimeout();
37 | if (timeout > 0) {
38 | m_timer->start(timeout * 1000);
39 | }
40 | }
41 |
42 | void TimeoutCommand::pendingLoadFinished(bool success) {
43 | disconnect(m_manager, SIGNAL(pageFinished(bool)), this, SLOT(pendingLoadFinished(bool)));
44 | if (success) {
45 | startCommand();
46 | } else {
47 | disconnect(m_timer, SIGNAL(timeout()), this, SLOT(commandTimeout()));
48 | disconnect(m_manager, SIGNAL(loadStarted()), this, SLOT(pageLoadingFromCommand()));
49 | finish(false, new ErrorMessage(m_manager->currentPage()->failureString()));
50 | }
51 | }
52 |
53 | void TimeoutCommand::pageLoadingFromCommand() {
54 | startTimeout();
55 | }
56 |
57 | void TimeoutCommand::commandTimeout() {
58 | disconnect(m_manager, SIGNAL(loadStarted()), this, SLOT(pageLoadingFromCommand()));
59 | disconnect(m_manager, SIGNAL(pageFinished(bool)), this, SLOT(pendingLoadFinished(bool)));
60 | disconnect(m_command, SIGNAL(finished(Response *)), this, SLOT(commandFinished(Response *)));
61 | m_manager->currentPage()->triggerAction(QWebPage::Stop);
62 | QString message = QString("Request timed out after %1 second(s)").arg(m_manager->getTimeout());
63 | finish(false, new ErrorMessage("TimeoutError", message));
64 | }
65 |
66 | void TimeoutCommand::commandFinished(Response *response) {
67 | disconnect(m_timer, SIGNAL(timeout()), this, SLOT(commandTimeout()));
68 | disconnect(m_manager, SIGNAL(loadStarted()), this, SLOT(pageLoadingFromCommand()));
69 | emit finished(response);
70 | }
71 |
72 |
--------------------------------------------------------------------------------
/src/TimeoutCommand.h:
--------------------------------------------------------------------------------
1 | #include "Command.h"
2 | #include
3 | #include
4 |
5 | class Response;
6 | class WebPageManager;
7 | class QTimer;
8 |
9 | /* Decorates a command with a timeout.
10 | *
11 | * If the timeout, using a QTimer is reached before
12 | * the command is finished, the load page load will
13 | * be stopped and failure response will be issued.
14 | *
15 | */
16 | class TimeoutCommand : public Command {
17 | Q_OBJECT
18 |
19 | public:
20 | TimeoutCommand(Command *command, WebPageManager *page, QObject *parent = 0);
21 | virtual void start();
22 |
23 | public slots:
24 | void commandTimeout();
25 | void commandFinished(Response *response);
26 | void pageLoadingFromCommand();
27 | void pendingLoadFinished(bool);
28 |
29 | protected:
30 | void startCommand();
31 | void startTimeout();
32 |
33 | private:
34 | WebPageManager *m_manager;
35 | QTimer *m_timer;
36 | Command *m_command;
37 | };
38 |
39 |
--------------------------------------------------------------------------------
/src/Title.cpp:
--------------------------------------------------------------------------------
1 | #include "Title.h"
2 | #include "WebPage.h"
3 | #include "WebPageManager.h"
4 | #include "NetworkAccessManager.h"
5 |
6 | Title::Title(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
7 | }
8 |
9 | void Title::start() {
10 | finish(true, page()->mainFrame()->title());
11 | }
12 |
--------------------------------------------------------------------------------
/src/Title.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 |
3 | class Title : public SocketCommand {
4 | Q_OBJECT
5 |
6 | public:
7 | Title(WebPageManager *, QStringList &arguments, QObject *parent = 0);
8 | virtual void start();
9 | };
10 |
--------------------------------------------------------------------------------
/src/UnknownUrlHandler.cpp:
--------------------------------------------------------------------------------
1 | #include "UnknownUrlHandler.h"
2 | #include "NetworkReplyProxy.h"
3 | #include "NoOpReply.h"
4 |
5 | UnknownUrlHandler::UnknownUrlHandler(
6 | RequestHandler *next,
7 | QObject *parent
8 | ) : RequestHandler(parent) {
9 | m_next = next;
10 | allowDefaultUrls();
11 | m_mode = WARN;
12 | }
13 |
14 | QNetworkReply* UnknownUrlHandler::handleRequest(
15 | NetworkAccessManager *manager,
16 | QNetworkAccessManager::Operation operation,
17 | QNetworkRequest &request,
18 | QIODevice *outgoingData
19 | ) {
20 | QUrl url(request.url());
21 | if (this->isUnknown(url)) {
22 | switch(m_mode) {
23 | case WARN:
24 | QTextStream(stderr) <<
25 | "Request to unknown URL: " << url.toString() << endl <<
26 | "To block requests to unknown URLs:" << endl <<
27 | " Capybara::Webkit.configure do |config|" << endl <<
28 | " config.block_unknown_urls" << endl <<
29 | " end" << endl <<
30 | "To allow just this URL:" << endl <<
31 | " Capybara::Webkit.configure do |config|" << endl <<
32 | " config.allow_url(\"" << url.toString() << "\")" << endl <<
33 | " end" << endl <<
34 | "To allow requests to URLs from this host:" << endl <<
35 | " Capybara::Webkit.configure do |config|" << endl <<
36 | " config.allow_url(\"" << url.host() << "\")" << endl <<
37 | " end" << endl;
38 | break;
39 | case BLOCK:
40 | return new NetworkReplyProxy(new NoOpReply(request), this);
41 | }
42 | }
43 |
44 | return m_next->handleRequest(manager, operation, request, outgoingData);
45 | }
46 |
47 | void UnknownUrlHandler::allowUrl(const QString &url) {
48 | m_allowedUrls << url;
49 | }
50 |
51 | void UnknownUrlHandler::setMode(Mode mode) {
52 | m_mode = mode;
53 | }
54 |
55 | bool UnknownUrlHandler::isUnknown(QUrl url) {
56 | QStringListIterator iterator(m_allowedUrls);
57 | QString urlString = url.toString();
58 |
59 | while (iterator.hasNext()) {
60 | QRegExp allowedUrl = QRegExp(iterator.next());
61 | allowedUrl.setPatternSyntax(QRegExp::Wildcard);
62 |
63 | if(urlString.contains(allowedUrl)) {
64 | return false;
65 | }
66 | }
67 |
68 | return true;
69 | }
70 |
71 | void UnknownUrlHandler::reset() {
72 | m_allowedUrls.clear();
73 | allowDefaultUrls();
74 | }
75 |
76 | void UnknownUrlHandler::allowDefaultUrls() {
77 | m_allowedUrls.append(QString("127.0.0.1"));
78 | m_allowedUrls.append(QString("localhost"));
79 | m_allowedUrls.append(QString("data:*,*"));
80 | }
81 |
--------------------------------------------------------------------------------
/src/UnknownUrlHandler.h:
--------------------------------------------------------------------------------
1 | #ifndef _REQUESTHANDLER_H
2 | #define _REQUESTHANDLER_H
3 |
4 | #include "RequestHandler.h"
5 |
6 | class UnknownUrlHandler : public RequestHandler {
7 | public:
8 | enum Mode { WARN, BLOCK };
9 |
10 | UnknownUrlHandler(RequestHandler *next, QObject *parent = 0);
11 | virtual QNetworkReply* handleRequest(
12 | NetworkAccessManager *,
13 | QNetworkAccessManager::Operation,
14 | QNetworkRequest &,
15 | QIODevice *
16 | );
17 | void allowUrl(const QString &);
18 | void setMode(Mode);
19 | void reset();
20 |
21 | private:
22 | QStringList m_allowedUrls;
23 | bool isUnknown(QUrl);
24 | Mode m_mode;
25 | RequestHandler *m_next;
26 | void allowDefaultUrls();
27 | };
28 |
29 | #endif
30 |
--------------------------------------------------------------------------------
/src/UnsupportedContentHandler.cpp:
--------------------------------------------------------------------------------
1 | #include "UnsupportedContentHandler.h"
2 | #include "WebPage.h"
3 | #include
4 |
5 | UnsupportedContentHandler::UnsupportedContentHandler(WebPage *page, QNetworkReply *reply, QObject *parent) : QObject(parent) {
6 | m_page = page;
7 | m_reply = reply;
8 | }
9 |
10 | void UnsupportedContentHandler::renderNonHtmlContent() {
11 | QByteArray text = m_reply->readAll();
12 | m_page->mainFrame()->setContent(text, QString("text/plain"), m_reply->url());
13 | m_page->unsupportedContentFinishedReply(m_reply);
14 | this->deleteLater();
15 | }
16 |
17 | void UnsupportedContentHandler::waitForReplyToFinish() {
18 | connect(m_reply, SIGNAL(finished()), this, SLOT(replyFinished()));
19 | }
20 |
21 | void UnsupportedContentHandler::replyFinished() {
22 | renderNonHtmlContent();
23 | }
24 |
--------------------------------------------------------------------------------
/src/UnsupportedContentHandler.h:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | class WebPage;
4 | class QNetworkReply;
5 |
6 | class UnsupportedContentHandler : public QObject {
7 | Q_OBJECT
8 |
9 | public:
10 | UnsupportedContentHandler(WebPage *page, QNetworkReply *reply, QObject *parent = 0);
11 | void waitForReplyToFinish();
12 | void renderNonHtmlContent();
13 |
14 | public slots:
15 | void replyFinished();
16 |
17 | private:
18 | WebPage *m_page;
19 | QNetworkReply *m_reply;
20 | };
21 |
--------------------------------------------------------------------------------
/src/Version.cpp:
--------------------------------------------------------------------------------
1 | #include "Version.h"
2 | #include "WebPage.h"
3 |
4 | Version::Version(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
5 | }
6 |
7 | void Version::start() {
8 | QString result =
9 | QString("Qt: ") + QT_VERSION_STR +
10 | QString("\nWebKit: ") + qWebKitVersion() +
11 | QString("\nQtWebKit: ") + QTWEBKIT_VERSION_STR;
12 | finish(true, result);
13 | }
14 |
--------------------------------------------------------------------------------
/src/Version.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 |
3 | #ifndef QTWEBKIT_VERSION_STR
4 | #include "qtwebkitversion.h"
5 | #endif
6 |
7 | class Version : public SocketCommand {
8 | Q_OBJECT
9 |
10 | public:
11 | Version(WebPageManager *, QStringList &arguments, QObject *parent = 0);
12 | virtual void start();
13 | };
14 |
15 |
--------------------------------------------------------------------------------
/src/Visit.cpp:
--------------------------------------------------------------------------------
1 | #include "Visit.h"
2 | #include "SocketCommand.h"
3 | #include "WebPage.h"
4 | #include "WebPageManager.h"
5 |
6 | Visit::Visit(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
7 | }
8 |
9 | void Visit::start() {
10 | QUrl requestedUrl = QUrl::fromEncoded(arguments()[0].toUtf8(), QUrl::TolerantMode);
11 | page()->currentFrame()->load(QUrl(requestedUrl));
12 | finish(true);
13 | }
14 |
--------------------------------------------------------------------------------
/src/Visit.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 |
3 | class Visit : public SocketCommand {
4 | Q_OBJECT
5 |
6 | public:
7 | Visit(WebPageManager *manager, QStringList &arguments, QObject *parent = 0);
8 | virtual void start();
9 | };
10 |
11 |
--------------------------------------------------------------------------------
/src/WebPage.h:
--------------------------------------------------------------------------------
1 | #ifndef _WEBPAGE_H
2 | #define _WEBPAGE_H
3 | #include
4 | #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
5 | #include
6 | #else
7 | #include
8 | #endif
9 | #include
10 |
11 | class WebPageManager;
12 | class InvocationResult;
13 | class NetworkReplyProxy;
14 | class QWebView;
15 |
16 | class WebPage : public QWebPage {
17 | Q_OBJECT
18 |
19 | public:
20 | WebPage(WebPageManager *, QObject *parent = 0);
21 | InvocationResult invokeCapybaraFunction(const char *name, bool allowUnattached, const QStringList &arguments);
22 | InvocationResult invokeCapybaraFunction(QString &name, bool allowUnattached, const QStringList &arguments);
23 | QString failureString();
24 | QString userAgentForUrl(const QUrl &url ) const;
25 | void setUserAgent(QString userAgent);
26 | void setConfirmAction(QString action);
27 | QString setConfirmAction(QString action, QString message);
28 | QString setPromptAction(QString action, QString message, QString response);
29 | QString setPromptAction(QString action, QString message);
30 | void setPromptAction(QString action);
31 | void setPromptText(QString action);
32 | QString acceptAlert(QString);
33 | int getLastStatus();
34 | void setCustomNetworkAccessManager();
35 | bool render(const QString &fileName, const QSize &minimumSize);
36 | virtual bool extension (Extension extension, const ExtensionOption *option=0, ExtensionReturn *output=0);
37 | void setSkipImageLoading(bool skip);
38 | QVariantList consoleMessages();
39 | QVariantList alertMessages();
40 | QVariantList confirmMessages();
41 | QVariantList promptMessages();
42 | void createWindow();
43 | void resetLocalStorage();
44 | QWebPage *createWindow(WebWindowType type);
45 | QString uuid();
46 | QString getWindowName();
47 | bool matchesWindowSelector(QString);
48 | void setFocus();
49 | void unsupportedContentFinishedReply(QNetworkReply *reply);
50 | QVariantMap pageHeaders();
51 | QByteArray body();
52 | QString contentType();
53 | void mouseEvent(QEvent::Type type, const QPoint &position, Qt::MouseButton button, Qt::KeyboardModifiers modifiers = Qt::NoModifier);
54 | bool clickTest(QWebElement element, int absoluteX, int absoluteY);
55 | void resize(int, int);
56 | int modalCount();
57 | QString modalMessage();
58 | void setCurrentFrameParent(QWebFrame* frame);
59 | QWebFrame* currentFrameParent();
60 |
61 | public slots:
62 | bool shouldInterruptJavaScript();
63 | void injectJavascriptHelpers();
64 | void loadStarted();
65 | void loadFinished(bool);
66 | bool isLoading() const;
67 | void frameCreated(QWebFrame *);
68 | void handleSslErrorsForReply(QNetworkReply *reply, const QList &);
69 | void handleUnsupportedContent(QNetworkReply *reply);
70 | void replyFinished(QUrl &, QNetworkReply *);
71 | void remove();
72 |
73 | signals:
74 | void pageFinished(bool);
75 | void requestCreated(QByteArray &url, QNetworkReply *reply);
76 | void replyFinished(QNetworkReply *reply);
77 | void modalReady();
78 |
79 | protected:
80 | virtual void javaScriptConsoleMessage(const QString &message, int lineNumber, const QString &sourceID);
81 | virtual void javaScriptAlert(QWebFrame *frame, const QString &message);
82 | virtual bool javaScriptConfirm(QWebFrame *frame, const QString &message);
83 | virtual bool javaScriptPrompt(QWebFrame *frame, const QString &message, const QString &defaultValue, QString *result);
84 | virtual QString chooseFile(QWebFrame * parentFrame, const QString &suggestedFile);
85 | virtual bool supportsExtension(Extension extension) const;
86 |
87 | private:
88 | QString m_capybaraJavascript;
89 | QString m_userAgent;
90 | bool m_loading;
91 | bool m_failed;
92 | QStringList getAttachedFileNames();
93 | void loadJavascript();
94 | void setUserStylesheet();
95 | bool m_confirmAction;
96 | bool m_promptAction;
97 | QVariantList m_consoleMessages;
98 | QVariantList m_alertMessages;
99 | QVariantList m_confirmMessages;
100 | QString m_prompt_text;
101 | QVariantList m_promptMessages;
102 | QString m_uuid;
103 | WebPageManager *m_manager;
104 | QString m_errorPageMessage;
105 | void setFrameProperties(QWebFrame *, QUrl &, NetworkReplyProxy *);
106 | QPoint m_mousePosition;
107 | QList m_modalResponses;
108 | QStringList m_modalMessages;
109 | void addModalMessage(bool, const QString &, const QRegExp &);
110 | QWebFrame* m_currentFrameParent;
111 | };
112 |
113 | #endif //_WEBPAGE_H
114 |
115 |
--------------------------------------------------------------------------------
/src/WebPageManager.h:
--------------------------------------------------------------------------------
1 | #ifndef _WEBPAGEMANAGER_H
2 | #define _WEBPAGEMANAGER_H
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | #include "UnknownUrlHandler.h"
11 |
12 | class WebPage;
13 | class NetworkCookieJar;
14 | class NetworkAccessManager;
15 | class BlacklistedRequestHandler;
16 | class CustomHeadersRequestHandler;
17 |
18 | class WebPageManager : public QObject {
19 | Q_OBJECT
20 |
21 | public:
22 | WebPageManager(QObject *parent = 0);
23 | void append(WebPage *value);
24 | QList pages() const;
25 | void setCurrentPage(WebPage *);
26 | WebPage *currentPage() const;
27 | WebPage *createPage();
28 | void removePage(WebPage *);
29 | void setIgnoreSslErrors(bool);
30 | bool ignoreSslErrors();
31 | void setTimeout(int);
32 | int getTimeout();
33 | void reset();
34 | NetworkCookieJar *cookieJar();
35 | bool isLoading() const;
36 | QDebug log() const;
37 | void enableLogging();
38 | void replyFinished(QNetworkReply *reply);
39 | NetworkAccessManager *networkAccessManager();
40 | void setUrlBlacklist(const QStringList &);
41 | void addHeader(QString, QString);
42 | void setUnknownUrlMode(UnknownUrlHandler::Mode);
43 | void allowUrl(const QString &);
44 | void blockUrl(const QString &);
45 |
46 | public slots:
47 | void emitLoadStarted();
48 | void setPageStatus(bool);
49 | void requestCreated(QByteArray &url, QNetworkReply *reply);
50 | void handleReplyFinished();
51 | void replyDestroyed(QObject *);
52 |
53 | signals:
54 | void pageFinished(bool);
55 | void loadStarted();
56 |
57 | private:
58 | void emitPageFinished();
59 | static void handleDebugMessage(QtMsgType type, const char *message);
60 | void initOfflineWebApplicationCache();
61 |
62 | QList m_pages;
63 | QList m_pendingReplies;
64 | WebPage *m_currentPage;
65 | bool m_ignoreSslErrors;
66 | NetworkCookieJar *m_cookieJar;
67 | QSet m_started;
68 | bool m_success;
69 | bool m_loggingEnabled;
70 | bool m_isCacheInitialized;
71 | QFile *m_ignoredOutput;
72 | int m_timeout;
73 | NetworkAccessManager *m_networkAccessManager;
74 | BlacklistedRequestHandler *m_blacklistedRequestHandler;
75 | CustomHeadersRequestHandler *m_customHeadersRequestHandler;
76 | UnknownUrlHandler *m_unknownUrlHandler;
77 | };
78 |
79 | #endif // _WEBPAGEMANAGER_H
80 |
--------------------------------------------------------------------------------
/src/WindowClose.cpp:
--------------------------------------------------------------------------------
1 | #include "WindowClose.h"
2 | #include "WebPage.h"
3 |
4 | WindowClose::WindowClose(WebPageManager *manager, QStringList &arguments, QObject *parent) : WindowCommand(manager, arguments, parent) {
5 | }
6 |
7 | void WindowClose::windowFound(WebPage *page) {
8 | page->remove();
9 | finish(true);
10 | }
11 |
--------------------------------------------------------------------------------
/src/WindowClose.h:
--------------------------------------------------------------------------------
1 | #include "WindowCommand.h"
2 |
3 | class WindowClose : public WindowCommand {
4 | Q_OBJECT
5 |
6 | public:
7 | WindowClose(WebPageManager *, QStringList &arguments, QObject *parent = 0);
8 |
9 | protected:
10 | virtual void windowFound(WebPage *);
11 | };
12 |
13 |
--------------------------------------------------------------------------------
/src/WindowCommand.cpp:
--------------------------------------------------------------------------------
1 | #include "WindowCommand.h"
2 | #include "WebPage.h"
3 | #include "WebPageManager.h"
4 | #include "ErrorMessage.h"
5 |
6 | WindowCommand::WindowCommand(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
7 | }
8 |
9 | void WindowCommand::start() {
10 | findWindow(arguments()[0]);
11 | }
12 |
13 | void WindowCommand::findWindow(QString selector) {
14 | foreach(WebPage *page, manager()->pages()) {
15 | if (page->matchesWindowSelector(selector)) {
16 | windowFound(page);
17 | return;
18 | }
19 | }
20 |
21 | windowNotFound();
22 | }
23 |
24 | void WindowCommand::windowNotFound() {
25 | finish(false,
26 | new ErrorMessage("NoSuchWindowError", "Unable to locate window."));
27 | }
28 |
--------------------------------------------------------------------------------
/src/WindowCommand.h:
--------------------------------------------------------------------------------
1 | #ifndef WINDOW_COMMAND_H
2 | #define WINDOW_COMMAND_H
3 |
4 | #include "SocketCommand.h"
5 |
6 | class WindowCommand : public SocketCommand {
7 | Q_OBJECT
8 |
9 | public:
10 | WindowCommand(WebPageManager *, QStringList &arguments, QObject *parent = 0);
11 | virtual void start();
12 |
13 | protected:
14 | virtual void windowFound(WebPage *) = 0;
15 |
16 | private:
17 | void findWindow(QString);
18 | void windowNotFound();
19 | };
20 |
21 | #endif
22 |
--------------------------------------------------------------------------------
/src/WindowFocus.cpp:
--------------------------------------------------------------------------------
1 | #include "WindowFocus.h"
2 | #include "WebPage.h"
3 |
4 | WindowFocus::WindowFocus(WebPageManager *manager, QStringList &arguments, QObject *parent) : WindowCommand(manager, arguments, parent) {
5 | }
6 |
7 | void WindowFocus::windowFound(WebPage *page) {
8 | page->setFocus();
9 | finish(true);
10 | }
11 |
--------------------------------------------------------------------------------
/src/WindowFocus.h:
--------------------------------------------------------------------------------
1 | #include "WindowCommand.h"
2 |
3 | class WindowFocus : public WindowCommand {
4 | Q_OBJECT
5 |
6 | public:
7 | WindowFocus(WebPageManager *, QStringList &arguments, QObject *parent = 0);
8 |
9 | protected:
10 | virtual void windowFound(WebPage *);
11 | };
12 |
13 |
--------------------------------------------------------------------------------
/src/WindowMaximize.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include "WindowMaximize.h"
3 | #include "WebPage.h"
4 | #include "WebPageManager.h"
5 |
6 | WindowMaximize::WindowMaximize(WebPageManager *manager, QStringList &arguments, QObject *parent) : WindowCommand(manager, arguments, parent) {
7 | }
8 |
9 | void WindowMaximize::windowFound(WebPage *page) {
10 | QDesktopWidget *desktop = QApplication::desktop();
11 | QRect area = desktop->availableGeometry();
12 | page->resize(area.width(), area.height());
13 | finish(true);
14 | }
15 |
--------------------------------------------------------------------------------
/src/WindowMaximize.h:
--------------------------------------------------------------------------------
1 | #include "WindowCommand.h"
2 |
3 | class WindowMaximize : public WindowCommand {
4 | Q_OBJECT
5 |
6 | public:
7 | WindowMaximize(WebPageManager *, QStringList &arguments, QObject *parent = 0);
8 |
9 | protected:
10 | virtual void windowFound(WebPage *);
11 | };
12 |
13 |
--------------------------------------------------------------------------------
/src/WindowOpen.cpp:
--------------------------------------------------------------------------------
1 | #include "WindowOpen.h"
2 | #include "SocketCommand.h"
3 | #include "WebPage.h"
4 | #include "WebPageManager.h"
5 |
6 | WindowOpen::WindowOpen(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
7 | }
8 |
9 | void WindowOpen::start() {
10 | manager()->createPage();
11 | finish(true);
12 | }
13 |
--------------------------------------------------------------------------------
/src/WindowOpen.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 |
3 | class WindowOpen : public SocketCommand {
4 | Q_OBJECT
5 |
6 | public:
7 | WindowOpen(WebPageManager *, QStringList &arguments, QObject *parent = 0);
8 | virtual void start();
9 | };
10 |
11 |
--------------------------------------------------------------------------------
/src/WindowResize.cpp:
--------------------------------------------------------------------------------
1 | #include "WindowResize.h"
2 | #include "WebPage.h"
3 | #include "WebPageManager.h"
4 |
5 | WindowResize::WindowResize(WebPageManager *manager, QStringList &arguments, QObject *parent) : WindowCommand(manager, arguments, parent) {
6 | }
7 |
8 | void WindowResize::windowFound(WebPage *page) {
9 | int width = arguments()[1].toInt();
10 | int height = arguments()[2].toInt();
11 |
12 | page->resize(width, height);
13 |
14 | finish(true);
15 | }
16 |
17 |
--------------------------------------------------------------------------------
/src/WindowResize.h:
--------------------------------------------------------------------------------
1 | #include "WindowCommand.h"
2 |
3 | class WindowResize : public WindowCommand {
4 | Q_OBJECT
5 |
6 | public:
7 | WindowResize(WebPageManager *, QStringList &arguments, QObject *parent = 0);
8 |
9 | protected:
10 | virtual void windowFound(WebPage *);
11 | };
12 |
13 |
--------------------------------------------------------------------------------
/src/WindowSize.cpp:
--------------------------------------------------------------------------------
1 | #include "WindowSize.h"
2 | #include "WebPage.h"
3 | #include "WebPageManager.h"
4 | #include "JsonSerializer.h"
5 |
6 | WindowSize::WindowSize(WebPageManager *manager, QStringList &arguments, QObject *parent) : WindowCommand(manager, arguments, parent) {
7 | }
8 |
9 | void WindowSize::windowFound(WebPage *page) {
10 | QSize size = page->viewportSize();
11 | QVariantList elements;
12 | elements << size.width();
13 | elements << size.height();
14 | JsonSerializer serializer;
15 | QByteArray json = serializer.serialize(elements);
16 | finish(true, json);
17 | }
18 |
--------------------------------------------------------------------------------
/src/WindowSize.h:
--------------------------------------------------------------------------------
1 | #include "WindowCommand.h"
2 |
3 | class WindowSize : public WindowCommand {
4 | Q_OBJECT
5 |
6 | public:
7 | WindowSize(WebPageManager *, QStringList &arguments, QObject *parent = 0);
8 |
9 | protected:
10 | virtual void windowFound(WebPage *);
11 | };
12 |
13 |
--------------------------------------------------------------------------------
/src/body.cpp:
--------------------------------------------------------------------------------
1 | #include "Body.h"
2 | #include "WebPage.h"
3 | #include "WebPageManager.h"
4 |
5 | Body::Body(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
6 | }
7 |
8 | void Body::start() {
9 | if (page()->contentType().contains("html"))
10 | finish(true, page()->currentFrame()->toHtml());
11 | else
12 | finish(true, page()->body());
13 | }
14 |
--------------------------------------------------------------------------------
/src/find_command.h:
--------------------------------------------------------------------------------
1 | #define CHECK_COMMAND(expectedName) \
2 | if (strcmp(#expectedName, name) == 0) { \
3 | return new expectedName(m_manager, arguments, this); \
4 | }
5 |
6 | CHECK_COMMAND(Visit)
7 | CHECK_COMMAND(FindXpath)
8 | CHECK_COMMAND(Reset)
9 | CHECK_COMMAND(Node)
10 | CHECK_COMMAND(Evaluate)
11 | CHECK_COMMAND(EvaluateAsync)
12 | CHECK_COMMAND(Execute)
13 | CHECK_COMMAND(FrameFocus)
14 | CHECK_COMMAND(Header)
15 | CHECK_COMMAND(Render)
16 | CHECK_COMMAND(Body)
17 | CHECK_COMMAND(Status)
18 | CHECK_COMMAND(Headers)
19 | CHECK_COMMAND(SetCookie)
20 | CHECK_COMMAND(ClearCookies)
21 | CHECK_COMMAND(GetCookies)
22 | CHECK_COMMAND(SetProxy)
23 | CHECK_COMMAND(ConsoleMessages)
24 | CHECK_COMMAND(CurrentUrl)
25 | CHECK_COMMAND(WindowResize)
26 | CHECK_COMMAND(IgnoreSslErrors)
27 | CHECK_COMMAND(SetSkipImageLoading)
28 | CHECK_COMMAND(WindowFocus)
29 | CHECK_COMMAND(GetWindowHandles)
30 | CHECK_COMMAND(GetWindowHandle)
31 | CHECK_COMMAND(Authenticate)
32 | CHECK_COMMAND(EnableLogging)
33 | CHECK_COMMAND(SetConfirmAction)
34 | CHECK_COMMAND(SetPromptAction)
35 | CHECK_COMMAND(SetPromptText)
36 | CHECK_COMMAND(ClearPromptText)
37 | CHECK_COMMAND(JavascriptAlertMessages)
38 | CHECK_COMMAND(JavascriptConfirmMessages)
39 | CHECK_COMMAND(JavascriptPromptMessages)
40 | CHECK_COMMAND(GetTimeout)
41 | CHECK_COMMAND(SetTimeout)
42 | CHECK_COMMAND(SetUrlBlacklist)
43 | CHECK_COMMAND(Title)
44 | CHECK_COMMAND(Version)
45 | CHECK_COMMAND(FindCss)
46 | CHECK_COMMAND(WindowClose)
47 | CHECK_COMMAND(WindowOpen)
48 | CHECK_COMMAND(WindowSize)
49 | CHECK_COMMAND(WindowMaximize)
50 | CHECK_COMMAND(GoBack)
51 | CHECK_COMMAND(GoForward)
52 | CHECK_COMMAND(Refresh)
53 | CHECK_COMMAND(AcceptAlert)
54 | CHECK_COMMAND(FindModal)
55 | CHECK_COMMAND(SetUnknownUrlMode)
56 | CHECK_COMMAND(AllowUrl)
57 | CHECK_COMMAND(BlockUrl)
58 | CHECK_COMMAND(FrameTitle)
59 | CHECK_COMMAND(FrameUrl)
60 |
--------------------------------------------------------------------------------
/src/main.cpp:
--------------------------------------------------------------------------------
1 | #include "Server.h"
2 | #include "IgnoreDebugOutput.h"
3 | #include "StdinNotifier.h"
4 | #include
5 | #include
6 | #include
7 | #ifdef Q_OS_UNIX
8 | #include
9 | #endif
10 |
11 | int main(int argc, char **argv) {
12 | #ifdef Q_OS_UNIX
13 | if (setpgid(0, 0) < 0) {
14 | std::cerr << "Unable to set new process group." << std::endl;
15 | return 1;
16 | }
17 | #endif
18 |
19 | #ifdef Q_OS_MAC
20 | QApplication::setStyle(QStyleFactory::create("Fusion"));
21 | #endif
22 |
23 | QApplication app(argc, argv);
24 | app.setApplicationName("capybara-webkit");
25 | app.setOrganizationName("thoughtbot, inc");
26 | app.setOrganizationDomain("thoughtbot.com");
27 |
28 | StdinNotifier notifier;
29 | QObject::connect(¬ifier, SIGNAL(eof()), &app, SLOT(quit()));
30 |
31 | ignoreDebugOutput();
32 | Server server(0);
33 |
34 | if (server.start()) {
35 | std::cout << "Capybara-webkit server started, listening on port: " << server.server_port() << std::endl;
36 | return app.exec();
37 | } else {
38 | std::cerr << "Couldn't start capybara-webkit server" << std::endl;
39 | return 1;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/pointer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thoughtbot/capybara-webkit/f429d668568ff7349f5e23a085df7fcf1c431fa7/src/pointer.png
--------------------------------------------------------------------------------
/src/stable.h:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 | #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
32 | #include
33 | #else
34 | #include
35 | #endif
36 | #include
37 | #include
38 | #include
39 | #include
40 |
--------------------------------------------------------------------------------
/src/webkit_server.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = app
2 | TARGET = webkit_server
3 | DESTDIR = .
4 | QMAKE_CXXFLAGS += $$(CXXFLAGS)
5 | QMAKE_CFLAGS += $$(CFLAGS)
6 | QMAKE_LFLAGS += $$(LDFLAGS)
7 | PROJECT_DIR = $$_PRO_FILE_PWD_
8 | BUILD_DIR = $${PROJECT_DIR}/build
9 | PRECOMPILED_DIR = $${BUILD_DIR}
10 | OBJECTS_DIR = $${BUILD_DIR}
11 | MOC_DIR = $${BUILD_DIR}
12 | HEADERS = \
13 | FrameUrl.h \
14 | FrameTitle.h \
15 | BlockUrl.h \
16 | AllowUrl.h \
17 | SetUnknownUrlMode.h \
18 | FindModal.h \
19 | AcceptAlert.h \
20 | GoForward.h \
21 | GoBack.h \
22 | Refresh.h \
23 | WindowMaximize.h \
24 | WindowSize.h \
25 | WindowCommand.h \
26 | WindowOpen.h \
27 | WindowClose.h \
28 | Version.h \
29 | EnableLogging.h \
30 | Authenticate.h \
31 | SetConfirmAction.h \
32 | SetPromptAction.h \
33 | SetPromptText.h \
34 | ClearPromptText.h \
35 | JavascriptAlertMessages.h \
36 | JavascriptConfirmMessages.h \
37 | JavascriptPromptMessages.h \
38 | IgnoreSslErrors.h \
39 | WindowResize.h \
40 | CurrentUrl.h \
41 | ConsoleMessages.h \
42 | WebPage.h \
43 | Server.h \
44 | Connection.h \
45 | Command.h \
46 | SocketCommand.h \
47 | Visit.h \
48 | Reset.h \
49 | Node.h \
50 | JavascriptInvocation.h \
51 | Evaluate.h \
52 | EvaluateAsync.h \
53 | Execute.h \
54 | FrameFocus.h \
55 | Response.h \
56 | NetworkAccessManager.h \
57 | NetworkCookieJar.h \
58 | Header.h \
59 | Render.h \
60 | Body.h \
61 | Status.h \
62 | Headers.h \
63 | UnsupportedContentHandler.h \
64 | SetCookie.h \
65 | ClearCookies.h \
66 | GetCookies.h \
67 | CommandParser.h \
68 | CommandFactory.h \
69 | SetProxy.h \
70 | NullCommand.h \
71 | PageLoadingCommand.h \
72 | SetSkipImageLoading.h \
73 | WebPageManager.h \
74 | WindowFocus.h \
75 | GetWindowHandles.h \
76 | GetWindowHandle.h \
77 | GetTimeout.h \
78 | SetTimeout.h \
79 | TimeoutCommand.h \
80 | SetUrlBlacklist.h \
81 | NoOpReply.h \
82 | JsonSerializer.h \
83 | InvocationResult.h \
84 | ErrorMessage.h \
85 | Title.h \
86 | FindCss.h \
87 | JavascriptCommand.h \
88 | FindXpath.h \
89 | NetworkReplyProxy.h \
90 | IgnoreDebugOutput.h \
91 | StdinNotifier.h \
92 | RequestHandler.h \
93 | BlacklistedRequestHandler.h \
94 | MissingContentHeaderRequestHandler.h \
95 | CustomHeadersRequestHandler.h \
96 | NetworkRequestFactory.h \
97 | UnknownUrlHandler.h
98 |
99 | SOURCES = \
100 | FrameUrl.cpp \
101 | FrameTitle.cpp \
102 | BlockUrl.cpp \
103 | AllowUrl.cpp \
104 | SetUnknownUrlMode.cpp \
105 | FindModal.cpp \
106 | AcceptAlert.cpp \
107 | GoForward.cpp \
108 | GoBack.cpp \
109 | Refresh.cpp \
110 | WindowMaximize.cpp \
111 | WindowSize.cpp \
112 | WindowCommand.cpp \
113 | WindowOpen.cpp \
114 | WindowClose.cpp \
115 | Version.cpp \
116 | EnableLogging.cpp \
117 | Authenticate.cpp \
118 | SetConfirmAction.cpp \
119 | SetPromptAction.cpp \
120 | SetPromptText.cpp \
121 | ClearPromptText.cpp \
122 | JavascriptAlertMessages.cpp \
123 | JavascriptConfirmMessages.cpp \
124 | JavascriptPromptMessages.cpp \
125 | IgnoreSslErrors.cpp \
126 | WindowResize.cpp \
127 | CurrentUrl.cpp \
128 | ConsoleMessages.cpp \
129 | main.cpp \
130 | WebPage.cpp \
131 | Server.cpp \
132 | Connection.cpp \
133 | Command.cpp \
134 | SocketCommand.cpp \
135 | Visit.cpp \
136 | Reset.cpp \
137 | Node.cpp \
138 | JavascriptInvocation.cpp \
139 | Evaluate.cpp \
140 | EvaluateAsync.cpp \
141 | Execute.cpp \
142 | FrameFocus.cpp \
143 | Response.cpp \
144 | NetworkAccessManager.cpp \
145 | NetworkCookieJar.cpp \
146 | Header.cpp \
147 | Render.cpp \
148 | body.cpp \
149 | Status.cpp \
150 | Headers.cpp \
151 | UnsupportedContentHandler.cpp \
152 | SetCookie.cpp \
153 | ClearCookies.cpp \
154 | GetCookies.cpp \
155 | CommandParser.cpp \
156 | CommandFactory.cpp \
157 | SetProxy.cpp \
158 | NullCommand.cpp \
159 | PageLoadingCommand.cpp \
160 | SetTimeout.cpp \
161 | GetTimeout.cpp \
162 | SetSkipImageLoading.cpp \
163 | WebPageManager.cpp \
164 | WindowFocus.cpp \
165 | GetWindowHandles.cpp \
166 | GetWindowHandle.cpp \
167 | TimeoutCommand.cpp \
168 | SetUrlBlacklist.cpp \
169 | NoOpReply.cpp \
170 | JsonSerializer.cpp \
171 | InvocationResult.cpp \
172 | ErrorMessage.cpp \
173 | Title.cpp \
174 | FindCss.cpp \
175 | JavascriptCommand.cpp \
176 | FindXpath.cpp \
177 | NetworkReplyProxy.cpp \
178 | IgnoreDebugOutput.cpp \
179 | StdinNotifier.cpp \
180 | RequestHandler.cpp \
181 | BlacklistedRequestHandler.cpp \
182 | MissingContentHeaderRequestHandler.cpp \
183 | CustomHeadersRequestHandler.cpp \
184 | NetworkRequestFactory.cpp \
185 | UnknownUrlHandler.cpp
186 |
187 | RESOURCES = webkit_server.qrc
188 | QT += network
189 | greaterThan(QT_MAJOR_VERSION, 4) {
190 | qtHaveModule(webkitwidgets) {
191 | QT += webkitwidgets
192 | } else {
193 | error("No QtWebKit installation found. QtWebKit is no longer included with Qt 5.6, so you may need to install it separately.")
194 | }
195 | } else {
196 | QT += webkit
197 | }
198 | lessThan(QT_MAJOR_VERSION, 5) {
199 | lessThan(QT_MINOR_VERSION, 8) {
200 | error(At least Qt 4.8.0 is required to run capybara-webkit.)
201 | }
202 | }
203 | CONFIG += console precompile_header
204 | CONFIG -= app_bundle
205 | PRECOMPILED_HEADER = stable.h
206 | macx {
207 | QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.9
208 | }
209 |
--------------------------------------------------------------------------------
/src/webkit_server.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | capybara.js
4 | pointer.png
5 |
6 |
7 |
--------------------------------------------------------------------------------
/templates/Command.cpp:
--------------------------------------------------------------------------------
1 | #include "NAME.h"
2 | #include "SocketCommand.h"
3 | #include "WebPage.h"
4 | #include "WebPageManager.h"
5 |
6 | NAME::NAME(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
7 | }
8 |
9 | void NAME::start() {
10 | }
11 |
--------------------------------------------------------------------------------
/templates/Command.h:
--------------------------------------------------------------------------------
1 | #include "SocketCommand.h"
2 |
3 | class NAME : public SocketCommand {
4 | Q_OBJECT
5 |
6 | public:
7 | NAME(WebPageManager *, QStringList &arguments, QObject *parent = 0);
8 | virtual void start();
9 | };
10 |
11 |
--------------------------------------------------------------------------------
/test/testignoredebugoutput.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include "../src/IgnoreDebugOutput.h"
6 |
7 | #define MAX_LEN 40
8 |
9 | class TestIgnoreDebugOutput: public QObject {
10 | Q_OBJECT
11 |
12 | private slots:
13 | void testIgnoreDebugOutput();
14 | };
15 |
16 | void TestIgnoreDebugOutput::testIgnoreDebugOutput() {
17 | char buffer[MAX_LEN+1] = {0};
18 | int out_pipe[2];
19 | int saved_stdout;
20 |
21 | saved_stdout = dup(STDOUT_FILENO);
22 |
23 | QVERIFY(pipe(out_pipe) == 0);
24 |
25 | dup2(out_pipe[1], STDOUT_FILENO);
26 | close(out_pipe[1]);
27 |
28 | long flags = fcntl(out_pipe[0], F_GETFL);
29 | flags |= O_NONBLOCK;
30 | fcntl(out_pipe[0], F_SETFL, flags);
31 |
32 | ignoreDebugOutput();
33 |
34 | qDebug() << "Message";
35 | fflush(stdout);
36 |
37 | read(out_pipe[0], buffer, MAX_LEN);
38 |
39 | dup2(saved_stdout, STDOUT_FILENO);
40 |
41 | QCOMPARE(QString(buffer), QString(""));
42 | }
43 |
44 | QTEST_MAIN(TestIgnoreDebugOutput)
45 | #include "testignoredebugoutput.moc"
46 |
--------------------------------------------------------------------------------
/test/testwebkitserver.pro:
--------------------------------------------------------------------------------
1 | SOURCES = testignoredebugoutput.cpp
2 | OBJECTS += ../src/build/IgnoreDebugOutput.o
3 | QT += testlib
4 | CONFIG += testcase console
5 | CONFIG -= app_bundle
6 | macx {
7 | QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.9
8 | }
9 |
--------------------------------------------------------------------------------
/vagrant_setup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | export DISPLAY=:99
4 |
5 | apt-get -y update
6 | apt-get -y install ruby1.9.1-dev
7 |
8 | if [ -z `which make` ]; then apt-get -y install build-essential; fi
9 | if [ -z `which qmake` ]; then apt-get -y install libqt4-dev libicu48; fi
10 | if [ -z `which git` ]; then apt-get -y install git-core; fi
11 | if [ -z `which xml2-config` ]; then apt-get -y install libxml2-dev; fi
12 | if [ -z `which xslt-config` ]; then apt-get -y install libxslt-dev; fi
13 | if [ -z `which convert` ]; then apt-get -y install imagemagick; fi
14 | if [ -z `which firefox` ]; then apt-get -y install firefox; fi
15 |
16 | if [ -z `which bundle` ];
17 | then
18 | gem install bundler
19 | cd /vagrant
20 | bundle
21 | fi
22 |
23 | if [ ! -f /etc/init.d/xvfb ];
24 | then
25 | apt-get -y install xvfb
26 | echo "export DISPLAY=${DISPLAY}" >> /home/vagrant/.bashrc
27 | tee /etc/init.d/xvfb <<-EOF
28 | #!/bin/bash
29 |
30 | XVFB=/usr/bin/Xvfb
31 | XVFBARGS="\$DISPLAY -ac -screen 0 1024x768x16"
32 | PIDFILE=\${HOME}/xvfb_\${DISPLAY:1}.pid
33 | case "\$1" in
34 | start)
35 | echo -n "Starting virtual X frame buffer: Xvfb"
36 | /sbin/start-stop-daemon --start --quiet --pidfile \$PIDFILE --make-pidfile --background --exec \$XVFB -- \$XVFBARGS
37 | echo "."
38 | ;;
39 | stop)
40 | echo -n "Stopping virtual X frame buffer: Xvfb"
41 | /sbin/start-stop-daemon --stop --quiet --pidfile \$PIDFILE
42 | echo "."
43 | ;;
44 | restart)
45 | \$0 stop
46 | \$0 start
47 | ;;
48 | *)
49 | echo "Usage: /etc/init.d/xvfb {start|stop|restart}"
50 | exit 1
51 | esac
52 | exit 0
53 | EOF
54 |
55 | chmod +x /etc/init.d/xvfb
56 | fi
57 |
58 | /etc/init.d/xvfb start
59 |
--------------------------------------------------------------------------------
/webkit_server.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = subdirs
2 | CONFIG += ordered
3 | SUBDIRS += src/webkit_server.pro
4 | test {
5 | SUBDIRS += test/testwebkitserver.pro
6 | }
7 |
--------------------------------------------------------------------------------