ferrum 0.11 → 0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/LICENSE +1 -1
 - data/README.md +178 -29
 - data/lib/ferrum/browser/binary.rb +46 -0
 - data/lib/ferrum/browser/client.rb +13 -12
 - data/lib/ferrum/browser/command.rb +7 -8
 - data/lib/ferrum/browser/options/base.rb +1 -7
 - data/lib/ferrum/browser/options/chrome.rb +17 -11
 - data/lib/ferrum/browser/options/firefox.rb +11 -4
 - data/lib/ferrum/browser/process.rb +41 -35
 - data/lib/ferrum/browser/subscriber.rb +1 -3
 - data/lib/ferrum/browser/web_socket.rb +9 -12
 - data/lib/ferrum/browser/xvfb.rb +4 -8
 - data/lib/ferrum/browser.rb +44 -12
 - data/lib/ferrum/context.rb +6 -2
 - data/lib/ferrum/contexts.rb +10 -8
 - data/lib/ferrum/cookies.rb +10 -9
 - data/lib/ferrum/errors.rb +115 -0
 - data/lib/ferrum/frame/runtime.rb +20 -17
 - data/lib/ferrum/frame.rb +32 -24
 - data/lib/ferrum/headers.rb +2 -2
 - data/lib/ferrum/keyboard.rb +11 -11
 - data/lib/ferrum/mouse.rb +8 -7
 - data/lib/ferrum/network/auth_request.rb +7 -2
 - data/lib/ferrum/network/exchange.rb +14 -10
 - data/lib/ferrum/network/intercepted_request.rb +10 -8
 - data/lib/ferrum/network/request.rb +5 -0
 - data/lib/ferrum/network/response.rb +4 -4
 - data/lib/ferrum/network.rb +124 -35
 - data/lib/ferrum/node.rb +69 -23
 - data/lib/ferrum/page/animation.rb +0 -1
 - data/lib/ferrum/page/frames.rb +46 -20
 - data/lib/ferrum/page/screenshot.rb +51 -65
 - data/lib/ferrum/page/stream.rb +38 -0
 - data/lib/ferrum/page/tracing.rb +71 -0
 - data/lib/ferrum/page.rb +81 -36
 - data/lib/ferrum/proxy.rb +58 -0
 - data/lib/ferrum/{rbga.rb → rgba.rb} +4 -2
 - data/lib/ferrum/target.rb +1 -0
 - data/lib/ferrum/utils/attempt.rb +20 -0
 - data/lib/ferrum/utils/elapsed_time.rb +27 -0
 - data/lib/ferrum/utils/platform.rb +28 -0
 - data/lib/ferrum/version.rb +1 -1
 - data/lib/ferrum.rb +4 -146
 - metadata +60 -51
 
    
        data/lib/ferrum/network.rb
    CHANGED
    
    | 
         @@ -14,6 +14,10 @@ module Ferrum 
     | 
|
| 
       14 
14 
     | 
    
         
             
                RESOURCE_TYPES = %w[Document Stylesheet Image Media Font Script TextTrack
         
     | 
| 
       15 
15 
     | 
    
         
             
                                    XHR Fetch EventSource WebSocket Manifest
         
     | 
| 
       16 
16 
     | 
    
         
             
                                    SignedExchange Ping CSPViolationReport Other].freeze
         
     | 
| 
      
 17 
     | 
    
         
            +
                AUTHORIZE_BLOCK_MISSING = "Block is missing, call `authorize(...) { |r| r.continue } " \
         
     | 
| 
      
 18 
     | 
    
         
            +
                                          "or subscribe to `on(:request)` events before calling it"
         
     | 
| 
      
 19 
     | 
    
         
            +
                AUTHORIZE_TYPE_WRONG = ":type should be in #{AUTHORIZE_TYPE}"
         
     | 
| 
      
 20 
     | 
    
         
            +
                ALLOWED_CONNECTION_TYPE = %w[none cellular2g cellular3g cellular4g bluetooth ethernet wifi wimax other].freeze
         
     | 
| 
       17 
21 
     | 
    
         | 
| 
       18 
22 
     | 
    
         
             
                attr_reader :traffic
         
     | 
| 
       19 
23 
     | 
    
         | 
| 
         @@ -21,13 +25,16 @@ module Ferrum 
     | 
|
| 
       21 
25 
     | 
    
         
             
                  @page = page
         
     | 
| 
       22 
26 
     | 
    
         
             
                  @traffic = []
         
     | 
| 
       23 
27 
     | 
    
         
             
                  @exchange = nil
         
     | 
| 
      
 28 
     | 
    
         
            +
                  @blacklist = nil
         
     | 
| 
      
 29 
     | 
    
         
            +
                  @whitelist = nil
         
     | 
| 
       24 
30 
     | 
    
         
             
                end
         
     | 
| 
       25 
31 
     | 
    
         | 
| 
       26 
32 
     | 
    
         
             
                def wait_for_idle(connections: 0, duration: 0.05, timeout: @page.browser.timeout)
         
     | 
| 
       27 
     | 
    
         
            -
                  start =  
     | 
| 
      
 33 
     | 
    
         
            +
                  start = Utils::ElapsedTime.monotonic_time
         
     | 
| 
       28 
34 
     | 
    
         | 
| 
       29 
35 
     | 
    
         
             
                  until idle?(connections)
         
     | 
| 
       30 
     | 
    
         
            -
                    raise TimeoutError if  
     | 
| 
      
 36 
     | 
    
         
            +
                    raise TimeoutError if Utils::ElapsedTime.timeout?(start, timeout)
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
       31 
38 
     | 
    
         
             
                    sleep(duration)
         
     | 
| 
       32 
39 
     | 
    
         
             
                  end
         
     | 
| 
       33 
40 
     | 
    
         
             
                end
         
     | 
| 
         @@ -61,9 +68,7 @@ module Ferrum 
     | 
|
| 
       61 
68 
     | 
    
         
             
                end
         
     | 
| 
       62 
69 
     | 
    
         | 
| 
       63 
70 
     | 
    
         
             
                def clear(type)
         
     | 
| 
       64 
     | 
    
         
            -
                  unless CLEAR_TYPE.include?(type)
         
     | 
| 
       65 
     | 
    
         
            -
                    raise ArgumentError, ":type should be in #{CLEAR_TYPE}"
         
     | 
| 
       66 
     | 
    
         
            -
                  end
         
     | 
| 
      
 71 
     | 
    
         
            +
                  raise ArgumentError, ":type should be in #{CLEAR_TYPE}" unless CLEAR_TYPE.include?(type)
         
     | 
| 
       67 
72 
     | 
    
         | 
