puppeteer-ruby 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +19 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +36 -0
  5. data/.travis.yml +7 -0
  6. data/Dockerfile +6 -0
  7. data/Gemfile +6 -0
  8. data/README.md +41 -0
  9. data/Rakefile +1 -0
  10. data/bin/console +11 -0
  11. data/bin/setup +8 -0
  12. data/docker-compose.yml +15 -0
  13. data/example.rb +7 -0
  14. data/lib/puppeteer.rb +192 -0
  15. data/lib/puppeteer/async_await_behavior.rb +34 -0
  16. data/lib/puppeteer/browser.rb +240 -0
  17. data/lib/puppeteer/browser_context.rb +90 -0
  18. data/lib/puppeteer/browser_fetcher.rb +6 -0
  19. data/lib/puppeteer/browser_runner.rb +142 -0
  20. data/lib/puppeteer/cdp_session.rb +78 -0
  21. data/lib/puppeteer/concurrent_ruby_utils.rb +37 -0
  22. data/lib/puppeteer/connection.rb +254 -0
  23. data/lib/puppeteer/console_message.rb +24 -0
  24. data/lib/puppeteer/debug_print.rb +20 -0
  25. data/lib/puppeteer/device.rb +12 -0
  26. data/lib/puppeteer/devices.rb +885 -0
  27. data/lib/puppeteer/dom_world.rb +447 -0
  28. data/lib/puppeteer/element_handle.rb +433 -0
  29. data/lib/puppeteer/emulation_manager.rb +46 -0
  30. data/lib/puppeteer/errors.rb +4 -0
  31. data/lib/puppeteer/event_callbackable.rb +88 -0
  32. data/lib/puppeteer/execution_context.rb +230 -0
  33. data/lib/puppeteer/frame.rb +278 -0
  34. data/lib/puppeteer/frame_manager.rb +380 -0
  35. data/lib/puppeteer/if_present.rb +18 -0
  36. data/lib/puppeteer/js_handle.rb +142 -0
  37. data/lib/puppeteer/keyboard.rb +183 -0
  38. data/lib/puppeteer/keyboard/key_description.rb +19 -0
  39. data/lib/puppeteer/keyboard/us_keyboard_layout.rb +283 -0
  40. data/lib/puppeteer/launcher.rb +26 -0
  41. data/lib/puppeteer/launcher/base.rb +48 -0
  42. data/lib/puppeteer/launcher/browser_options.rb +41 -0
  43. data/lib/puppeteer/launcher/chrome.rb +165 -0
  44. data/lib/puppeteer/launcher/chrome_arg_options.rb +49 -0
  45. data/lib/puppeteer/launcher/launch_options.rb +68 -0
  46. data/lib/puppeteer/lifecycle_watcher.rb +168 -0
  47. data/lib/puppeteer/mouse.rb +120 -0
  48. data/lib/puppeteer/network_manager.rb +122 -0
  49. data/lib/puppeteer/page.rb +1001 -0
  50. data/lib/puppeteer/page/screenshot_options.rb +78 -0
  51. data/lib/puppeteer/remote_object.rb +124 -0
  52. data/lib/puppeteer/target.rb +150 -0
  53. data/lib/puppeteer/timeout_settings.rb +15 -0
  54. data/lib/puppeteer/touch_screen.rb +43 -0
  55. data/lib/puppeteer/version.rb +3 -0
  56. data/lib/puppeteer/viewport.rb +36 -0
  57. data/lib/puppeteer/wait_task.rb +6 -0
  58. data/lib/puppeteer/web_socket.rb +117 -0
  59. data/lib/puppeteer/web_socket_transport.rb +49 -0
  60. data/puppeteer-ruby.gemspec +29 -0
  61. metadata +213 -0
