webkit_remote 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/Gemfile +16 -0
- data/Gemfile.lock +45 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +114 -0
- data/Rakefile +38 -0
- data/VERSION +1 -0
- data/lib/webkit_remote/browser.rb +150 -0
- data/lib/webkit_remote/client/page.rb +59 -0
- data/lib/webkit_remote/client/page_events.rb +45 -0
- data/lib/webkit_remote/client/runtime.rb +406 -0
- data/lib/webkit_remote/client.rb +114 -0
- data/lib/webkit_remote/event.rb +138 -0
- data/lib/webkit_remote/process.rb +160 -0
- data/lib/webkit_remote/rpc.rb +205 -0
- data/lib/webkit_remote/top_level.rb +31 -0
- data/lib/webkit_remote.rb +12 -0
- data/test/fixtures/config.ru +12 -0
- data/test/fixtures/html/load.html +7 -0
- data/test/fixtures/html/runtime.html +31 -0
- data/test/helper.rb +44 -0
- data/test/webkit_remote/browser_test.rb +80 -0
- data/test/webkit_remote/client/page_test.rb +31 -0
- data/test/webkit_remote/client/remote_object_group_test.rb +81 -0
- data/test/webkit_remote/client/remote_object_test.rb +133 -0
- data/test/webkit_remote/client/runtime_test.rb +109 -0
- data/test/webkit_remote/client_test.rb +99 -0
- data/test/webkit_remote/event_test.rb +83 -0
- data/test/webkit_remote/process_test.rb +52 -0
- data/test/webkit_remote/rpc_test.rb +55 -0
- data/test/webkit_remote_test.rb +63 -0
- metadata +274 -0
data/.document
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
source 'http://rubygems.org'
|
2
|
+
gem 'eventmachine', '>= 1.0.0'
|
3
|
+
gem 'faye-websocket', '>= 0.4.6'
|
4
|
+
gem 'posix-spawn', '>= 0.3.6'
|
5
|
+
|
6
|
+
group :development do
|
7
|
+
gem 'bundler', '>= 1.2.1'
|
8
|
+
gem 'jeweler', '>= 1.8.4'
|
9
|
+
gem 'minitest', '>= 4.1.0'
|
10
|
+
gem 'puma', '>= 1.6.3'
|
11
|
+
gem 'rack', '>= 1.4.1'
|
12
|
+
gem 'rdoc', '>= 3.12'
|
13
|
+
gem 'ruby-prof', '>= 0.11.2'
|
14
|
+
gem 'simplecov', '>= 0.7.1'
|
15
|
+
gem 'yard', '>= 0.8.3'
|
16
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
eventmachine (1.0.0)
|
5
|
+
faye-websocket (0.4.6)
|
6
|
+
eventmachine (>= 0.12.0)
|
7
|
+
git (1.2.5)
|
8
|
+
jeweler (1.8.4)
|
9
|
+
bundler (~> 1.0)
|
10
|
+
git (>= 1.2.5)
|
11
|
+
rake
|
12
|
+
rdoc
|
13
|
+
json (1.7.5)
|
14
|
+
minitest (4.1.0)
|
15
|
+
multi_json (1.3.6)
|
16
|
+
posix-spawn (0.3.6)
|
17
|
+
puma (1.6.3)
|
18
|
+
rack (~> 1.2)
|
19
|
+
rack (1.4.1)
|
20
|
+
rake (0.9.2.2)
|
21
|
+
rdoc (3.12)
|
22
|
+
json (~> 1.4)
|
23
|
+
ruby-prof (0.11.2)
|
24
|
+
simplecov (0.7.1)
|
25
|
+
multi_json (~> 1.0)
|
26
|
+
simplecov-html (~> 0.7.1)
|
27
|
+
simplecov-html (0.7.1)
|
28
|
+
yard (0.8.3)
|
29
|
+
|
30
|
+
PLATFORMS
|
31
|
+
ruby
|
32
|
+
|
33
|
+
DEPENDENCIES
|
34
|
+
bundler (>= 1.2.1)
|
35
|
+
eventmachine (>= 1.0.0)
|
36
|
+
faye-websocket (>= 0.4.6)
|
37
|
+
jeweler (>= 1.8.4)
|
38
|
+
minitest (>= 4.1.0)
|
39
|
+
posix-spawn (>= 0.3.6)
|
40
|
+
puma (>= 1.6.3)
|
41
|
+
rack (>= 1.4.1)
|
42
|
+
rdoc (>= 3.12)
|
43
|
+
ruby-prof (>= 0.11.2)
|
44
|
+
simplecov (>= 0.7.1)
|
45
|
+
yard (>= 0.8.3)
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Victor Costan
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
# webkit_remote
|
2
|
+
|
3
|
+
Ruby gem for driving
|
4
|
+
[Google Chrome](https://www.google.com/chrome/) and possibly other
|
5
|
+
WebKit-based browsers via the
|
6
|
+
[WebKit remote debugging protocol](https://www.webkit.org/blog/1875/announcing-remote-debugging-protocol-v1-0/).
|
7
|
+
|
8
|
+
|
9
|
+
## Requirements
|
10
|
+
|
11
|
+
The gem is tested against the OSX and Linux builds of Google Chrome. The only
|
12
|
+
platform-dependent functionality is launching and shutting down the browser
|
13
|
+
process, everything else should work for any WebKit-based browser that
|
14
|
+
implements the remote debugging protocol.
|
15
|
+
|
16
|
+
|
17
|
+
## Installation
|
18
|
+
|
19
|
+
Use RubyGems.
|
20
|
+
|
21
|
+
```bash
|
22
|
+
gem install webkit_remote
|
23
|
+
```
|
24
|
+
|
25
|
+
|
26
|
+
## Usage
|
27
|
+
|
28
|
+
This section only showcases a few features. Read the
|
29
|
+
[latest gem release YARD docs](http://rubydoc.info/gems/webkit_remote)
|
30
|
+
to see everything this gem has to offer.
|
31
|
+
|
32
|
+
### Session Setup
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
client = WebkitRemote.local
|
36
|
+
```
|
37
|
+
|
38
|
+
launches a separate instance of Google Chrome that is not connected to your
|
39
|
+
profile, and sets up a connection to it. Alternatively,
|
40
|
+
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
client = WebkitRemote.remote host: 'phone-ip-here', port: 9222
|
44
|
+
```
|
45
|
+
|
46
|
+
connects to a remote WebKit instance
|
47
|
+
[running on a phone](https://developers.google.com/chrome/mobile/docs/debugging).
|
48
|
+
|
49
|
+
### Load a Page
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
client.page_events = true
|
53
|
+
client.navigate_to 'http://translate.google.com'
|
54
|
+
client.wait_for type: WebkitRemote::Event::PageLoaded
|
55
|
+
```
|
56
|
+
|
57
|
+
### Run JavaScript
|
58
|
+
|
59
|
+
Evaluate some JavaScript.
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
element = client.remote_eval 'document.querySelector("[name=text]")'
|
63
|
+
```
|
64
|
+
|
65
|
+
Take a look at the result.
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
element.js_class_name
|
69
|
+
element.description
|
70
|
+
element.properties[:tagName].value
|
71
|
+
element.properties[:tagName].writable?
|
72
|
+
```
|
73
|
+
|
74
|
+
Pass an object to some JavaScript code.
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
js_code = <<END_JS
|
78
|
+
function(element, value) {
|
79
|
+
element.value = value;
|
80
|
+
return "Check the browser window";
|
81
|
+
}
|
82
|
+
END_JS
|
83
|
+
client.remote_eval('window').bound_call js_code, element, '你好'
|
84
|
+
```
|
85
|
+
|
86
|
+
Finally, release the JavaScript objects that the debugger is holding onto.
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
element.group.release_all
|
90
|
+
```
|
91
|
+
|
92
|
+
### Clean Up
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
client.close
|
96
|
+
```
|
97
|
+
|
98
|
+
closes the debugging connection and shuts down the Google Chrome instance.
|
99
|
+
|
100
|
+
|
101
|
+
## Contributing
|
102
|
+
|
103
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
104
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
105
|
+
* Fork the project.
|
106
|
+
* Start a feature/bugfix branch.
|
107
|
+
* Commit and push until you are happy with your contribution.
|
108
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
109
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
110
|
+
|
111
|
+
## Copyright
|
112
|
+
|
113
|
+
Copyright (c) 2012 Victor Costan. See LICENSE.txt for further details.
|
114
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
+
gem.name = "webkit_remote"
|
18
|
+
gem.homepage = "http://github.com/pwnall/webkit_remote"
|
19
|
+
gem.license = "MIT"
|
20
|
+
gem.summary = %Q{Client for the Webkit Remote Debugging server}
|
21
|
+
gem.description = %Q{Launches Google Chrome instances and controls them via the Remote Debugging server}
|
22
|
+
gem.email = "victor@costan.us"
|
23
|
+
gem.authors = ["Victor Costan"]
|
24
|
+
# dependencies defined in Gemfile
|
25
|
+
end
|
26
|
+
Jeweler::RubygemsDotOrgTasks.new
|
27
|
+
|
28
|
+
require 'rake/testtask'
|
29
|
+
Rake::TestTask.new(:test) do |test|
|
30
|
+
test.libs << 'lib' << 'test'
|
31
|
+
test.pattern = 'test/**/*_test.rb'
|
32
|
+
test.verbose = true
|
33
|
+
end
|
34
|
+
|
35
|
+
task :default => :test
|
36
|
+
|
37
|
+
require 'yard'
|
38
|
+
YARD::Rake::YardocTask.new
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
@@ -0,0 +1,150 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'net/http'
|
3
|
+
|
4
|
+
module WebkitRemote
|
5
|
+
|
6
|
+
# The master connection to the remote debugging server in a Webkit process.
|
7
|
+
class Browser
|
8
|
+
# Sets up a debugging connection to a Webkit processr.
|
9
|
+
#
|
10
|
+
# @param [Hash] opts info on the browser to connect to
|
11
|
+
# @option opts [String] host the hostname / IP address of the Webkit remote
|
12
|
+
# debugging server
|
13
|
+
# @option opts [Integer] port the port that the Webkit remote debugging
|
14
|
+
# server listens to
|
15
|
+
# @option opts [WebkitRemote::Process] process a process on the local machine
|
16
|
+
# to connect to; the process will automatically be stopped when the
|
17
|
+
# debugging connection is closed; the host and port will be configured
|
18
|
+
# automatically
|
19
|
+
# @option opts [Boolean] stop_process if true, the WebkitRemote::Process
|
20
|
+
# passed to the constructor will be automatically stopped
|
21
|
+
# @raise [SystemCallError] if the connection could not be established; this
|
22
|
+
# most likely means that there is no remote debugging server at the given
|
23
|
+
# host / port
|
24
|
+
def initialize(opts = {})
|
25
|
+
if opts[:process]
|
26
|
+
@process = opts[:process]
|
27
|
+
@stop_process = opts.fetch :stop_process, false
|
28
|
+
@host = 'localhost'
|
29
|
+
@port = process.port
|
30
|
+
else
|
31
|
+
@process = nil
|
32
|
+
@stop_process = false
|
33
|
+
@host = opts[:host] || 'localhost'
|
34
|
+
@port = opts[:port] || 9292
|
35
|
+
end
|
36
|
+
@closed = false
|
37
|
+
|
38
|
+
@http = Net::HTTP.start @host, @port
|
39
|
+
end
|
40
|
+
|
41
|
+
# Closes the connection the browser.
|
42
|
+
#
|
43
|
+
# If the Browser instance was given a WebkitRemote::Process, the process will
|
44
|
+
# also be stopped. This instance becomes useless after closing.
|
45
|
+
#
|
46
|
+
# @return [WebkitRemote::Browser] self
|
47
|
+
def close
|
48
|
+
return self if @closed
|
49
|
+
@closed = true
|
50
|
+
@http.finish
|
51
|
+
@http = nil
|
52
|
+
@process.stop if @stop_process
|
53
|
+
self
|
54
|
+
end
|
55
|
+
|
56
|
+
# Retrieves the tabs that are currently open in the browser.
|
57
|
+
#
|
58
|
+
# These tabs can be used to start debugging.
|
59
|
+
#
|
60
|
+
# @return [Array<WebkitRemote::Browser::Tab>] the open tabs
|
61
|
+
def tabs
|
62
|
+
http_response = @http.request Net::HTTP::Get.new('/json')
|
63
|
+
tabs = JSON.parse(http_response.body).map do |json_tab|
|
64
|
+
title = json_tab['title']
|
65
|
+
url = json_tab['url']
|
66
|
+
debug_url = json_tab['webSocketDebuggerUrl']
|
67
|
+
Tab.new self, debug_url, title: title, url: url
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# @return [Boolean] if true, a WebkitRemote::Process will be stopped when
|
72
|
+
# this browser connection is closed
|
73
|
+
attr_reader :stop_process
|
74
|
+
alias_method :stop_process?, :stop_process
|
75
|
+
|
76
|
+
# Changes the automated WebkitRemote::Process stopping behavior.
|
77
|
+
#
|
78
|
+
# This should only be set to true if this Browser instance was given a
|
79
|
+
# WebkitRemote::Process at creation time.
|
80
|
+
#
|
81
|
+
# @param [Boolean] new_stop_process if true, the WebkitRemote::Process
|
82
|
+
# passed to this instance's constructor will be stopped when this
|
83
|
+
# connection is closed
|
84
|
+
# @return [Boolean] new_stop_process
|
85
|
+
def stop_process=(new_stop_process)
|
86
|
+
if new_stop_process
|
87
|
+
unless @process
|
88
|
+
raise ArgumentError, "Browser instance not backed by a Webkit process"
|
89
|
+
end
|
90
|
+
@stop_process = true
|
91
|
+
else
|
92
|
+
@stop_process = false
|
93
|
+
end
|
94
|
+
new_stop_process
|
95
|
+
end
|
96
|
+
|
97
|
+
# @return [WebkitRemote::Process, nil] Process instance passed to this
|
98
|
+
# connection's constructor
|
99
|
+
attr_reader :process
|
100
|
+
|
101
|
+
# @return [String] hostname or IP of the Webkit remote debugging server
|
102
|
+
attr_reader :host
|
103
|
+
|
104
|
+
# @return [Integer] port that the Webkit remote debugging server listens on
|
105
|
+
attr_reader :port
|
106
|
+
|
107
|
+
# @return [Boolean] if true, the connection to the remote debugging server
|
108
|
+
# has been closed, and this instance is mostly useless
|
109
|
+
attr_reader :closed
|
110
|
+
alias_method :closed?, :closed
|
111
|
+
|
112
|
+
# Clean up when garbage collected.
|
113
|
+
def finalize
|
114
|
+
close unless @closed
|
115
|
+
end
|
116
|
+
|
117
|
+
# References a tab open in a Webkit process with a remote debugging server.
|
118
|
+
class Tab
|
119
|
+
# @return [Webkit::Remote] connection to the browser that this tab belongs to
|
120
|
+
attr_reader :browser
|
121
|
+
|
122
|
+
# @return [String] URL of the tab's remote debugging endpoint
|
123
|
+
attr_reader :debug_url
|
124
|
+
|
125
|
+
# @return [String, nil] title of the Web page open in the browser tab
|
126
|
+
attr_reader :title
|
127
|
+
|
128
|
+
# @return [String, nil] URL of the Web page open in the browser tab
|
129
|
+
attr_reader :url
|
130
|
+
|
131
|
+
# Creates a tab reference.
|
132
|
+
#
|
133
|
+
# @param [WebkitRemote::Browser] browser the master debugging connection to
|
134
|
+
# the Webkit process
|
135
|
+
# @param [String] debug_url URL of the tab's remote debugging endpoint
|
136
|
+
# @param [Hash] metadata non-essential information about the tab
|
137
|
+
# @option metadata [String, nil] title title of the page open in the browser
|
138
|
+
# tab
|
139
|
+
# @option metadata [String, nil] url URL of the page open in the browser tab
|
140
|
+
def initialize(browser, debug_url, metadata)
|
141
|
+
@browser = browser
|
142
|
+
@debug_url = debug_url
|
143
|
+
@title = metadata[:title]
|
144
|
+
@url = metadata[:url]
|
145
|
+
end
|
146
|
+
end # class WebkitRemote::Browser::Tab
|
147
|
+
|
148
|
+
end # class WebkitRemote::Browser
|
149
|
+
|
150
|
+
end # namespace WebkitRemote
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module WebkitRemote
|
2
|
+
|
3
|
+
class Client
|
4
|
+
|
5
|
+
# API for the Page domain.
|
6
|
+
module Page
|
7
|
+
# Loads a new URL into the tab under debugging.
|
8
|
+
#
|
9
|
+
# @param [String] url the URL to be loaded into the tab
|
10
|
+
# @return [WebkitRemote::Client] self
|
11
|
+
def navigate_to(url)
|
12
|
+
@rpc.call 'Page.navigate', url: url
|
13
|
+
self
|
14
|
+
end
|
15
|
+
|
16
|
+
# Reloads
|
17
|
+
#
|
18
|
+
# @param [Hash] opts quirky behavior bits
|
19
|
+
# @option opts [Boolean] skip_cache if true, the cache is not used; this is
|
20
|
+
# what happens when the user presses Shift+refresh
|
21
|
+
# @option opts [String] onload a JavaScript that will be injected in all the
|
22
|
+
# page's frames after reloading
|
23
|
+
# @return [WebkitRemote::Client] self
|
24
|
+
def refresh(opts = {})
|
25
|
+
options = {}
|
26
|
+
options[:ignoreCache] = true if opts[:skip_cache]
|
27
|
+
options[:scriptToEvaluateOnLoad] = opts[:onload] if opts[:onload]
|
28
|
+
@rpc.call 'Page.refresh', options
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
# Enables or disables the generation of events in the Page domain.
|
33
|
+
#
|
34
|
+
# @param [Boolean] new_page_events if true, the browser debugger will
|
35
|
+
# generate Page.* events
|
36
|
+
def page_events=(new_page_events)
|
37
|
+
if !!new_page_events != page_events
|
38
|
+
@page_events = !!new_page_events
|
39
|
+
@rpc.call(@page_events ? 'Page.enable' : 'Page.disable')
|
40
|
+
end
|
41
|
+
new_page_events
|
42
|
+
end
|
43
|
+
|
44
|
+
# @return [Boolean] true if the debugger generates Page.* events
|
45
|
+
attr_reader :page_events
|
46
|
+
|
47
|
+
# @private Called by the Client constructor to set up Page data structures.
|
48
|
+
def initialize_page
|
49
|
+
@page_events = nil
|
50
|
+
end
|
51
|
+
|
52
|
+
end # module WebkitRemote::Client::Page
|
53
|
+
|
54
|
+
initializer :initialize_page
|
55
|
+
include WebkitRemote::Client::Page
|
56
|
+
|
57
|
+
end # namespace WebkitRemote::Client
|
58
|
+
|
59
|
+
end # namespace WebkitRemote
|