| 
       68 
73 
     | 
    
         
             
                  if type == :traffic
         
     | 
| 
       69 
74 
     | 
    
         
             
                    @traffic.clear
         
     | 
| 
         @@ -74,23 +79,28 @@ module Ferrum 
     | 
|
| 
       74 
79 
     | 
    
         
             
                  true
         
     | 
| 
       75 
80 
     | 
    
         
             
                end
         
     | 
| 
       76 
81 
     | 
    
         | 
| 
      
 82 
     | 
    
         
            +
                def blacklist=(patterns)
         
     | 
| 
      
 83 
     | 
    
         
            +
                  @blacklist = Array(patterns)
         
     | 
| 
      
 84 
     | 
    
         
            +
                  blacklist_subscribe
         
     | 
| 
      
 85 
     | 
    
         
            +
                end
         
     | 
| 
      
 86 
     | 
    
         
            +
                alias blocklist= blacklist=
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                def whitelist=(patterns)
         
     | 
| 
      
 89 
     | 
    
         
            +
                  @whitelist = Array(patterns)
         
     | 
| 
      
 90 
     | 
    
         
            +
                  whitelist_subscribe
         
     | 
| 
      
 91 
     | 
    
         
            +
                end
         
     | 
| 
      
 92 
     | 
    
         
            +
                alias allowlist= whitelist=
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
       77 
94 
     | 
    
         
             
                def intercept(pattern: "*", resource_type: nil)
         
     | 
| 
       78 
95 
     | 
    
         
             
                  pattern = { urlPattern: pattern }
         
     | 
| 
       79 
     | 
    
         
            -
                  if resource_type && RESOURCE_TYPES.include?(resource_type.to_s)
         
     | 
| 
       80 
     | 
    
         
            -
                    pattern[:resourceType] = resource_type
         
     | 
| 
       81 
     | 
    
         
            -
                  end
         
     | 
| 
      
 96 
     | 
    
         
            +
                  pattern[:resourceType] = resource_type if resource_type && RESOURCE_TYPES.include?(resource_type.to_s)
         
     | 
| 
       82 
97 
     | 
    
         | 
| 
       83 
98 
     | 
    
         
             
                  @page.command("Fetch.enable", handleAuthRequests: true, patterns: [pattern])
         
     | 
| 
       84 
99 
     | 
    
         
             
                end
         
     | 
| 
       85 
100 
     | 
    
         | 
| 
       86 
101 
     | 
    
         
             
                def authorize(user:, password:, type: :server, &block)
         
     | 
| 
       87 
     | 
    
         
            -
                  unless AUTHORIZE_TYPE.include?(type)
         
     | 
| 
       88 
     | 
    
         
            -
             
     | 
| 
       89 
     | 
    
         
            -
                  end
         
     | 
| 
       90 
     | 
    
         
            -
             
     | 
| 
       91 
     | 
    
         
            -
                  if !block_given? && !@page.subscribed?("Fetch.requestPaused")
         
     | 
| 
       92 
     | 
    
         
            -
                    raise ArgumentError, "Block is missing, call `authorize(...) { |r| r.continue } or subscribe to `on(:request)` events before calling it"
         
     | 
| 
       93 
     | 
    
         
            -
                  end
         
     | 
| 
      
 102 
     | 
    
         
            +
                  raise ArgumentError, AUTHORIZE_TYPE_WRONG unless AUTHORIZE_TYPE.include?(type)
         
     | 
| 
      
 103 
     | 
    
         
            +
                  raise ArgumentError, AUTHORIZE_BLOCK_MISSING if !block_given? && !@page.subscribed?("Fetch.requestPaused")
         
     | 
| 
       94 
104 
     | 
    
         | 
| 
       95 
105 
     | 
    
         
             
                  @authorized_ids ||= {}
         
     | 
| 
       96 
106 
     | 
    
         
             
                  @authorized_ids[type] ||= []
         
     | 
| 
         @@ -116,6 +126,53 @@ module Ferrum 
     | 
|
| 
       116 
126 
     | 
    
         
             
                end
         
     | 
| 
       117 
127 
     | 
    
         | 
| 
       118 
128 
     | 
    
         
             
                def subscribe
         
     | 
| 
      
 129 
     | 
    
         
            +
                  subscribe_request_will_be_sent
         
     | 
| 
      
 130 
     | 
    
         
            +
                  subscribe_response_received
         
     | 
| 
      
 131 
     | 
    
         
            +
                  subscribe_loading_finished
         
     | 
| 
      
 132 
     | 
    
         
            +
                  subscribe_loading_failed
         
     | 
| 
      
 133 
     | 
    
         
            +
                  subscribe_log_entry_added
         
     | 
| 
      
 134 
     | 
    
         
            +
                end
         
     | 
| 
      
 135 
     | 
    
         
            +
             
     | 
| 
      
 136 
     | 
    
         
            +
                def authorized_response(ids, request_id, username, password)
         
     | 
| 
      
 137 
     | 
    
         
            +
                  if ids.include?(request_id)
         
     | 
| 
      
 138 
     | 
    
         
            +
                    { response: "CancelAuth" }
         
     | 
| 
      
 139 
     | 
    
         
            +
                  elsif username && password
         
     | 
| 
      
 140 
     | 
    
         
            +
                    { response: "ProvideCredentials",
         
     | 
| 
      
 141 
     | 
    
         
            +
                      username: username,
         
     | 
| 
      
 142 
     | 
    
         
            +
                      password: password }
         
     | 
| 
      
 143 
     | 
    
         
            +
                  end
         
     | 
| 
      
 144 
     | 
    
         
            +
                end
         
     | 
| 
      
 145 
     | 
    
         
            +
             
     | 
| 
      
 146 
     | 
    
         
            +
                def select(request_id)
         
     | 
| 
      
 147 
     | 
    
         
            +
                  @traffic.select { |e| e.id == request_id }
         
     | 
| 
      
 148 
     | 
    
         
            +
                end
         
     | 
| 
      
 149 
     | 
    
         
            +
             
     | 
| 
      
 150 
     | 
    
         
            +
                def build_exchange(id)
         
     | 
| 
      
 151 
     | 
    
         
            +
                  Network::Exchange.new(@page, id).tap { |e| @traffic << e }
         
     | 
| 
      
 152 
     | 
    
         
            +
                end
         
     | 
| 
      
 153 
     | 
    
         
            +
             
     | 
| 
      
 154 
     | 
    
         
            +
                def emulate_network_conditions(offline: false, latency: 0,
         
     | 
| 
      
 155 
     | 
    
         
            +
                                               download_throughput: -1, upload_throughput: -1,
         
     | 
| 
      
 156 
     | 
    
         
            +
                                               connection_type: nil)
         
     | 
