Class: Bidi2pdf::Bidi::BrowserTab
- Inherits:
-
Object
- Object
- Bidi2pdf::Bidi::BrowserTab
- Includes:
- JsLoggerHelper
- Defined in:
- lib/bidi2pdf/bidi/browser_tab.rb
Overview
Represents a browser tab for managing interactions and communication using the Bidi2pdf library. This class provides methods for creating browser tabs, managing cookies, navigating to URLs, executing scripts, handling network events, and general tab lifecycle management.
Instance Attribute Summary collapse
-
#browsing_context_id ⇒ String
readonly
The browsing context ID.
-
#client ⇒ Object
readonly
The WebSocket client.
-
#logger_events ⇒ LoggerEvents
readonly
The logger events handler.
-
#navigation_failed_events ⇒ NavigationFailedEvents
readonly
The navigation failed events handler.
-
#network_events ⇒ NetworkEvents
readonly
The network events handler.
-
#open ⇒ Boolean
readonly
Whether the tab is open.
-
#tabs ⇒ Array<BrowserTab>
readonly
The list of tabs.
-
#user_context_id ⇒ String
readonly
The user context ID.
Instance Method Summary collapse
-
#add_headers(headers:, url_patterns:) ⇒ AddHeadersInterceptor
Adds headers to requests in the browser tab.
-
#basic_auth(username:, password:, url_patterns:) ⇒ AuthInterceptor
Configures basic authentication for requests in the browser tab.
-
#close ⇒ Object
Closes the browser tab and its associated resources.
-
#create_browser_tab ⇒ BrowserTab
Creates a new browser tab.
-
#execute_script(script, wrap_in_promise: false) ⇒ Object
Executes a script in the browser tab.
-
#initialize(client, browsing_context_id, user_context_id) ⇒ BrowserTab
constructor
Initializes a new browser tab.
-
#inject_script(url: nil, content: nil, id: nil) ⇒ Object
Injects a JavaScript script element into the page, either from a URL or with inline content.
-
#inject_style(url: nil, content: nil, id: nil) ⇒ Object
Injects a CSS style element into the page, either from a URL or with inline content.
-
#log_network_traffic(format: :console, output: nil, print_options: { background: true }) {|pdf_base64| ... } ⇒ Object
Logs network traffic in the browser tab.
-
#navigate_to(url, wait: "complete") ⇒ Object
Navigates the browser tab to a specified URL.
-
#print(outputfile = nil, print_options: { background: true }) {|pdf_base64| ... } ⇒ String?
Prints the content of the browser tab.
-
#render_html_content(html_content, wait: "complete") ⇒ Object
Renders HTML content in the browser tab.
-
#set_cookie(name:, value:, domain:, path: "/", secure: true, http_only: false, same_site: "strict", ttl: 30) ⇒ Object
Sets a cookie in the browser tab.
-
#wait_until_network_idle(timeout: 10, poll_interval: 0.1) ⇒ Object
Waits until the network is idle in the browser tab.
-
#wait_until_page_loaded(check_script: nil) ⇒ Object
Waits until the page is fully loaded in the browser tab.
Constructor Details
#initialize(client, browsing_context_id, user_context_id) ⇒ BrowserTab
Initializes a new browser tab.
73 74 75 76 77 78 79 80 81 82 |
# File 'lib/bidi2pdf/bidi/browser_tab.rb', line 73 def initialize(client, browsing_context_id, user_context_id) @client = client @browsing_context_id = browsing_context_id @user_context_id = user_context_id @tabs = [] @network_events = NetworkEvents.new browsing_context_id @logger_events = LoggerEvents.new browsing_context_id = .new browsing_context_id @open = true end |
Instance Attribute Details
#browsing_context_id ⇒ String (readonly)
Returns The browsing context ID.
48 49 50 |
# File 'lib/bidi2pdf/bidi/browser_tab.rb', line 48 def browsing_context_id @browsing_context_id end |
#client ⇒ Object (readonly)
Returns The WebSocket client.
45 46 47 |
# File 'lib/bidi2pdf/bidi/browser_tab.rb', line 45 def client @client end |
#logger_events ⇒ LoggerEvents (readonly)
Returns The logger events handler.
63 64 65 |
# File 'lib/bidi2pdf/bidi/browser_tab.rb', line 63 def logger_events @logger_events end |
#navigation_failed_events ⇒ NavigationFailedEvents (readonly)
Returns The navigation failed events handler.
66 67 68 |
# File 'lib/bidi2pdf/bidi/browser_tab.rb', line 66 def end |
#network_events ⇒ NetworkEvents (readonly)
Returns The network events handler.
57 58 59 |
# File 'lib/bidi2pdf/bidi/browser_tab.rb', line 57 def network_events @network_events end |
#open ⇒ Boolean (readonly)
Returns Whether the tab is open.
60 61 62 |
# File 'lib/bidi2pdf/bidi/browser_tab.rb', line 60 def open @open end |
#tabs ⇒ Array<BrowserTab> (readonly)
Returns The list of tabs.
54 55 56 |
# File 'lib/bidi2pdf/bidi/browser_tab.rb', line 54 def tabs @tabs end |
#user_context_id ⇒ String (readonly)
Returns The user context ID.
51 52 53 |
# File 'lib/bidi2pdf/bidi/browser_tab.rb', line 51 def user_context_id @user_context_id end |
Instance Method Details
#add_headers(headers:, url_patterns:) ⇒ AddHeadersInterceptor
Adds headers to requests in the browser tab.
140 141 142 143 144 145 146 147 148 149 |
# File 'lib/bidi2pdf/bidi/browser_tab.rb', line 140 def add_headers( headers:, url_patterns: ) @header_interceptor = AddHeadersInterceptor.new( context: browsing_context_id, url_patterns: url_patterns, headers: headers ).tap { |interceptor| interceptor.register_with_client(client: client) } end |
#basic_auth(username:, password:, url_patterns:) ⇒ AuthInterceptor
Configures basic authentication for requests in the browser tab.
157 158 159 160 161 162 163 |
# File 'lib/bidi2pdf/bidi/browser_tab.rb', line 157 def basic_auth(username:, password:, url_patterns:) @basic_auth_interceptor = AuthInterceptor.new( context: browsing_context_id, url_patterns: url_patterns, username: username, password: password ).tap { |interceptor| interceptor.register_with_client(client: client) } end |
#close ⇒ Object
Closes the browser tab and its associated resources.
397 398 399 400 401 402 403 404 405 |
# File 'lib/bidi2pdf/bidi/browser_tab.rb', line 397 def close return unless open close_tabs remove_event_listeners close_context @open = false end |
#create_browser_tab ⇒ BrowserTab
Creates a new browser tab.
87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/bidi2pdf/bidi/browser_tab.rb', line 87 def create_browser_tab cmd = Bidi2pdf::Bidi::Commands::CreateTab.new(user_context_id: user_context_id) client.send_cmd_and_wait(cmd) do |response| tab_browsing_context_id = response["result"]["context"] BrowserTab.new(client, tab_browsing_context_id, user_context_id).tap do |tab| tabs << tab Bidi2pdf.logger.debug1 "Created new browser tab: #{tab.inspect}" end end end |
#execute_script(script, wrap_in_promise: false) ⇒ Object
Executes a script in the browser tab.
This method allows you to execute JavaScript code within the context of the browser tab. Optionally, the script can be wrapped in a JavaScript Promise to handle asynchronous operations.
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 |
# File 'lib/bidi2pdf/bidi/browser_tab.rb', line 258 def execute_script(script, wrap_in_promise: false) Bidi2pdf.notification_service.instrument("execute_script.bidi2pdf") do if wrap_in_promise script = " new Promise((resolve, reject) => {\n try {\n let result;\n\n \#{script}\n\n resolve(result);\n } catch (error) {\n reject(error);\n }\n });\n JS\n end\n\n cmd = Bidi2pdf::Bidi::Commands::ScriptEvaluate.new context: browsing_context_id, expression: script\n client.send_cmd_and_wait(cmd) do |response|\n Bidi2pdf.logger.debug2 \"Script Result: \#{response.inspect}\"\n\n response[\"result\"]\n end\n end\nend\n" |
#inject_script(url: nil, content: nil, id: nil) ⇒ Object
Injects a JavaScript script element into the page, either from a URL or with inline content.
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 |
# File 'lib/bidi2pdf/bidi/browser_tab.rb', line 291 def inject_script(url: nil, content: nil, id: nil) script_code = generate_script_element_code(url: url, content: content, id: id) response = execute_script(script_code) if response if response["type"] == "exception" handle_injection_exception(response, url, ScriptInjectionError) elsif response["type"] == "success" Bidi2pdf.logger.debug1 "Script injected successfully: #{response.inspect}" response else Bidi2pdf.logger.warn "Script injected unknown state: #{response.inspect}" response end else Bidi2pdf.logger.error "Failed to inject script: #{url || content}" raise ScriptInjectionError, "Failed to inject script: #{url || content}" end end |
#inject_style(url: nil, content: nil, id: nil) ⇒ Object
Injects a CSS style element into the page, either from a URL or with inline content.
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 |
# File 'lib/bidi2pdf/bidi/browser_tab.rb', line 317 def inject_style(url: nil, content: nil, id: nil) style_code = generate_style_element_code(url: url, content: content, id: id) response = execute_script(style_code) if response if response["type"] == "exception" handle_injection_exception(response, url, StyleInjectionError) elsif response["type"] == "success" Bidi2pdf.logger.debug1 "Style injected successfully: #{response.inspect}" response else Bidi2pdf.logger.warn "Style injection unknown state: #{response.inspect}" response end else Bidi2pdf.logger.error "Failed to inject style: #{url || content}" raise StyleInjectionError, "Failed to inject style: #{url || content}" end end |
#log_network_traffic(format: :console, output: nil, print_options: { background: true }) {|pdf_base64| ... } ⇒ Object
Logs network traffic in the browser tab.
375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 |
# File 'lib/bidi2pdf/bidi/browser_tab.rb', line 375 def log_network_traffic(format: :console, output: nil, print_options: { background: true }, &) format = format.to_sym if format == :console network_events.log_network_traffic format: :console elsif format == :pdf html_content = network_events.log_network_traffic format: :html return unless html_content logging_tab = create_browser_tab logging_tab.render_html_content(html_content) logging_tab.wait_until_network_idle logging_tab.print(output, print_options: , &) logging_tab.close end end |
#navigate_to(url, wait: "complete") ⇒ Object
Navigates the browser tab to a specified URL.
This method registers necessary event listeners and sends a navigation command to the browser tab, instructing it to load the specified URL. It validates that the URL is properly formatted before attempting navigation.
175 176 177 178 179 180 181 182 183 184 185 |
# File 'lib/bidi2pdf/bidi/browser_tab.rb', line 175 def navigate_to(url, wait: "complete") begin URI.parse(url) rescue URI::InvalidURIError => e raise , "Invalid URL: #{url} - #{e.message}" end Bidi2pdf.notification_service.instrument("navigate_to.bidi2pdf", url: url) do navigate_with_listeners url, wait: wait end end |
#print(outputfile = nil, print_options: { background: true }) {|pdf_base64| ... } ⇒ String?
Prints the content of the browser tab.
rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity
414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 |
# File 'lib/bidi2pdf/bidi/browser_tab.rb', line 414 def print(outputfile = nil, print_options: { background: true }, &block) Bidi2pdf.notification_service.instrument("print.bidi2pdf") do |instrumentation_payload| cmd, extractor = build_command_and_extractor() instrumentation_payload[:cmd] = cmd client.send_cmd_and_wait(cmd) do |response| if response["result"] pdf_base64 = extractor.call response instrumentation_payload[:pdf_base64] = pdf_base64 if outputfile raise PrintError, "Folder does not exist: #{File.dirname(outputfile)}" unless File.directory?(File.dirname(outputfile)) File.binwrite(outputfile, Base64.decode64(pdf_base64)) Bidi2pdf.logger.info "PDF saved as '#{outputfile}'." else Bidi2pdf.logger.info "PDF generated successfully." end block.call(pdf_base64) if block_given? return pdf_base64 unless outputfile || block_given? else Bidi2pdf.logger.error "Error printing: #{response}" end end end end |
#render_html_content(html_content, wait: "complete") ⇒ Object
Renders HTML content in the browser tab.
rubocop:disable Metrics/BlockLength
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 |
# File 'lib/bidi2pdf/bidi/browser_tab.rb', line 191 def render_html_content(html_content, wait: "complete") Bidi2pdf.notification_service.instrument("render_html_content.bidi2pdf", url: "data:text/html") do |instrumentation_payload| base64_encoded = Base64.strict_encode64(html_content) instrumentation_payload[:data] = base64_encoded data_url = "data:text/html;charset=utf-8;base64,#{base64_encoded}" begin navigate_with_listeners data_url, wait: wait rescue Bidi2pdf::CmdTimeoutError Bidi2pdf.logger.info "Waiting for page to load seemed to have timed out. Checking if page is ready via javascript..." # check if the page is still loading execute_script " const desiredState = \"interactive\";\#{\" \"}\n const timeoutMs = 30000;\#{\" \"}\n const intervalMs = 50;\n\n result = new Promise((resolveReady, rejectReady) => {\n const start = Date.now();\n\n const reachedDesired = (state) =>\n desiredState === \"interactive\"\n ? state === \"interactive\" || state === \"complete\"\n : state === \"complete\";\n\n const check = () => {\n const state = document.readyState;\n if (reachedDesired(state)) {\n resolveReady(state); // resolves to \"interactive\" or \"complete\"\n return;\n }\n if (Date.now() - start >= timeoutMs) {\n rejectReady(new Error(`Timeout waiting for document.readyState=\"${desiredState}\". Last state=\"${state}\".`));\n return;\n }\n\n console.warn(`Waiting for document.readyState=\"${desiredState}\". Last state=\"${state}\".`);\n\n setTimeout(check, intervalMs);\n };\n\n // Immediate check in case the page is already loaded\n check();\n });\n JS\n end\n end\nend\n", wrap_in_promise: false |
#set_cookie(name:, value:, domain:, path: "/", secure: true, http_only: false, same_site: "strict", ttl: 30) ⇒ Object
Sets a cookie in the browser tab.
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
# File 'lib/bidi2pdf/bidi/browser_tab.rb', line 109 def ( name:, value:, domain:, path: "/", secure: true, http_only: false, same_site: "strict", ttl: 30 ) cmd = Bidi2pdf::Bidi::Commands::SetTabCookie.new( browsing_context_id: browsing_context_id, name: name, value: value, domain: domain, path: path, secure: secure, http_only: http_only, same_site: same_site, ttl: ttl ) client.send_cmd_and_wait(cmd) do |response| Bidi2pdf.logger.debug1 "Cookie set: #{response.inspect}" end end |
#wait_until_network_idle(timeout: 10, poll_interval: 0.1) ⇒ Object
Waits until the network is idle in the browser tab.
341 342 343 344 345 346 347 |
# File 'lib/bidi2pdf/bidi/browser_tab.rb', line 341 def wait_until_network_idle(timeout: 10, poll_interval: 0.1) Bidi2pdf.notification_service.instrument("network_idle.bidi2pdf") do |instrumentation_payload| network_events.wait_until_network_idle(timeout: timeout, poll_interval: poll_interval) instrumentation_payload[:requests] = network_events.all_events.dup end end |
#wait_until_page_loaded(check_script: nil) ⇒ Object
Waits until the page is fully loaded in the browser tab.
This method executes a JavaScript script that checks if the page has finished loading.
359 360 361 362 363 364 365 366 367 |
# File 'lib/bidi2pdf/bidi/browser_tab.rb', line 359 def wait_until_page_loaded(check_script: nil) check_script ||= " new Promise(resolve => { const check = () => window.loaded ? resolve('done') : setTimeout(check, 100); check(); });\n JS\n\n Bidi2pdf.notification_service.instrument(\"page_loaded.bidi2pdf\") do\n execute_script check_script\n end\nend\n" |