@@ -0,0 +1,46 @@
1
+ class Puppeteer::EmulationManager
2
+ using Puppeteer::AsyncAwaitBehavior
3
+
4
+ # @param {!Puppeteer.CDPSession} client
5
+ def initialize(client)
6
+ @client = client
7
+ @emulating_mobile = false
8
+ @has_touch = false
9
+ end
10
+
11
+ # @param viewport [Puppeteer::Viewport]
12
+ # @return [true|false]
13
+ def emulate_viewport(viewport)
14
+ mobile = viewport.mobile?
15
+ width = viewport.width
16
+ height = viewport.height
17
+ device_scale_factor = viewport.device_scale_factor
18
+ # /** @type {Protocol.Emulation.ScreenOrientation} */
19
+ # const screenOrientation = viewport.isLandscape ? { angle: 90, type: 'landscapePrimary' } : { angle: 0, type: 'portraitPrimary' };
20
+ has_touch = viewport.has_touch?
21
+
22
+ await_all(
23
+ @client.async_send_message('Emulation.setDeviceMetricsOverride',
24
+ mobile: mobile,
25
+ width: width,
26
+ height: height,
27
+ deviceScaleFactor: device_scale_factor,
28
+ # screenOrientation: screen_orientation,
29
+ ),
30
+ @client.async_send_message('Emulation.setTouchEmulationEnabled',
31
+ enabled: has_touch,
32
+ ),
33
+ )
34
+
35
+ reload_needed = @emulating_mobile != mobile || @hasTouch != has_touch;
36
+ @emulating_mobile = mobile
37
+ @has_touch = has_touch
38
+ return reload_needed
39
+ end
40
+
41
+ # @param viewport [Puppeteer::Viewport]
42
+ # @return [Future<true|false>]
43
+ async def async_emulate_viewport(viewport)
44
+ emulate_viewport(viewport)
45
+ end
46
+ end
@@ -0,0 +1,4 @@
1
+ # ref: https://github.com/puppeteer/puppeteer/blob/master/lib/Errors.js
2
+ class Puppeteer::TimeoutError < StandardError ; end
3
+
4
+ class Puppeteer::WebSocktTransportError < StandardError ; end
@@ -0,0 +1,88 @@
1
+ require 'securerandom'
2
+
3
+ module Puppeteer::EventCallbackable
4
+ class EventListeners
5
+ include Enumerable
6
+
7
+ def initialize
8
+ @listeners = {}
9
+ end
10
+
11
+ # @return [String] Listener ID
12
+ def add(&block)
13
+ id = SecureRandom.hex(8)
14
+ @listeners[id] = block
15
+ id
16
+ end
17
+
18
+ # @param id [String] Listener ID returned on #add
19
+ def delete(id)
20
+ @listeners.delete(id)
21
+ end
22
+
23
+ # @implement Enumerable#each
24
+ def each(&block)
25
+ @listeners.values.each(&block)
26
+ end
27
+ end
28
+
29
+ def add_event_listener(event_name, &block)
30
+ @event_listeners ||= {}
31
+ (@event_listeners[event_name] ||= EventListeners.new).add(&block)
32
+ end
33
+
34
+ def remove_event_listener(*id_args)
35
+ (@event_listeners ||= {}).each do |event_name, listeners|
36
+ id_args.each do |id|
37
+ listeners.delete(id)
38
+ end
39
+ end
40
+ end
41
+
42
+ def on_event(event_name, &block)
43
+ @event_callbackable_handlers ||= {}
44
+ @event_callbackable_handlers[event_name] = block
45
+ end
46
+
47
+ def emit_event(event_name, *args, **kwargs)
48
+ @event_callbackable_handlers ||= {}
49
+ @event_listeners ||= {}
50
+
51
+ if kwargs.empty?
52
+ # In Ruby's specification (version < 2.7),
53
+ # `method(:x).call(*args, **kwargs)` is equivalent to `x(*args, {})`
54
+ # It often causes unexpected ArgumentError.
55
+ #
56
+ # ----------------
57
+ # def greet
58
+ # puts 'Hello!'
59
+ # end
60
+ #
61
+ # def call_me(*args, **kwargs)
62
+ # greet(*args, **kwargs) # => 'Hello!'
63
+ #
64
+ # method(:greet).call(*args, **kwargs) # => `greet': wrong number of arguments (given 1, expected 0) (ArgumentError)
65
+ # end
66
+ #
67
+ # call_me
68
+ # ----------------
69
+ #
70
+ # This behavior is really annoying, and should be avoided, because we often want to set event handler as below:
71
+ #
72
+ # `on_event 'Some.Event.awesome', &method(:handle_awesome_event)`
73
+ #
74
+ # So Let's avoid it by checking kwargs.
75
+ @event_callbackable_handlers[event_name]&.call(*args)
76
+ @event_listeners[event_name]&.each do |proc|
77
+ proc.call(*args)
78
+ end
79
+ else
80
+ @event_callbackable_handlers[event_name]&.call(*args, **kwargs)
81
+ @event_listeners[event_name]&.each do |proc|
82
+ proc.call(*args, **kwargs)
83
+ end
84
+ end
85
+
86
+ event_name
87
+ end
88
+ end
@@ -0,0 +1,230 @@
1
+ class Puppeteer::ExecutionContext
2
+ include Puppeteer::IfPresent
3
+ using Puppeteer::AsyncAwaitBehavior
4
+
5
+ EVALUATION_SCRIPT_URL = '__puppeteer_evaluation_script__'
6
+ SOURCE_URL_REGEX = /^[\040\t]*\/\/[@#] sourceURL=\s*(\S*?)\s*$/m
7
+
8
+ # @param client [Puppeteer::CDPSession]
9
+ # @param context_payload [Hash]
10
+ # @param world [Puppeteer::DOMWorld?]
11
+ def initialize(client, context_payload, world)
12
+ @client = client
13
+ @world = world
14
+ @context_id = context_payload['id']
15
+ end
16
+
17
+ attr_reader :client, :world
18
+
19
+ # @return [Puppeteer::Frame]
20
+ def frame
21
+ if_present(@world) do |world|
22
+ world.frame
23
+ end
24
+ end
25
+
26
+ # @param page_function [String]
27
+ # @return [Object]
28
+ def evaluate(page_function, *args)
29
+ evaluate_internal(true, page_function, *args)
30
+ end
31
+
32
+ # @param page_function [String]
33
+ # @return [Puppeteer::JSHandle]
34
+ def evaluate_handle(page_function, *args)
35
+ evaluate_internal(false, page_function, *args)
36
+ end
37
+
38
+ class JavaScriptExpression
39
+ def initialize(execution_context, expression, return_by_value)
40
+ @execution_context = execution_context
41
+ @expression = expression
42
+ @return_by_value = return_by_value
43
+ end
44
+
45
+ # @param client [Puppeteer::CDPSession]
46
+ # @param context_id [String]
47
+ # @return [Object|JSHandle]
48
+ def evaluate_with(client:, context_id:)
49
+ result = client.send_message('Runtime.evaluate',
50
+ expression: expression_with_source_url,
51
+ contextId: context_id,
52
+ returnByValue: @return_by_value,
53
+ awaitPromise: true,
54
+ userGesture: true,
55
+ )
56
+ # }).catch(rewriteError);
57
+
58
+ exception_details = result["exceptionDetails"]
59
+ if exception_details
60
+ raise EvaluationError.new("Evaluation failed: #{exception_details}")
61
+ end
62
+
63
+ remote_object = Puppeteer::RemoteObject.new(result['result'])
64
+ if @return_by_value
65
+ remote_object.value
66
+ else
67
+ Puppeteer::JSHandle.create(
68
+ context: @execution_context,
69
+ remote_object: remote_object,
70
+ )
71
+ end
72
+ end
73
+
74
+ private def suffix
75
+ "//# sourceURL=#{EVALUATION_SCRIPT_URL}"
76
+ end
77
+
78
+ private def expression_with_source_url
79
+ if SOURCE_URL_REGEX.match?(@expression)
80
+ @expression
81
+ else
82
+ "#{@expression}\n#{suffix}"
83
+ end
84
+ end
85
+ end
86
+
87
+ class JavaScriptFunction
88
+ include Puppeteer::IfPresent
89
+
90
+ def initialize(execution_context, expression, args, return_by_value)
91
+ @execution_context = execution_context
92
+ @expression = expression
93
+ @return_by_value = return_by_value
94
+ @args = args
95
+ end
96
+
97
+ # @param client [Puppeteer::CDPSession]
98
+ # @param context_id [String]
99
+ # @return [Object|JSHandle]
100
+ def evaluate_with(client:, context_id:)
101
+ # `function` can be omitted in JS after ES2015.
102
+ # https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Object_initializer
103
+ #
104
+ # Original puppeteer implementation take it into consideration.
105
+ # But we don't support the syntax here.
106
+
107
+ result = client.send_message('Runtime.callFunctionOn',
108
+ functionDeclaration: "#{@expression}\n#{suffix}\n",
109
+ executionContextId: context_id,
110
+ arguments: converted_args,
111
+ returnByValue: @return_by_value,
112
+ awaitPromise: true,
113
+ userGesture: true,
114
+ ) #.catch(rewriteError);
115
+
116
+ exception_details = result["exceptionDetails"]
117
+ remote_object = Puppeteer::RemoteObject.new(result["result"])
118
+
119
+ if exception_details
120
+ raise EvaluationError.new("Evaluation failed: #{exceptionDetails}")
121
+ end
122
+
123
+ if @return_by_value
124
+ remote_object.value
125
+ else
126
+ Puppeteer::JSHandle.create(
127
+ context: @execution_context,
128
+ remote_object: remote_object,
129
+ )
130
+ end
131
+ end
132
+
133
+ private def converted_args
134
+ # if (typeof arg === 'bigint') // eslint-disable-line valid-typeof
135
+ # return { unserializableValue: `${arg.toString()}n` };
136
+ # if (Object.is(arg, -0))
137
+ # return { unserializableValue: '-0' };
138
+ # if (Object.is(arg, Infinity))
139
+ # return { unserializableValue: 'Infinity' };
140
+ # if (Object.is(arg, -Infinity))
141
+ # return { unserializableValue: '-Infinity' };
142
+ # if (Object.is(arg, NaN))
143
+ # return { unserializableValue: 'NaN' };
144
+ @args.map do |arg|
145
+ if arg && arg.is_a?(Puppeteer::JSHandle)
146
+ if arg.context != @execution_context
147
+ raise EvaluationError.new('JSHandles can be evaluated only in the context they were created!')
148
+ elsif arg.disposed?
149
+ raise EvaluationError.new('JSHandles is disposed!')
150
+ end
151
+
152
+ arg.remote_object.converted_arg
153
+ else
154
+ { value: arg }
155
+ end
156
+ end
157
+ end
158
+
159
+ # /**
160
+ # * @param {!Error} error
161
+ # * @return {!Protocol.Runtime.evaluateReturnValue}
162
+ # */
163
+ # function rewriteError(error) {
164
+ # if (error.message.includes('Object reference chain is too long'))
165
+ # return {result: {type: 'undefined'}};
166
+ # if (error.message.includes('Object couldn\'t be returned by value'))
167
+ # return {result: {type: 'undefined'}};
168
+
169
+ # if (error.message.endsWith('Cannot find context with specified id') || error.message.endsWith('Inspected target navigated or closed'))
170
+ # throw new Error('Execution context was destroyed, most likely because of a navigation.');
171
+ # throw error;
172
+ # }
173
+
174
+ private def suffix
175
+ "//# sourceURL=#{EVALUATION_SCRIPT_URL}"
176
+ end
177
+ end
178
+
179
+ class EvaluationError < StandardError ; end
180
+
181
+ # @param return_by_value [Boolean]
182
+ # @param page_function [String]
183
+ # @return [Object|Puppeteer::JSHandle]
184
+ private def evaluate_internal(return_by_value, page_function, *args)
185
+ # `function` can be omitted in JS after ES2015.
186
+ # https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Object_initializer
187
+ # But we don't support the syntax here.
188
+ js_object =
189
+ if ["=>", "async", "function"].any? { |keyword| page_function.include?(keyword) }
190
+ JavaScriptFunction.new(self, page_function, args, return_by_value)
191
+ else
192
+ JavaScriptExpression.new(self, page_function, return_by_value)
193
+ end
194
+
195
+ js_object.evaluate_with(
196
+ client: @client,
197
+ context_id: @context_id,
198
+ )
199
+ end
200
+
201
+ # /**
202
+ # * @param {!JSHandle} prototypeHandle
203
+ # * @return {!Promise<!JSHandle>}
204
+ # */
205
+ # async queryObjects(prototypeHandle) {
206
+ # assert(!prototypeHandle._disposed, 'Prototype JSHandle is disposed!');
207
+ # assert(prototypeHandle._remoteObject.objectId, 'Prototype JSHandle must not be referencing primitive value');
208
+ # const response = await this._client.send('Runtime.queryObjects', {
209
+ # prototypeObjectId: prototypeHandle._remoteObject.objectId
210
+ # });
211
+ # return createJSHandle(this, response.objects);
212
+ # }
213
+
214
+ # /**
215
+ # * @param {Puppeteer.ElementHandle} elementHandle
216
+ # * @return {Promise<Puppeteer.ElementHandle>}
217
+ # */
218
+ # async _adoptElementHandle(elementHandle) {
219
+ # assert(elementHandle.executionContext() !== this, 'Cannot adopt handle that already belongs to this execution context');
220
+ # assert(this._world, 'Cannot adopt handle without DOMWorld');
221
+ # const nodeInfo = await this._client.send('DOM.describeNode', {
222
+ # objectId: elementHandle._remoteObject.objectId,
223
+ # });
224
+ # const {object} = await this._client.send('DOM.resolveNode', {
225
+ # backendNodeId: nodeInfo.node.backendNodeId,
226
+ # executionContextId: this._contextId,
227
+ # });
228
+ # return /** @type {Puppeteer.ElementHandle}*/(createJSHandle(this, object));
229
+ # }
230
+ end
@@ -0,0 +1,278 @@
1
+ class Puppeteer::Frame
2
+ # @param {!FrameManager} frameManager
3
+ # @param {!Puppeteer.CDPSession} client
4
+ # @param {?Frame} parentFrame
5
+ # @param {string} frameId
6
+ def initialize(frame_manager, client, parent_frame, frame_id)
7
+ @frame_manager = frame_manager
8
+ @client = client
9
+ @parent_frame = parent_frame
10
+ @id = frame_id
11
+ @detached = false
12
+
13
+ @loader_id = ''
14
+ @lifecycle_events = Set.new
15
+ @main_world = Puppeteer::DOMWorld.new(frame_manager, self, frame_manager.timeout_settings)
16
+ @secondary_world = Puppeteer::DOMWorld.new(frame_manager, self, frame_manager.timeout_settings)
17
+ @child_frames = Set.new
18
+ if parent_frame
19
+ parent_frame._child_frames << self
20
+ end
21
+ end
22
+
23
+ attr_accessor :frame_manager, :id, :loader_id, :lifecycle_events, :main_world, :secondary_world
24
+
25
+ # @param url [String]
26
+ # @param rederer [String]
27
+ # @param timeout [number|nil]
28
+ # @param wait_until [string|nil] 'load' | 'domcontentloaded' | 'networkidle0' | 'networkidle2'
29
+ # @return [Puppeteer::Response]
30
+ def goto(url, referer: nil, timeout: nil, wait_until: nil)
31
+ @frame_manager.navigate_frame(self, url, referer: referer, timeout: timeout, wait_until: wait_until)
32
+ end
33
+
34
+ # @param timeout [number|nil]
35
+ # @param wait_until [string|nil] 'load' | 'domcontentloaded' | 'networkidle0' | 'networkidle2'
36
+ def wait_for_navigation(timeout: nil, wait_until: nil)
37
+ @frame_manager.wait_for_frame_navigation(self, timeout: timeout, wait_until: wait_until)
38
+ end
39
+
40
+ def execution_context
41
+ @main_world.execution_context
42
+ end
43
+
44
+ # @param {Function|string} pageFunction
45
+ # @return {!Promise<!Puppeteer.JSHandle>}
46
+ def evaluate_handle(page_function, *args)
47
+ @main_world.evaluate_handle(page_function, *args)
48
+ end
49
+
50
+ # @param {Function|string} pageFunction
51
+ # @param {!Array<*>} args
52
+ def evaluate(page_function, *args)
53
+ @main_world.evaluate(page_function, *args)
54
+ end
55
+
56
+ # `$()` in JavaScript. $ is not allowed to use as a method name in Ruby.
57
+ # @param {string} selector
58
+ # @return {!Promise<?Puppeteer.ElementHandle>}
59
+ def S(selector)
60
+ @main_world.S(selector)
61
+ end
62
+
63
+
64
+ # `$x()` in JavaScript. $ is not allowed to use as a method name in Ruby.
65
+ # @param {string} expression
66
+ # @return {!Promise<!Array<!Puppeteer.ElementHandle>>}
67
+ def Sx(expression)
68
+ @main_world.Sx(expression)
69
+ end
70
+
71
+
72
+ # `$eval()` in JavaScript. $ is not allowed to use as a method name in Ruby.
73
+ # @param {string} selector
74
+ # @param {Function|string} pageFunction
75
+ # @param {!Array<*>} args
76
+ # @return {!Promise<(!Object|undefined)>}
77
+ def Seval(selector, page_function, *args)
78
+ @main_world.Seval(selector, page_function, *args)
79
+ end
80
+
81
+ # `$$eval()` in JavaScript. $ is not allowed to use as a method name in Ruby.
82
+ # @param {string} selector
83
+ # @param {Function|string} pageFunction
84
+ # @param {!Array<*>} args
85
+ # @return {!Promise<(!Object|undefined)>}
86
+ def SSeval(selector, page_function, *args)
87
+ @main_world.SSeval(selector, page_function, *args)
88
+ end
89
+
90
+ # `$$()` in JavaScript. $ is not allowed to use as a method name in Ruby.
91
+ # @param {string} selector
92
+ # @return {!Promise<!Array<!Puppeteer.ElementHandle>>}
93
+ def SS(selector)
94
+ @main_world.SS(selector)
95
+ end
96
+
97
+ def content
98
+ @secondary_world.content
99
+ end
100
+
101
+ # @param {string} html
102
+ # @param {!{timeout?: number, waitUntil?: string|!Array<string>}=} options
103
+ def set_content(html, timeout: nil, wait_until: nil)
104
+ @secondary_world.set_content(html, timeout: timeout, wait_until: wait_until)
105
+ end
106
+
107
+ # @return [String]
108
+ def name
109
+ @name || ''
110
+ end
111
+
112
+ # @return [String]
113
+ def url
114
+ @url
115
+ end
116
+
117
+ # @return [Frame?]
118
+ def parent_frame
119
+ @parent_frame
120
+ end
121
+
122
+ protected def _child_frames
123
+ @child_frames
124
+ end
125
+
126
+ def child_frames
127
+ @child_frames.dup
128
+ end
129
+
130
+ def detached?
131
+ @detached
132
+ end
133
+
134
+ # @param style_tag [Puppeteer::Page::ScriptTag]
135
+ # @return {!Promise<!ElementHandle>}
136
+ def add_script_tag(script_tag)
137
+ @main_world.add_script_tag(script_tag)
138
+ end
139
+
140
+ # @param style_tag [Puppeteer::Page::StyleTag]
141
+ # @return {!Promise<!ElementHandle>}
142
+ def add_style_tag(style_tag)
143
+ @main_world.add_style_tag(style_tag)
144
+ end
145
+
146
+ # @param {string} selector
147
+ # @param {!{delay?: number, button?: "left"|"right"|"middle", clickCount?: number}=} options
148
+ def click(selector, delay: nil, button: nil, click_count: nil)
149
+ @secondary_world.click(selector, delay: delay, button: button, click_count: click_count)
150
+ end
151
+
152
+ # @param {string} selector
153
+ def focus(selector)
154
+ @secondary_world.focus(selector)
155
+ end
156
+
157
+ # @param {string} selector
158
+ def hover(selector)
159
+ @secondary_world.hover(selector)
160
+ end
161
+
162
+ # @param {string} selector
163
+ # @param {!Array<string>} values
164
+ # @return {!Promise<!Array<string>>}
165
+ def select(selector, *values)
166
+ @secondary_world.select(selector, *values)
167
+ end
168
+
169
+ # @param {string} selector
170
+ def tap(selector)
171
+ @secondary_world.tap(selector)
172
+ end
173
+
174
+ # @param {string} selector
175
+ # @param {string} text
176
+ # @param {{delay: (number|undefined)}=} options
177
+ def type(selector, text, delay: nil)
178
+ @main_world.type(selector, text, delay: delay)
179
+ end
180
+
181
+ # /**
182
+ # * @param {(string|number|Function)} selectorOrFunctionOrTimeout
183
+ # * @param {!Object=} options
184
+ # * @param {!Array<*>} args
185
+ # * @return {!Promise<?Puppeteer.JSHandle>}
186
+ # */
187
+ # waitFor(selectorOrFunctionOrTimeout, options = {}, ...args) {
188
+ # const xPathPattern = '//';
189
+
190
+ # if (helper.isString(selectorOrFunctionOrTimeout)) {
191
+ # const string = /** @type {string} */ (selectorOrFunctionOrTimeout);
192
+ # if (string.startsWith(xPathPattern))
193
+ # return this.waitForXPath(string, options);
194
+ # return this.waitForSelector(string, options);
195
+ # }
196
+ # if (helper.isNumber(selectorOrFunctionOrTimeout))
197
+ # return new Promise(fulfill => setTimeout(fulfill, /** @type {number} */ (selectorOrFunctionOrTimeout)));
198
+ # if (typeof selectorOrFunctionOrTimeout === 'function')
199
+ # return this.waitForFunction(selectorOrFunctionOrTimeout, options, ...args);
200
+ # return Promise.reject(new Error('Unsupported target type: ' + (typeof selectorOrFunctionOrTimeout)));
201
+ # }
202
+
203
+ # @param {string} selector
204
+ # @param {!{visible?: boolean, hidden?: boolean, timeout?: number}=} options
205
+ # @return {!Promise<?Puppeteer.ElementHandle>}
206
+ def wait_for_selector(selector, visible: nil, hidden: nil, timeout: nil)
207
+ handle = @secondary_world.wait_for_selector(selector, visible: visible, hidden: hidden, timeout: timeout)
208
+ if !handle
209
+ return nil
210
+ end
211
+ main_execution_context = @main_world.execution_context
212
+ result = main_execution_context.adopt_element_handle(handle)
213
+ handle.dispose
214
+ return result
215
+ end
216
+
217
+ # @param {string} xpath
218
+ # @param {!{visible?: boolean, hidden?: boolean, timeout?: number}=} options
219
+ # @return {!Promise<?Puppeteer.ElementHandle>}
220
+ def wait_for_xpath(xpath, visible: nil, hidden: nil, timeout: nil)
221
+ handle = @secondary_world.wait_for_xpath(xpath, visible: visible, hidden: hidden, timeout: timeout)
222
+ if !handle
223
+ return nil
224
+ end
225
+ main_execution_context = @main_world.execution_context
226
+ result = main_execution_context.adopt_element_handle(handle)
227
+ handle.dispose
228
+ return result
229
+ end
230
+
231
+ # @param {Function|string} pageFunction
232
+ # @param {!{polling?: string|number, timeout?: number}=} options
233
+ # @param {!Array<*>} args
234
+ # @return {!Promise<!Puppeteer.JSHandle>}
235
+ def wait_for_function(page_function, options = {}, *args)
236
+ @main_world.wait_for_function(page_function, options, *args)
237
+ end
238
+
239
+ def title
240
+ @secondary_world.title
241
+ end
242
+
243
+ # @param frame_payload [Hash]
244
+ def navigated(frame_payload)
245
+ @name = frame_payload['name']
246
+ # TODO(lushnikov): remove this once requestInterception has loaderId exposed.
247
+ @navigation_url = frame_payload['url']
248
+ @url = frame_payload['url']
249
+ end
250
+
251
+ # @param url [String]
252
+ def navigated_within_document(url)
253
+ @url = url
254
+ end
255
+
256
+ def handle_lifecycle_event(loader_id, name)
257
+ if name == 'init'
258
+ @loader_id = loader_id
259
+ @lifecycle_events.clear
260
+ end
261
+ @lifecycle_events << name
262
+ end
263
+
264
+ def handle_loading_stopped
265
+ @lifecycle_events << 'DOMContentLoaded'
266
+ @lifecycle_events << 'load'
267
+ end
268
+
269
+ def detach
270
+ @detached = true
271
+ # this._mainWorld._detach();
272
+ # this._secondaryWorld._detach();
273
+ if @parent_frame
274
+ @parent_frame._child_frames.delete(self)
275
+ end
276
+ @parent_frame = nil
277
+ end
278
+ end