Class: Obxcura::Page
- Inherits:
-
Object
- Object
- Obxcura::Page
- Extended by:
- Forwardable
- Defined in:
- lib/obxcura/page.rb
Overview
A single page/tab: a CDP target with its own attached session. Created via Browser#create_page — you rarely instantiate this directly.
Page owns the CDP session, navigation, in-page POSTs and lifecycle. Reading
the DOM and executing JS belong to its main Frame (see Frame::DOM and
Frame::Runtime), and Page delegates those (#evaluate, #html, #title,
#at_css, ...).
Obscura has no paint engine, so there is deliberately no screenshot API.
Instance Attribute Summary collapse
- #client ⇒ String, ... readonly
- #frame ⇒ String, ... readonly
- #session_id ⇒ String, ... readonly
- #target_id ⇒ String, ... readonly
Instance Method Summary collapse
-
#close ⇒ Obxcura::Page?
Close this page's target and stop listening for its events.
-
#close_connection ⇒ void
Drop the underlying WebSocket connection (affects the whole browser).
-
#command(method, params = {}) ⇒ Hash
Send a CDP command scoped to this page's session.
-
#cookies ⇒ Array<Hash>
The page's cookies as CDP cookie hashes.
-
#goto(url) ⇒ self
(also: #go_to)
Navigate to
urland block until the page's load event fires. -
#initialize(browser, target_id:, session_id:) ⇒ Page
constructor
A new instance of Page.
-
#refresh ⇒ Hash
(also: #reload)
Reload the page and block until it loads again.
-
#xhr_post(url, payload, content_type, headers, timeout: nil) ⇒ Hash
POST via XMLHttpRequest from the page context.
Constructor Details
#initialize(browser, target_id:, session_id:) ⇒ Page
Returns a new instance of Page.
39 40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/obxcura/page.rb', line 39 def initialize(browser, target_id:, session_id:) @browser = browser @client = browser.client @target_id = target_id @session_id = session_id @frame = Frame.new(target_id, self) @load_queue = Queue.new @network_log = [] @network_mutex = Mutex.new @client.subscribe(@session_id) { |method, params| dispatch_event(method, params) } end |
Instance Attribute Details
#client ⇒ String, ... (readonly)
30 31 32 |
# File 'lib/obxcura/page.rb', line 30 def client @client end |
#frame ⇒ String, ... (readonly)
30 31 32 |
# File 'lib/obxcura/page.rb', line 30 def frame @frame end |
#session_id ⇒ String, ... (readonly)
30 31 32 |
# File 'lib/obxcura/page.rb', line 30 def session_id @session_id end |
#target_id ⇒ String, ... (readonly)
30 31 32 |
# File 'lib/obxcura/page.rb', line 30 def target_id @target_id end |
Instance Method Details
#close ⇒ Obxcura::Page?
Close this page's target and stop listening for its events.
68 69 70 71 72 73 74 75 76 |
# File 'lib/obxcura/page.rb', line 68 def close @client.unsubscribe(@session_id) @client.command("Network.clearBrowserCookies", { targetId: @target_id }) @client.command("Target.closeTarget", { targetId: @target_id }) rescue ProtocolError # Target already gone — nothing to do. ensure @browser.remove_page(self) end |
#close_connection ⇒ void
This method returns an undefined value.
Drop the underlying WebSocket connection (affects the whole browser).
81 82 83 |
# File 'lib/obxcura/page.rb', line 81 def close_connection @client.close end |
#command(method, params = {}) ⇒ Hash
Send a CDP command scoped to this page's session.
104 105 106 |
# File 'lib/obxcura/page.rb', line 104 def command(method, params = {}) @client.command(method, params, session_id: @session_id) end |
#cookies ⇒ Array<Hash>
Returns the page's cookies as CDP cookie hashes.
95 96 97 |
# File 'lib/obxcura/page.rb', line 95 def command("Storage.getCookies")["cookies"] end |
#goto(url) ⇒ self Also known as: go_to
Navigate to url and block until the page's load event fires. Aliased as
go_to.
57 58 59 60 61 62 |
# File 'lib/obxcura/page.rb', line 57 def goto(url) @load_queue = Queue.new command("Page.navigate", url: url) wait_for_load self end |
#refresh ⇒ Hash Also known as: reload
Reload the page and block until it loads again. Aliased as reload.
88 89 90 91 |
# File 'lib/obxcura/page.rb', line 88 def refresh command("Page.reload") wait_for_load end |
#xhr_post(url, payload, content_type, headers, timeout: nil) ⇒ Hash
POST via XMLHttpRequest from the page context. Obscura routes XHR but not fetch, so this is the reliable POST path. All values cross as arguments, never interpolated into the JS. Returns { status, ok, body } on any HTTP reply (including 4xx/5xx). A transport failure — the request never reached the server (blocked by CORS / private-network SSRF guard, mixed origin, or a dead host) — raises ConnectionError instead of silently returning nil.
timeout (seconds) bounds how long we wait for the reply. If the server
accepts the connection but never answers the XHR (some anti-bot endpoints
tarpit non-stealth clients), the wait ends with a TimeoutError that points
at the likely cause. Note: Obscura ignores XMLHttpRequest#timeout, so the
effective bound is this CDP-level one.
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
# File 'lib/obxcura/page.rb', line 129 def xhr_post(url, payload, content_type, headers, timeout: nil) result = evaluate_func(<<~JS, url, payload, content_type, headers, timeout:) function(url, payload, contentType, headers) { return new Promise((resolve) => { const x = new XMLHttpRequest(); x.onreadystatechange = () => { if (x.readyState === 4) { resolve({ status: x.status, ok: x.status >= 200 && x.status < 300, body: x.responseText }); } }; x.onerror = () => resolve({ error: "network error or request blocked (CORS / private network / mixed origin)" }); try { x.open("POST", url, true); x.setRequestHeader("Content-Type", contentType); Object.keys(headers).forEach((k) => x.setRequestHeader(k, headers[k])); x.send(payload); } catch (e) { resolve({ error: String(e) }); } }); } JS if result.nil? || result["error"] reason = result&.dig("error") || "no response (request blocked or never settled)" raise ConnectionError, "POST #{url} failed: #{reason}" end result rescue TimeoutError raise TimeoutError, "POST #{url} did not complete in time. The server accepted the connection " \ "but never answered the XHR — likely anti-bot tarpitting. Try submitting the " \ "real form with #type/#submit, run `obscura serve --stealth`, or pass a larger timeout:." end |