puppeteer-ruby 0.37.1 → 0.38.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +31 -1
- data/README.md +1 -1
- data/docs/api_coverage.md +38 -34
- data/lib/puppeteer/cdp_session.rb +5 -0
- data/lib/puppeteer/dom_world.rb +9 -5
- data/lib/puppeteer/element_handle.rb +10 -8
- data/lib/puppeteer/executable_path_finder.rb +28 -0
- data/lib/puppeteer/frame.rb +31 -9
- data/lib/puppeteer/frame_manager.rb +138 -61
- data/lib/puppeteer/{request.rb → http_request.rb} +159 -22
- data/lib/puppeteer/{response.rb → http_response.rb} +2 -2
- data/lib/puppeteer/launcher/chrome.rb +26 -6
- data/lib/puppeteer/launcher/chrome_arg_options.rb +2 -1
- data/lib/puppeteer/launcher/firefox.rb +16 -6
- data/lib/puppeteer/lifecycle_watcher.rb +2 -2
- data/lib/puppeteer/network_manager.rb +10 -5
- data/lib/puppeteer/page/screenshot_options.rb +1 -1
- data/lib/puppeteer/page.rb +85 -22
- data/lib/puppeteer/puppeteer.rb +2 -0
- data/lib/puppeteer/target.rb +5 -0
- data/lib/puppeteer/version.rb +1 -1
- data/lib/puppeteer/wait_task.rb +1 -1
- data/lib/puppeteer.rb +3 -2
- data/puppeteer-ruby.gemspec +1 -1
- metadata +7 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d18f0aae5e331fee42f66c8140bdc3cced9c0514a03ab65460c4036a94bc6eb2
|
4
|
+
data.tar.gz: babeb261ac3661a6738eb3d02dddeef22685590791fbd579483edd2da7800606
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0d70da4b6dc257f39d020286672180d93a4fe2aa321828035eeba80aeff92e7b1f28ff07acd15396d03aad33ab84b4f3474f1c75b587cb833d90f17dde4b024f
|
7
|
+
data.tar.gz: ccb8a58f1ffff28d1498c560c8f17671064ca00ce4b98af9dbc5e844ab1c77d9c28fc503c021ae019d9e799a287976fe01a88bbce3f87979d163f7d384336c0e
|
data/CHANGELOG.md
CHANGED
@@ -1,7 +1,37 @@
|
|
1
|
-
###
|
1
|
+
### main [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.38.0...main)]
|
2
2
|
|
3
3
|
- xxx
|
4
4
|
|
5
|
+
### 0.38.0 [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.37.4...0.38.0)]
|
6
|
+
|
7
|
+
New features:
|
8
|
+
|
9
|
+
- Puppeteer 11.0 functionalities
|
10
|
+
* OOP iframe support
|
11
|
+
* Customize debugging port
|
12
|
+
* HTTPRequest#initiator
|
13
|
+
* Customize temp directory for user data
|
14
|
+
* Add webp to screenshot quality option allow list
|
15
|
+
|
16
|
+
|
17
|
+
### 0.37.4 [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.37.3...0.37.4)]
|
18
|
+
|
19
|
+
Bugfix:
|
20
|
+
|
21
|
+
- Fix ElementHandle#screenshot to work.
|
22
|
+
|
23
|
+
### 0.37.3 [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.37.2...0.37.3)]
|
24
|
+
|
25
|
+
Improvement:
|
26
|
+
|
27
|
+
- Improve the logic of detection of Chrome/Firefox executable path on Linux.
|
28
|
+
|
29
|
+
### 0.37.2 [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.37.1...0.37.2)]
|
30
|
+
|
31
|
+
Bugfix:
|
32
|
+
|
33
|
+
- `timeout: 0` did set the timeout value to zero. Now it disables the timeout as expected.
|
34
|
+
|
5
35
|
### 0.37.1 [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.37.0...0.37.1)]
|
6
36
|
|
7
37
|
Bugfix:
|
data/README.md
CHANGED
@@ -41,7 +41,7 @@ NOTE: `require 'puppeteer-ruby'` is not necessary in Rails.
|
|
41
41
|
```ruby
|
42
42
|
require 'puppeteer-ruby'
|
43
43
|
|
44
|
-
Puppeteer.launch(headless: false, slow_mo: 50, args: ['--
|
44
|
+
Puppeteer.launch(headless: false, slow_mo: 50, args: ['--window-size=1280,800']) do |browser|
|
45
45
|
page = browser.new_page
|
46
46
|
page.viewport = Puppeteer::Viewport.new(width: 1280, height: 800)
|
47
47
|
page.goto("https://github.com/", wait_until: 'domcontentloaded')
|
data/docs/api_coverage.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# API coverages
|
2
|
-
- Puppeteer version:
|
3
|
-
- puppeteer-ruby version: 0.
|
2
|
+
- Puppeteer version: v12.0.0
|
3
|
+
- puppeteer-ruby version: 0.38.0
|
4
4
|
|
5
5
|
## Puppeteer
|
6
6
|
|
@@ -134,6 +134,7 @@
|
|
134
134
|
* viewport
|
135
135
|
* ~~waitFor~~
|
136
136
|
* waitForFileChooser => `#wait_for_file_chooser`
|
137
|
+
* waitForFrame => `#wait_for_frame`
|
137
138
|
* waitForFunction => `#wait_for_function`
|
138
139
|
* waitForNavigation => `#wait_for_navigation`
|
139
140
|
* ~~waitForNetworkIdle~~
|
@@ -226,6 +227,7 @@
|
|
226
227
|
* goto
|
227
228
|
* hover
|
228
229
|
* isDetached => `#detached?`
|
230
|
+
* isOOPFrame => `#oop_frame?`
|
229
231
|
* name
|
230
232
|
* parentFrame => `#parent_frame`
|
231
233
|
* select
|
@@ -295,43 +297,44 @@
|
|
295
297
|
* type => `#type_text`
|
296
298
|
* uploadFile => `#upload_file`
|
297
299
|
|
298
|
-
##
|
299
|
-
|
300
|
-
*
|
301
|
-
*
|
302
|
-
*
|
303
|
-
*
|
304
|
-
*
|
305
|
-
*
|
306
|
-
*
|
307
|
-
*
|
308
|
-
*
|
309
|
-
*
|
310
|
-
*
|
311
|
-
*
|
312
|
-
*
|
313
|
-
*
|
314
|
-
*
|
315
|
-
*
|
316
|
-
*
|
317
|
-
*
|
300
|
+
## HTTPRequest
|
301
|
+
|
302
|
+
* abort
|
303
|
+
* abortErrorReason => `#abort_error_reason`
|
304
|
+
* continue
|
305
|
+
* continueRequestOverrides => `#continue_request_overrides`
|
306
|
+
* enqueueInterceptAction => `#enqueue_intercept_action`
|
307
|
+
* failure
|
308
|
+
* finalizeInterceptions => `#finalize_interceptions`
|
309
|
+
* frame
|
310
|
+
* headers
|
311
|
+
* initiator
|
312
|
+
* isNavigationRequest => `#navigation_request?`
|
313
|
+
* method
|
314
|
+
* postData => `#post_data`
|
315
|
+
* redirectChain => `#redirect_chain`
|
316
|
+
* resourceType => `#resource_type`
|
317
|
+
* respond
|
318
|
+
* response
|
319
|
+
* responseForRequest => `#response_for_request`
|
320
|
+
* url
|
318
321
|
|
319
|
-
##
|
322
|
+
## HTTPResponse
|
320
323
|
|
321
|
-
*
|
322
|
-
*
|
324
|
+
* buffer
|
325
|
+
* frame
|
323
326
|
* ~~fromCache~~
|
324
327
|
* ~~fromServiceWorker~~
|
325
|
-
*
|
326
|
-
*
|
328
|
+
* headers
|
329
|
+
* json
|
327
330
|
* ~~ok~~
|
328
|
-
*
|
329
|
-
*
|
330
|
-
*
|
331
|
-
*
|
332
|
-
*
|
333
|
-
*
|
334
|
-
*
|
331
|
+
* remoteAddress => `#remote_address`
|
332
|
+
* request
|
333
|
+
* securityDetails => `#security_details`
|
334
|
+
* status
|
335
|
+
* statusText => `#status_text`
|
336
|
+
* text
|
337
|
+
* url
|
335
338
|
|
336
339
|
## ~~SecurityDetails~~
|
337
340
|
|
@@ -357,6 +360,7 @@
|
|
357
360
|
|
358
361
|
* connection
|
359
362
|
* detach
|
363
|
+
* id
|
360
364
|
* send
|
361
365
|
|
362
366
|
## Coverage
|
data/lib/puppeteer/dom_world.rb
CHANGED
@@ -45,10 +45,14 @@ class Puppeteer::DOMWorld
|
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
|
-
# @param
|
49
|
-
# @param
|
50
|
-
# @param
|
51
|
-
|
48
|
+
# @param client [Puppeteer::CDPSession]
|
49
|
+
# @param frame_manager [Puppeteer::FrameManager]
|
50
|
+
# @param frame [Puppeteer::Frame]
|
51
|
+
# @param timeout_settings [Puppeteer::TimeoutSettings]
|
52
|
+
def initialize(client, frame_manager, frame, timeout_settings)
|
53
|
+
# Keep own reference to client because it might differ from the FrameManager's
|
54
|
+
# client for OOP iframes.
|
55
|
+
@client = client
|
52
56
|
@frame_manager = frame_manager
|
53
57
|
@frame = frame
|
54
58
|
@timeout_settings = timeout_settings
|
@@ -58,7 +62,7 @@ class Puppeteer::DOMWorld
|
|
58
62
|
@ctx_bindings = Set.new
|
59
63
|
@detached = false
|
60
64
|
|
61
|
-
|
65
|
+
@client.on_event('Runtime.bindingCalled', &method(:handle_binding_called))
|
62
66
|
end
|
63
67
|
|
64
68
|
attr_reader :frame
|
@@ -323,7 +323,7 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
|
|
323
323
|
end
|
324
324
|
end
|
325
325
|
|
326
|
-
def screenshot(
|
326
|
+
def screenshot(type: nil, path: nil, full_page: nil, clip: nil, quality: nil, omit_background: nil, encoding: nil)
|
327
327
|
needs_viewport_reset = false
|
328
328
|
|
329
329
|
box = bounding_box
|
@@ -358,14 +358,16 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
|
|
358
358
|
page_x = layout_metrics["layoutViewport"]["pageX"]
|
359
359
|
page_y = layout_metrics["layoutViewport"]["pageY"]
|
360
360
|
|
361
|
-
clip
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
361
|
+
if clip.nil?
|
362
|
+
clip = {
|
363
|
+
x: page_x + box.x,
|
364
|
+
y: page_y + box.y,
|
365
|
+
width: box.width,
|
366
|
+
height: box.height,
|
367
|
+
}
|
368
|
+
end
|
367
369
|
|
368
|
-
@page.screenshot(
|
370
|
+
@page.screenshot(type: type, path: path, full_page: full_page, clip: clip, quality: quality, omit_background: omit_background, encoding: encoding)
|
369
371
|
ensure
|
370
372
|
if needs_viewport_reset
|
371
373
|
@page.viewport = viewport
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class Puppeteer::ExecutablePathFinder
|
2
|
+
# @param executable_names [Array<String>] executable file names to find.
|
3
|
+
def initialize(*executable_names)
|
4
|
+
@executable_names = executable_names
|
5
|
+
end
|
6
|
+
|
7
|
+
def find_executables_in_path
|
8
|
+
Enumerator.new do |result|
|
9
|
+
@executable_names.each do |name|
|
10
|
+
# Find the first existing path.
|
11
|
+
paths.each do |path|
|
12
|
+
candidate = File.join(path, name)
|
13
|
+
next unless File.exist?(candidate)
|
14
|
+
result << candidate
|
15
|
+
break
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def find_first
|
22
|
+
find_executables_in_path.first
|
23
|
+
end
|
24
|
+
|
25
|
+
private def paths
|
26
|
+
ENV['PATH'].split(File::PATH_SEPARATOR)
|
27
|
+
end
|
28
|
+
end
|
data/lib/puppeteer/frame.rb
CHANGED
@@ -1,25 +1,47 @@
|
|
1
1
|
class Puppeteer::Frame
|
2
2
|
using Puppeteer::DefineAsyncMethod
|
3
3
|
|
4
|
-
# @param
|
5
|
-
# @param
|
6
|
-
# @param
|
7
|
-
# @param
|
8
|
-
def initialize(frame_manager,
|
4
|
+
# @param frame_manager [Puppeteer::FrameManager]
|
5
|
+
# @param parent_frame [Puppeteer::Frame|nil]
|
6
|
+
# @param frame_id [String]
|
7
|
+
# @param client [Puppeteer::CDPSession]
|
8
|
+
def initialize(frame_manager, parent_frame, frame_id, client)
|
9
9
|
@frame_manager = frame_manager
|
10
|
-
@client = client
|
11
10
|
@parent_frame = parent_frame
|
12
11
|
@id = frame_id
|
13
12
|
@detached = false
|
14
13
|
|
15
14
|
@loader_id = ''
|
16
15
|
@lifecycle_events = Set.new
|
17
|
-
@main_world = Puppeteer::DOMWorld.new(frame_manager, self, frame_manager.timeout_settings)
|
18
|
-
@secondary_world = Puppeteer::DOMWorld.new(frame_manager, self, frame_manager.timeout_settings)
|
19
16
|
@child_frames = Set.new
|
20
17
|
if parent_frame
|
21
18
|
parent_frame._child_frames << self
|
22
19
|
end
|
20
|
+
|
21
|
+
update_client(client)
|
22
|
+
end
|
23
|
+
|
24
|
+
def inspect
|
25
|
+
values = %i[id parent_frame detached loader_id lifecycle_events child_frames].map do |sym|
|
26
|
+
value = instance_variable_get(:"@#{sym}")
|
27
|
+
"@#{sym}=#{value}"
|
28
|
+
end
|
29
|
+
"#<Puppeteer::Frame #{values.join(' ')}>"
|
30
|
+
end
|
31
|
+
|
32
|
+
def _client
|
33
|
+
@client
|
34
|
+
end
|
35
|
+
|
36
|
+
# @param client [Puppeteer::CDPSession]
|
37
|
+
private def update_client(client)
|
38
|
+
@client = client
|
39
|
+
@main_world = Puppeteer::DOMWorld.new(@client, @frame_manager, self, @frame_manager.timeout_settings)
|
40
|
+
@secondary_world = Puppeteer::DOMWorld.new(@client, @frame_manager, self, @frame_manager.timeout_settings)
|
41
|
+
end
|
42
|
+
|
43
|
+
def oop_frame?
|
44
|
+
@client != @frame_manager.client
|
23
45
|
end
|
24
46
|
|
25
47
|
attr_accessor :frame_manager, :id, :loader_id, :lifecycle_events, :main_world, :secondary_world
|
@@ -28,7 +50,7 @@ class Puppeteer::Frame
|
|
28
50
|
# @param rederer [String]
|
29
51
|
# @param timeout [number|nil]
|
30
52
|
# @param wait_until [string|nil] 'load' | 'domcontentloaded' | 'networkidle0' | 'networkidle2'
|
31
|
-
# @return [Puppeteer::
|
53
|
+
# @return [Puppeteer::HTTPResponse]
|
32
54
|
def goto(url, referer: nil, timeout: nil, wait_until: nil)
|
33
55
|
@frame_manager.navigate_frame(self, url, referer: referer, timeout: timeout, wait_until: wait_until)
|
34
56
|
end
|
@@ -27,50 +27,67 @@ class Puppeteer::FrameManager
|
|
27
27
|
# @type {!Set<string>}
|
28
28
|
@isolated_worlds = Set.new
|
29
29
|
|
30
|
-
@client
|
31
|
-
|
30
|
+
setup_listeners(@client)
|
31
|
+
end
|
32
|
+
|
33
|
+
private def setup_listeners(client)
|
34
|
+
client.on_event('Page.frameAttached') do |event|
|
35
|
+
handle_frame_attached(client, event['frameId'], event['parentFrameId'])
|
32
36
|
end
|
33
|
-
|
37
|
+
client.on_event('Page.frameNavigated') do |event|
|
34
38
|
handle_frame_navigated(event['frame'])
|
35
39
|
end
|
36
|
-
|
40
|
+
client.on_event('Page.navigatedWithinDocument') do |event|
|
37
41
|
handle_frame_navigated_within_document(event['frameId'], event['url'])
|
38
42
|
end
|
39
|
-
|
40
|
-
handle_frame_detached(event['frameId'])
|
43
|
+
client.on_event('Page.frameDetached') do |event|
|
44
|
+
handle_frame_detached(event['frameId'], event['reason'])
|
41
45
|
end
|
42
|
-
|
46
|
+
client.on_event('Page.frameStoppedLoading') do |event|
|
43
47
|
handle_frame_stopped_loading(event['frameId'])
|
44
48
|
end
|
45
|
-
|
46
|
-
handle_execution_context_created(event['context'])
|
49
|
+
client.on_event('Runtime.executionContextCreated') do |event|
|
50
|
+
handle_execution_context_created(event['context'], client)
|
47
51
|
end
|
48
|
-
|
49
|
-
handle_execution_context_destroyed(event['executionContextId'])
|
52
|
+
client.on_event('Runtime.executionContextDestroyed') do |event|
|
53
|
+
handle_execution_context_destroyed(event['executionContextId'], client)
|
50
54
|
end
|
51
|
-
|
52
|
-
handle_execution_contexts_cleared
|
55
|
+
client.on_event('Runtime.executionContextsCleared') do |event|
|
56
|
+
handle_execution_contexts_cleared(client)
|
53
57
|
end
|
54
|
-
|
58
|
+
client.on_event('Page.lifecycleEvent') do |event|
|
55
59
|
handle_lifecycle_event(event)
|
56
60
|
end
|
61
|
+
client.on_event('Target.attachedToTarget') do |event|
|
62
|
+
handle_attached_to_target(event)
|
63
|
+
end
|
64
|
+
client.on_event('Target.detachedFromTarget') do |event|
|
65
|
+
handle_detached_from_target(event)
|
66
|
+
end
|
57
67
|
end
|
58
68
|
|
59
69
|
attr_reader :client, :timeout_settings
|
60
70
|
|
61
|
-
private def init
|
71
|
+
private def init(cdp_session = nil)
|
72
|
+
client = cdp_session || @client
|
73
|
+
|
62
74
|
results = await_all(
|
63
|
-
|
64
|
-
|
75
|
+
client.async_send_message('Page.enable'),
|
76
|
+
client.async_send_message('Page.getFrameTree'),
|
65
77
|
)
|
66
78
|
frame_tree = results.last['frameTree']
|
67
|
-
handle_frame_tree(frame_tree)
|
79
|
+
handle_frame_tree(client, frame_tree)
|
68
80
|
await_all(
|
69
|
-
|
70
|
-
|
81
|
+
client.async_send_message('Page.setLifecycleEventsEnabled', enabled: true),
|
82
|
+
client.async_send_message('Runtime.enable'),
|
71
83
|
)
|
72
|
-
ensure_isolated_world(UTILITY_WORLD_NAME)
|
73
|
-
@network_manager.init
|
84
|
+
ensure_isolated_world(client, UTILITY_WORLD_NAME)
|
85
|
+
@network_manager.init unless cdp_session
|
86
|
+
rescue => err
|
87
|
+
# The target might have been closed before the initialization finished.
|
88
|
+
return if err.message.include?('Target closed') || err.message.include?('Session closed')
|
89
|
+
|
90
|
+
raise
|
74
91
|
end
|
75
92
|
|
76
93
|
define_async_method :async_init
|
@@ -82,7 +99,7 @@ class Puppeteer::FrameManager
|
|
82
99
|
# @param frame [Puppeteer::Frame]
|
83
100
|
# @param url [String]
|
84
101
|
# @param {!{referer?: string, timeout?: number, waitUntil?: string|!Array<string>}=} options
|
85
|
-
# @return [Puppeteer::
|
102
|
+
# @return [Puppeteer::HTTPResponse]
|
86
103
|
def navigate_frame(frame, url, referer: nil, timeout: nil, wait_until: nil)
|
87
104
|
assert_no_legacy_navigation_options(wait_until: wait_until)
|
88
105
|
|
@@ -132,7 +149,7 @@ class Puppeteer::FrameManager
|
|
132
149
|
|
133
150
|
# @param timeout [number|nil]
|
134
151
|
# @param wait_until [string|nil] 'load' | 'domcontentloaded' | 'networkidle0' | 'networkidle2'
|
135
|
-
# @return [Puppeteer::
|
152
|
+
# @return [Puppeteer::HTTPResponse]
|
136
153
|
def wait_for_frame_navigation(frame, timeout: nil, wait_until: nil)
|
137
154
|
assert_no_legacy_navigation_options(wait_until: wait_until)
|
138
155
|
|
@@ -154,6 +171,28 @@ class Puppeteer::FrameManager
|
|
154
171
|
watcher.navigation_response
|
155
172
|
end
|
156
173
|
|
174
|
+
# @param event [Hash]
|
175
|
+
def handle_attached_to_target(event)
|
176
|
+
return if event['targetInfo']['type'] != 'iframe'
|
177
|
+
|
178
|
+
frame = @frames[event['targetInfo']['targetId']]
|
179
|
+
session = Puppeteer::Connection.from_session(@client).session(event['sessionId'])
|
180
|
+
|
181
|
+
frame.send(:update_client, session)
|
182
|
+
setup_listeners(session)
|
183
|
+
async_init(session)
|
184
|
+
end
|
185
|
+
|
186
|
+
# @param event [Hash]
|
187
|
+
def handle_detached_from_target(event)
|
188
|
+
frame = @frames[event['targetId']]
|
189
|
+
if frame && frame.oop_frame?
|
190
|
+
# When an OOP iframe is removed from the page, it
|
191
|
+
# will only get a Target.detachedFromTarget event.
|
192
|
+
remove_frame_recursively(frame)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
157
196
|
# @param event [Hash]
|
158
197
|
def handle_lifecycle_event(event)
|
159
198
|
frame = @frames[event['frameId']]
|
@@ -170,16 +209,17 @@ class Puppeteer::FrameManager
|
|
170
209
|
emit_event(FrameManagerEmittedEvents::LifecycleEvent, frame)
|
171
210
|
end
|
172
211
|
|
212
|
+
# @param session [Puppeteer::CDPSession]
|
173
213
|
# @param frame_tree [Hash]
|
174
|
-
def handle_frame_tree(frame_tree)
|
214
|
+
def handle_frame_tree(session, frame_tree)
|
175
215
|
if frame_tree['frame']['parentId']
|
176
|
-
handle_frame_attached(frame_tree['frame']['id'], frame_tree['frame']['parentId'])
|
216
|
+
handle_frame_attached(session, frame_tree['frame']['id'], frame_tree['frame']['parentId'])
|
177
217
|
end
|
178
218
|
handle_frame_navigated(frame_tree['frame'])
|
179
219
|
return if !frame_tree['childFrames']
|
180
220
|
|
181
221
|
frame_tree['childFrames'].each do |child|
|
182
|
-
handle_frame_tree(child)
|
222
|
+
handle_frame_tree(session, child)
|
183
223
|
end
|
184
224
|
end
|
185
225
|
|
@@ -204,15 +244,25 @@ class Puppeteer::FrameManager
|
|
204
244
|
@frames[frame_id]
|
205
245
|
end
|
206
246
|
|
207
|
-
# @param
|
208
|
-
# @param
|
209
|
-
|
210
|
-
|
247
|
+
# @param session [Puppeteer::CDPSession]
|
248
|
+
# @param frameId [String]
|
249
|
+
# @param parentFrameId [String|nil]
|
250
|
+
def handle_frame_attached(session, frame_id, parent_frame_id)
|
251
|
+
if @frames.has_key?(frame_id)
|
252
|
+
frame = @frames[frame_id]
|
253
|
+
if session && frame.oop_frame?
|
254
|
+
# If an OOP iframes becomes a normal iframe again
|
255
|
+
# it is first attached to the parent page before
|
256
|
+
# the target is removed.
|
257
|
+
frame.send(:update_client, session)
|
258
|
+
end
|
259
|
+
return
|
260
|
+
end
|
211
261
|
if !parent_frame_id
|
212
262
|
raise ArgymentError.new('parent_frame_id must not be nil')
|
213
263
|
end
|
214
264
|
parent_frame = @frames[parent_frame_id]
|
215
|
-
frame = Puppeteer::Frame.new(self,
|
265
|
+
frame = Puppeteer::Frame.new(self, parent_frame, frame_id, session)
|
216
266
|
@frames[frame_id] = frame
|
217
267
|
|
218
268
|
emit_event(FrameManagerEmittedEvents::FrameAttached, frame)
|
@@ -247,7 +297,7 @@ class Puppeteer::FrameManager
|
|
247
297
|
frame.id = frame_payload['id']
|
248
298
|
else
|
249
299
|
# Initial main frame navigation.
|
250
|
-
frame = Puppeteer::Frame.new(self,
|
300
|
+
frame = Puppeteer::Frame.new(self, nil, frame_payload['id'], @client)
|
251
301
|
end
|
252
302
|
@frames[frame_payload['id']] = frame
|
253
303
|
@main_frame = frame
|
@@ -259,22 +309,26 @@ class Puppeteer::FrameManager
|
|
259
309
|
emit_event(FrameManagerEmittedEvents::FrameNavigated, frame)
|
260
310
|
end
|
261
311
|
|
312
|
+
# @param session [Puppeteer::CDPSession]
|
262
313
|
# @param name [String]
|
263
|
-
def ensure_isolated_world(name)
|
264
|
-
|
265
|
-
@isolated_worlds
|
314
|
+
private def ensure_isolated_world(session, name)
|
315
|
+
key = "#{session.id}:#{name}"
|
316
|
+
return if @isolated_worlds.include?(key)
|
317
|
+
@isolated_worlds << key
|
266
318
|
|
267
|
-
|
319
|
+
session.send_message('Page.addScriptToEvaluateOnNewDocument',
|
268
320
|
source: "//# sourceURL=#{Puppeteer::ExecutionContext::EVALUATION_SCRIPT_URL}",
|
269
321
|
worldName: name,
|
270
322
|
)
|
271
|
-
create_isolated_worlds_promises = frames.
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
323
|
+
create_isolated_worlds_promises = frames.
|
324
|
+
select { |frame| frame._client == session }.
|
325
|
+
map do |frame|
|
326
|
+
session.async_send_message('Page.createIsolatedWorld',
|
327
|
+
frameId: frame.id,
|
328
|
+
grantUniveralAccess: true,
|
329
|
+
worldName: name,
|
330
|
+
)
|
331
|
+
end
|
278
332
|
await_all(*create_isolated_worlds_promises)
|
279
333
|
end
|
280
334
|
|
@@ -289,19 +343,31 @@ class Puppeteer::FrameManager
|
|
289
343
|
end
|
290
344
|
|
291
345
|
# @param frame_id [String]
|
292
|
-
|
346
|
+
# @param reason [String]
|
347
|
+
def handle_frame_detached(frame_id, reason)
|
293
348
|
frame = @frames[frame_id]
|
294
|
-
if
|
295
|
-
|
349
|
+
if reason == 'remove'
|
350
|
+
# Only remove the frame if the reason for the detached event is
|
351
|
+
# an actual removement of the frame.
|
352
|
+
# For frames that become OOP iframes, the reason would be 'swap'.
|
353
|
+
if frame
|
354
|
+
remove_frame_recursively(frame)
|
355
|
+
end
|
296
356
|
end
|
297
357
|
end
|
298
358
|
|
299
359
|
# @param context_payload [Hash]
|
300
|
-
|
360
|
+
# @pram session [Puppeteer::CDPSession]
|
361
|
+
def handle_execution_context_created(context_payload, session)
|
301
362
|
frame = if_present(context_payload.dig('auxData', 'frameId')) { |frame_id| @frames[frame_id] }
|
302
363
|
|
303
364
|
world = nil
|
304
365
|
if frame
|
366
|
+
# commented out the original implementation for allowing us to use Frame#evaluate on OOP iframe.
|
367
|
+
#
|
368
|
+
# # Only care about execution contexts created for the current session.
|
369
|
+
# return if @client != session
|
370
|
+
|
305
371
|
if context_payload.dig('auxData', 'isDefault')
|
306
372
|
world = frame.main_world
|
307
373
|
elsif context_payload['name'] == UTILITY_WORLD_NAME && !frame.secondary_world.has_context?
|
@@ -316,34 +382,45 @@ class Puppeteer::FrameManager
|
|
316
382
|
@isolated_worlds << context_payload['name']
|
317
383
|
end
|
318
384
|
|
319
|
-
context = Puppeteer::ExecutionContext.new(@client, context_payload, world)
|
385
|
+
context = Puppeteer::ExecutionContext.new(frame._client || @client, context_payload, world)
|
320
386
|
if world
|
321
387
|
world.context = context
|
322
388
|
end
|
323
|
-
|
389
|
+
key = "#{session.id}:#{context_payload['id']}"
|
390
|
+
@context_id_to_context[key] = context
|
324
391
|
end
|
325
392
|
|
326
|
-
# @param
|
327
|
-
|
328
|
-
|
393
|
+
# @param execution_context_id [Integer]
|
394
|
+
# @param session [Puppeteer::CDPSEssion]
|
395
|
+
def handle_execution_context_destroyed(execution_context_id, session)
|
396
|
+
key = "#{session.id}:#{execution_context_id}"
|
397
|
+
context = @context_id_to_context[key]
|
329
398
|
return unless context
|
330
|
-
@context_id_to_context.delete(
|
399
|
+
@context_id_to_context.delete(key)
|
331
400
|
if context.world
|
332
401
|
context.world.delete_context(execution_context_id)
|
333
402
|
end
|
334
403
|
end
|
335
404
|
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
405
|
+
# @param session [Puppeteer::CDPSession]
|
406
|
+
def handle_execution_contexts_cleared(session)
|
407
|
+
@context_id_to_context.select! do |execution_context_id, context|
|
408
|
+
# Make sure to only clear execution contexts that belong
|
409
|
+
# to the current session.
|
410
|
+
if context.client != session
|
411
|
+
true # keep
|
412
|
+
else
|
413
|
+
if context.world
|
414
|
+
context.world.delete_context(execution_context_id)
|
415
|
+
end
|
416
|
+
false # remove
|
340
417
|
end
|
341
418
|
end
|
342
|
-
@context_id_to_context.clear
|
343
419
|
end
|
344
420
|
|
345
|
-
def execution_context_by_id(context_id)
|
346
|
-
|
421
|
+
def execution_context_by_id(context_id, session)
|
422
|
+
key = "#{session.id}:#{context_id}"
|
423
|
+
@context_id_to_context[key] or raise "INTERNAL ERROR: missing context with id = #{context_id}"
|
347
424
|
end
|
348
425
|
|
349
426
|
# @param {!Frame} frame
|