Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix!: remove root from WaitForSelectorOptions #262

Merged
merged 1 commit into from Sep 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion development/DOCS_VERSION
@@ -1 +1 @@
16.2.0
17.1.3
1,119 changes: 390 additions & 729 deletions development/puppeteer.api.json

Large diffs are not rendered by default.

15 changes: 4 additions & 11 deletions docs/api_coverage.md
@@ -1,5 +1,5 @@
# API coverages
- Puppeteer version: v16.2.0
- Puppeteer version: v17.1.3
- puppeteer-ruby version: 0.43.1

## Puppeteer
Expand Down Expand Up @@ -150,13 +150,6 @@
* ~~removeAllListeners~~
* ~~removeListener~~

## ExecutionContext

* evaluate
* evaluateHandle => `#evaluate_handle`
* frame
* ~~queryObjects~~

## FileChooser

* accept
Expand All @@ -172,12 +165,12 @@
* $x => `#Sx`
* addScriptTag => `#add_script_tag`
* addStyleTag => `#add_style_tag`
* addStyleTag => `#add_style_tag`
* childFrames => `#child_frames`
* click
* content
* evaluate
* evaluateHandle => `#evaluate_handle`
* executionContext => `#execution_context`
* focus
* goto
* hover
Expand Down Expand Up @@ -251,7 +244,6 @@
* dispose
* evaluate
* evaluateHandle => `#evaluate_handle`
* executionContext => `#execution_context`
* getProperties => `#properties`
* getProperty => `#[]`
* getProperty => `#[]`
Expand Down Expand Up @@ -289,6 +281,7 @@
* $x => `#Sx`
* addScriptTag => `#add_script_tag`
* addStyleTag => `#add_style_tag`
* addStyleTag => `#add_style_tag`
* authenticate
* bringToFront => `#bring_to_front`
* browser
Expand All @@ -313,6 +306,7 @@
* exposeFunction => `#expose_function`
* focus
* frames
* ~~getDefaultTimeout~~
* goBack => `#go_back`
* goForward => `#go_forward`
* goto
Expand Down Expand Up @@ -401,5 +395,4 @@

* ~~evaluate~~
* ~~evaluateHandle~~
* ~~executionContext~~
* ~~url~~
2 changes: 1 addition & 1 deletion lib/puppeteer.rb
Expand Up @@ -32,7 +32,6 @@ module Puppeteer; end
require 'puppeteer/custom_query_handler'
require 'puppeteer/devices'
require 'puppeteer/dialog'
require 'puppeteer/dom_world'
require 'puppeteer/emulation_manager'
require 'puppeteer/exception_details'
require 'puppeteer/executable_path_finder'
Expand All @@ -43,6 +42,7 @@ module Puppeteer; end
require 'puppeteer/frame_manager'
require 'puppeteer/http_request'
require 'puppeteer/http_response'
require 'puppeteer/isolated_world'
require 'puppeteer/js_coverage'
require 'puppeteer/js_handle'
require 'puppeteer/keyboard'
Expand Down
62 changes: 51 additions & 11 deletions lib/puppeteer/aria_query_handler.rb
Expand Up @@ -25,43 +25,83 @@ class Puppeteer::AriaQueryHandler
query_options
end

def query_one(element, selector)
context = element.execution_context
# @param element [Puppeteer::ElementHandle]
# @param selector [String]
private def query_one_id(element, selector)
parse_result = parse_aria_selector(selector)
res = element.query_ax_tree(accessible_name: parse_result[:name], role: parse_result[:role])
if res.empty?

if res.first.is_a?(Hash)
res.first['backendDOMNodeId']
else
nil
end
end

def query_one(element, selector)
id = query_one_id(element, selector)

if id
element.frame.main_world.adopt_backend_node(id)
else
context.adopt_backend_node_id(res.first['backendDOMNodeId'])
nil
end
end