| 
      
 157 
     | 
    
         
            +
                  params = {
         
     | 
| 
      
 158 
     | 
    
         
            +
                    offline: offline, latency: latency,
         
     | 
| 
      
 159 
     | 
    
         
            +
                    downloadThroughput: download_throughput,
         
     | 
| 
      
 160 
     | 
    
         
            +
                    uploadThroughput: upload_throughput
         
     | 
| 
      
 161 
     | 
    
         
            +
                  }
         
     | 
| 
      
 162 
     | 
    
         
            +
             
     | 
| 
      
 163 
     | 
    
         
            +
                  params[:connectionType] = connection_type if connection_type && ALLOWED_CONNECTION_TYPE.include?(connection_type)
         
     | 
| 
      
 164 
     | 
    
         
            +
             
     | 
| 
      
 165 
     | 
    
         
            +
                  @page.command("Network.emulateNetworkConditions", **params)
         
     | 
| 
      
 166 
     | 
    
         
            +
                  true
         
     | 
| 
      
 167 
     | 
    
         
            +
                end
         
     | 
| 
      
 168 
     | 
    
         
            +
             
     | 
| 
      
 169 
     | 
    
         
            +
                def offline_mode
         
     | 
| 
      
 170 
     | 
    
         
            +
                  emulate_network_conditions(offline: true, latency: 0, download_throughput: 0, upload_throughput: 0)
         
     | 
| 
      
 171 
     | 
    
         
            +
                end
         
     | 
| 
      
 172 
     | 
    
         
            +
             
     | 
| 
      
 173 
     | 
    
         
            +
                private
         
     | 
| 
      
 174 
     | 
    
         
            +
             
     | 
| 
      
 175 
     | 
    
         
            +
                def subscribe_request_will_be_sent
         
     | 
| 
       119 
176 
     | 
    
         
             
                  @page.on("Network.requestWillBeSent") do |params|
         
     | 
| 
       120 
177 
     | 
    
         
             
                    request = Network::Request.new(params)
         
     | 
| 
       121 
178 
     | 
    
         | 
| 
         @@ -140,25 +197,29 @@ module Ferrum 
     | 
|
| 
       140 
197 
     | 
    
         | 
| 
       141 
198 
     | 
    
         
             
                    exchange.request = request
         
     | 
| 
       142 
199 
     | 
    
         | 
| 
       143 
     | 
    
         
            -
                    if exchange.navigation_request?(@page.main_frame.id)
         
     | 
| 
       144 
     | 
    
         
            -
                      @exchange = exchange
         
     | 
| 
       145 
     | 
    
         
            -
                    end
         
     | 
| 
      
 200 
     | 
    
         
            +
                    @exchange = exchange if exchange.navigation_request?(@page.main_frame.id)
         
     | 
| 
       146 
201 
     | 
    
         
             
                  end
         
     | 
| 
      
 202 
     | 
    
         
            +
                end
         
     | 
| 
       147 
203 
     | 
    
         | 
| 
      
 204 
     | 
    
         
            +
                def subscribe_response_received
         
     | 
| 
       148 
205 
     | 
    
         
             
                  @page.on("Network.responseReceived") do |params|
         
     | 
| 
       149 
     | 
    
         
            -
                     
     | 
| 
      
 206 
     | 
    
         
            +
                    exchange = select(params["requestId"]).last
         
     | 
| 
      
 207 
     | 
    
         
            +
             
     | 
| 
      
 208 
     | 
    
         
            +
                    if exchange
         
     | 
| 
       150 
209 
     | 
    
         
             
                      response = Network::Response.new(@page, params)
         
     | 
| 
       151 
210 
     | 
    
         
             
                      exchange.response = response
         
     | 
| 
       152 
211 
     | 
    
         
             
                    end
         
     | 
| 
       153 
212 
     | 
    
         
             
                  end
         
     | 
| 
      
 213 
     | 
    
         
            +
                end
         
     | 
| 
       154 
214 
     | 
    
         | 
| 
      
 215 
     | 
    
         
            +
                def subscribe_loading_finished
         
     | 
| 
       155 
216 
     | 
    
         
             
                  @page.on("Network.loadingFinished") do |params|
         
     | 
| 
       156 
217 
     | 
    
         
             
                    exchange = select(params["requestId"]).last
         
     | 
| 
       157 
     | 
    
         
            -
                     
     | 
| 
       158 
     | 
    
         
            -
                      exchange.response.body_size = params["encodedDataLength"]
         
     | 
| 
       159 
     | 
    
         
            -
                    end
         
     | 
| 
      
 218 
     | 
    
         
            +
                    exchange.response.body_size = params["encodedDataLength"] if exchange&.response
         
     | 
| 
       160 
219 
     | 
    
         
             
                  end
         
     | 
| 
      
 220 
     | 
    
         
            +
                end
         
     | 
| 
       161 
221 
     | 
    
         | 
| 
      
 222 
     | 
    
         
            +
                def subscribe_loading_failed
         
     | 
| 
       162 
223 
     | 
    
         
             
                  @page.on("Network.loadingFailed") do |params|
         
     | 
| 
       163 
224 
     | 
    
         
             
                    exchange = select(params["requestId"]).last
         
     | 
| 
       164 
225 
     | 
    
         
             
                    exchange.error ||= Network::Error.new
         
     | 
| 
         @@ -169,7 +230,9 @@ module Ferrum 
     | 
|
| 
       169 
230 
     | 
    
         
             
                    exchange.error.monotonic_time = params["timestamp"]
         
     | 
| 
       170 
231 
     | 
    
         
             
                    exchange.error.canceled = params["canceled"]
         
     | 
| 
       171 
232 
     | 
    
         
             
                  end
         
     | 
| 
      
 233 
     | 
    
         
            +
                end
         
     | 
| 
       172 
234 
     | 
    
         | 
| 
      
 235 
     | 
    
         
            +
                def subscribe_log_entry_added
         
     | 
| 
       173 
236 
     | 
    
         
             
                  @page.on("Log.entryAdded") do |params|
         
     | 
| 
       174 
237 
     | 
    
         
             
                    entry = params["entry"] || {}
         
     | 
| 
       175 
238 
     | 
    
         
             
                    if entry["source"] == "network" && entry["level"] == "error"
         
     | 
| 
         @@ -184,24 +247,50 @@ module Ferrum 
     | 
|
| 
       184 
247 
     | 
    
         
             
                  end
         
     | 
| 
       185 
248 
     | 
    
         
             
                end
         
     | 
| 
       186 
249 
     | 
    
         | 
