ferrum 0.17 → 0.17.2

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.
data/README.md CHANGED
@@ -1,70 +1,31 @@
1
1
  # Ferrum - high-level API to control Chrome in Ruby
2
2
 
3
+ ## [Documentation](https://docs.rubycdp.com/docs/ferrum/introduction)
4
+
3
5
  <img align="right"
4
6
  width="320" height="241"
5
7
  alt="Ferrum logo"
6
8
  src="https://raw.githubusercontent.com/rubycdp/ferrum/main/logo.svg?sanitize=true">
7
9
 
8
- #### As simple as Puppeteer, though even simpler.
9
-
10
10
  It is Ruby clean and high-level API to Chrome. Runs headless by default, but you
11
- can configure it to run in a headful mode. All you need is Ruby and
12
- [Chrome](https://www.google.com/chrome/) or
13
- [Chromium](https://www.chromium.org/). Ferrum connects to the browser by [CDP
14
- protocol](https://chromedevtools.github.io/devtools-protocol/) and there's _no_
11
+ can configure it to run in a headful mode. All you need is Ruby and [Chrome](https://www.google.com/chrome/) or [Chromium](https://www.chromium.org/).
12
+ Ferrum connects to the browser by [CDP protocol](https://chromedevtools.github.io/devtools-protocol/) and there's _no_
15
13
  Selenium/WebDriver/ChromeDriver dependency. The emphasis was made on a raw CDP
16
14
  protocol because Chrome allows you to do so many things that are barely
17
15
  supported by WebDriver because it should have consistent design with other
18
16
  browsers.
19
17
 
20
- * [Cuprite](https://github.com/rubycdp/cuprite) is a pure Ruby driver for
21
- [Capybara](https://github.com/teamcapybara/capybara) based on Ferrum. If you are
22
- going to crawl sites you better use Ferrum or
23
- [Vessel](https://github.com/rubycdp/vessel) because you crawl, not test.
24
-
25
- * [Vessel](https://github.com/rubycdp/vessel) high-level web crawling framework
26
- based on Ferrum and Mechanize.
27
-
28
- ## Index
29
-
30
- * [Install](https://github.com/rubycdp/ferrum#install)
31
- * [Examples](https://github.com/rubycdp/ferrum#examples)
32
- * [Docker](https://github.com/rubycdp/ferrum#docker)
33
- * [Customization](https://github.com/rubycdp/ferrum#customization)
34
- * [Navigation](https://github.com/rubycdp/ferrum#navigation)
35
- * [Finders](https://github.com/rubycdp/ferrum#finders)
36
- * [Screenshots](https://github.com/rubycdp/ferrum#screenshots)
37
- * [Screencast](https://github.com/rubycdp/ferrum#screencast)
38
- * [Network](https://github.com/rubycdp/ferrum#network)
39
- * [Downloads](https://github.com/rubycdp/ferrum#downloads)
40
- * [Proxy](https://github.com/rubycdp/ferrum#proxy)
41
- * [Mouse](https://github.com/rubycdp/ferrum#mouse)
42
- * [Keyboard](https://github.com/rubycdp/ferrum#keyboard)
43
- * [Cookies](https://github.com/rubycdp/ferrum#cookies)
44
- * [Headers](https://github.com/rubycdp/ferrum#headers)
45
- * [JavaScript](https://github.com/rubycdp/ferrum#javascript)
46
- * [Frames](https://github.com/rubycdp/ferrum#frames)
47
- * [Frame](https://github.com/rubycdp/ferrum#frame)
48
- * [Dialogs](https://github.com/rubycdp/ferrum#dialogs)
49
- * [Animation](https://github.com/rubycdp/ferrum#animation)
50
- * [Node](https://github.com/rubycdp/ferrum#node)
51
- * [Tracing](https://github.com/rubycdp/ferrum#tracing)
52
- * [Clean Up](https://github.com/rubycdp/ferrum#clean-up)
53
- * [Thread safety](https://github.com/rubycdp/ferrum#thread-safety)
54
- * [Development](https://github.com/rubycdp/ferrum#development)
55
- * [Contributing](https://github.com/rubycdp/ferrum#contributing)
56
- * [License](https://github.com/rubycdp/ferrum#license)
57
-
18
+ * [Cuprite](https://github.com/rubycdp/cuprite) is a pure Ruby driver for[Capybara](https://github.com/teamcapybara/capybara) based on Ferrum.
19
+ * [Vessel](https://github.com/rubycdp/vessel) high-level web crawling framework based on Ferrum and Mechanize.
58
20
 
59
21
  ## Install
60
22
 
61
23
  There's no official Chrome or Chromium package for Linux don't install it this
62
24
  way because it's either outdated or unofficial, both are bad. Download it from
63
- official source for [Chrome](https://www.google.com/chrome/) or
64
- [Chromium](https://www.chromium.org/getting-involved/download-chromium).
25
+ official source for [Chrome](https://www.google.com/chrome/) or [Chromium](https://www.chromium.org/getting-involved/download-chromium).
65
26
  Chrome binary should be in the `PATH` or `BROWSER_PATH` and you can pass it as an
66
27
  option to browser instance see `:browser_path` in
67
- [Customization](https://github.com/rubycdp/ferrum#customization).
28
+ [Customization](https://docs.rubycdp.com/docs/ferrum/customization).
68
29
 
69
30
  Add this to your `Gemfile` and run `bundle install`.
70
31
 
@@ -72,8 +33,7 @@ Add this to your `Gemfile` and run `bundle install`.
72
33
  gem "ferrum"
73
34
  ```
74
35
 
75
-
76
- ## Examples
36
+ ## Quick Start
77
37
 
78
38
  Navigate to a website and save a screenshot:
79
39
 
@@ -131,1326 +91,6 @@ page.mouse
131
91
  browser.quit
132
92
  ```
133
93
 
134
-
135
- ## Docker
136
-
137
- In docker as root you must pass the no-sandbox browser option:
138
-
139
- ```ruby
140
- Ferrum::Browser.new(browser_options: { "no-sandbox": nil })
141
- ```
142
-
143
- It has also been reported that the Chrome process repeatedly crashes when running inside a Docker container on an M1 Mac preventing Ferrum from working. Ferrum should work as expected when deployed to a Docker container on a non-M1 Mac.
144
-
145
- ## Customization
146
-
147
- You can customize options with the following code in your test setup:
148
-
149
- ``` ruby
150
- Ferrum::Browser.new(options)
151
- ```
152
-
153
- * options `Hash`
154
- * `:headless` (Boolean) - Set browser as headless or not, `true` by default.
155
- * `:incognito` (Boolean) - Create an incognito profile for the browser startup window, `true` by default.
156
- * `:xvfb` (Boolean) - Run browser in a virtual framebuffer, `false` by default.
157
- * `:flatten` (Boolean) - Use one websocket connection to the browser and all the pages in flatten mode.
158
- * `:window_size` (Array) - The dimensions of the browser window in which to
159
- test, expressed as a 2-element array, e.g. [1024, 768]. Default: [1024, 768]
160
- * `:extensions` (Array[String | Hash]) - An array of paths to files or JS
161
- source code to be preloaded into the browser e.g.:
162
- `["/path/to/script.js", { source: "window.secret = 'top'" }]`
163
- * `:logger` (Object responding to `puts`) - When present, debug output is
164
- written to this object.
165
- * `:slowmo` (Integer | Float) - Set a delay in seconds to wait before sending command.
166
- Useful companion of headless option, so that you have time to see changes.
167
- * `:timeout` (Numeric) - The number of seconds we'll wait for a response when
168
- communicating with browser. Default is 5.
169
- * `:js_errors` (Boolean) - When true, JavaScript errors get re-raised in Ruby.
170
- * `:pending_connection_errors` (Boolean) - When main frame is still waiting for slow responses while timeout is
171
- reached `PendingConnectionsError` is raised. It's better to figure out why you have slow responses and fix or
172
- block them rather than turn this setting off. Default is true.
173
- * `:browser_name` (Symbol) - `:chrome` by default, only experimental support
174
- for `:firefox` for now.
175
- * `:browser_path` (String) - Path to Chrome binary, you can also set ENV
176
- variable as `BROWSER_PATH=some/path/chrome bundle exec rspec`.
177
- * `:browser_options` (Hash) - Additional command line options,
178
- [see them all](https://peter.sh/experiments/chromium-command-line-switches/)
179
- e.g. `{ "ignore-certificate-errors" => nil }`
180
- * `:ignore_default_browser_options` (Boolean) - Ferrum has a number of default
181
- options it passes to the browser, if you set this to `true` then only
182
- options you put in `:browser_options` will be passed to the browser,
183
- except required ones of course.
184
- * `:port` (Integer) - Remote debugging port for headless Chrome.
185
- * `:host` (String) - Remote debugging address for headless Chrome.
186
- * `:url` (String) - URL for a running instance of Chrome. If this is set, a
187
- browser process will not be spawned.
188
- * `:ws_url` (String) - Websocket url for a running instance of Chrome. If this is set, a
189
- browser process will not be spawned. It's higher priority than `:url`, setting both doesn't make sense.
190
- * `:process_timeout` (Integer) - How long to wait for the Chrome process to
191
- respond on startup.
192
- * `:ws_max_receive_size` (Integer) - How big messages to accept from Chrome
193
- over the web socket, in bytes. Defaults to 64MB. Incoming messages larger
194
- than this will cause a `Ferrum::DeadBrowserError`.
195
- * `:proxy` (Hash) - Specify proxy settings, [read more](https://github.com/rubycdp/ferrum#proxy)
196
- * `:save_path` (String) - Path to save attachments with [Content-Disposition](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition) header.
197
- * `:env` (Hash) - Environment variables you'd like to pass through to the process
198
-
199
-
200
- ## Navigation
201
-
202
- #### go_to(url) : `String`
203
-
204
- Navigate page to.
205
-
206
- * url `String` The url should include scheme unless you set `base_url` when
207
- configuring driver.
208
-
209
- ```ruby
210
- page.go_to("https://github.com/")
211
- ```
212
-
213
- #### back
214
-
215
- Navigate to the previous page in history.
216
-
217
- ```ruby
218
- page.go_to("https://github.com/")
219
- page.at_xpath("//a").click
220
- page.back
221
- ```
222
-
223
- #### forward
224
-
225
- Navigate to the next page in history.
226
-
227
- ```ruby
228
- page.go_to("https://github.com/")
229
- page.at_xpath("//a").click
230
- page.back
231
- page.forward
232
- ```
233
-
234
- #### refresh
235
-
236
- Reload current page.
237
-
238
- ```ruby
239
- page.go_to("https://github.com/")
240
- page.refresh
241
- ```
242
-
243
- #### stop
244
-
245
- Stop all navigations and loading pending resources on the page
246
-
247
- ```ruby
248
- page.go_to("https://github.com/")
249
- page.stop
250
- ```
251
-
252
- #### position = \*\*options
253
-
254
- Set the position for the browser window
255
-
256
- * options `Hash`
257
- * :left `Integer`
258
- * :top `Integer`
259
-
260
- ```ruby
261
- browser.position = { left: 10, top: 20 }
262
- ```
263
-
264
- #### position : `Array<Integer>`
265
-
266
- Get the position for the browser window
267
-
268
- ```ruby
269
- browser.position # => [10, 20]
270
- ```
271
-
272
- #### window_bounds = \*\*options
273
-
274
- Set window bounds
275
-
276
- * options `Hash`
277
- * :left `Integer`
278
- * :top `Integer`
279
- * :width `Integer`
280
- * :height `Integer`
281
- * :window_state `String`
282
-
283
- ```ruby
284
- browser.window_bounds = { left: 10, top: 20, width: 1024, height: 768, window_state: "normal" }
285
- ```
286
-
287
- #### window_bounds : `Hash<String, Integer | String>`
288
-
289
- Get window bounds
290
-
291
- ```ruby
292
- browser.window_bounds # => { "left": 0, "top": 1286, "width": 10, "height": 10, "windowState": "normal" }
293
- ```
294
-
295
- #### window_id : `Integer`
296
-
297
- Current window id
298
-
299
- ```ruby
300
- browser.window_id # => 1
301
- ```
302
-
303
- ## Finders
304
-
305
- #### at_css(selector, \*\*options) : `Node` | `nil`
306
-
307
- Find node by selector. Runs `document.querySelector` within the document or
308
- provided node.
309
-
310
- * selector `String`
311
- * options `Hash`
312
- * :within `Node` | `nil`
313
-
314
- ```ruby
315
- page.go_to("https://github.com/")
316
- page.at_css("a[aria-label='Issues you created']") # => Node
317
- ```
318
-
319
-
320
- #### css(selector, \*\*options) : `Array<Node>` | `[]`
321
-
322
- Find nodes by selector. The method runs `document.querySelectorAll` within the
323
- document or provided node.
324
-
325
- * selector `String`
326
- * options `Hash`
327
- * :within `Node` | `nil`
328
-
329
- ```ruby
330
- page.go_to("https://github.com/")
331
- page.css("a[aria-label='Issues you created']") # => [Node]
332
- ```
333
-
334
- #### at_xpath(selector, \*\*options) : `Node` | `nil`
335
-
336
- Find node by xpath.
337
-
338
- * selector `String`
339
- * options `Hash`
340
- * :within `Node` | `nil`
341
-
342
- ```ruby
343
- page.go_to("https://github.com/")
344
- page.at_xpath("//a[@aria-label='Issues you created']") # => Node
345
- ```
346
-
347
- #### xpath(selector, \*\*options) : `Array<Node>` | `[]`
348
-
349
- Find nodes by xpath.
350
-
351
- * selector `String`
352
- * options `Hash`
353
- * :within `Node` | `nil`
354
-
355
- ```ruby
356
- page.go_to("https://github.com/")
357
- page.xpath("//a[@aria-label='Issues you created']") # => [Node]
358
- ```
359
-
360
- #### current_url : `String`
361
-
362
- Returns current top window location href.
363
-
364
- ```ruby
365
- page.go_to("https://google.com/")
366
- page.current_url # => "https://www.google.com/"
367
- ```
368
-
369
- #### current_title : `String`
370
-
371
- Returns current top window title
372
-
373
- ```ruby
374
- page.go_to("https://google.com/")
375
- page.current_title # => "Google"
376
- ```
377
-
378
- #### body : `String`
379
-
380
- Returns current page's html.
381
-
382
- ```ruby
383
- page.go_to("https://google.com/")
384
- page.body # => '<html itemscope="" itemtype="http://schema.org/WebPage" lang="ru"><head>...
385
- ```
386
-
387
-
388
- ## Screenshots
389
-
390
- #### screenshot(\*\*options) : `String` | `Integer`
391
-
392
- Saves screenshot on a disk or returns it as base64.
393
-
394
- * options `Hash`
395
- * :path `String` to save a screenshot on the disk. `:encoding` will be set to
396
- `:binary` automatically
397
- * :encoding `Symbol` `:base64` | `:binary` you can set it to return image as
398
- Base64
399
- * :format `String` "jpeg" ("jpg") | "png" | "webp"
400
- * :quality `Integer` 0-100 works for jpeg only
401
- * :full `Boolean` whether you need full page screenshot or a viewport
402
- * :selector `String` css selector for given element, optional
403
- * :area `Hash` area for screenshot, optional
404
- * :x `Integer`
405
- * :y `Integer`
406
- * :width `Integer`
407
- * :height `Integer`
408
- * :scale `Float` zoom in/out
409
- * :background_color `Ferrum::RGBA.new(0, 0, 0, 0.0)` to have specific background color
410
-
411
- ```ruby
412
- page.go_to("https://google.com/")
413
- # Save on the disk in PNG
414
- page.screenshot(path: "google.png") # => 134660
415
- # Save on the disk in JPG
416
- page.screenshot(path: "google.jpg") # => 30902
417
- # Save to Base64 the whole page not only viewport and reduce quality
418
- page.screenshot(full: true, quality: 60, encoding: :base64) # "iVBORw0KGgoAAAANSUhEUgAABAAAAAMACAYAAAC6uhUNAAAAAXNSR0IArs4c6Q...
419
- # Save on the disk with the selected element in PNG
420
- page.screenshot(path: "google.png", selector: "textarea") # => 11340
421
- # Save to Base64 with an area of the page in PNG
422
- page.screenshot(path: "google.png", area: { x: 0, y: 0, width: 400, height: 300 }) # => 54239
423
- # Save with specific background color
424
- page.screenshot(background_color: Ferrum::RGBA.new(0, 0, 0, 0.0))
425
- ```
426
-
427
- #### pdf(\*\*options) : `String` | `Boolean`
428
-
429
- Saves PDF on a disk or returns it as base64.
430
-
431
- * options `Hash`
432
- * :path `String` to save a pdf on the disk. `:encoding` will be set to
433
- `:binary` automatically
434
- * :encoding `Symbol` `:base64` | `:binary` you can set it to return pdf as
435
- Base64
436
- * :landscape `Boolean` paper orientation. Defaults to false.
437
- * :scale `Float` zoom in/out
438
- * :format `symbol` standard paper sizes :letter, :legal, :tabloid, :ledger, :A0, :A1, :A2, :A3, :A4, :A5, :A6
439
-
440
- * :paper_width `Float` set paper width
441
- * :paper_height `Float` set paper height
442
- * See other [native options](https://chromedevtools.github.io/devtools-protocol/tot/Page#method-printToPDF) you can pass
443
-
444
- ```ruby
445
- page.go_to("https://google.com/")
446
- # Save to disk as a PDF
447
- page.pdf(path: "google.pdf", paper_width: 1.0, paper_height: 1.0) # => true
448
- ```
449
-
450
- #### mhtml(\*\*options) : `String` | `Integer`
451
-
452
- Saves MHTML on a disk or returns it as a string.
453
-
454
- * options `Hash`
455
- * :path `String` to save a file on the disk.
456
-
457
- ```ruby
458
- page.go_to("https://google.com/")
459
- page.mhtml(path: "google.mhtml") # => 87742
460
- ```
461
-
462
- ## Screencast
463
-
464
- #### start_screencast(\*\*options) { |data, metadata, session_id| ... }
465
-
466
- Starts sending frames to record screencast to the given block.
467
-
468
- * options `Hash`
469
- * :format `Symbol` `:jpeg` | `:png` The format the image should be returned in.
470
- * :quality `Integer` The image quality. **Note:** 0-100 works for JPEG only.
471
- * :max_width `Integer` Maximum screencast frame width.
472
- * :max_height `Integer` Maximum screencast frame height.
473
- * :every_nth_frame `Integer` Send every n-th frame.
474
-
475
- * Block inputs:
476
- * data `String` Base64-encoded compressed image.
477
- * metadata `Hash` Screencast frame metadata.
478
- * "offsetTop" `Integer` Top offset in DIP.
479
- * "pageScaleFactor" `Integer` Page scale factor.
480
- * "deviceWidth" `Integer` Device screen width in DIP.
481
- * "deviceHeight" `Integer` Device screen height in DIP.
482
- * "scrollOffsetX" `Integer` Position of horizontal scroll in CSS pixels.
483
- * "scrollOffsetY" `Integer` Position of vertical scroll in CSS pixels.
484
- * "timestamp" `Float` (optional) Frame swap timestamp in seconds since Unix epoch.
485
- * session_id `Integer` Frame number.
486
-
487
- ```ruby
488
- require "base64"
489
-
490
- page.go_to("https://apple.com/ipad")
491
-
492
- page.start_screencast(format: :jpeg, quality: 75) do |data, metadata|
493
- timestamp = (metadata["timestamp"] * 1000).to_i
494
- File.binwrite("image_#{timestamp}.jpg", Base64.decode64(data))
495
- end
496
-
497
- sleep 10
498
-
499
- page.stop_screencast
500
- ```
501
-
502
- > ### 📝 NOTE
503
- >
504
- > Chrome only sends new frames while page content is changing. For example, if
505
- > there is an animation or a video on the page, Chrome sends frames at the rate
506
- > requested. On the other hand, if the page is nothing but a wall of static text,
507
- > Chrome sends frames while the page renders. Once Chrome has finished rendering
508
- > the page, it sends no more frames until something changes (e.g., navigating to
509
- > another location).
510
-
511
- #### stop_screencast
512
-
513
- Stops sending frames.
514
-
515
- ## Network
516
-
517
- `page.network`
518
-
519
- #### traffic `Array<Network::Exchange>`
520
-
521
- Returns all information about network traffic as `Network::Exchange` instance
522
- which in general is a wrapper around `request`, `response` and `error`.
523
-
524
- ```ruby
525
- page.go_to("https://github.com/")
526
- page.network.traffic # => [#<Ferrum::Network::Exchange, ...]
527
- ```
528
-
529
- #### request : `Network::Request`
530
-
531
- Page request of the main frame.
532
-
533
- ```ruby
534
- page.go_to("https://github.com/")
535
- page.network.request # => #<Ferrum::Network::Request...
536
- ```
537
-
538
- #### response : `Network::Response`
539
-
540
- Page response of the main frame.
541
-
542
- ```ruby
543
- page.go_to("https://github.com/")
544
- page.network.response # => #<Ferrum::Network::Response...
545
- ```
546
-
547
- #### status : `Integer`
548
-
549
- Contains the status code of the main page response (e.g., 200 for a
550
- success). This is just a shortcut for `response.status`.
551
-
552
- ```ruby
553
- page.go_to("https://github.com/")
554
- page.network.status # => 200
555
- ```
556
-
557
- #### wait_for_idle(\*\*options) : `Boolean`
558
-
559
- Waits for network idle, returns `true` in case of success and `false` if there are still connections.
560
-
561
- * options `Hash`
562
- * :connections `Integer` how many connections are allowed for network to be
563
- idling, `0` by default
564
- * :duration `Float` sleep for given amount of time and check again, `0.05` by
565
- default
566
- * :timeout `Float` during what time we try to check idle, `browser.timeout`
567
- by default
568
-
569
- ```ruby
570
- page.go_to("https://example.com/")
571
- page.at_xpath("//a[text() = 'No UI changes button']").click
572
- page.network.wait_for_idle # => true
573
- ```
574
-
575
- #### wait_for_idle!(\*\*options)
576
-
577
- Waits for network idle or raises `Ferrum::TimeoutError` error. Accepts same arguments as `wait_for_idle`.
578
-
579
- ```ruby
580
- page.go_to("https://example.com/")
581
- page.at_xpath("//a[text() = 'No UI changes button']").click
582
- page.network.wait_for_idle! # might raise an error
583
- ```
584
-
585
- #### clear(type)
586
-
587
- Clear page's cache or collected traffic.
588
-
589
- * type `Symbol` it is either `:traffic` or `:cache`
590
-
591
- ```ruby
592
- traffic = page.network.traffic # => []
593
- page.go_to("https://github.com/")
594
- traffic.size # => 51
595
- page.network.clear(:traffic)
596
- traffic.size # => 0
597
- ```
598
-
599
- #### intercept(\*\*options)
600
-
601
- Set request interception for given options. This method is only sets request
602
- interception, you should use `on` callback to catch requests and abort or
603
- continue them.
604
-
605
- * options `Hash`
606
- * :pattern `String` \* by default
607
- * :resource_type `Symbol` one of the [resource types](https://chromedevtools.github.io/devtools-protocol/tot/Network#type-ResourceType)
608
-
609
- ```ruby
610
- browser = Ferrum::Browser.new
611
- page = browser.create_page
612
- page.network.intercept
613
- page.on(:request) do |request|
614
- if request.match?(/bla-bla/)
615
- request.abort
616
- elsif request.match?(/lorem/)
617
- request.respond(body: "Lorem ipsum")
618
- else
619
- request.continue
620
- end
621
- end
622
- page.go_to("https://google.com")
623
- ```
624
-
625
- #### authorize(\*\*options, &block)
626
-
627
- If site or proxy uses authorization you can provide credentials using this method.
628
-
629
- * options `Hash`
630
- * :type `Symbol` `:server` | `:proxy` site or proxy authorization
631
- * :user `String`
632
- * :password `String`
633
- * &block accepts authenticated request, which you must subsequently allow or deny, if you don't
634
- care about unwanted requests just call `request.continue`.
635
-
636
- ```ruby
637
- page.network.authorize(user: "login", password: "pass") { |req| req.continue }
638
- page.go_to("http://example.com/authenticated")
639
- puts page.network.status # => 200
640
- puts page.body # => Welcome, authenticated client
641
- ```
642
-
643
- Since Chrome implements authorize using request interception you must continue or abort authorized requests. If you
644
- already have code that uses interception you can use `authorize` without block, but if not you are obliged to pass
645
- block, so this is version doesn't pass block and can work just fine:
646
-
647
- ```ruby
648
- browser = Ferrum::Browser.new
649
- page = browser.create_page
650
- page.network.intercept
651
- page.on(:request) do |request|
652
- if request.resource_type == "Image"
653
- request.abort
654
- else
655
- request.continue
656
- end
657
- end
658
-
659
- page.network.authorize(user: "login", password: "pass", type: :proxy)
660
-
661
- page.go_to("https://google.com")
662
- ```
663
-
664
- You used to call `authorize` method without block, but since it's implemented using request interception there could be
665
- a collision with another part of your code that also uses request interception, so that authorize allows the request
666
- while your code denies but it's too late. The block is mandatory now.
667
-
668
- #### emulate_network_conditions(\*\*options)
669
-
670
- Activates emulation of network conditions.
671
-
672
- * options `Hash`
673
- * :offline `Boolean` emulate internet disconnection, `false` by default
674
- * :latency `Integer` minimum latency from request sent to response headers received (ms), `0` by
675
- default
676
- * :download_throughput `Integer` maximal aggregated download throughput (bytes/sec), `-1`
677
- by default, disables download throttling
678
- * :upload_throughput `Integer` maximal aggregated upload throughput (bytes/sec), `-1`
679
- by default, disables download throttling
680
- * :connection_type `String` connection type if known, one of: none, cellular2g, cellular3g, cellular4g,
681
- bluetooth, ethernet, wifi, wimax, other. `nil` by default
682
-
683
- ```ruby
684
- page.network.emulate_network_conditions(connection_type: "cellular2g")
685
- page.go_to("https://github.com/")
686
- ```
687
-
688
- #### offline_mode
689
-
690
- Activates offline mode for a page.
691
-
692
- ```ruby
693
- page.network.offline_mode
694
- page.go_to("https://github.com/") # => Ferrum::StatusError (Request to https://github.com/ failed(net::ERR_INTERNET_DISCONNECTED))
695
- ```
696
-
697
- #### cache(disable: `Boolean`)
698
-
699
- Toggles ignoring cache for each request. If true, cache will not be used.
700
-
701
- ```ruby
702
- page.network.cache(disable: true)
703
- ```
704
-
705
-
706
- ## Downloads
707
-
708
- `page.downloads`
709
-
710
- #### files `Array<Hash>`
711
-
712
- Returns all information about downloaded files as a `Hash`.
713
-
714
- ```ruby
715
- page.go_to("http://localhost/attachment.pdf")
716
- page.downloads.files # => [{"frameId"=>"E3316DF1B5383D38F8ADF7485005FDE3", "guid"=>"11a68745-98ac-4d54-9b57-9f9016c268b3", "url"=>"http://localhost/attachment.pdf", "suggestedFilename"=>"attachment.pdf", "totalBytes"=>4911, "receivedBytes"=>4911, "state"=>"completed"}]
717
- ```
718
-
719
- #### wait(timeout)
720
-
721
- Waits until the download is finished.
722
-
723
- ```ruby
724
- page.go_to("http://localhost/attachment.pdf")
725
- page.downloads.wait
726
- ```
727
-
728
- or
729
-
730
- ```ruby
731
- page.go_to("http://localhost/page")
732
- page.downloads.wait { page.at_css("#download").click }
733
- ```
734
-
735
- #### set_behavior(\*\*options)
736
-
737
- Sets behavior in case of file to be downloaded.
738
-
739
- * options `Hash`
740
- * :save_path `String` absolute path of where to store the file
741
- * :behavior `Symbol` `deny | allow | allowAndName | default`, `allow` by default
742
-
743
- ```ruby
744
- page.go_to("https://example.com/")
745
- page.downloads.set_behavior(save_path: "/tmp", behavior: :allow)
746
- ```
747
-
748
-
749
- ## Proxy
750
-
751
- You can set a proxy with a `:proxy` option:
752
-
753
- ```ruby
754
- Ferrum::Browser.new(proxy: { host: "x.x.x.x", port: "8800", user: "user", password: "pa$$" })
755
- ```
756
-
757
- `:bypass` can specify semi-colon-separated list of hosts for which proxy shouldn't be used:
758
-
759
- ```ruby
760
- Ferrum::Browser.new(proxy: { host: "x.x.x.x", port: "8800", bypass: "*.google.com;*foo.com" })
761
- ```
762
-
763
- In general passing a proxy option when instantiating a browser results in a browser running with proxy command line
764
- flags, so that it affects all pages and contexts. You can create a page in a new context which can use its own proxy
765
- settings:
766
-
767
- ```ruby
768
- browser = Ferrum::Browser.new
769
-
770
- browser.create_page(proxy: { host: "x.x.x.x", port: 31337, user: "user", password: "password" }) do |page|
771
- page.go_to("https://api.ipify.org?format=json")
772
- page.body # => "x.x.x.x"
773
- end
774
-
775
- browser.create_page(proxy: { host: "y.y.y.y", port: 31337, user: "user", password: "password" }) do |page|
776
- page.go_to("https://api.ipify.org?format=json")
777
- page.body # => "y.y.y.y"
778
- end
779
- ```
780
-
781
-
782
- ### Mouse
783
-
784
- `page.mouse`
785
-
786
- #### scroll_to(x, y)
787
-
788
- Scroll page to a given x, y
789
-
790
- * x `Integer` the pixel along the horizontal axis of the document that you
791
- want displayed in the upper left
792
- * y `Integer` the pixel along the vertical axis of the document that you want
793
- displayed in the upper left
794
-
795
- ```ruby
796
- page.go_to("https://www.google.com/search?q=Ruby+headless+driver+for+Capybara")
797
- page.mouse.scroll_to(0, 400)
798
- ```
799
-
800
- #### click(\*\*options) : `Mouse`
801
-
802
- Click given coordinates, fires mouse move, down and up events.
803
-
804
- * options `Hash`
805
- * :x `Integer`
806
- * :y `Integer`
807
- * :delay `Float` defaults to 0. Delay between mouse down and mouse up events
808
- * :button `Symbol` :left | :right, defaults to :left
809
- * :count `Integer` defaults to 1
810
- * :modifiers `Integer` bitfield for key modifiers. See`keyboard.modifiers`
811
-
812
- #### down(\*\*options) : `Mouse`
813
-
814
- Mouse down for given coordinates.
815
-
816
- * options `Hash`
817
- * :button `Symbol` :left | :right, defaults to :left
818
- * :count `Integer` defaults to 1
819
- * :modifiers `Integer` bitfield for key modifiers. See`keyboard.modifiers`
820
-
821
- #### up(\*\*options) : `Mouse`
822
-
823
- Mouse up for given coordinates.
824
-
825
- * options `Hash`
826
- * :button `Symbol` :left | :right, defaults to :left
827
- * :count `Integer` defaults to 1
828
- * :modifiers `Integer` bitfield for key modifiers. See`keyboard.modifiers`
829
-
830
- #### move(x:, y:, steps: 1) : `Mouse`
831
-
832
- Mouse move to given x and y.
833
-
834
- * options `Hash`
835
- * :x `Integer`
836
- * :y `Integer`
837
- * :steps `Integer` defaults to 1. Sends intermediate mousemove events.
838
-
839
- ### Keyboard
840
-
841
- `page.keyboard`
842
-
843
- #### down(key) : `Keyboard`
844
-
845
- Dispatches a keydown event.
846
-
847
- * key `String` | `Symbol` Name of key such as "a", :enter, :backspace
848
-
849
- #### up(key) : `Keyboard`
850
-
851
- Dispatches a keyup event.
852
-
853
- * key `String` | `Symbol` Name of key such as "b", :enter, :backspace
854
-
855
- #### type(\*keys) : `Keyboard`
856
-
857
- Sends a keydown, keypress/input, and keyup event for each character in the text.
858
-
859
- * text `String` | `Array<String> | Array<Symbol>` A text to type into a focused
860
- element, `[:Shift, "s"], "tring"`
861
-
862
- #### modifiers(keys) : `Integer`
863
-
864
- Returns bitfield for a given keys
865
-
866
- * keys `Array<Symbol>` :alt | :ctrl | :command | :shift
867
-
868
-
869
- ## Cookies
870
-
871
- `page.cookies`
872
-
873
- #### all : `Hash<String, Cookie>`
874
-
875
- Returns cookies hash
876
-
877
- ```ruby
878
- page.cookies.all # => {"NID"=>#<Ferrum::Cookies::Cookie:0x0000558624b37a40 @attributes={"name"=>"NID", "value"=>"...", "domain"=>".google.com", "path"=>"/", "expires"=>1583211046.575681, "size"=>178, "httpOnly"=>true, "secure"=>false, "session"=>false}>}
879
- ```
880
-
881
- #### [](value) : `Cookie`
882
-
883
- Returns cookie
884
-
885
- * value `String`
886
-
887
- ```ruby
888
- page.cookies["NID"] # => <Ferrum::Cookies::Cookie:0x0000558624b67a88 @attributes={"name"=>"NID", "value"=>"...", "domain"=>".google.com", "path"=>"/", "expires"=>1583211046.575681, "size"=>178, "httpOnly"=>true, "secure"=>false, "session"=>false}>
889
- ```
890
-
891
- #### set(value) : `Boolean`
892
-
893
- Sets a cookie
894
-
895
- * value `Hash`
896
- * :name `String`
897
- * :value `String`
898
- * :domain `String`
899
- * :expires `Integer`
900
- * :samesite `String`
901
- * :httponly `Boolean`
902
-
903
- ```ruby
904
- page.cookies.set(name: "stealth", value: "omg", domain: "google.com") # => true
905
- ```
906
-
907
- * value `Cookie`
908
-
909
- ```ruby
910
- nid_cookie = page.cookies["NID"] # => <Ferrum::Cookies::Cookie:0x0000558624b67a88>
911
- page.cookies.set(nid_cookie) # => true
912
- ```
913
-
914
- #### remove(\*\*options) : `Boolean`
915
-
916
- Removes given cookie
917
-
918
- * options `Hash`
919
- * :name `String`
920
- * :domain `String`
921
- * :url `String`
922
-
923
- ```ruby
924
- page.cookies.remove(name: "stealth", domain: "google.com") # => true
925
- ```
926
-
927
- #### clear : `Boolean`
928
-
929
- Removes all cookies for current page
930
-
931
- ```ruby
932
- page.cookies.clear # => true
933
- ```
934
-
935
- #### store(path) : `Boolean`
936
-
937
- Stores all cookies of current page in a file.
938
-
939
- ```ruby
940
- # Cookies are saved into cookies.yml
941
- page.cookies.store # => 15657
942
- ```
943
-
944
- #### load(path) : `Boolean`
945
-
946
- Loads all cookies from the file and sets them for current page.
947
-
948
- ```ruby
949
- # Cookies are loaded from cookies.yml
950
- page.cookies.load # => true
951
- ```
952
-
953
-
954
- ## Headers
955
-
956
- `page.headers`
957
-
958
- #### get : `Hash`
959
-
960
- Get all headers
961
-
962
- #### set(headers) : `Boolean`
963
-
964
- Set given headers. Eventually clear all headers and set given ones.
965
-
966
- * headers `Hash` key-value pairs for example `"User-Agent" => "Browser"`
967
-
968
- #### add(headers) : `Boolean`
969
-
970
- Adds given headers to already set ones.
971
-
972
- * headers `Hash` key-value pairs for example `"Referer" => "http://example.com"`
973
-
974
- #### clear : `Boolean`
975
-
976
- Clear all headers.
977
-
978
-
979
- ## JavaScript
980
-
981
- #### evaluate(expression, \*args)
982
-
983
- Evaluate and return result for given JS expression
984
-
985
- * expression `String` should be valid JavaScript
986
- * args `Object` you can pass arguments, though it should be a valid `Node` or a
987
- simple value.
988
-
989
- ```ruby
990
- page.evaluate("[window.scrollX, window.scrollY]")
991
- ```
992
-
993
- #### evaluate_async(expression, wait_time, \*args)
994
-
995
- Evaluate asynchronous expression and return result
996
-
997
- * expression `String` should be valid JavaScript
998
- * wait_time How long we should wait for Promise to resolve or reject
999
- * args `Object` you can pass arguments, though it should be a valid `Node` or a
1000
- simple value.
1001
-
1002
- ```ruby
1003
- page.evaluate_async(%(arguments[0]({foo: "bar"})), 5) # => { "foo" => "bar" }
1004
- ```
1005
-
1006
- #### execute(expression, \*args)
1007
-
1008
- Execute expression. Doesn't return the result
1009
-
1010
- * expression `String` should be valid JavaScript
1011
- * args `Object` you can pass arguments, though it should be a valid `Node` or a
1012
- simple value.
1013
-
1014
- ```ruby
1015
- page.execute(%(1 + 1)) # => true
1016
- ```
1017
-
1018
- #### evaluate_on_new_document(expression)
1019
-
1020
- Evaluate JavaScript to modify things before a page load
1021
-
1022
- * expression `String` should be valid JavaScript
1023
-
1024
- ```ruby
1025
- browser.evaluate_on_new_document <<~JS
1026
- Object.defineProperty(navigator, "languages", {
1027
- get: function() { return ["tlh"]; }
1028
- });
1029
- JS
1030
- ```
1031
-
1032
- #### add_script_tag(\*\*options) : `Boolean`
1033
-
1034
- * options `Hash`
1035
- * :url `String`
1036
- * :path `String`
1037
- * :content `String`
1038
- * :type `String` - `text/javascript` by default
1039
-
1040
- ```ruby
1041
- page.add_script_tag(url: "http://example.com/stylesheet.css") # => true
1042
- ```
1043
-
1044
- #### add_style_tag(\*\*options) : `Boolean`
1045
-
1046
- * options `Hash`
1047
- * :url `String`
1048
- * :path `String`
1049
- * :content `String`
1050
-
1051
- ```ruby
1052
- page.add_style_tag(content: "h1 { font-size: 40px; }") # => true
1053
-
1054
- ```
1055
- #### bypass_csp(\*\*options) : `Boolean`
1056
-
1057
- * options `Hash`
1058
- * :enabled `Boolean`, `true` by default
1059
-
1060
- ```ruby
1061
- page.bypass_csp # => true
1062
- page.go_to("https://github.com/ruby-concurrency/concurrent-ruby/blob/master/docs-source/promises.in.md")
1063
- page.refresh
1064
- page.add_script_tag(content: "window.__injected = 42")
1065
- page.evaluate("window.__injected") # => 42
1066
- ```
1067
-
1068
-
1069
- ## Emulation
1070
-
1071
- #### disable_javascript
1072
-
1073
- Disables Javascripts from the loaded HTML source.
1074
- You can still evaluate JavaScript with `evaluate` or `execute`.
1075
- Returns nothing.
1076
-
1077
- ```ruby
1078
- page.disable_javascript
1079
- ```
1080
-
1081
-
1082
- #### set_viewport
1083
-
1084
- Overrides device screen dimensions and emulates viewport.
1085
-
1086
- * options `Hash`
1087
- * :width `Integer`, viewport width. `0` by default
1088
- * :height `Integer`, viewport height. `0` by default
1089
- * :scale_factor `Float`, device scale factor. `0` by default
1090
- * :mobile `Boolean`, whether to emulate mobile device. `false` by default
1091
-
1092
- ```ruby
1093
- page.set_viewport(width: 1000, height: 600, scale_factor: 3)
1094
- ```
1095
-
1096
-
1097
- ## Frames
1098
-
1099
- #### frames : `Array[Frame] | []`
1100
-
1101
- Returns all the frames current page have.
1102
-
1103
- ```ruby
1104
- page.go_to("https://www.w3schools.com/tags/tag_frame.asp")
1105
- page.frames # =>
1106
- # [
1107
- # #<Ferrum::Frame @id="C6D104CE454A025FBCF22B98DE612B12" @parent_id=nil @name=nil @state=:stopped_loading @execution_id=1>,
1108
- # #<Ferrum::Frame @id="C09C4E4404314AAEAE85928EAC109A93" @parent_id="C6D104CE454A025FBCF22B98DE612B12" @state=:stopped_loading @execution_id=2>,
1109
- # #<Ferrum::Frame @id="2E9C7F476ED09D87A42F2FEE3C6FBC3C" @parent_id="C6D104CE454A025FBCF22B98DE612B12" @state=:stopped_loading @execution_id=3>,
1110
- # ...
1111
- # ]
1112
- ```
1113
-
1114
- #### main_frame : `Frame`
1115
-
1116
- Returns page's main frame, the top of the tree and the parent of all frames.
1117
-
1118
- #### frame_by(\*\*options) : `Frame | nil`
1119
-
1120
- Find frame by given options.
1121
-
1122
- * options `Hash`
1123
- * :id `String` - Unique frame's id that browser provides
1124
- * :name `String` - Frame's name if there's one
1125
-
1126
- ```ruby
1127
- page.frame_by(id: "C6D104CE454A025FBCF22B98DE612B12")
1128
- ```
1129
-
1130
-
1131
- ## Frame
1132
-
1133
- #### id : `String`
1134
-
1135
- Frame's unique id.
1136
-
1137
- #### parent_id : `String | nil`
1138
-
1139
- Parent frame id if this one is nested in another one.
1140
-
1141
- #### parent : `Frame | nil`
1142
-
1143
- Parent frame if this one is nested in another one.
1144
-
1145
- #### frame_element : `Node | nil`
1146
-
1147
- Returns the element in which the window is embedded.
1148
-
1149
- #### execution_id : `Integer`
1150
-
1151
- Execution context id which is used by JS, each frame has its own context in
1152
- which JS evaluates.
1153
-
1154
- #### name : `String | nil`
1155
-
1156
- If frame was given a name it should be here.
1157
-
1158
- #### state : `Symbol | nil`
1159
-
1160
- One of the states frame's in:
1161
-
1162
- * `:started_loading`
1163
- * `:navigated`
1164
- * `:stopped_loading`
1165
-
1166
- #### url : `String`
1167
-
1168
- Returns current frame's location href.
1169
-
1170
- ```ruby
1171
- page.go_to("https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe")
1172
- frame = page.frames[1]
1173
- frame.url # => https://interactive-examples.mdn.mozilla.net/pages/tabbed/iframe.html
1174
- ```
1175
-
1176
- #### title
1177
-
1178
- Returns current frame's title.
1179
-
1180
- ```ruby
1181
- page.go_to("https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe")
1182
- frame = page.frames[1]
1183
- frame.title # => HTML Demo: <iframe>
1184
- ```
1185
-
1186
- #### main? : `Boolean`
1187
-
1188
- If current frame is the main frame of the page (top of the tree).
1189
-
1190
- ```ruby
1191
- page.go_to("https://www.w3schools.com/tags/tag_frame.asp")
1192
- frame = page.frame_by(id: "C09C4E4404314AAEAE85928EAC109A93")
1193
- frame.main? # => false
1194
- ```
1195
-
1196
- #### current_url : `String`
1197
-
1198
- Returns current frame's top window location href.
1199
-
1200
- ```ruby
1201
- page.go_to("https://www.w3schools.com/tags/tag_frame.asp")
1202
- frame = page.frame_by(id: "C09C4E4404314AAEAE85928EAC109A93")
1203
- frame.current_url # => "https://www.w3schools.com/tags/tag_frame.asp"
1204
- ```
1205
-
1206
- #### current_title : `String`
1207
-
1208
- Returns current frame's top window title.
1209
-
1210
- ```ruby
1211
- page.go_to("https://www.w3schools.com/tags/tag_frame.asp")
1212
- frame = page.frame_by(id: "C09C4E4404314AAEAE85928EAC109A93")
1213
- frame.current_title # => "HTML frame tag"
1214
- ```
1215
-
1216
- #### body : `String`
1217
-
1218
- Returns current frame's html.
1219
-
1220
- ```ruby
1221
- page.go_to("https://www.w3schools.com/tags/tag_frame.asp")
1222
- frame = page.frame_by(id: "C09C4E4404314AAEAE85928EAC109A93")
1223
- frame.body # => "<html><head></head><body></body></html>"
1224
- ```
1225
-
1226
- #### doctype
1227
-
1228
- Returns current frame's doctype.
1229
-
1230
- ```ruby
1231
- page.go_to("https://www.w3schools.com/tags/tag_frame.asp")
1232
- page.main_frame.doctype # => "<!DOCTYPE html>"
1233
- ```
1234
-
1235
- #### content = html
1236
-
1237
- Sets a content of a given frame.
1238
-
1239
- * html `String`
1240
-
1241
- ```ruby
1242
- page.go_to("https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe")
1243
- frame = page.frames[1]
1244
- frame.body # <html lang="en"><head><style>body {transition: opacity ease-in 0.2s; }...
1245
- frame.content = "<html><head></head><body><p>lol</p></body></html>"
1246
- frame.body # => <html><head></head><body><p>lol</p></body></html>
1247
- ```
1248
-
1249
-
1250
- ## Dialogs
1251
-
1252
- #### accept(text)
1253
-
1254
- Accept dialog with given text or default prompt if applicable
1255
-
1256
- * text `String`
1257
-
1258
- #### dismiss
1259
-
1260
- Dismiss dialog
1261
-
1262
- ```ruby
1263
- page.on(:dialog) do |dialog|
1264
- if dialog.match?(/bla-bla/)
1265
- dialog.accept
1266
- else
1267
- dialog.dismiss
1268
- end
1269
- end
1270
- page.go_to("https://google.com")
1271
- ```
1272
-
1273
-
1274
- ## Animation
1275
-
1276
- You can slow down or speed up CSS animations.
1277
-
1278
- #### playback_rate : `Integer`
1279
-
1280
- Returns playback rate for CSS animations, defaults to `1`.
1281
-
1282
-
1283
- #### playback_rate = value
1284
-
1285
- Sets playback rate of CSS animations
1286
-
1287
- * value `Integer`
1288
-
1289
- ```ruby
1290
- page.playback_rate = 2000
1291
- page.go_to("https://google.com")
1292
- page.playback_rate # => 2000
1293
- ```
1294
-
1295
-
1296
- ## Node
1297
-
1298
- #### node? : `Boolean`
1299
- #### frame_id
1300
- #### frame : `Frame`
1301
-
1302
- Returns [Frame](https://github.com/rubycdp/ferrum#frame) object for current node, you can keep using
1303
- [Finders](https://github.com/rubycdp/ferrum#Finders) for that object:
1304
-
1305
- ```ruby
1306
- frame = page.at_xpath("//iframe").frame # => Frame
1307
- frame.at_css("//a[text() = 'Log in']") # => Node
1308
- ```
1309
-
1310
- #### focus
1311
- #### focusable?
1312
- #### moving? : `Boolean`
1313
- #### wait_for_stop_moving
1314
- #### blur
1315
- #### type
1316
- #### click
1317
- #### hover
1318
- #### select_file
1319
- #### at_xpath
1320
- #### at_css
1321
- #### xpath
1322
- #### css
1323
- #### text
1324
- #### inner_text
1325
- #### value
1326
- #### property
1327
- #### attribute
1328
- #### evaluate
1329
- #### selected : `Array<Node>`
1330
- #### select
1331
- #### scroll_into_view
1332
- #### in_viewport?(of: `Node | nil`) : `Boolean`
1333
- #### remove
1334
- #### exists?
1335
-
1336
- (chainable) Selects options by passed attribute.
1337
-
1338
- ```ruby
1339
- page.at_xpath("//*[select]").select(["1"]) # => Node (select)
1340
- page.at_xpath("//*[select]").select(["text"], by: :text) # => Node (select)
1341
- ```
1342
-
1343
- Accept string, array or strings:
1344
- ```ruby
1345
- page.at_xpath("//*[select]").select("1")
1346
- page.at_xpath("//*[select]").select("1", "2")
1347
- page.at_xpath("//*[select]").select(["1", "2"])
1348
- ```
1349
-
1350
-
1351
- ## Tracing
1352
-
1353
- You can use `tracing.record` to create a trace file which can be opened in Chrome DevTools or
1354
- [timeline viewer](https://chromedevtools.github.io/timeline-viewer/).
1355
-
1356
- ```ruby
1357
- page.tracing.record(path: "trace.json") do
1358
- page.go_to("https://www.google.com")
1359
- end
1360
- ```
1361
-
1362
- #### tracing.record(\*\*options) : `String`
1363
-
1364
- Accepts block, records trace and by default returns trace data from `Tracing.tracingComplete` event as output. When
1365
- `path` is specified returns `true` and stores trace data into file.
1366
-
1367
- * options `Hash`
1368
- * :path `String` save data on the disk, `nil` by default
1369
- * :encoding `Symbol` `:base64` | `:binary` encode output as Base64 or plain text. `:binary` by default
1370
- * :timeout `Float` wait until file streaming finishes in the specified time or raise error, defaults to `nil`
1371
- * :screenshots `Boolean` capture screenshots in the trace, `false` by default
1372
- * :trace_config `Hash<String, Object>` config for
1373
- [trace](https://chromedevtools.github.io/devtools-protocol/tot/Tracing/#type-TraceConfig), for categories
1374
- see [getCategories](https://chromedevtools.github.io/devtools-protocol/tot/Tracing/#method-getCategories),
1375
- only one trace config can be active at a time per browser.
1376
-
1377
-
1378
- ## Clean Up
1379
-
1380
- #### reset
1381
-
1382
- Closes browser tabs opened by the `Browser` instance.
1383
-
1384
- ```ruby
1385
- # connect to a long-running Chrome process
1386
- browser = Ferrum::Browser.new(url: "http://localhost:9222")
1387
-
1388
- browser.go_to("https://github.com/")
1389
-
1390
- # clean up, lest the tab stays there hanging forever
1391
- browser.reset
1392
-
1393
- browser.quit
1394
- ```
1395
-
1396
-
1397
- ## Thread safety ##
1398
-
1399
- Ferrum is fully thread-safe. You can create one browser or a few as you wish and
1400
- start playing around using threads. Example below shows how to create a few pages
1401
- which share the same context. Context is similar to an incognito profile but you
1402
- can have more than one, think of it like it's independent browser session:
1403
-
1404
- ```ruby
1405
- browser = Ferrum::Browser.new
1406
- context = browser.contexts.create
1407
-
1408
- t1 = Thread.new(context) do |c|
1409
- page = c.create_page
1410
- page.go_to("https://www.google.com/search?q=Ruby+headless+driver+for+Capybara")
1411
- page.screenshot(path: "t1.png")
1412
- end
1413
-
1414
- t2 = Thread.new(context) do |c|
1415
- page = c.create_page
1416
- page.go_to("https://www.google.com/search?q=Ruby+static+typing")
1417
- page.screenshot(path: "t2.png")
1418
- end
1419
-
1420
- t1.join
1421
- t2.join
1422
-
1423
- context.dispose
1424
- browser.quit
1425
- ```
1426
-
1427
- or you can create two independent contexts:
1428
-
1429
- ```ruby
1430
- browser = Ferrum::Browser.new
1431
-
1432
- t1 = Thread.new(browser) do |b|
1433
- context = b.contexts.create
1434
- page = context.create_page
1435
- page.go_to("https://www.google.com/search?q=Ruby+headless+driver+for+Capybara")
1436
- page.screenshot(path: "t1.png")
1437
- context.dispose
1438
- end
1439
-
1440
- t2 = Thread.new(browser) do |b|
1441
- context = b.contexts.create
1442
- page = context.create_page
1443
- page.go_to("https://www.google.com/search?q=Ruby+static+typing")
1444
- page.screenshot(path: "t2.png")
1445
- context.dispose
1446
- end
1447
-
1448
- t1.join
1449
- t2.join
1450
-
1451
- browser.quit
1452
- ```
1453
-
1454
94
  ## Development
1455
95
 
1456
96
  After checking out the repo, run `bundle install` to install dependencies.