def wait_for(dom_world, selector, visible: nil, hidden: nil, timeout: nil, root: nil)
def wait_for(element_or_frame, selector, visible: nil, hidden: nil, timeout: nil)
case element_or_frame
when Puppeteer::Frame
frame = element_or_frame
element = nil
when Puppeteer::ElementHandle
frame = element_or_frame.frame
element = frame.puppeteer_world.adopt_handle(element_or_frame)
else
raise ArgumentError.new("element_or_frame must be a Frame or ElementHandle. #{element_or_frame.inspect}")
end

# addHandlerToWorld
binding_function = Puppeteer::DOMWorld::BindingFunction.new(
binding_function = Puppeteer::IsolaatedWorld::BindingFunction.new(
name: 'ariaQuerySelector',
proc: -> (sel) { query_one(root || dom_world.send(:document), sel) },
proc: -> (sel) {
id = query_one_id(element || frame.puppeteer_world.document, sel)

if id
frame.puppeteer_world.adopt_backend_node(id)
else
nil
end
},
)
dom_world.send(:wait_for_selector_in_page,
result = frame.puppeteer_world.send(:wait_for_selector_in_page,
'(_, selector) => globalThis.ariaQuerySelector(selector)',
element,
selector,
visible: visible,
hidden: hidden,
timeout: timeout,
binding_function: binding_function,
root: root,
)

element&.dispose

if result.is_a?(Puppeteer::ElementHandle)
result.frame.main_world.transfer_handle(result)
else
result&.dispose
nil
end
end

def query_all(element, selector)
context = element.execution_context
world = element.frame.main_world
parse_result = parse_aria_selector(selector)
res = element.query_ax_tree(accessible_name: parse_result[:name], role: parse_result[:role])
if res.empty?
nil
else
promises = res.map do |ax_node|
context.send(:async_adopt_backend_node_id, ax_node['backendDOMNodeId'])
world.send(:async_adopt_backend_node, ax_node['backendDOMNodeId'])
end
await_all(*promises)
end
Expand Down
31 changes: 29 additions & 2 deletions lib/puppeteer/custom_query_handler.rb
Expand Up @@ -21,12 +21,39 @@ def query_one(element, selector)
nil
end

def wait_for(dom_world, selector, visible: nil, hidden: nil, timeout: nil, root: nil)
def wait_for(element_or_frame, selector, visible: nil, hidden: nil, timeout: nil)
case element_or_frame
when Puppeteer::Frame
frame = element_or_frame
element = nil
when Puppeteer::ElementHandle
frame = element_or_frame.frame
element = frame.puppeteer_world.adopt_handle(element_or_frame)
else
raise ArgumentError.new("element_or_frame must be a Frame or ElementHandle. #{element_or_frame.inspect}")
end

unless @query_one
raise NotImplementedError.new("#{self.class}##{__method__} is not implemented.")
end

dom_world.send(:wait_for_selector_in_page, @query_one, selector, visible: visible, hidden: hidden, timeout: timeout, root: root)
result = frame.puppeteer_world.send(:wait_for_selector_in_page,
@query_one,
element,
selector,
visible: visible,
hidden: hidden,
timeout: timeout,
)

element&.dispose

if result.is_a?(Puppeteer::ElementHandle)
result.frame.main_world.transfer_handle(result)
else
result&.dispose
nil
end
end

def query_all(element, selector)
Expand Down
25 changes: 7 additions & 18 deletions lib/puppeteer/element_handle.rb
Expand Up @@ -12,16 +12,16 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
# @param client [Puppeteer::CDPSession]
# @param remote_object [Puppeteer::RemoteObject]
# @param frame [Puppeteer::Frame]
# @param page [Puppeteer::Page]
# @param frame_manager [Puppeteer::FrameManager]
def initialize(context:, client:, remote_object:, frame:, page:, frame_manager:)
def initialize(context:, client:, remote_object:, frame:)
super(context: context, client: client, remote_object: remote_object)
@frame = frame
@page = page
@frame_manager = frame_manager
@page = frame.page
@frame_manager = frame.frame_manager
@disposed = false
end

attr_reader :page, :frame, :frame_manager

def inspect
values = %i[context remote_object page disposed].map do |sym|
value = instance_variable_get(:"@#{sym}")
Expand Down Expand Up @@ -60,18 +60,7 @@ def inspect
# (30 seconds). Pass `0` to disable timeout. The default value can be changed
# by using the {@link Page.setDefaultTimeout} method.
def wait_for_selector(selector, visible: nil, hidden: nil, timeout: nil)
frame = @context.frame

secondary_world = frame.secondary_world
adopted_root = secondary_world.execution_context.adopt_element_handle(self)
handle = secondary_world.wait_for_selector(selector, visible: visible, hidden: hidden, timeout: timeout, root: adopted_root)
adopted_root.dispose
return nil unless handle

main_world = frame.main_world
result = main_world.execution_context.adopt_element_handle(handle)
handle.dispose
result
query_handler_manager.detect_query_handler(selector).wait_for(self, visible: visible, hidden: hidden, timeout: timeout)
end

define_async_method :async_wait_for_selector
Expand Down Expand Up @@ -653,6 +642,6 @@ def intersecting_viewport?(threshold: nil)
# used in AriaQueryHandler
def query_ax_tree(accessible_name: nil, role: nil)
@remote_object.query_ax_tree(@client,
accessible_name: accessible_name, role: role)
accessible_name: accessible_name, role: role)
end
end
63 changes: 3 additions & 60 deletions lib/puppeteer/execution_context.rb
Expand Up @@ -7,7 +7,7 @@ class Puppeteer::ExecutionContext

# @param client [Puppeteer::CDPSession]
# @param context_payload [Hash]
# @param world [Puppeteer::DOMWorld?]
# @param world [Puppeteer::IsolaatedWorld?]
def initialize(client, context_payload, world)
@client = client
@world = world
Expand All @@ -17,23 +17,16 @@ def initialize(client, context_payload, world)

attr_reader :client, :world

# only used in DOMWorld
# only used in IsolaatedWorld
private def _context_id
@context_id
end

# only used in DOMWorld::BindingFunction#add_binding_to_context
# only used in IsolaatedWorld::BindingFunction#add_binding_to_context
private def _context_name
@context_name
end

# @return [Puppeteer::Frame]
def frame
if_present(@world) do |world|
world.frame
end
end

# @param page_function [String]
# @return [Object]
def evaluate(page_function, *args)
Expand Down Expand Up @@ -208,54 +201,4 @@ class EvaluationError < StandardError; end
context_id: @context_id,
)
end

