puppeteer-ruby 0.0.15 → 0.0.20

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.
@@ -149,9 +149,10 @@ class Puppeteer::Keyboard
149
149
  define_async_method :async_type_text
150
150
 
151
151
  # @param key [String]
152
+ # @param text [String]
152
153
  # @return [Future]
153
- def press(key, delay: nil)
154
- down(key)
154
+ def press(key, delay: nil, text: nil)
155
+ down(key, text: text)
155
156
  if delay
156
157
  sleep(delay.to_i / 1000.0)
157
158
  end
@@ -2,6 +2,7 @@ require_relative './launcher/base'
2
2
  require_relative './launcher/browser_options'
3
3
  require_relative './launcher/chrome'
4
4
  require_relative './launcher/chrome_arg_options'
5
+ require_relative './launcher/firefox'
5
6
  require_relative './launcher/launch_options'
6
7
 
7
8
  # https://github.com/puppeteer/puppeteer/blob/main/src/node/Launcher.ts
@@ -9,10 +10,19 @@ module Puppeteer::Launcher
9
10
  # @param project_root [String]
10
11
  # @param prefereed_revision [String]
11
12
  # @param is_puppeteer_core [String]
12
- # @param product [String] 'chrome' or 'firefox' (not implemented yet)
13
+ # @param product [String] 'chrome' or 'firefox'
13
14
  # @return [Puppeteer::Launcher::Chrome]
14
15
  module_function def new(project_root:, preferred_revision:, is_puppeteer_core:, product:)
16
+ unless is_puppeteer_core
17
+ product ||= ENV['PUPPETEER_PRODUCT']
18
+ end
19
+
15
20
  if product == 'firefox'
21
+ return Firefox.new(
22
+ project_root: project_root,
23
+ preferred_revision: preferred_revision,
24
+ is_puppeteer_core: is_puppeteer_core,
25
+ )
16
26
  raise NotImplementedError.new('FirefoxLauncher is not implemented yet.')
17
27
  end
18
28
 
@@ -20,15 +20,25 @@ module Puppeteer::Launcher
20
20
  return executable_path
21
21
  end
22
22
  raise ExecutablePathNotFound.new(
23
- "Tried to use PUPPETEER_EXECUTABLE_PATH env variable to launch browser but did not find any executable at: #{executablePath}",
23
+ "Tried to use PUPPETEER_EXECUTABLE_PATH env variable to launch browser but did not find any executable at: #{executable_path}",
24
24
  )
25
25
  end
26
26
 
27
27
  # temporal logic.
28
- if RUBY_PLATFORM.include?('darwin') # MacOS
29
- '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'
28
+ if Puppeteer.env.darwin?
29
+ case self
30
+ when Chrome
31
+ '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'
32
+ when Firefox
33
+ '/Applications/Firefox Nightly.app/Contents/MacOS/firefox'
34
+ end
30
35
  else
31
- '/usr/bin/google-chrome'
36
+ case self
37
+ when Chrome
38
+ '/usr/bin/google-chrome'
39
+ when Firefox
40
+ '/usr/bin/firefox'
41
+ end
32
42
  end
33
43
 
34
44
  # const browserFetcher = new BrowserFetcher(launcher._projectRoot);
@@ -34,7 +34,7 @@ module Puppeteer::Launcher
34
34
 
35
35
  temporary_user_data_dir = nil
36
36
  if chrome_arguments.none? { |arg| arg.start_with?('--user-data-dir') }
37
- temporary_user_data_dir = Dir.mktmpdir('puppeteer_dev_profile-')
37
+ temporary_user_data_dir = Dir.mktmpdir('puppeteer_dev_chrome_profile-')
38
38
  chrome_arguments << "--user-data-dir=#{temporary_user_data_dir}"
39
39
  end
40
40
 
