lib/puppeteer/page.rb in puppeteer-ruby-0.37.4 vs lib/puppeteer/page.rb in puppeteer-ruby-0.38.0

- old
+ new

@@ -48,23 +48,30 @@ @screenshot_task_queue = ScreenshotTaskQueue.new @workers = {} @user_drag_interception_enabled = false - @client.on_event('Target.attachedToTarget') do |event| - if event['targetInfo']['type'] != 'worker' + @client.add_event_listener('Target.attachedToTarget') do |event| + if event['targetInfo']['type'] != 'worker' && event['targetInfo']['type'] != 'iframe' # If we don't detach from service workers, they will never die. + # We still want to attach to workers for emitting events. + # We still want to attach to iframes so sessions may interact with them. + # We detach from all other types out of an abundance of caution. + # See https://source.chromium.org/chromium/chromium/src/+/main:content/browser/devtools/devtools_agent_host_impl.cc?ss=chromium&q=f:devtools%20-f:out%20%22::kTypePage%5B%5D%22 + # for the complete list of available types. @client.async_send_message('Target.detachFromTarget', sessionId: event['sessionId']) next end - session = Puppeteer::Connection.from_session(@client).session(event['sessionId']) # rubocop:disable Lint/UselessAssignment - # const worker = new Worker(session, event.targetInfo.url, this._addConsoleMessage.bind(this), this._handleException.bind(this)); - # this._workers.set(event.sessionId, worker); - # this.emit(PageEmittedEvents::WorkerCreated, worker); + if event['targetInfo']['type'] == 'worker' + session = Puppeteer::Connection.from_session(@client).session(event['sessionId']) # rubocop:disable Lint/UselessAssignment + # const worker = new Worker(session, event.targetInfo.url, this._addConsoleMessage.bind(this), this._handleException.bind(this)); + # this._workers.set(event.sessionId, worker); + # this.emit(PageEmittedEvents::WorkerCreated, worker); + end end - @client.on_event('Target.detachedFromTarget') do |event| + @client.add_event_listener('Target.detachedFromTarget') do |event| session_id = event['sessionId'] worker = @workers[session_id] next unless worker emit_event(PageEmittedEvents::WorkerDestroyed, worker) @@ -101,14 +108,14 @@ emit_event(PageEmittedEvents::DOMContentLoaded) end @client.on_event('Page.loadEventFired') do |event| emit_event(PageEmittedEvents::Load) end - @client.on('Runtime.consoleAPICalled') do |event| + @client.add_event_listener('Runtime.consoleAPICalled') do |event| handle_console_api(event) end - @client.on('Runtime.bindingCalled') do |event| + @client.add_event_listener('Runtime.bindingCalled') do |event| handle_binding_called(event) end @client.on_event('Page.javascriptDialogOpening') do |event| handle_dialog_opening(event) end @@ -516,11 +523,11 @@ # # @see https://github.com/puppeteer/puppeteer/issues/3865 return end - context = @frame_manager.execution_context_by_id(event['executionContextId']) + context = @frame_manager.execution_context_by_id(event['executionContextId'], @client) values = event['args'].map do |arg| remote_object = Puppeteer::RemoteObject.new(arg) Puppeteer::JSHandle.create(context: context, remote_object: remote_object) end add_console_message(event['type'], values, event['stackTrace']) @@ -662,33 +669,54 @@ define_async_method :async_wait_for_navigation private def wait_for_network_manager_event(event_name, predicate:, timeout:) option_timeout = timeout || @timeout_settings.timeout - @wait_for_network_manager_event_listener_ids ||= {} - if_present(@wait_for_network_manager_event_listener_ids[event_name]) do |listener_id| + promise = resolvable_future + + listener_id = @frame_manager.network_manager.add_event_listener(event_name) do |event_target| + if predicate.call(event_target) + promise.fulfill(event_target) + end + end + + begin + # Timeout.timeout(0) means "no limit" for timeout. + Timeout.timeout(option_timeout / 1000.0) do + await_any(promise, session_close_promise) + end + rescue Timeout::Error + raise Puppeteer::TimeoutError.new("waiting for #{event_name} failed: timeout #{option_timeout}ms exceeded") + ensure @frame_manager.network_manager.remove_event_listener(listener_id) end + end + private def wait_for_frame_manager_event(*event_names, predicate:, timeout:) + option_timeout = timeout || @timeout_settings.timeout + promise = resolvable_future - @wait_for_network_manager_event_listener_ids[event_name] = - @frame_manager.network_manager.add_event_listener(event_name) do |event_target| + listener_ids = event_names.map do |event_name| + @frame_manager.add_event_listener(event_name) do |event_target| if predicate.call(event_target) - promise.fulfill(event_target) + promise.fulfill(event_target) unless promise.resolved? end end + end begin # Timeout.timeout(0) means "no limit" for timeout. Timeout.timeout(option_timeout / 1000.0) do await_any(promise, session_close_promise) end rescue Timeout::Error - raise Puppeteer::TimeoutError.new("waiting for #{event_name} failed: timeout #{timeout}ms exceeded") + raise Puppeteer::TimeoutError.new("waiting for #{event_names.join(" or ")} failed: timeout #{option_timeout}ms exceeded") ensure - @frame_manager.network_manager.remove_event_listener(@wait_for_network_manager_event_listener_ids[event_name]) + listener_ids.each do |listener_id| + @frame_manager.remove_event_listener(listener_id) + end end end private def session_close_promise @disconnect_promise ||= resolvable_future do |future| @@ -707,11 +735,11 @@ end request_predicate = if url -> (request) { request.url == url } else - -> (request) { predicate.call(request) } + predicate end wait_for_network_manager_event(NetworkManagerEmittedEvents::Request, predicate: request_predicate, timeout: timeout, @@ -741,11 +769,11 @@ end response_predicate = if url -> (response) { response.url == url } else - -> (response) { predicate.call(response) } + predicate end wait_for_network_manager_event(NetworkManagerEmittedEvents::Response, predicate: response_predicate, timeout: timeout, @@ -755,9 +783,37 @@ # @!method async_wait_for_response(url: nil, predicate: nil, timeout: nil) # # @param url [String] # @param predicate [Proc(Puppeteer::HTTPRequest -> Boolean)] define_async_method :async_wait_for_response + + def wait_for_frame(url: nil, predicate: nil, timeout: nil) + if !url && !predicate + raise ArgumentError.new('url or predicate must be specified') + end + if predicate && !predicate.is_a?(Proc) + raise ArgumentError.new('predicate must be a proc.') + end + frame_predicate = + if url + -> (frame) { frame.url == url } + else + predicate + end + + wait_for_frame_manager_event( + FrameManagerEmittedEvents::FrameAttached, + FrameManagerEmittedEvents::FrameNavigated, + predicate: frame_predicate, + timeout: timeout, + ) + end + + # @!method async_wait_for_frame(url: nil, predicate: nil, timeout: nil) + # + # @param url [String] + # @param predicate [Proc(Puppeteer::Frame -> Boolean)] + define_async_method :async_wait_for_frame # @param timeout [number|nil] # @param wait_until [string|nil] 'load' | 'domcontentloaded' | 'networkidle0' | 'networkidle2' def go_back(timeout: nil, wait_until: nil) go(-1, timeout: timeout, wait_until: wait_until)