| 
       187 
     | 
    
         
            -
                def  
     | 
| 
       188 
     | 
    
         
            -
                   
     | 
| 
       189 
     | 
    
         
            -
             
     | 
| 
       190 
     | 
    
         
            -
             
     | 
| 
       191 
     | 
    
         
            -
             
     | 
| 
       192 
     | 
    
         
            -
             
     | 
| 
       193 
     | 
    
         
            -
             
     | 
| 
       194 
     | 
    
         
            -
             
     | 
| 
       195 
     | 
    
         
            -
             
     | 
| 
      
 250 
     | 
    
         
            +
                def blacklist_subscribe
         
     | 
| 
      
 251 
     | 
    
         
            +
                  return unless blacklist?
         
     | 
| 
      
 252 
     | 
    
         
            +
                  raise ArgumentError, "You can't use blacklist along with whitelist" if whitelist?
         
     | 
| 
      
 253 
     | 
    
         
            +
             
     | 
| 
      
 254 
     | 
    
         
            +
                  @blacklist_subscribe ||= begin
         
     | 
| 
      
 255 
     | 
    
         
            +
                    intercept
         
     | 
| 
      
 256 
     | 
    
         
            +
             
     | 
| 
      
 257 
     | 
    
         
            +
                    @page.on(:request) do |request|
         
     | 
| 
      
 258 
     | 
    
         
            +
                      if @blacklist.any? { |p| request.match?(p) }
         
     | 
| 
      
 259 
     | 
    
         
            +
                        request.abort
         
     | 
| 
      
 260 
     | 
    
         
            +
                      else
         
     | 
| 
      
 261 
     | 
    
         
            +
                        request.continue
         
     | 
| 
      
 262 
     | 
    
         
            +
                      end
         
     | 
| 
      
 263 
     | 
    
         
            +
                    end
         
     | 
| 
      
 264 
     | 
    
         
            +
             
     | 
| 
      
 265 
     | 
    
         
            +
                    true
         
     | 
| 
       196 
266 
     | 
    
         
             
                  end
         
     | 
| 
       197 
267 
     | 
    
         
             
                end
         
     | 
| 
       198 
268 
     | 
    
         | 
| 
       199 
     | 
    
         
            -
                def  
     | 
| 
       200 
     | 
    
         
            -
                   
     | 
| 
      
 269 
     | 
    
         
            +
                def whitelist_subscribe
         
     | 
| 
      
 270 
     | 
    
         
            +
                  return unless whitelist?
         
     | 
| 
      
 271 
     | 
    
         
            +
                  raise ArgumentError, "You can't use whitelist along with blacklist" if blacklist?
         
     | 
| 
      
 272 
     | 
    
         
            +
             
     | 
| 
      
 273 
     | 
    
         
            +
                  @whitelist_subscribe ||= begin
         
     | 
| 
      
 274 
     | 
    
         
            +
                    intercept
         
     | 
| 
      
 275 
     | 
    
         
            +
             
     | 
| 
      
 276 
     | 
    
         
            +
                    @page.on(:request) do |request|
         
     | 
| 
      
 277 
     | 
    
         
            +
                      if @whitelist.any? { |p| request.match?(p) }
         
     | 
| 
      
 278 
     | 
    
         
            +
                        request.continue
         
     | 
| 
      
 279 
     | 
    
         
            +
                      else
         
     | 
| 
      
 280 
     | 
    
         
            +
                        request.abort
         
     | 
| 
      
 281 
     | 
    
         
            +
                      end
         
     | 
| 
      
 282 
     | 
    
         
            +
                    end
         
     | 
| 
      
 283 
     | 
    
         
            +
             
     | 
| 
      
 284 
     | 
    
         
            +
                    true
         
     | 
| 
      
 285 
     | 
    
         
            +
                  end
         
     | 
| 
       201 
286 
     | 
    
         
             
                end
         
     | 
| 
       202 
287 
     | 
    
         | 
| 
       203 
     | 
    
         
            -
                def  
     | 
| 
       204 
     | 
    
         
            -
                   
     | 
| 
      
 288 
     | 
    
         
            +
                def blacklist?
         
     | 
| 
      
 289 
     | 
    
         
            +
                  Array(@blacklist).any?
         
     | 
| 
      
 290 
     | 
    
         
            +
                end
         
     | 
| 
      
 291 
     | 
    
         
            +
             
     | 
| 
      
 292 
     | 
    
         
            +
                def whitelist?
         
     | 
| 
      
 293 
     | 
    
         
            +
                  Array(@whitelist).any?
         
     | 
| 
       205 
294 
     | 
    
         
             
                end
         
     | 
| 
       206 
295 
     | 
    
         
             
              end
         
     | 
| 
       207 
296 
     | 
    
         
             
            end
         
     | 
    
        data/lib/ferrum/node.rb
    CHANGED
    
    | 
         @@ -10,7 +10,8 @@ module Ferrum 
     | 
|
| 
       10 
10 
     | 
    
         
             
                def initialize(frame, target_id, node_id, description)
         
     | 
| 
       11 
11 
     | 
    
         
             
                  @page = frame.page
         
     | 
| 
       12 
12 
     | 
    
         
             
                  @target_id = target_id
         
     | 
| 
       13 
     | 
    
         
            -
                  @node_id 
     | 
| 
      
 13 
     | 
    
         
            +
                  @node_id = node_id
         
     | 
| 
      
 14 
     | 
    
         
            +
                  @description = description
         
     | 
| 
       14 
15 
     | 
    
         
             
                  @tag_name = description["nodeName"].downcase
         
     | 
| 
       15 
16 
     | 
    
         
             
                end
         
     | 
| 
       16 
17 
     | 
    
         | 
| 
         @@ -38,15 +39,16 @@ module Ferrum 
     | 
|
| 
       38 
39 
     | 
    
         
             
                end
         
     | 
| 
       39 
40 
     | 
    
         | 
| 
       40 
41 
     | 
    
         
             
                def wait_for_stop_moving(delay: MOVING_WAIT_DELAY, attempts: MOVING_WAIT_ATTEMPTS)
         
     | 
| 
       41 
     | 
    
         
            -
                   
     | 
| 
       42 
     | 
    
         
            -
                    previous, current =  
     | 
| 
      
 42 
     | 
    
         
            +
                  Utils::Attempt.with_retry(errors: NodeMovingError, max: attempts, wait: 0) do
         
     | 
| 
      
 43 
     | 
    
         
            +
                    previous, current = content_quads_with(delay: delay)
         
     | 
| 
       43 
44 
     | 
    
         
             
                    raise NodeMovingError.new(self, previous, current) if previous != current
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
       44 
