Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
## [Unreleased](https://github.com/rubycdp/ferrum/compare/v0.20.1...main) ##

### Added
- `cookies[name, domain:, path:]` picks one of the cookies sharing a name
- `context.cookies` with every cookie in the context whatever its domain, `browser.cookies` is the default context's
- `network.emulate(user_agent:, accept_language:, platform:, metadata:)` overrides what the page reports in the request
headers and in `navigator`, `platform` had no way to be set before, and `metadata` keeps the client hints Chrome
otherwise stops sending once the user agent is overridden
- `go_to(url, on_timeout: :raise)` raises `NavigationTimeoutError`, a `TimeoutError` naming the requests still
pending, when the page doesn't load in time

### Changed
- `page.cookies` is the cookies the page sees, the ones sent to its URL and its frames' URLs, or `each(urls:)`. It
returned every cookie in the context, use `browser.cookies` or `browser.contexts[page.context_id].cookies` for that.
`set` and `remove` default to the page's host, `clear` keeps other domains, `store`/`load` are on the context only
- `NoSuchPageError` is a `NoSuchTargetError` now, so rescuing `NoSuchTargetError` covers a target Chrome no longer
knows, like a closed page, as well as one Ferrum doesn't track
- Every script runs the new way: keyword arguments are named parameters, promises and thenables are awaited, all
statements run and the last one's value is returned, and `Node#evaluate` returns nodes and dates, see
[upgrading](docs/13-javascript.md#upgrading-to-021) [#673]

### Deprecated
- `cookies.all`, will be removed in 0.22. Keyed by name, it kept one of the cookies sharing a name, use `to_a`, or
`map(&:name)` for the names
- `current_url` and `current_title` on pages and frames, will be removed in 0.22. Use `url` and `title`, or
`frame.page.url` for the top window from a frame, `frame.url` is the frame's own
- `:js_errors`, will be removed in 0.22. Uncaught exceptions are in `page.console` with `source: :exception`, use it or
Expand Down
127 changes: 100 additions & 27 deletions docs/11-cookies.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,84 +4,157 @@ sidebar_position: 11

# Cookies

`page.cookies`
Cookies belong to a [context](https://github.com/rubycdp/ferrum#contexts), and every page in it shares them.
`context.cookies` has all of them, whatever their domain. `browser.cookies` is the cookies of the default context.
`page.cookies` narrows them down to what the page sees: the cookies sent to its URL and the URLs of its frames.

#### all : `Hash<String, Cookie>`
```ruby
# After logging in on example.com through sso.example.com
browser.cookies.map(&:name) # => ["session", "sso_token"]
browser.page.cookies.map(&:name) # => ["session"]
```

## Context cookies

`browser.cookies`, `context.cookies`

Returns cookies hash
#### each : `Enumerator<Cookie>`

All cookies in the context. Cookies are `Enumerable`, so `to_a`, `select`, `map` and the rest work too.

```ruby
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}>}
browser.cookies.to_a # => [#<Ferrum::Cookies::Cookie:0x0000558624b37a40 @attributes={"name"=>"NID", "value"=>"...", "domain"=>".google.com", "path"=>"/", "expires"=>1583211046.575681, "size"=>178, "httpOnly"=>true, "secure"=>false, "session"=>false}>]
browser.cookies.select { |cookie| cookie.domain == ".google.com" }
```

#### \[\](value) : `Cookie`
#### all : `Hash<String, Cookie>`

Deprecated, will be removed in 0.22. Cookies keyed by name, so of the cookies sharing a name only one is kept. Use
`to_a`, or `map(&:name)` for the names.

Returns cookie
#### \[\](name, domain: `String`, path: `String`) : `Cookie` | `nil`

* value `String`
The first cookie with the given name. A name can be used by several cookies, on different domains or paths, `domain`
and `path` pick one of them. Both are exact, a cookie sent to every subdomain has a domain with a leading dot.

* name `String`
* domain `String`
* path `String`

```ruby
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}>
browser.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}>
browser.cookies["id", domain: "www.example.com", path: "/app"]
```

#### set(value) : `Boolean`
#### set(value) : `true`

Sets a cookie
Sets a cookie, for the `:base_url` host unless `:domain` or `:url` is given.

* value `Hash`
* :name `String`
* :value `String`
* :domain `String`
* :url `String`
* :path `String`
* :expires `Integer`
* :samesite `String`
* :httponly `Boolean`
* :secure `Boolean`

```ruby
page.cookies.set(name: "stealth", value: "omg", domain: "google.com") # => true
browser.cookies.set(name: "stealth", value: "omg", domain: "google.com") # => true
```

* value `Cookie`

```ruby
nid_cookie = page.cookies["NID"] # => <Ferrum::Cookies::Cookie:0x0000558624b67a88>
page.cookies.set(nid_cookie) # => true
nid_cookie = browser.cookies["NID"] # => <Ferrum::Cookies::Cookie:0x0000558624b67a88>
browser.cookies.set(nid_cookie) # => true
```

#### remove(\*\*options) : `Boolean`
#### remove(\*\*options) : `true`

Removes given cookie
Removes the cookies with the given name, on the exact domain, the `:base_url` host by default, or the ones sent to the
URL.

* options `Hash`
* :name `String`
* :domain `String`
* :url `String`
* :path `String`

```ruby
page.cookies.remove(name: "stealth", domain: "google.com") # => true
browser.cookies.remove(name: "stealth", domain: "google.com") # => true
browser.cookies.remove(name: "sso_token", url: "https://sso.example.com") # => true
```

#### clear : `Boolean`
#### clear : `true`

Removes all cookies for current page
Removes all cookies in the context.

```ruby
page.cookies.clear # => true
browser.cookies.clear # => true
```

#### store(path) : `Boolean`
#### store(path) : `Integer`

Stores all cookies of current page in a file.
Stores all cookies in the context in a file, `cookies.yml` by default.

```ruby
# Cookies are saved into cookies.yml
page.cookies.store # => 15657
browser.cookies.store # => 15657
```

#### load(path) : `Boolean`
#### load(path) : `true`

Loads all cookies from the file and sets them for current page.
Loads the cookies from a file, `cookies.yml` by default, and sets them in the context.

```ruby
# Cookies are loaded from cookies.yml
page.cookies.load # => true
browser.cookies.load # => true
```

## Page cookies

`page.cookies`

#### each(urls: `Array<String>`) : `Enumerator<Cookie>`

The cookies sent to the page's URL and the URLs of its frames, or to the given URLs. Nothing before the page has
loaded a URL. `all` is deprecated here too.

```ruby
page.cookies.map(&:name) # => ["session"]
page.cookies.each(urls: ["https://sso.example.com"]).map(&:name) # => ["sso_token"]
```

#### \[\](name, domain: `String`, path: `String`) : `Cookie` | `nil`

The page's first cookie with the given name, narrowed down like the context's.

```ruby
page.cookies["NID"] # => <Ferrum::Cookies::Cookie:0x0000558624b67a88>
```

#### set(value) : `Boolean`

Sets a cookie, takes the same options as the context's `set`. Without `:domain` or `:url` it's set for the page's host,
or the `:base_url` host before the page has loaded anything.

```ruby
page.cookies.set(name: "stealth", value: "omg") # => true
```

#### remove(\*\*options) : `true`

Removes the cookies with the given name, on the page's host unless `:domain` or `:url` is given.

```ruby
page.cookies.remove(name: "stealth") # => true
```

#### clear : `true`

Removes the page's cookies, the ones `each` yields. Cookies on other domains stay.

```ruby
page.cookies.clear # => true
```
5 changes: 3 additions & 2 deletions lib/ferrum/browser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,13 @@ class Browser
extend Forwardable

delegate %i[default_context] => :contexts
delegate %i[targets create_target page pages windows workers service_workers attach_target] => :default_context
delegate %i[targets create_target page pages windows workers service_workers attach_target
cookies] => :default_context
delegate %i[go_to goto go back forward refresh reload stop wait_for_reload
wait_for_navigation wait_for_navigation!
at_css at_xpath css xpath wait_for_selector current_url current_title url title
body doctype content=
headers cookies network console accessibility downloads
headers network console accessibility downloads
mouse keyboard
screenshot pdf mhtml viewport_size device_pixel_ratio
start_screencast stop_screencast
Expand Down
7 changes: 7 additions & 0 deletions lib/ferrum/context.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ def default_target
@default_target ||= create_target
end

# The cookies of the context, which every page in it shares.
#
# @return [Cookies]
def cookies
@cookies ||= Cookies.new(@client, @id)
end

# Connects to and returns the {#default_target}'s page.
#
# @return [Page]
Expand Down
Loading
Loading