puppeteer-ruby 0.37.2 → 0.39.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +2 -0
- data/CHANGELOG.md +31 -1
- data/README.md +1 -1
- data/docs/api_coverage.md +6 -6
- data/lib/puppeteer/aria_query_handler.rb +3 -3
- data/lib/puppeteer/browser_runner.rb +21 -10
- 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 +30 -8
- data/lib/puppeteer/frame_manager.rb +136 -59
- data/lib/puppeteer/http_request.rb +9 -1
- data/lib/puppeteer/launcher/chrome.rb +66 -30
- data/lib/puppeteer/launcher/chrome_arg_options.rb +2 -1
- data/lib/puppeteer/launcher/firefox.rb +268 -228
- data/lib/puppeteer/launcher/launch_options.rb +2 -1
- data/lib/puppeteer/page/screenshot_options.rb +1 -1
- data/lib/puppeteer/page.rb +75 -19
- data/lib/puppeteer/puppeteer.rb +2 -0
- data/lib/puppeteer/target.rb +5 -0
- data/lib/puppeteer/version.rb +1 -1
- data/lib/puppeteer.rb +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e09022ffac5da45a7c40bca6b612c8a67afece662142675625794b15b89a044b
|
4
|
+
data.tar.gz: 9af45888cfcfaff3bc368facbd1b5fb28572924ccf0c8d33b05ba310894638bb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 580410458c1e0f2ac6dd8fb678fce42ed5f12ee6a1ff70b90600b6601b945af4342db53f6b98fe73ba249a9d85e3e8d64efb664534f7b1231265dbfbeb2f8e09
|
7
|
+
data.tar.gz: adecd0293ec7b7ade882d9c331b794b4a14e2e52999c164880e7f4b0fa3d723e7bc6b44a238a5ad04c50ad0bfc9c3d5d33e60b50ea40904dc49af1506ce3e72e
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,7 +1,37 @@
|
|
1
|
-
###
|
1
|
+
### main [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.39.0...main)]
|
2
2
|
|
3
3
|
- xxx
|
4
4
|
|
5
|
+
### 0.39.0 [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.38.0...0.39.0)]
|
6
|
+
|
7
|
+
New features:
|
8
|
+
|
9
|
+
- Puppeteer 12.0 functionalities
|
10
|
+
|
11
|
+
### 0.38.0 [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.37.4...0.38.0)]
|
12
|
+
|
13
|
+
New features:
|
14
|
+
|
15
|
+
- Puppeteer 11.0 functionalities
|
16
|
+
* OOP iframe support
|
17
|
+
* Customize debugging port
|
18
|
+
* HTTPRequest#initiator
|
19
|
+
* Customize temp directory for user data
|
20
|
+
* Add webp to screenshot quality option allow list
|
21
|
+
|
22
|
+
|
23
|
+
### 0.37.4 [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.37.3...0.37.4)]
|
24
|
+
|
25
|
+
Bugfix:
|
26
|
+
|
27
|
+
- Fix ElementHandle#screenshot to work.
|
28
|
+
|
29
|
+
### 0.37.3 [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.37.2...0.37.3)]
|
30
|
+
|
31
|
+
Improvement:
|
32
|
+
|
33
|
+
- Improve the logic of detection of Chrome/Firefox executable path on Linux.
|
34
|
+
|
5
35
|
### 0.37.2 [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.37.1...0.37.2)]
|
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.39.0
|
4
4
|
|
5
5
|
## Puppeteer
|
6
6
|
|
@@ -134,7 +134,7 @@
|
|
134
134
|
* viewport
|
135
135
|
* ~~waitFor~~
|
136
136
|
* waitForFileChooser => `#wait_for_file_chooser`
|
137
|
-
*
|
137
|
+
* waitForFrame => `#wait_for_frame`
|
138
138
|
* waitForFunction => `#wait_for_function`
|
139
139
|
* waitForNavigation => `#wait_for_navigation`
|
140
140
|
* ~~waitForNetworkIdle~~
|
@@ -227,7 +227,7 @@
|
|
227
227
|
* goto
|
228
228
|
* hover
|
229
229
|
* isDetached => `#detached?`
|
230
|
-
*
|
230
|
+
* isOOPFrame => `#oop_frame?`
|
231
231
|
* name
|
232
232
|
* parentFrame => `#parent_frame`
|
233
233
|
* select
|
@@ -308,7 +308,7 @@
|
|
308
308
|
* finalizeInterceptions => `#finalize_interceptions`
|
309
309
|
* frame
|
310
310
|
* headers
|
311
|
-
*
|
311
|
+
* initiator
|
312
312
|
* isNavigationRequest => `#navigation_request?`
|
313
313
|
* method
|
314
314
|
* postData => `#post_data`
|
@@ -360,7 +360,7 @@
|
|
360
360
|
|
361
361
|
* connection
|
362
362
|
* detach
|
363
|
-
*
|
363
|
+
* id
|
364
364
|
* send
|
365
365
|
|
366
366
|
## Coverage
|
@@ -7,10 +7,10 @@ class Puppeteer::AriaQueryHandler
|
|
7
7
|
private def parse_aria_selector(selector)
|
8
8
|
known_attributes = %w(name role)
|
9
9
|
query_options = {}
|
10
|
-
attribute_regexp = /\[\s*(?<attribute>\w+)\s*=\s*"(?<value
|
10
|
+
attribute_regexp = /\[\s*(?<attribute>\w+)\s*=\s*(?<quote>"|')(?<value>\\.|.*?(?=\k<quote>))\k<quote>\s*\]/
|
11
11
|
default_name = selector.gsub(attribute_regexp) do
|
12
12
|
attribute = $1.strip
|
13
|
-
value = $
|
13
|
+
value = $3
|
14
14
|
unless known_attributes.include?(attribute)
|
15
15
|
raise ArgumentError.new("Unkown aria attribute \"#{attribute}\" in selector")
|
16
16
|
end
|
@@ -39,7 +39,7 @@ class Puppeteer::AriaQueryHandler
|
|
39
39
|
def wait_for(dom_world, selector, visible: nil, hidden: nil, timeout: nil)
|
40
40
|
binding_function = Puppeteer::DOMWorld::BindingFunction.new(
|
41
41
|
name: 'ariaQuerySelector',
|
42
|
-
proc: -> (
|
42
|
+
proc: -> (sel) { query_one(dom_world.send(:document), sel) },
|
43
43
|
)
|
44
44
|
dom_world.send(:wait_for_selector_in_page,
|
45
45
|
'(_, selector) => globalThis.ariaQuerySelector(selector)',
|
@@ -4,13 +4,17 @@ require 'timeout'
|
|
4
4
|
|
5
5
|
# https://github.com/puppeteer/puppeteer/blob/master/lib/Launcher.js
|
6
6
|
class Puppeteer::BrowserRunner
|
7
|
+
include Puppeteer::DebugPrint
|
8
|
+
|
7
9
|
# @param {string} executablePath
|
8
10
|
# @param {!Array<string>} processArguments
|
9
11
|
# @param {string=} tempDirectory
|
10
|
-
def initialize(executable_path, process_arguments,
|
12
|
+
def initialize(for_firefox, executable_path, process_arguments, user_data_dir, using_temp_user_data_dir)
|
13
|
+
@for_firefox = for_firefox
|
11
14
|
@executable_path = executable_path
|
12
15
|
@process_arguments = process_arguments
|
13
|
-
@
|
16
|
+
@user_data_dir = user_data_dir
|
17
|
+
@using_temp_user_data_dir = using_temp_user_data_dir
|
14
18
|
@proc = nil
|
15
19
|
@connection = nil
|
16
20
|
@closed = true
|
@@ -90,8 +94,8 @@ class Puppeteer::BrowserRunner
|
|
90
94
|
@process_closing = -> {
|
91
95
|
@proc.dispose
|
92
96
|
@closed = true
|
93
|
-
if @
|
94
|
-
FileUtils.rm_rf(@
|
97
|
+
if @using_temp_user_data_dir
|
98
|
+
FileUtils.rm_rf(@user_data_dir)
|
95
99
|
end
|
96
100
|
}
|
97
101
|
at_exit do
|
@@ -122,7 +126,7 @@ class Puppeteer::BrowserRunner
|
|
122
126
|
def close
|
123
127
|
return if @closed
|
124
128
|
|
125
|
-
if @
|
129
|
+
if @using_temp_user_data_dir && !@for_firefox
|
126
130
|
kill
|
127
131
|
elsif @connection
|
128
132
|
begin
|
@@ -137,11 +141,18 @@ class Puppeteer::BrowserRunner
|
|
137
141
|
|
138
142
|
# @return {Promise}
|
139
143
|
def kill
|
140
|
-
if
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
144
|
+
# If the process failed to launch (for example if the browser executable path
|
145
|
+
# is invalid), then the process does not get a pid assigned. A call to
|
146
|
+
# `proc.kill` would error, as the `pid` to-be-killed can not be found.
|
147
|
+
@proc&.kill
|
148
|
+
|
149
|
+
# Attempt to remove temporary profile directory to avoid littering.
|
150
|
+
begin
|
151
|
+
if @using_temp_user_data_dir
|
152
|
+
FileUtils.rm_rf(@temp_directory)
|
153
|
+
end
|
154
|
+
rescue => err
|
155
|
+
debug_puts(err)
|
145
156
|
end
|
146
157
|
end
|
147
158
|
|
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
|
@@ -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
|
@@ -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
|