Skip to content

Commit 27611e9

Browse files
committed
FEAT: including source of webdriver and websocket modules
1 parent 9bb9108 commit 27611e9

File tree

4 files changed

+469
-0
lines changed

4 files changed

+469
-0
lines changed

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ It is possible to extend Rebol functionality using external modules (native and
4545
* [Rebol/OpenCV](https://github.com/Oldes/Rebol-OpenCV) - Computer Vision Library
4646
* [Rebol/SQLite](https://github.com/Siskin-framework/Rebol-SQLite) - SQL database engine
4747
* [Rebol/Triangulate](https://github.com/Siskin-framework/Rebol-Triangulate) - Two-Dimensional Quality Mesh Generator and Delaunay Triangulator
48+
* [Rebol/WebDriver](https://github.com/Oldes/Rebol-WebDriver) - WebDriver scheme for automating Chromium based browser sessions
49+
* [Rebol/WebSocket](https://github.com/Oldes/Rebol-WebSocket) - WebSocket scheme and codec
4850

4951
It should be noted that on macOS it may be required to resign _downloaded native extensions_ using command like:
5052
```

src/boot/sysobj.reb

+2
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,8 @@ modules: object [
271271
mime-field: https://src.rebol.tech/mezz/codec-mime-field.reb
272272
mime-types: https://src.rebol.tech/mezz/codec-mime-types.reb
273273
quoted-printable: https://src.rebol.tech/mezz/codec-quoted-printable.reb
274+
webdriver: https://src.rebol.tech/modules/webdriver.reb
275+
websocket: https://src.rebol.tech/modules/websocket.reb
274276
;; and..
275277
window: none ;- internal extension for gui (on Windows so far!)
276278
]

src/modules/webdriver.reb

+218
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
Rebol [
2+
Title: "WebDriver (chrome) scheme"
3+
Type: module
4+
Name: webdriver
5+
Date: 03-Jan-2024
6+
Version: 0.1.0
7+
Author: @Oldes
8+
Home: https://github.com/Oldes/Rebol-WebDriver
9+
Rights: http://opensource.org/licenses/Apache-2.0
10+
Purpose: {Can be used to automate browser sessions.}
11+
History: [
12+
03-Jan-2024 "Oldes" {Initial version}
13+
]
14+
Needs: [
15+
3.11.0 ;; Minimal Rebol version required by WebScocket module
16+
websocket
17+
json
18+
]
19+
Notes: {
20+
Currently only `chrome` scheme is implemented which is supposed to be working
21+
with Chromium, Chrome and other Blink-based browsers.
22+
23+
The browser must be started with `remote-debugging` enabled.
24+
25+
For example on macOS using a Brave browser:
26+
```terminal
27+
/Applications/Brave\ Browser.app/Contents/MacOS/Brave\ Browser --remote-debugging-port=9222
28+
```
29+
30+
Available methods are documented here: https://chromedevtools.github.io/devtools-protocol/
31+
}
32+
]
33+
34+
system/options/log/chrome: 4
35+
36+
;; internal functions....
37+
read-and-wait: function[
38+
"Wait specified time while processing read events"
39+
port [port!] "Internal websocket port of the webdrive scheme"
40+
time [time!]
41+
][
42+
start: now/precise
43+
end: start + time
44+
until [
45+
read port
46+
wait [port time]
47+
48+
process-packets port
49+
50+
time: difference end now/precise
51+
time <= 0:0:0
52+
]
53+
]
54+
55+
process-packets: function[
56+
"Process incomming webscocket packets of the webdrive scheme"
57+
conn [port!]
58+
][
59+
port: conn/parent ;; outter webdrive scheme
60+
ctx: port/extra
61+
foreach packet conn/data [
62+
try/with [
63+
packet: decode 'json packet
64+
either packet/id [
65+
ctx/pending: ctx/pending - 1
66+
append port/data packet
67+
][ port/actor/on-method packet ]
68+
] :print
69+
]
70+
clear conn/data
71+
]
72+
73+
ws-decode: :codecs/ws/decode
74+
75+
;- The Chrome scheme ---------------------------------------------------------------
76+
sys/make-scheme [
77+
name: 'chrome
78+
title: "Chrome WebDriver API"
79+
spec: object [title: scheme: ref: host: none port: 9222]
80+
81+
actor: [
82+
open: func [port [port!] /local ctx spec host conn data port-spec][
83+
spec: port/spec
84+
spec/host: any [spec/host "localhost"]
85+
spec/port: any [spec/port 9222]
86+
87+
port/data: copy [] ;; holds decoded websocket responses
88+
port/extra: ctx: context [
89+
host: rejoin [http:// spec/host #":" spec/port]
90+
version: none
91+
browser: none
92+
counter: 0
93+
pending: 0 ;; increments when a new method is sent, decremented when response is received
94+
req: #(id: 0 method: none params: #[none]) ;; used to send a command (to avoid cerating a new map)
95+
page-info: none ;; holds resolved info from an attached page
96+
page-conn: none ;; webscocket connection to an attached page
97+
]
98+
99+
ctx/version: data: try/with [
100+
decode 'json read ctx/host/json/version
101+
][
102+
sys/log/error 'CHROME "Failed to get browser info!"
103+
sys/log/error 'CHROME system/state/last-error
104+
return none
105+
]
106+
107+
ctx/browser: conn: open as url! data/webSocketDebuggerUrl
108+
conn/parent: port
109+
wait [conn 15]
110+
sys/log/more 'CHROME "Browser connection opened."
111+
port
112+
]
113+
open?: func[port /local ctx][
114+
all [
115+
ctx: port/extra
116+
any [ctx/browser ctx/page-conn]
117+
]
118+
]
119+
close: func[port /local ctx][
120+
ctx: port/extra
121+
if ctx/port-conn [
122+
try [close ctx/port-conn wait [ctx/page-conn 1]]
123+
ctx/port-conn: ctx/port-info: none
124+
125+
]
126+
if ctx/browser [
127+
try [close ctx/browser wait [ctx/browser 1]]
128+
ctx/browser: none
129+
]
130+
port
131+
]
132+
133+
write: func[port data /local ctx url time method params conn][
134+
unless block? data [data: reduce [data]]
135+
136+
sys/log/info 'CHROME ["WRITE:" as-green mold/flat data]
137+
138+
clear port/data
139+
140+
ctx: port/extra
141+
either open? ctx/browser [
142+
parse data [some [
143+
144+
set url: url! (
145+
;- Open a new target (page)
146+
try/with [
147+
ctx/page-info: decode 'json write join ctx/host/json/new? url [PUT]
148+
;?? ctx/page-info
149+
append port/data ctx/page-info
150+
151+
ctx/page-conn: conn: open as url! ctx/page-info/webSocketDebuggerUrl
152+
;conn/awake: :ws-web-awake
153+
conn/parent: port
154+
wait [conn 15]
155+
conn: none
156+
] :print
157+
)
158+
| set time: [time! | decimal! | integer!] (
159+
;- Wait some time while processing incomming messages
160+
time: to time! time
161+
sys/log/info 'CHROME ["WAIT" as-green time]
162+
read-and-wait any [ctx/page-conn ctx/browser] time
163+
)
164+
|
165+
set method: word! set params: opt [map! | block!] (
166+
;- Send a command with optional options
167+
if block? params [params: make map! reduce/no-set params]
168+
sys/log/info 'CHROME ["Command:" as-red method as-green mold/flat/part params 100]
169+
;; resusing `req` value for all commands as it is just used to form a json anyway
170+
ctx/req/id: ctx/counter: ctx/counter + 1 ;; each command has an unique id
171+
ctx/req/method: method
172+
ctx/req/params: params
173+
ctx/pending: ctx/pending + 1
174+
write conn: any [ctx/page-conn ctx/browser] ctx/req
175+
;; don't wake up until received responses for all command requests
176+
forever [
177+
;@@TODO: handle the timeout to awoid infinite loop!
178+
wait [conn 15] ;; wait for any events
179+
process-packets conn ;; process incomming websocket messages
180+
if ctx/pending <= 0 [break] ;; exit the loop if there are no pending requests
181+
read conn ;; keep reading
182+
]
183+
)
184+
]]
185+
either 1 = length? port/data [first port/data][port/data]
186+
][ sys/log/error 'CHROME "Not open!"]
187+
]
188+
189+
read: func[port /local ctx conn packet][
190+
;; waits for any number of incomming messages
191+
if all [
192+
ctx: port/extra
193+
conn: any [ctx/page-conn ctx/browser]
194+
][
195+
clear port/data
196+
read conn
197+
wait [conn 1] ;; don't wait more then 1 second if there are no incomming messages
198+
process-packets conn
199+
]
200+
port/data
201+
]
202+
203+
pick: func[port value /local result][
204+
;; just a shortcut to get a single result direcly
205+
unless block? value [value: reduce [value]]
206+
result: write port value
207+
if block? result [result: last result]
208+
result/result
209+
]
210+
211+
on-method: func[packet][
212+
;; this function is supposed to be user defined and used to process incomming messages
213+
;; in this case it just prints its content...
214+
sys/log/info 'CHROME [as-red packet/method mold packet/params]
215+
]
216+
]
217+
]
218+

0 commit comments

Comments
 (0)