module Ferrum::Page::Frames

Attributes

main_frame[R]

Public Instance Methods

frame_by(id: nil, name: nil) click to toggle source
# File lib/ferrum/page/frames.rb, line 14
def frame_by(id: nil, name: nil)
  if id
    @frames[id]
  elsif name
    frames.find { |f| f.name == name }
  else
    raise ArgumentError
  end
end
frames() click to toggle source
# File lib/ferrum/page/frames.rb, line 10
def frames
  @frames.values
end
frames_subscribe() click to toggle source
# File lib/ferrum/page/frames.rb, line 24
def frames_subscribe
  on("Page.frameAttached") do |params|
    parent_frame_id, frame_id = params.values_at("parentFrameId", "frameId")
    @frames[frame_id] = Frame.new(frame_id, self, parent_frame_id)
  end

  on("Page.frameStartedLoading") do |params|
    frame = @frames[params["frameId"]]
    frame.state = :started_loading
    @event.reset
  end

  on("Page.frameNavigated") do |params|
    frame_id, name = params["frame"]&.values_at("id", "name")
    frame = @frames[frame_id]
    frame.state = :navigated
    frame.name = name unless name.to_s.empty?
  end

  on("Page.frameStoppedLoading") do |params|
    # `DOM.performSearch` doesn't work without getting #document node first.
    # It returns node with nodeId 1 and nodeType 9 from which descend the
    # tree and we save it in a variable because if we call that again root
    # node will change the id and all subsequent nodes have to change id too.
    if @main_frame.id == params["frameId"]
      @event.set if idling?
      get_document_id
    end

    frame = @frames[params["frameId"]]
    frame.state = :stopped_loading

    @event.set if idling?
  end

  on("Page.navigatedWithinDocument") do
    @event.set if idling?
  end

  on("Network.requestWillBeSent") do |params|
    if params["frameId"] == @main_frame.id
      # Possible types:
      # Document, Stylesheet, Image, Media, Font, Script, TextTrack, XHR,
      # Fetch, EventSource, WebSocket, Manifest, SignedExchange, Ping,
      # CSPViolationReport, Other
      @event.reset if params["type"] == "Document"
    end
  end

  on("Runtime.executionContextCreated") do |params|
    setting_up_main_frame = false
    context_id = params.dig("context", "id")
    frame_id = params.dig("context", "auxData", "frameId")

    unless @main_frame.id
      root_frame = command("Page.getFrameTree").dig("frameTree", "frame", "id")
      if frame_id == root_frame
        setting_up_main_frame = true
        @main_frame.id = frame_id
        @frames[frame_id] = @main_frame
      end
    end

    frame = @frames[frame_id] || Frame.new(frame_id, self)
    frame.set_execution_id(context_id)

    # Set event because `execution_id` might raise NoExecutionContextError
    @event.set if setting_up_main_frame

    @frames[frame_id] ||= frame
  end

  on("Runtime.executionContextDestroyed") do |params|
    execution_id = params["executionContextId"]
    frame = frames.find { |f| f.execution_id?(execution_id) }
    frame.reset_execution_id
  end

  on("Runtime.executionContextsCleared") do
    @frames.delete_if { |_, f| !f.main? }
    @main_frame.reset_execution_id
  end
end

Private Instance Methods

idling?() click to toggle source
# File lib/ferrum/page/frames.rb, line 110
def idling?
  @frames.all? { |_, f| f.state == :stopped_loading }
end