@@ -31,6 +31,7 @@ class DevTools
31
31
autoload :Response , 'selenium/webdriver/devtools/response'
32
32
33
33
def initialize ( url :)
34
+ @callback_threads = ThreadGroup . new
34
35
@messages = [ ]
35
36
@session_id = nil
36
37
@url = url
@@ -41,6 +42,7 @@ def initialize(url:)
41
42
end
42
43
43
44
def close
45
+ @callback_threads . list . each ( &:exit )
44
46
socket . close
45
47
end
46
48
@@ -93,27 +95,24 @@ def process_handshake
93
95
end
94
96
95
97
def attach_socket_listener
96
- socket_listener = Thread . new do
98
+ Thread . new do
99
+ Thread . current . abort_on_exception = true
100
+ Thread . current . report_on_exception = false
101
+
97
102
until socket . eof?
98
103
incoming_frame << socket . readpartial ( 1024 )
99
104
100
105
while ( frame = incoming_frame . next )
101
- # Firefox will periodically fail on unparsable empty frame
102
- break if frame . to_s . empty?
103
-
104
- message = JSON . parse ( frame . to_s )
105
- @messages << message
106
- WebDriver . logger . debug "DevTools <- #{ message } "
106
+ message = process_frame ( frame )
107
107
next unless message [ 'method' ]
108
108
109
+ params = message [ 'params' ]
109
110
callbacks [ message [ 'method' ] ] . each do |callback |
110
- callback_thread = Thread . new ( message [ 'params' ] , &callback )
111
- callback_thread . abort_on_exception = true
111
+ @callback_threads . add ( callback_thread ( params , &callback ) )
112
112
end
113
113
end
114
114
end
115
115
end
116
- socket_listener . abort_on_exception = true
117
116
end
118
117
119
118
def start_session
@@ -127,6 +126,34 @@ def incoming_frame
127
126
@incoming_frame ||= WebSocket ::Frame ::Incoming ::Client . new ( version : ws . version )
128
127
end
129
128
129
+ def process_frame ( frame )
130
+ message = frame . to_s
131
+
132
+ # Firefox will periodically fail on unparsable empty frame
133
+ return { } if message . empty?
134
+
135
+ message = JSON . parse ( message )
136
+ @messages << message
137
+ WebDriver . logger . debug "DevTools <- #{ message } "
138
+
139
+ message
140
+ end
141
+
142
+ def callback_thread ( params )
143
+ Thread . new do
144
+ Thread . current . abort_on_exception = true
145
+
146
+ # We might end up blocked forever when we have an error in event.
147
+ # For example, if network interception event raises error,
148
+ # the browser will keep waiting for the request to be proceeded
149
+ # before returning back to the original thread. In this case,
150
+ # we should at least print the error.
151
+ Thread . current . report_on_exception = true
152
+
153
+ yield params
154
+ end
155
+ end
156
+
130
157
def wait
131
158
@wait ||= Wait . new ( timeout : RESPONSE_WAIT_TIMEOUT , interval : RESPONSE_WAIT_INTERVAL )
132
159
end
0 commit comments