# /**
# * @param {!JSHandle} prototypeHandle
# * @return {!Promise<!JSHandle>}
# */
# async queryObjects(prototypeHandle) {
# assert(!prototypeHandle._disposed, 'Prototype JSHandle is disposed!');
# assert(prototypeHandle._remoteObject.objectId, 'Prototype JSHandle must not be referencing primitive value');
# const response = await this._client.send('Runtime.queryObjects', {
# prototypeObjectId: prototypeHandle._remoteObject.objectId
# });
# return createJSHandle(this, response.objects);
# }

# @param backend_node_id [Integer]
# @return [Puppeteer::ElementHandle]
def adopt_backend_node_id(backend_node_id)
response = @client.send_message('DOM.resolveNode',
backendNodeId: backend_node_id,
executionContextId: @context_id,
)
Puppeteer::JSHandle.create(
context: self,
remote_object: Puppeteer::RemoteObject.new(response["object"]),
)
end
private define_async_method :async_adopt_backend_node_id

# @param element_handle [Puppeteer::ElementHandle]
# @return [Puppeteer::ElementHandle]
def adopt_element_handle(element_handle)
if element_handle.execution_context == self
raise ArgumentError.new('Cannot adopt handle that already belongs to this execution context')
end

unless @world
raise 'Cannot adopt handle without DOMWorld'
end

node_info = element_handle.remote_object.node_info(@client)
response = @client.send_message('DOM.resolveNode',
backendNodeId: node_info["node"]["backendNodeId"],
executionContextId: @context_id,
)

Puppeteer::JSHandle.create(
context: self,
remote_object: Puppeteer::RemoteObject.new(response["object"]),
)
end
end