@@ -0,0 +1,392 @@
1
+ require 'tmpdir'
2
+
3
+ # https://github.com/puppeteer/puppeteer/blob/main/src/node/Launcher.ts
4
+ module Puppeteer::Launcher
5
+ class Firefox < Base
6
+ # @param {!(Launcher.LaunchOptions & Launcher.ChromeArgOptions & Launcher.BrowserOptions)=} options
7
+ # @return {!Promise<!Browser>}
8
+ def launch(options = {})
9
+ @chrome_arg_options = ChromeArgOptions.new(options)
10
+ @launch_options = LaunchOptions.new(options)
11
+ @browser_options = BrowserOptions.new(options)
12
+
13
+ firefox_arguments =
14
+ if !@launch_options.ignore_default_args
15
+ default_args.to_a
16
+ elsif @launch_options.ignore_default_args.is_a?(Enumerable)
17
+ default_args.reject do |arg|
18
+ @launch_options.ignore_default_args.include?(arg)
19
+ end.to_a
20
+ else
21
+ @chrome_arg_options.args.dup
22
+ end
23
+
24
+ if firefox_arguments.none? { |arg| arg.start_with?('--remote-debugging-') }
25
+ firefox_arguments << '--remote-debugging-port=0'
26
+ end
27
+
28
+ temporary_user_data_dir = nil
29
+ if firefox_arguments.none? { |arg| arg.start_with?('--profile') || arg.start_with?('-profile') }
30
+ temporary_user_data_dir = create_profile
31
+ firefox_arguments << "--profile"
32
+ firefox_arguments << temporary_user_data_dir
33
+ end
34
+
35
+ firefox_executable = @launch_options.executable_path || resolve_executable_path
36
+ runner = Puppeteer::BrowserRunner.new(firefox_executable, firefox_arguments, temporary_user_data_dir)
37
+ runner.start(
38
+ handle_SIGHUP: @launch_options.handle_SIGHUP?,
39
+ handle_SIGTERM: @launch_options.handle_SIGTERM?,
40
+ handle_SIGINT: @launch_options.handle_SIGINT?,
41
+ dumpio: @launch_options.dumpio?,
42
+ env: @launch_options.env,
43
+ pipe: @launch_options.pipe?,
44
+ )
45
+
46
+ begin
47
+ connection = runner.setup_connection(
48
+ use_pipe: @launch_options.pipe?,
49
+ timeout: @launch_options.timeout,
50
+ slow_mo: @browser_options.slow_mo,
51
+ preferred_revision: @preferred_revision,
52
+ )
53
+
54
+ browser = Puppeteer::Browser.create(
55
+ connection: connection,
56
+ context_ids: [],
57
+ ignore_https_errors: @browser_options.ignore_https_errors?,
58
+ default_viewport: @browser_options.default_viewport,
59
+ process: runner.proc,
60
+ close_callback: -> { runner.close },
61
+ )
62
+
63
+ browser.wait_for_target(predicate: ->(target) { target.type == 'page' })
64
+
65
+ browser
66
+ rescue
67
+ runner.kill
68
+ raise
69
+ end
70
+ end
71
+
72
+ # @return [Puppeteer::Browser]
73
+ def connect(options = {})
74
+ @browser_options = BrowserOptions.new(options)
75
+ browser_ws_endpoint = options[:browser_ws_endpoint]
76
+ browser_url = options[:browser_url]
77
+ transport = options[:transport]
78
+
79
+ connection =
80
+ if browser_ws_endpoint && browser_url.nil? && transport.nil?
81
+ connect_with_browser_ws_endpoint(browser_ws_endpoint)
82
+ elsif browser_ws_endpoint.nil? && browser_url && transport.nil?
83
+ connect_with_browser_url(browser_url)
84
+ elsif browser_ws_endpoint.nil? && browser_url.nil? && transport
85
+ connect_with_transport(transport)
86
+ else
87
+ raise ArgumentError.new("Exactly one of browserWSEndpoint, browserURL or transport must be passed to puppeteer.connect")
88
+ end
89
+
90
+ result = connection.send_message('Target.getBrowserContexts')
91
+ browser_context_ids = result['browserContextIds']
92
+
93
+ Puppeteer::Browser.create(
94
+ connection: connection,
95
+ context_ids: browser_context_ids,
96
+ ignore_https_errors: @browser_options.ignore_https_errors?,
97
+ default_viewport: @browser_options.default_viewport,
98
+ process: nil,
99
+ close_callback: -> { connection.send_message('Browser.close') },
100
+ )
101
+ end
102
+
103
+ # @return [Puppeteer::Connection]
104
+ private def connect_with_browser_ws_endpoint(browser_ws_endpoint)
105
+ transport = Puppeteer::WebSocketTransport.create(browser_ws_endpoint)
106
+ Puppeteer::Connection.new(browser_ws_endpoint, transport, @browser_options.slow_mo)
107
+ end
108
+
109
+ # @return [Puppeteer::Connection]
110
+ private def connect_with_browser_url(browser_url)
111
+ require 'net/http'
112
+ uri = URI(browser_url)
113
+ uri.path = '/json/version'
114
+ response_body = Net::HTTP.get(uri)
115
+ json = JSON.parse(response_body)
116
+ connection_url = json['webSocketDebuggerUrl']
117
+ connect_with_browser_ws_endpoint(connection_url)
118
+ end
119
+
120
+ # @return [Puppeteer::Connection]
121
+ private def connect_with_transport(transport)
122
+ Puppeteer::Connection.new('', transport, @browser_options.slow_mo)
123
+ end
124
+
125
+ # @return {string}
126
+ def executable_path
127
+ resolve_executable_path
128
+ end
129
+
130
+ private def product
131
+ 'firefox'
132
+ end
133
+
134
+ class DefaultArgs
135
+ include Enumerable
136
+
137
+ # @param options [Launcher::ChromeArgOptions]
138
+ def initialize(chrome_arg_options)
139
+ firefox_arguments = ['--no-remote', '--foreground']
140
+
141
+ # if (os.platform().startsWith('win')) {
142
+ # firefoxArguments.push('--wait-for-browser');
143
+ # }
144
+
145
+ if chrome_arg_options.user_data_dir
146
+ firefox_arguments << "--profile"
147
+ firefox_arguments << chrome_arg_options.user_data_dir
148
+ end
149
+
150
+ if chrome_arg_options.headless?
151
+ firefox_arguments << '--headless'
152
+ end
153
+
154
+ if chrome_arg_options.devtools?
155
+ firefox_arguments << '--devtools'
156
+ end
157
+
158
+ if chrome_arg_options.args.all? { |arg| arg.start_with?('-') }
159
+ firefox_arguments << 'about:blank'
160
+ end
161
+
162
+ firefox_arguments.concat(chrome_arg_options.args)
163
+
164
+ @firefox_arguments = firefox_arguments
165
+ end
166
+
167
+ def each(&block)
168
+ @firefox_arguments.each do |opt|
169
+ block.call(opt)
170
+ end
171
+ end
172
+ end
173
+
174
+ # @return [DefaultArgs]
175
+ def default_args(options = nil)
176
+ if options.nil?
177
+ @default_args ||= DefaultArgs.new(@chrome_arg_options)
178
+ else
179
+ DefaultArgs.new(ChromeArgOptions.new(options))
180
+ end
181
+ end
182
+
183
+ private def create_profile(extra_prefs = {})
184
+ Dir.mktmpdir('puppeteer_dev_firefox_profile-').tap do |profile_path|
185
+ server = 'dummy.test'
186
+ default_preferences = {
187
+ # Make sure Shield doesn't hit the network.
188
+ 'app.normandy.api_url': '',
189
+ # Disable Firefox old build background check
190
+ 'app.update.checkInstallTime': false,
191
+ # Disable automatically upgrading Firefox
192
+ 'app.update.disabledForTesting': true,
193
+
194
+ # Increase the APZ content response timeout to 1 minute
195
+ 'apz.content_response_timeout': 60000,
196
+
197
+ # Prevent various error message on the console
198
+ # jest-puppeteer asserts that no error message is emitted by the console
199
+ 'browser.contentblocking.features.standard': '-tp,tpPrivate,cookieBehavior0,-cm,-fp',
200
+
201
+ # Enable the dump function: which sends messages to the system
202
+ # console
203
+ # https://bugzilla.mozilla.org/show_bug.cgi?id=1543115
204
+ 'browser.dom.window.dump.enabled': true,
205
+ # Disable topstories
206
+ 'browser.newtabpage.activity-stream.feeds.section.topstories': false,
207
+ # Always display a blank page
208
+ 'browser.newtabpage.enabled': false,
209
+ # Background thumbnails in particular cause grief: and disabling
210
+ # thumbnails in general cannot hurt
211
+ 'browser.pagethumbnails.capturing_disabled': true,
212
+
213
+ # Disable safebrowsing components.
214
+ 'browser.safebrowsing.blockedURIs.enabled': false,
215
+ 'browser.safebrowsing.downloads.enabled': false,
216
+ 'browser.safebrowsing.malware.enabled': false,
217
+ 'browser.safebrowsing.passwords.enabled': false,
218
+ 'browser.safebrowsing.phishing.enabled': false,
219
+
220
+ # Disable updates to search engines.
221
+ 'browser.search.update': false,
222
+ # Do not restore the last open set of tabs if the browser has crashed
223
+ 'browser.sessionstore.resume_from_crash': false,
224
+ # Skip check for default browser on startup
225
+ 'browser.shell.checkDefaultBrowser': false,
226
+
227
+ # Disable newtabpage
228
+ 'browser.startup.homepage': 'about:blank',
229
+ # Do not redirect user when a milstone upgrade of Firefox is detected
230
+ 'browser.startup.homepage_override.mstone': 'ignore',
231
+ # Start with a blank page about:blank
232
+ 'browser.startup.page': 0,
233
+
234
+ # Do not allow background tabs to be zombified on Android: otherwise for
235
+ # tests that open additional tabs: the test harness tab itself might get
236
+ # unloaded
237
+ 'browser.tabs.disableBackgroundZombification': false,
238
+ # Do not warn when closing all other open tabs
239
+ 'browser.tabs.warnOnCloseOtherTabs': false,
240
+ # Do not warn when multiple tabs will be opened
241
+ 'browser.tabs.warnOnOpen': false,
242
+
243
+ # Disable the UI tour.
244
+ 'browser.uitour.enabled': false,
245
+ # Turn off search suggestions in the location bar so as not to trigger
246
+ # network connections.
247
+ 'browser.urlbar.suggest.searches': false,
248
+ # Disable first run splash page on Windows 10
249
+ 'browser.usedOnWindows10.introURL': '',
250
+ # Do not warn on quitting Firefox
251
+ 'browser.warnOnQuit': false,
252
+
253
+ # Do not show datareporting policy notifications which can
254
+ # interfere with tests
255
+ 'datareporting.healthreport.about.reportUrl': "http://#{server}/dummy/abouthealthreport/",
256
+ 'datareporting.healthreport.documentServerURI': "http://#{server}/dummy/healthreport/",
257
+ 'datareporting.healthreport.logging.consoleEnabled': false,
258
+ 'datareporting.healthreport.service.enabled': false,
259
+ 'datareporting.healthreport.service.firstRun': false,
260
+ 'datareporting.healthreport.uploadEnabled': false,
261
+ 'datareporting.policy.dataSubmissionEnabled': false,
262
+ 'datareporting.policy.dataSubmissionPolicyAccepted': false,
263
+ 'datareporting.policy.dataSubmissionPolicyBypassNotification': true,
264
+
265
+ # DevTools JSONViewer sometimes fails to load dependencies with its require.js.
266
+ # This doesn't affect Puppeteer but spams console (Bug 1424372)
267
+ 'devtools.jsonview.enabled': false,
268
+
269
+ # Disable popup-blocker
270
+ 'dom.disable_open_during_load': false,
271
+
272
+ # Enable the support for File object creation in the content process
273
+ # Required for |Page.setFileInputFiles| protocol method.
274
+ 'dom.file.createInChild': true,
275
+
276
+ # Disable the ProcessHangMonitor
277
+ 'dom.ipc.reportProcessHangs': false,
278
+
279
+ # Disable slow script dialogues
280
+ 'dom.max_chrome_script_run_time': 0,
281
+ 'dom.max_script_run_time': 0,
282
+
283
+ # Only load extensions from the application and user profile
284
+ # AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_APPLICATION
285
+ 'extensions.autoDisableScopes': 0,
286
+ 'extensions.enabledScopes': 5,
287
+
288
+ # Disable metadata caching for installed add-ons by default
289
+ 'extensions.getAddons.cache.enabled': false,
290
+
291
+ # Disable installing any distribution extensions or add-ons.
292
+ 'extensions.installDistroAddons': false,
293
+
294
+ # Disabled screenshots extension
295
+ 'extensions.screenshots.disabled': true,
296
+
297
+ # Turn off extension updates so they do not bother tests
298
+ 'extensions.update.enabled': false,
299
+
300
+ # Turn off extension updates so they do not bother tests
301
+ 'extensions.update.notifyUser': false,
302
+
303
+ # Make sure opening about:addons will not hit the network
304
+ 'extensions.webservice.discoverURL': "http://#{server}/dummy/discoveryURL",
305
+
306
+ # Allow the application to have focus even it runs in the background
307
+ 'focusmanager.testmode': true,
308
+ # Disable useragent updates
309
+ 'general.useragent.updates.enabled': false,
310
+ # Always use network provider for geolocation tests so we bypass the
311
+ # macOS dialog raised by the corelocation provider
312
+ 'geo.provider.testing': true,
313
+ # Do not scan Wifi
314
+ 'geo.wifi.scan': false,
315
+ # No hang monitor
316
+ 'hangmonitor.timeout': 0,
317
+ # Show chrome errors and warnings in the error console
318
+ 'javascript.options.showInConsole': true,
319
+
320
+ # Disable download and usage of OpenH264: and Widevine plugins
321
+ 'media.gmp-manager.updateEnabled': false,
322
+ # Prevent various error message on the console
323
+ # jest-puppeteer asserts that no error message is emitted by the console
324
+ 'network.cookie.cookieBehavior': 0,
325
+
326
+ # Do not prompt for temporary redirects
327
+ 'network.http.prompt-temp-redirect': false,
328
+
329
+ # Disable speculative connections so they are not reported as leaking
330
+ # when they are hanging around
331
+ 'network.http.speculative-parallel-limit': 0,
332
+
333
+ # Do not automatically switch between offline and online
334
+ 'network.manage-offline-status': false,
335
+
336
+ # Make sure SNTP requests do not hit the network
337
+ 'network.sntp.pools': server,
338
+
339
+ # Disable Flash.
340
+ 'plugin.state.flash': 0,
341
+
342
+ 'privacy.trackingprotection.enabled': false,
343
+
344
+ # Enable Remote Agent
345
+ # https://bugzilla.mozilla.org/show_bug.cgi?id=1544393
346
+ 'remote.enabled': true,
347
+
348
+ # Don't do network connections for mitm priming
349
+ 'security.certerrors.mitm.priming.enabled': false,
350
+ # Local documents have access to all other local documents,
351
+ # including directory listings
352
+ 'security.fileuri.strict_origin_policy': false,
353
+ # Do not wait for the notification button security delay
354
+ 'security.notification_enable_delay': 0,
355
+
356
+ # Ensure blocklist updates do not hit the network
357
+ 'services.settings.server': "http://#{server}/dummy/blocklist/",
358
+
359
+ # Do not automatically fill sign-in forms with known usernames and
360
+ # passwords
361
+ 'signon.autofillForms': false,
362
+ # Disable password capture, so that tests that include forms are not
363
+ # influenced by the presence of the persistent doorhanger notification
364
+ 'signon.rememberSignons': false,
365
+
366
+ # Disable first-run welcome page
367
+ 'startup.homepage_welcome_url': 'about:blank',
368
+
369
+ # Disable first-run welcome page
370
+ 'startup.homepage_welcome_url.additional': '',
371
+
372
+ # Disable browser animations (tabs, fullscreen, sliding alerts)
373
+ 'toolkit.cosmeticAnimations.enabled': false,
374
+
375
+ # We want to collect telemetry, but we don't want to send in the results
376
+ 'toolkit.telemetry.server': "https://#{server}/dummy/telemetry/",
377
+ # Prevent starting into safe mode after application crashes
378
+ 'toolkit.startup.max_resumed_crashes': -1,
379
+ }
380
+
381
+ preferences = default_preferences.merge(extra_prefs)
382
+
383
+ File.open(File.join(profile_path, 'user.js'), 'w') do |f|
384
+ preferences.each do |key, value|
385
+ f.write("user_pref(#{JSON.generate(key)}, #{JSON.generate(value)});\n")
386
+ end
387
+ end
388
+ IO.write(File.join(profile_path, 'prefs.js'), "")
389
+ end
390
+ end
391
+ end
392
+ end