diff --git a/Gemfile b/Gemfile index 9c15ec98..738bbc05 100644 --- a/Gemfile +++ b/Gemfile @@ -16,7 +16,7 @@ gem "rspec", "~> 3.8" gem "rspec-wait" gem "rubocop", "~> 1.22" gem "rubocop-rake", require: false -gem "sinatra", "~> 3.2" +gem "sinatra", "~> 4.0" gem "yard", "~> 0.9", require: false gemspec diff --git a/lib/ferrum/frame/runtime.rb b/lib/ferrum/frame/runtime.rb index 24c40ced..b1ef52dc 100644 --- a/lib/ferrum/frame/runtime.rb +++ b/lib/ferrum/frame/runtime.rb @@ -152,13 +152,8 @@ def handle_response(response) case response["subtype"] when "node" - # We cannot store object_id in the node because page can be reloaded - # and node destroyed so we need to retrieve it each time for given id. - # Though we can try to subscribe to `DOM.childNodeRemoved` and - # `DOM.childNodeInserted` in the future. - node_id = @page.command("DOM.requestNode", objectId: object_id)["nodeId"] - description = @page.command("DOM.describeNode", nodeId: node_id)["node"] - Node.new(self, @page.target_id, node_id, description) + description = @page.command("DOM.describeNode", objectId: object_id)["node"] + Node.new(self, @page.target_id, description, object_id: object_id) when "array" reduce_props(object_id, []) do |memo, key, value| next(memo) unless Integer(key, exception: false) diff --git a/lib/ferrum/node.rb b/lib/ferrum/node.rb index 3209aa5f..b55e07bf 100644 --- a/lib/ferrum/node.rb +++ b/lib/ferrum/node.rb @@ -5,14 +5,28 @@ class Node MOVING_WAIT_DELAY = ENV.fetch("FERRUM_NODE_MOVING_WAIT", 0.01).to_f MOVING_WAIT_ATTEMPTS = ENV.fetch("FERRUM_NODE_MOVING_ATTEMPTS", 50).to_i - attr_reader :page, :target_id, :node_id, :description, :tag_name + attr_reader :page, :target_id, :description, :tag_name - def initialize(frame, target_id, node_id, description) + def initialize(frame, target_id, description, object_id: nil, node_id: nil) @page = frame.page @target_id = target_id - @node_id = node_id @description = description @tag_name = description["nodeName"].downcase + @object_id = object_id + @node_id = node_id + end + + # Frontend nodeId is resolved lazily, on first actual need (focus, click, scroll_into_view, etc.) + # Though we can try to subscribe to `DOM.childNodeRemoved` and `DOM.childNodeInserted` in the future. + def node_id + @node_id ||= begin + id = page.command("DOM.requestNode", objectId: @object_id)["nodeId"] + raise NodeNotFoundError, "node is not trackable" if id.zero? + + id + rescue NoExecutionContextError + raise NodeNotFoundError, "node is not trackable" + end end def node? diff --git a/lib/ferrum/page.rb b/lib/ferrum/page.rb index 4e641135..612c7f4c 100644 --- a/lib/ferrum/page.rb +++ b/lib/ferrum/page.rb @@ -459,7 +459,7 @@ def subscribe def prepare_page command("Page.enable") command("Runtime.enable") - command("DOM.enable") + command("DOM.enable", includeWhitespace: "all") command("CSS.enable") command("Log.enable") command("Network.enable") diff --git a/sig/ferrum/node.rbs b/sig/ferrum/node.rbs index 63872cb5..af49d39e 100644 --- a/sig/ferrum/node.rbs +++ b/sig/ferrum/node.rbs @@ -4,12 +4,13 @@ module Ferrum MOVING_WAIT_ATTEMPTS: untyped + @node_id: untyped + @object_id: untyped + attr_reader page: untyped attr_reader target_id: untyped - attr_reader node_id: untyped - attr_reader description: untyped attr_reader tag_name: untyped @@ -26,6 +27,8 @@ module Ferrum def focusable?: () -> untyped + def node_id: -> untyped + def wait_for_stop_moving: (?delay: untyped, ?attempts: untyped) -> untyped def moving?: (?delay: untyped) -> untyped