46 
     | 
    
         
             
                    current
         
     | 
| 
       45 
47 
     | 
    
         
             
                  end
         
     | 
| 
       46 
48 
     | 
    
         
             
                end
         
     | 
| 
       47 
49 
     | 
    
         | 
| 
       48 
50 
     | 
    
         
             
                def moving?(delay: MOVING_WAIT_DELAY)
         
     | 
| 
       49 
     | 
    
         
            -
                  previous, current =  
     | 
| 
      
 51 
     | 
    
         
            +
                  previous, current = content_quads_with(delay: delay)
         
     | 
| 
       50 
52 
     | 
    
         
             
                  previous == current
         
     | 
| 
       51 
53 
     | 
    
         
             
                end
         
     | 
| 
       52 
54 
     | 
    
         | 
| 
         @@ -122,17 +124,52 @@ module Ferrum 
     | 
|
| 
       122 
124 
     | 
    
         
             
                def property(name)
         
     | 
| 
       123 
125 
     | 
    
         
             
                  evaluate("this['#{name}']")
         
     | 
| 
       124 
126 
     | 
    
         
             
                end
         
     | 
| 
      
 127 
     | 
    
         
            +
                alias [] property
         
     | 
| 
       125 
128 
     | 
    
         | 
| 
       126 
129 
     | 
    
         
             
                def attribute(name)
         
     | 
| 
       127 
130 
     | 
    
         
             
                  evaluate("this.getAttribute('#{name}')")
         
     | 
| 
       128 
131 
     | 
    
         
             
                end
         
     | 
| 
       129 
132 
     | 
    
         | 
| 
      
 133 
     | 
    
         
            +
                def selected
         
     | 
| 
      
 134 
     | 
    
         
            +
                  function = <<~JS
         
     | 
| 
      
 135 
     | 
    
         
            +
                    function(element) {
         
     | 
| 
      
 136 
     | 
    
         
            +
                      if (element.nodeName.toLowerCase() !== 'select') {
         
     | 
| 
      
 137 
     | 
    
         
            +
                        throw new Error('Element is not a <select> element.');
         
     | 
| 
      
 138 
     | 
    
         
            +
                      }
         
     | 
| 
      
 139 
     | 
    
         
            +
                      return Array.from(element).filter(option => option.selected);
         
     | 
| 
      
 140 
     | 
    
         
            +
                    }
         
     | 
| 
      
 141 
     | 
    
         
            +
                  JS
         
     | 
| 
      
 142 
     | 
    
         
            +
                  page.evaluate_func(function, self, on: self)
         
     | 
| 
      
 143 
     | 
    
         
            +
                end
         
     | 
| 
      
 144 
     | 
    
         
            +
             
     | 
| 
      
 145 
     | 
    
         
            +
                def select(*values, by: :value)
         
     | 
| 
      
 146 
     | 
    
         
            +
                  tap do
         
     | 
| 
      
 147 
     | 
    
         
            +
                    function = <<~JS
         
     | 
| 
      
 148 
     | 
    
         
            +
                      function(element, values, by) {
         
     | 
| 
      
 149 
     | 
    
         
            +
                        if (element.nodeName.toLowerCase() !== 'select') {
         
     | 
| 
      
 150 
     | 
    
         
            +
                          throw new Error('Element is not a <select> element.');
         
     | 
| 
      
 151 
     | 
    
         
            +
                        }
         
     | 
| 
      
 152 
     | 
    
         
            +
                        const options = Array.from(element.options);
         
     | 
| 
      
 153 
     | 
    
         
            +
                        element.value = undefined;
         
     | 
| 
      
 154 
     | 
    
         
            +
                        for (const option of options) {
         
     | 
| 
      
 155 
     | 
    
         
            +
                          option.selected = values.some((value) => option[by] === value);
         
     | 
| 
      
 156 
     | 
    
         
            +
                          if (option.selected && !element.multiple) break;
         
     | 
| 
      
 157 
     | 
    
         
            +
                        }
         
     | 
| 
      
 158 
     | 
    
         
            +
                        element.dispatchEvent(new Event('input', { bubbles: true }));
         
     | 
| 
      
 159 
     | 
    
         
            +
                        element.dispatchEvent(new Event('change', { bubbles: true }));
         
     | 
| 
      
 160 
     | 
    
         
            +
                      }
         
     | 
| 
      
 161 
     | 
    
         
            +
                    JS
         
     | 
| 
      
 162 
     | 
    
         
            +
                    page.evaluate_func(function, self, values.flatten, by, on: self)
         
     | 
| 
      
 163 
     | 
    
         
            +
                  end
         
     | 
| 
      
 164 
     | 
    
         
            +
                end
         
     | 
| 
      
 165 
     | 
    
         
            +
             
     | 
| 
       130 
166 
     | 
    
         
             
                def evaluate(expression)
         
     | 
| 
       131 
167 
     | 
    
         
             
                  page.evaluate_on(node: self, expression: expression)
         
     | 
| 
       132 
168 
     | 
    
         
             
                end
         
     | 
| 
       133 
169 
     | 
    
         | 
| 
       134 
170 
     | 
    
         
             
                def ==(other)
         
     | 
| 
       135 
171 
     | 
    
         
             
                  return false unless other.is_a?(Node)
         
     | 
| 
      
 172 
     | 
    
         
            +
             
     | 
| 
       136 
173 
     | 
    
         
             
                  # We compare backendNodeId because once nodeId is sent to frontend backend
         
     | 
| 
       137 
174 
     | 
    
         
             
                  # never returns same nodeId sending 0. In other words frontend is
         
     | 
| 
       138 
175 
     | 
    
         
             
                  # responsible for keeping track of node ids.
         
     | 
| 
         @@ -147,30 +184,39 @@ module Ferrum 
     | 
|
| 
       147 
184 
     | 
    
         
             
                  points = wait_for_stop_moving.map { |q| to_points(q) }.first
         
     | 
| 
       148 
185 
     | 
    
         
             
                  get_position(points, x, y, position)
         
     | 
| 
       149 
186 
     | 
    
         
             
                rescue CoordinatesNotFoundError
         
     | 
| 
       150 
     | 
    
         
            -
                  x, y =  
     | 
| 
       151 
     | 
    
         
            -
                  raise if x  
     | 
| 
      
 187 
     | 
    
         
            +
                  x, y = bounding_rect_coordinates
         
     | 
| 
      
 188 
     | 
    
         
            +
                  raise if x.zero? && y.zero?
         
     | 
| 
      
 189 
     | 
    
         
            +
             
     | 
| 
       152 
190 
     | 
    
         
             
                  [x, y]
         
     | 
| 
       153 
