-
-
Notifications
You must be signed in to change notification settings - Fork 36
/
js_handle.rb
151 lines (133 loc) · 4.14 KB
/
js_handle.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
class Puppeteer::JSHandle
using Puppeteer::DefineAsyncMethod
include Puppeteer::IfPresent
# @param context [Puppeteer::ExecutionContext]
# @param remote_object [Puppeteer::RemoteObject]
def self.create(context:, remote_object:)
frame = context.frame
if remote_object.sub_type == 'node' && frame
frame_manager = frame.frame_manager
Puppeteer::ElementHandle.new(
context: context,
client: context.client,
remote_object: remote_object,
page: frame_manager.page,
frame_manager: frame_manager,
)
else
Puppeteer::JSHandle.new(
context: context,
client: context.client,
remote_object: remote_object,
)
end
end
# @param context [Puppeteer::ExecutionContext]
# @param client [Puppeteer::CDPSession]
# @param remote_object [Puppeteer::RemoteObject]
def initialize(context:, client:, remote_object:)
@context = context
@client = client
@remote_object = remote_object
@disposed = false
end
attr_reader :context, :remote_object
def inspect
values = %i[context remote_object disposed].map do |sym|
value = instance_variable_get(:"@#{sym}")
"@#{sym}=#{value}"
end
"#<Puppeteer::JSHandle #{values.join(' ')}>"
end
# @return [Puppeteer::ExecutionContext]
def execution_context
@context
end
# @param page_function [String]
# @return [Object]
def evaluate(page_function, *args)
execution_context.evaluate(page_function, self, *args)
end
define_async_method :async_evaluate
# @param page_function [String]
# @param args {Array<*>}
# @return [Puppeteer::JSHandle]
def evaluate_handle(page_function, *args)
execution_context.evaluate_handle(page_function, self, *args)
end
define_async_method :async_evaluate_handle
# getProperty(propertyName) in JavaScript
# @param name [String]
# @return [Puppeteer::JSHandle]
def property(name)
js = <<~JAVASCRIPT
(object, propertyName) => {
const result = {__proto__: null};
result[propertyName] = object[propertyName];
return result;
}
JAVASCRIPT
object_handle = evaluate_handle(js, name)
properties = object_handle.properties
result = properties[name]
object_handle.dispose
result
end
# @param name [String]
# @return [Puppeteer::JSHandle]
def [](name)
property(name)
end
# getProperties in JavaScript.
# @return [Hash<String, JSHandle>]
def properties
response = @remote_object.properties(@client)
response['result'].each_with_object({}) do |prop, h|
next unless prop['enumerable']
h[prop['name']] = Puppeteer::JSHandle.create(
context: @context,
remote_object: Puppeteer::RemoteObject.new(prop['value']),
)
end
end
def json_value
# original logic was:
# if (this._remoteObject.objectId) {
# const response = await this._client.send('Runtime.callFunctionOn', {
# functionDeclaration: 'function() { return this; }',
# objectId: this._remoteObject.objectId,
# returnByValue: true,
# awaitPromise: true,
# });
# return helper.valueFromRemoteObject(response.result);
# }
# return helper.valueFromRemoteObject(this._remoteObject);
#
# However it would be better that RemoteObject is responsible for
# the logic `if (this._remoteObject.objectId) { ... }`.
@remote_object.evaluate_self(@client)&.value || @remote_object.value
end
def as_element
nil
end
def dispose
return if @disposed
@disposed = true
@remote_object.release(@client)
end
def disposed?
@disposed
end
def to_s
# original logic was:
# if (this._remoteObject.objectId) {
# const type = this._remoteObject.subtype || this._remoteObject.type;
# return 'JSHandle@' + type;
# }
# return 'JSHandle:' + helper.valueFromRemoteObject(this._remoteObject);
#
# However it would be better that RemoteObject is responsible for
# the logic `if (this._remoteObject.objectId) { ... }`.
if_present(@remote_object.type_str) { |type_str| "JSHandle@#{type_str}" } || "JSHandle:#{@remote_object.value || 'undefined'}"
end
end