Module: Obxcura::Frame::Runtime
- Included in:
- Obxcura::Frame
- Defined in:
- lib/obxcura/frame/runtime.rb
Overview
Runtime: executing JavaScript in the frame's context. This is the side that reaches the Client and puts CDP commands on the wire; everything in DOM is built on top of #evaluate.
Ruby values cross into JS as real arguments (Runtime.callFunctionOn), not by being string-interpolated into source. Node-handle resolution, cyclic-node detection and a retry loop are deliberately left out.
Constant Summary collapse
- EVALUATE_CHUNK =
Obscura won't send a single CDP message larger than ~500-700KB, so we pull large strings back in slices this size and stitch them together. Well under the ceiling, and each slice is fast over the websocket-driver transport.
400_000
Instance Method Summary collapse
-
#call_on(object_id, function_declaration, args = [], timeout: nil) ⇒ Object?
Call a function declaration bound to a specific remote object (its
this). -
#evaluate(expression, *args) ⇒ Object?
Evaluate a JS expression and return its value (awaits promises).
-
#evaluate_func(expression, *args, timeout: nil) ⇒ Object?
Call a JS function declaration with the given arguments, bound to the live global object.
-
#read_string(js_expression) ⇒ String
Pull a possibly-large JS string back in EVALUATE_CHUNK-sized slices.
Instance Method Details
#call_on(object_id, function_declaration, args = [], timeout: nil) ⇒ Object?
Call a function declaration bound to a specific remote object (its this).
Used by #evaluate_func (bound to globalThis) and by Node reads (bound
to the node's handle).
64 65 66 67 68 69 70 71 72 73 74 75 76 |
# File 'lib/obxcura/frame/runtime.rb', line 64 def call_on(object_id, function_declaration, args = [], timeout: nil) handle_result(command( "Runtime.callFunctionOn", { functionDeclaration: function_declaration, objectId: object_id, arguments: args.map { |value| { value: value } }, awaitPromise: true, returnByValue: true }, timeout: timeout )) end |
#evaluate(expression, *args) ⇒ Object?
Evaluate a JS expression and return its value (awaits promises).
Extra args are passed to the page as real values, reachable in the
expression as arguments[0], arguments[1], ... Without args it's a
single Runtime.evaluate (the hot path); with args it goes through
Runtime.callFunctionOn so nothing is interpolated into source.
33 34 35 36 37 |
# File 'lib/obxcura/frame/runtime.rb', line 33 def evaluate(expression, *args) return handle_result(evaluate_command(expression)) if args.empty? evaluate_func("function() { return (#{expression}); }", *args) end |
#evaluate_func(expression, *args, timeout: nil) ⇒ Object?
Call a JS function declaration with the given arguments, bound to the live
global object. Unlike #evaluate, expression is the function itself.
49 50 51 |
# File 'lib/obxcura/frame/runtime.rb', line 49 def evaluate_func(expression, *args, timeout: nil) call_on(global_object_id, expression, args, timeout: timeout) end |
#read_string(js_expression) ⇒ String
Pull a possibly-large JS string back in EVALUATE_CHUNK-sized slices. The expression is snapshotted into a page global once, so it's evaluated a single time no matter how big the result is; we then slice that global.
84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/obxcura/frame/runtime.rb', line 84 def read_string(js_expression) length = evaluate("(window.__obxcura_read = String(#{js_expression})).length").to_i return "" if length.zero? buffer = String.new(capacity: length) offset = 0 while offset < length buffer << evaluate("window.__obxcura_read.slice(#{offset}, #{offset + EVALUATE_CHUNK})") offset += EVALUATE_CHUNK end buffer end |