191 
     | 
    
         
             
                end
         
     | 
| 
       154 
192 
     | 
    
         | 
| 
      
 193 
     | 
    
         
            +
                # Returns a hash of the computed styles for the node
         
     | 
| 
      
 194 
     | 
    
         
            +
                def computed_style
         
     | 
| 
      
 195 
     | 
    
         
            +
                  page
         
     | 
| 
      
 196 
     | 
    
         
            +
                    .command("CSS.getComputedStyleForNode", nodeId: node_id)["computedStyle"]
         
     | 
| 
      
 197 
     | 
    
         
            +
                    .each_with_object({}) { |style, memo| memo.merge!(style["name"] => style["value"]) }
         
     | 
| 
      
 198 
     | 
    
         
            +
                end
         
     | 
| 
      
 199 
     | 
    
         
            +
             
     | 
| 
       155 
200 
     | 
    
         
             
                private
         
     | 
| 
       156 
201 
     | 
    
         | 
| 
       157 
     | 
    
         
            -
                def  
     | 
| 
      
 202 
     | 
    
         
            +
                def bounding_rect_coordinates
         
     | 
| 
       158 
203 
     | 
    
         
             
                  evaluate <<~JS
         
     | 
| 
       159 
204 
     | 
    
         
             
                    [this.getBoundingClientRect().left + window.pageXOffset + (this.offsetWidth / 2),
         
     | 
| 
       160 
205 
     | 
    
         
             
                     this.getBoundingClientRect().top + window.pageYOffset + (this.offsetHeight / 2)]
         
     | 
| 
       161 
206 
     | 
    
         
             
                  JS
         
     | 
| 
       162 
207 
     | 
    
         
             
                end
         
     | 
| 
       163 
208 
     | 
    
         | 
| 
       164 
     | 
    
         
            -
                def  
     | 
| 
      
 209 
     | 
    
         
            +
                def content_quads
         
     | 
| 
       165 
210 
     | 
    
         
             
                  quads = page.command("DOM.getContentQuads", nodeId: node_id)["quads"]
         
     | 
| 
       166 
     | 
    
         
            -
                  raise CoordinatesNotFoundError, "Node is either not visible or not an HTMLElement" if quads.size 
     | 
| 
      
 211 
     | 
    
         
            +
                  raise CoordinatesNotFoundError, "Node is either not visible or not an HTMLElement" if quads.size.zero?
         
     | 
| 
      
 212 
     | 
    
         
            +
             
     | 
| 
       167 
213 
     | 
    
         
             
                  quads
         
     | 
| 
       168 
214 
     | 
    
         
             
                end
         
     | 
| 
       169 
215 
     | 
    
         | 
| 
       170 
     | 
    
         
            -
                def  
     | 
| 
       171 
     | 
    
         
            -
                  previous =  
     | 
| 
      
 216 
     | 
    
         
            +
                def content_quads_with(delay: MOVING_WAIT_DELAY)
         
     | 
| 
      
 217 
     | 
    
         
            +
                  previous = content_quads
         
     | 
| 
       172 
218 
     | 
    
         
             
                  sleep(delay)
         
     | 
| 
       173 
     | 
    
         
            -
                  current =  
     | 
| 
      
 219 
     | 
    
         
            +
                  current = content_quads
         
     | 
| 
       174 
220 
     | 
    
         
             
                  [previous, current]
         
     | 
| 
       175 
221 
     | 
    
         
             
                end
         
     | 
| 
       176 
222 
     | 
    
         | 
| 
         @@ -182,28 +228,28 @@ module Ferrum 
     | 
|
| 
       182 
228 
     | 
    
         
             
                    x = point[:x] + offset_x.to_i
         
     | 
| 
       183 
229 
     | 
    
         
             
                    y = point[:y] + offset_y.to_i
         
     | 
| 
       184 
230 
     | 
    
         
             
                  else
         
     | 
| 
       185 
     | 
    
         
            -
                    x, y = points.inject([0, 0]) do |memo,  
     | 
| 
       186 
     | 
    
         
            -
                      [memo[0] +  
     | 
| 
       187 
     | 
    
         
            -
                       memo[1] +  
     | 
| 
      
 231 
     | 
    
         
            +
                    x, y = points.inject([0, 0]) do |memo, coordinate|
         
     | 
| 
      
 232 
     | 
    
         
            +
                      [memo[0] + coordinate[:x],
         
     | 
| 
      
 233 
     | 
    
         
            +
                       memo[1] + coordinate[:y]]
         
     | 
| 
       188 
234 
     | 
    
         
             
                    end
         
     | 
| 
       189 
235 
     | 
    
         | 
| 
       190 
     | 
    
         
            -
                    x  
     | 
| 
       191 
     | 
    
         
            -
                    y  
     | 
| 
      
 236 
     | 
    
         
            +
                    x /= 4
         
     | 
| 
      
 237 
     | 
    
         
            +
                    y /= 4
         
     | 
| 
       192 
238 
     | 
    
         
             
                  end
         
     | 
| 
       193 
239 
     | 
    
         | 
| 
       194 
240 
     | 
    
         
             
                  if offset_x && offset_y && position == :center
         
     | 
| 
       195 
     | 
    
         
            -
                    x  
     | 
| 
       196 
     | 
    
         
            -
                    y  
     | 
| 
      
 241 
     | 
    
         
            +
                    x += offset_x.to_i
         
     | 
| 
      
 242 
     | 
    
         
            +
                    y += offset_y.to_i
         
     | 
| 
       197 
243 
     | 
    
         
             
                  end
         
     | 
| 
       198 
244 
     | 
    
         | 
| 
       199 
245 
     | 
    
         
             
                  [x, y]
         
     | 
| 
       200 
246 
     | 
    
         
             
                end
         
     | 
| 
       201 
247 
     | 
    
         | 
| 
       202 
248 
     | 
    
         
             
                def to_points(quad)
         
     | 
| 
       203 
     | 
    
         
            -
                  [{x: quad[0], y: quad[1]},
         
     | 
| 
       204 
     | 
    
         
            -
                   {x: quad[2], y: quad[3]},
         
     | 
| 
       205 
     | 
    
         
            -
                   {x: quad[4], y: quad[5]},
         
     | 
| 
       206 
     | 
    
         
            -
                   {x: quad[6], y: quad[7]}]
         
     | 
| 
      
 249 
     | 
    
         
            +
                  [{ x: quad[0], y: quad[1] },
         
     | 
| 
      
 250 
     | 
    
         
            +
                   { x: quad[2], y: quad[3] },
         
     | 
| 
      
 251 
     | 
    
         
            +
                   { x: quad[4], y: quad[5] },
         
     | 
| 
      
 252 
     | 
    
         
            +
                   { x: quad[6], y: quad[7] }]
         
     | 
| 
       207 
253 
     | 
    
         
             
                end
         
     | 
| 
       208 
254 
     | 
    
         
             
              end
         
     | 
| 
       209 
255 
     | 
    
         
             
            end
         
     | 
    
        data/lib/ferrum/page/frames.rb
    CHANGED
    
    | 
         @@ -11,35 +11,60 @@ module Ferrum 
     | 
|
| 
       11 
11 
     | 
    
         
             
                    @frames.values
         
     | 
| 
       12 
12 
     | 
    
         
             
                  end
         
     | 
| 
       13 
13 
     | 
    
         | 
| 
       14 
     | 
    
         
            -
                  def frame_by(id: nil, name: nil)
         
     | 
| 
      
 14 
     | 
    
         
            +
                  def frame_by(id: nil, name: nil, execution_id: nil)
         
     | 
| 
       15 
15 
     | 
    
         
             
                    if id
         
     | 
| 
       16 
16 
     | 
    
         
             
                      @frames[id]
         
     | 
| 
       17 
17 
     | 
    
         
             
                    elsif name
         
     | 
| 
       18 
18 
     | 
    
         
             
                      frames.find { |f| f.name == name }
         
     | 
| 
      
 19 
     | 
    
         
            +
                    elsif execution_id
         
     | 
| 
      
 20 
     | 
    
         
            +
                      frames.find { |f| f.execution_id == execution_id }
         
     | 
| 
       19 
21 
     | 
    
         
             
                    else
         
     | 
| 
       20 
22 
     | 
    
         
             
                      raise ArgumentError
         
     | 
| 
       21 
23 
     | 
    
         
             
                    end
         
     | 
| 
       22 
24 
     | 
    
         
             
                  end
         
     | 
| 
       23 
25 
     | 
    
         | 
| 
       24 
26 
     | 
    
         
             
                  def frames_subscribe
         
     | 
| 
      
 27 
     | 
    
         
            +
                    subscribe_frame_attached
         
     | 
| 
      
 28 
     | 
    
         
            +
                    subscribe_frame_started_loading
         
     | 
| 
      
 29 
     | 
    
         
            +
                    subscribe_frame_navigated
         
     | 
| 
      
 30 
     | 
    
         
            +
                    subscribe_frame_stopped_loading
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                    subscribe_navigated_within_document
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                    subscribe_request_will_be_sent
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                    subscribe_execution_context_created
         
     | 
| 
      
 37 
     | 
    
         
            +
                    subscribe_execution_context_destroyed
         
     | 
| 
      
 38 
     | 
    
         
            +
                    subscribe_execution_contexts_cleared
         
     | 
| 
      
 39 
     | 
    
         
            +
                  end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                  private
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                  def subscribe_frame_attached
         
     | 
| 
       25 
44 
     | 
    
         
             
                    on("Page.frameAttached") do |params|
         
     | 
| 
       26 
45 
     | 
    
         
             
                      parent_frame_id, frame_id = params.values_at("parentFrameId", "frameId")
         
     | 
| 
       27 
46 
     | 
    
         
             
                      @frames[frame_id] = Frame.new(frame_id, self, parent_frame_id)
         
     | 
| 
       28 
47 
     | 
    
         
             
                    end
         
     | 
| 
      
 48 
     | 
    
         
            +
                  end
         
     | 
| 
       29 
49 
     | 
    
         | 
| 
      
 50 
     | 
    
         
            +
                  def subscribe_frame_started_loading
         
     | 
| 
       30 
51 
     | 
    
         
             
                    on("Page.frameStartedLoading") do |params|
         
     | 
| 
       31 
52 
     | 
    
         
             
                      frame = @frames[params["frameId"]]
         
     | 
| 
       32 
53 
     | 
    
         
             
                      frame.state = :started_loading
         
     | 
| 
       33 
54 
     | 
    
         
             
                      @event.reset
         
     | 
| 
       34 
55 
     | 
    
         
             
                    end
         
     | 
| 
      
 56 
     | 
    
         
            +
                  end
         
     | 
| 
       35 
57 
     | 
    
         | 
| 
      
 58 
     | 
    
         
            +
                  def subscribe_frame_navigated
         
     | 
| 
       36 
59 
     | 
    
         
             
                    on("Page.frameNavigated") do |params|
         
     | 
| 
       37 
60 
     | 
    
         
             
                      frame_id, name = params["frame"]&.values_at("id", "name")
         
     | 
| 
       38 
61 
     | 
    
         
             
                      frame = @frames[frame_id]
         
     | 
| 
       39 
62 
     | 
    
         
             
                      frame.state = :navigated
         
     | 
| 
       40 
63 
     | 
    
         
             
                      frame.name = name unless name.to_s.empty?
         
     | 
| 
       41 
64 
     | 
    
         
             
                    end
         
     | 
| 
      
 65 
     | 
    
         
            +
                  end
         
     | 
| 
       42 
66 
     | 
    
         | 
| 
      
 67 
     | 
    
         
            +
                  def subscribe_frame_stopped_loading
         
     | 
| 
       43 
68 
     | 
    
         
             
                    on("Page.frameStoppedLoading") do |params|
         
     | 
| 
       44 
69 
     | 
    
         
             
                      # `DOM.performSearch` doesn't work without getting #document node first.
         
     | 
| 
       45 
70 
     | 
    
         
             
                      # It returns node with nodeId 1 and nodeType 9 from which descend the
         
     | 
| 
         @@ -47,7 +72,7 @@ module Ferrum 
     | 
|
| 
       47 
72 
     | 
    
         
             
                      # node will change the id and all subsequent nodes have to change id too.
         
     | 
| 
       48 
73 
     | 
    
         
             
                      if @main_frame.id == params["frameId"]
         
     | 
| 
       49 
74 
     | 
    
         
             
                        @event.set if idling?
         
     | 
| 
       50 
     | 
    
         
            -
                         
     | 
| 
      
 75 
     | 
    
         
            +
                        document_node_id
         
     | 
| 
       51 
76 
     | 
    
         
             
                      end
         
     | 
| 
       52 
77 
     | 
    
         | 
| 
       53 
78 
     | 
    
         
             
                      frame = @frames[params["frameId"]]
         
     | 
| 
         @@ -55,58 +80,59 @@ module Ferrum 
     | 
|
| 
       55 
80 
     | 
    
         | 
| 
       56 
81 
     | 
    
         
             
                      @event.set if idling?
         
     | 
| 
       57 
82 
     | 
    
         
             
                    end
         
     | 
| 
      
 83 
     | 
    
         
            +
                  end
         
     | 
| 
       58 
84 
     | 
    
         | 
| 
      
 85 
     | 
    
         
            +
                  def subscribe_navigated_within_document
         
     | 
| 
       59 
86 
     | 
    
         
             
                    on("Page.navigatedWithinDocument") do
         
     | 
| 
       60 
87 
     | 
    
         
             
                      @event.set if idling?
         
     | 
| 
       61 
88 
     | 
    
         
             
                    end
         
     | 
| 
      
 89 
     | 
    
         
            +
                  end
         
     | 
| 
       62 
90 
     | 
    
         | 
| 
      
 91 
     | 
    
         
            +
                  def subscribe_request_will_be_sent
         
     | 
| 
       63 
92 
     | 
    
         
             
                    on("Network.requestWillBeSent") do |params|
         
     | 
| 
       64 
     | 
    
         
            -
                       
     | 
| 
       65 
     | 
    
         
            -
             
     | 
| 
       66 
     | 
    
         
            -
             
     | 
| 
       67 
     | 
    
         
            -
             
     | 
| 
       68 
     | 
    
         
            -
             
     | 
| 
       69 
     | 
    
         
            -
                        @event.reset if params["type"] == "Document"
         
     | 
| 
       70 
     | 
    
         
            -
                      end
         
     | 
| 
      
 93 
     | 
    
         
            +
                      # Possible types:
         
     | 
| 
      
 94 
     | 
    
         
            +
                      # Document, Stylesheet, Image, Media, Font, Script, TextTrack, XHR,
         
     | 
| 
      
 95 
     | 
    
         
            +
                      # Fetch, EventSource, WebSocket, Manifest, SignedExchange, Ping,
         
     | 
| 
      
 96 
     | 
    
         
            +
                      # CSPViolationReport, Other
         
     | 
| 
      
 97 
     | 
    
         
            +
                      @event.reset if params["frameId"] == @main_frame.id && params["type"] == "Document"
         
     | 
| 
       71 
98 
     | 
    
         
             
                    end
         
     | 
| 
      
 99 
     | 
    
         
            +
                  end
         
     | 
| 
       72 
100 
     | 
    
         | 
| 
      
 101 
     | 
    
         
            +
                  def subscribe_execution_context_created
         
     | 
| 
       73 
102 
     | 
    
         
             
                    on("Runtime.executionContextCreated") do |params|
         
     | 
| 
       74 
     | 
    
         
            -
                      setting_up_main_frame = false
         
     | 
| 
       75 
103 
     | 
    
         
             
                      context_id = params.dig("context", "id")
         
     | 
| 
       76 
104 
     | 
    
         
             
                      frame_id = params.dig("context", "auxData", "frameId")
         
     | 
| 
       77 
105 
     | 
    
         | 
| 
       78 
106 
     | 
    
         
             
                      unless @main_frame.id
         
     | 
| 
       79 
107 
     | 
    
         
             
                        root_frame = command("Page.getFrameTree").dig("frameTree", "frame", "id")
         
     | 
| 
       80 
108 
     | 
    
         
             
                        if frame_id == root_frame
         
     | 
| 
       81 
     | 
    
         
            -
                          setting_up_main_frame = true
         
     | 
| 
       82 
109 
     | 
    
         
             
                          @main_frame.id = frame_id
         
     | 
| 
       83 
110 
     | 
    
         
             
                          @frames[frame_id] = @main_frame
         
     | 
| 
       84 
111 
     | 
    
         
             
                        end
         
     | 
| 
       85 
112 
     | 
    
         
             
                      end
         
     | 
| 
       86 
113 
     | 
    
         | 
| 
       87 
114 
     | 
    
         
             
                      frame = @frames[frame_id] || Frame.new(frame_id, self)
         
     | 
| 
       88 
     | 
    
         
            -
                      frame. 
     | 
| 
       89 
     | 
    
         
            -
             
     | 
| 
       90 
     | 
    
         
            -
                      # Set event because `execution_id` might raise NoExecutionContextError
         
     | 
| 
       91 
     | 
    
         
            -
                      @event.set if setting_up_main_frame
         
     | 
| 
      
 115 
     | 
    
         
            +
                      frame.execution_id = context_id
         
     | 
| 
       92 
116 
     | 
    
         | 
| 
       93 
117 
     | 
    
         
             
                      @frames[frame_id] ||= frame
         
     | 
| 
       94 
118 
     | 
    
         
             
                    end
         
     | 
| 
      
 119 
     | 
    
         
            +
                  end
         
     | 
| 
       95 
120 
     | 
    
         | 
| 
      
 121 
     | 
    
         
            +
                  def subscribe_execution_context_destroyed
         
     | 
| 
       96 
122 
     | 
    
         
             
                    on("Runtime.executionContextDestroyed") do |params|
         
     | 
| 
       97 
123 
     | 
    
         
             
                      execution_id = params["executionContextId"]
         
     | 
| 
       98 
     | 
    
         
            -
                      frame =  
     | 
| 
       99 
     | 
    
         
            -
                      frame 
     | 
| 
      
 124 
     | 
    
         
            +
                      frame = frame_by(execution_id: execution_id)
         
     | 
| 
      
 125 
     | 
    
         
            +
                      frame&.execution_id = nil
         
     | 
| 
       100 
126 
     | 
    
         
             
                    end
         
     | 
| 
      
 127 
     | 
    
         
            +
                  end
         
     | 
| 
       101 
128 
     | 
    
         | 
| 
      
 129 
     | 
    
         
            +
                  def subscribe_execution_contexts_cleared
         
     | 
| 
       102 
130 
     | 
    
         
             
                    on("Runtime.executionContextsCleared") do
         
     | 
| 
       103 
131 
     | 
    
         
             
                      @frames.delete_if { |_, f| !f.main? }
         
     | 
| 
       104 
     | 
    
         
            -
                      @main_frame. 
     | 
| 
      
 132 
     | 
    
         
            +
                      @main_frame.execution_id = nil
         
     | 
| 
       105 
133 
     | 
    
         
             
                    end
         
     | 
| 
       106 
134 
     | 
    
         
             
                  end
         
     | 
| 
       107 
135 
     | 
    
         | 
| 
       108 
     | 
    
         
            -
                  private
         
     | 
| 
       109 
     | 
    
         
            -
             
     | 
| 
       110 
136 
     | 
    
         
             
                  def idling?
         
     | 
| 
       111 
137 
     | 
    
         
             
                    @frames.all? { |_, f| f.state == :stopped_loading }
         
     | 
| 
       112 
138 
     | 
    
         
             
                  end
         
     |