Class: Puppeteer::IsolaatedWorld

Inherits:
Object
  • Object
show all
Defined in:
lib/puppeteer/isolated_world.rb

Overview

Defined Under Namespace

Classes: BindingFunction, DetachedError, ElementNotFoundError

Constant Summary collapse

ADD_SCRIPT_URL =
<<~JAVASCRIPT
async (url, id, type) => {
  const script = document.createElement('script');
  script.src = url;
  if (id) script.id = id;
  if (type) script.type = type;
  const promise = new Promise((res, rej) => {
    script.onload = res;
    script.onerror = rej;
  });
  document.head.appendChild(script);
  await promise;
  return script;
}
JAVASCRIPT
ADD_SCRIPT_CONTENT =
<<~JAVASCRIPT
(content, id, type) => {
  const script = document.createElement('script');
  script.type = type;
  script.text = content;
  if (id) script.id = id;
  let error = null;
  script.onerror = e => error = e;
  document.head.appendChild(script);
  if (error)
    throw error;
  return script;
}
JAVASCRIPT
ADD_STYLE_URL =
<<~JAVASCRIPT
  async (url) => {
    const link = document.createElement('link');
    link.rel = 'stylesheet';
    link.href = url;
    const promise = new Promise((res, rej) => {
      link.onload = res;
      link.onerror = rej;
    });
    document.head.appendChild(link);
    await promise;
    return link;
  }
JAVASCRIPT
ADD_STYLE_CONTENT =
<<~JAVASCRIPT
  async (content) => {
    const style = document.createElement('style');
    style.type = 'text/css';
    style.appendChild(document.createTextNode(content));
    const promise = new Promise((res, rej) => {
      style.onload = res;
      style.onerror = rej;
    });
    document.head.appendChild(style);
    await promise;
    return style;
  }
JAVASCRIPT

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(client, frame_manager, frame, timeout_settings) ⇒ IsolaatedWorld

Returns a new instance of IsolaatedWorld.

Parameters:



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/puppeteer/isolated_world.rb', line 52

def initialize(client, frame_manager, frame, timeout_settings)
  # Keep own reference to client because it might differ from the FrameManager's
  # client for OOP iframes.
  @client = client
  @frame_manager = frame_manager
  @frame = frame
  @timeout_settings = timeout_settings
  @context_promise = Async::Promise.new
  @task_manager = Puppeteer::TaskManager.new
  @bound_functions = {}
  @ctx_bindings = Set.new
  @detached = false
  @context = nil
  @origin = nil
  @world_id = nil

  @client.on_event('Runtime.bindingCalled', &method(:handle_binding_called))
end

Instance Attribute Details

#frameObject (readonly)

Returns the value of attribute frame.



71
72
73
# File 'lib/puppeteer/isolated_world.rb', line 71

def frame
  @frame
end

#originObject

Returns the value of attribute origin.



71
72
73
# File 'lib/puppeteer/isolated_world.rb', line 71

def origin
  @origin
end

#task_managerObject (readonly)

Returns the value of attribute task_manager.



71
72
73
# File 'lib/puppeteer/isolated_world.rb', line 71

def task_manager
  @task_manager
end

#world_idObject

Returns the value of attribute world_id.



71
72
73
# File 'lib/puppeteer/isolated_world.rb', line 71

def world_id
  @world_id
end

Instance Method Details

#add_binding_to_context(context, binding_function) ⇒ Object



462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
# File 'lib/puppeteer/isolated_world.rb', line 462

def add_binding_to_context(context, binding_function)
  return if @ctx_bindings.include?(binding_identifier(binding_function.name, context))

  expression = binding_function.page_binding_init_string
  begin
    context.client.send_message('Runtime.addBinding',
      name: binding_function.name,
      executionContextName: context.send(:_context_name))
    context.evaluate(expression, 'internal', binding_function.name)
  rescue => err
    # We could have tried to evaluate in a context which was already
    # destroyed. This happens, for example, if the page is navigated while
    # we are trying to add the binding
    allowed = [
      'Execution context was destroyed',
      'Cannot find context with specified id',
    ]
    if allowed.any? { |msg| err.message.include?(msg) }
      # ignore
    else
      raise
    end
  end
  @ctx_bindings << binding_identifier(binding_function.name, context)
end

#add_script_tag(url: nil, path: nil, content: nil, id: nil, type: nil) ⇒ Object

Parameters:

  • url (String?) (defaults to: nil)
  • path (String?) (defaults to: nil)
  • content (String?) (defaults to: nil)
  • id (String?) (defaults to: nil)
  • type (String?) (defaults to: nil)

Raises:

  • (ArgumentError)


278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
# File 'lib/puppeteer/isolated_world.rb', line 278

def add_script_tag(url: nil, path: nil, content: nil, id: nil, type: nil)
  if url
    begin
      return execution_context.
        evaluate_handle(ADD_SCRIPT_URL, url, id, type || '').
        as_element
    rescue Puppeteer::ExecutionContext::EvaluationError, Puppeteer::Connection::ProtocolError
      raise "Loading script from #{url} failed"
    end
  end

  if path
    contents = File.read(path)
    contents += "//# sourceURL=#{path.gsub(/\n/, '')}"
    return execution_context.
      evaluate_handle(ADD_SCRIPT_CONTENT, contents, id, type || 'text/javascript').
      as_element
  end

  if content
    return execution_context.
      evaluate_handle(ADD_SCRIPT_CONTENT, content, id, type || 'text/javascript').
      as_element
  end

  raise ArgumentError.new('Provide an object with a `url`, `path` or `content` property')
end

#add_style_tag(url: nil, path: nil, content: nil) ⇒ Object

Parameters:

  • url (String?) (defaults to: nil)
  • path (String?) (defaults to: nil)
  • content (String?) (defaults to: nil)

Raises:

  • (ArgumentError)


340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
# File 'lib/puppeteer/isolated_world.rb', line 340

def add_style_tag(url: nil, path: nil, content: nil)
  if url
    begin
      return execution_context.evaluate_handle(ADD_STYLE_URL, url).as_element
    rescue Puppeteer::ExecutionContext::EvaluationError, Puppeteer::Connection::ProtocolError
      raise "Loading style from #{url} failed"
    end
  end

  if path
    contents = File.read(path)
    contents += "/*# sourceURL=#{path.gsub(/\n/, '')}*/"
    return execution_context.evaluate_handle(ADD_STYLE_CONTENT, contents).as_element
  end

  if content
    return execution_context.evaluate_handle(ADD_STYLE_CONTENT, content).as_element
  end

  raise ArgumentError.new('Provide an object with a `url`, `path` or `content` property')
end

#adopt_backend_node(backend_node_id) ⇒ Puppeteer::ElementHandle

Parameters:

  • backend_node_id (Integer)

Returns:



627
628
629
630
631
632
633
634
635
636
# File 'lib/puppeteer/isolated_world.rb', line 627

def adopt_backend_node(backend_node_id)
  response = @client.send_message('DOM.resolveNode',
    backendNodeId: backend_node_id,
    executionContextId: execution_context.send(:_context_id),
  )
  Puppeteer::JSHandle.create(
    context: execution_context,
    remote_object: Puppeteer::RemoteObject.new(response["object"]),
  )
end

#adopt_handle(element_handle) ⇒ Puppeteer::ElementHandle

Parameters:

Returns:



641
642
643
644
645
646
647
648
# File 'lib/puppeteer/isolated_world.rb', line 641

def adopt_handle(element_handle)
  if element_handle.execution_context == execution_context
    raise ArgumentError.new('Cannot adopt handle that already belongs to this execution context')
  end

  node_info = element_handle.remote_object.node_info(@client)
  adopt_backend_node(node_info["node"]["backendNodeId"])
end

#click(selector, delay: nil, button: nil, click_count: nil, count: nil) ⇒ Object

Parameters:

  • selector (String)
  • delay (Number) (defaults to: nil)
  • button (String) (defaults to: nil)

    “left”|“right”|“middle”

  • click_count (Number) (defaults to: nil)

    Deprecated: use count (click_count only sets clickCount)

  • count (Number) (defaults to: nil)


403
404
405
406
407
# File 'lib/puppeteer/isolated_world.rb', line 403

def click(selector, delay: nil, button: nil, click_count: nil, count: nil)
  handle = query_selector(selector) or raise ElementNotFoundError.new(selector)
  handle.click(delay: delay, button: button, click_count: click_count, count: count)
  handle.dispose
end

#contentString

Returns:

  • (String)


225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/puppeteer/isolated_world.rb', line 225

def content
  evaluate(<<-JAVASCRIPT)
  () => {
    let content = '';
    for (const node of document.childNodes) {
      switch (node) {
        case document.documentElement:
          content += document.documentElement.outerHTML;
          break;
        default:
          content += new XMLSerializer().serializeToString(node);
          break;
      }
    }
    return content;
  }
  JAVASCRIPT
end

#context=(context) ⇒ Object

Parameters:



79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/puppeteer/isolated_world.rb', line 79

def context=(context)
  if context
    @ctx_bindings.clear
    @context = context
    unless @context_promise.resolved?
      @context_promise.resolve(context)
    end
    @task_manager.async_rerun_all
  else
    raise ArgumentError.new("context should now be nil. Use #delete_context for clearing document.")
  end
end

#delete_context(context_or_id) ⇒ Object



92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/puppeteer/isolated_world.rb', line 92

def delete_context(context_or_id)
  @document = nil
  if context_or_id
    if context_or_id.is_a?(Puppeteer::ExecutionContext)
      return unless @context.equal?(context_or_id)
    elsif @context && @context.respond_to?(:_context_id, true)
      return unless @context.send(:_context_id).to_s == context_or_id.to_s
    end
  end
  @context = nil
  @context_promise = Async::Promise.new
end

#detachObject



109
110
111
112
# File 'lib/puppeteer/isolated_world.rb', line 109

def detach
  @detached = true
  @task_manager.terminate_all(Puppeteer::WaitTask::TerminatedError.new('waitForFunction failed: frame got detached.'))
end

#detached?Boolean

Returns:

  • (Boolean)


133
134
135
# File 'lib/puppeteer/isolated_world.rb', line 133

def detached?
  @detached
end

#documentObject



185
186
187
# File 'lib/puppeteer/isolated_world.rb', line 185

def document
  @document ||= evaluate_document.as_element
end

#eval_on_selector(selector, page_function, *args) ⇒ !Promise<(!Object|undefined)> Also known as: Seval

‘$eval()` in JavaScript.

Parameters:

  • selector (string)
  • pageFunction (Function|string)
  • args (!Array<*>)

Returns:

  • (!Promise<(!Object|undefined)>)


201
202
203
# File 'lib/puppeteer/isolated_world.rb', line 201

def eval_on_selector(selector, page_function, *args)
  document.eval_on_selector(selector, page_function, *args)
end

#eval_on_selector_all(selector, page_function, *args) ⇒ !Promise<(!Object|undefined)> Also known as: SSeval

‘$$eval()` in JavaScript.

Parameters:

  • selector (string)
  • pageFunction (Function|string)
  • args (!Array<*>)

Returns:

  • (!Promise<(!Object|undefined)>)


211
212
213
# File 'lib/puppeteer/isolated_world.rb', line 211

def eval_on_selector_all(selector, page_function, *args)
  document.eval_on_selector_all(selector, page_function, *args)
end

#evaluate(page_function, *args) ⇒ !Promise<*>

Parameters:

  • pageFunction (Function|string)
  • args (!Array<*>)

Returns:

  • (!Promise<*>)


159
160
161
# File 'lib/puppeteer/isolated_world.rb', line 159

def evaluate(page_function, *args)
  execution_context.evaluate(page_function, *args)
end

#evaluate_handle(page_function, *args) ⇒ !Promise<!Puppeteer.JSHandle>

Parameters:

  • pageFunction (Function|string)
  • args (!Array<*>)

Returns:



152
153
154
# File 'lib/puppeteer/isolated_world.rb', line 152

def evaluate_handle(page_function, *args)
  execution_context.evaluate_handle(page_function, *args)
end

#execution_context!Promise<!Puppeteer.ExecutionContext>

Returns:



140
141
142
143
144
145
146
147
# File 'lib/puppeteer/isolated_world.rb', line 140

def execution_context
  if @detached
    raise DetachedError.new("Execution Context is not available in detached frame \"#{@frame.url}\" (are you trying to evaluate?)")
  end
  return @context if @context

  @context = @context_promise.wait
end

#extensionObject



127
128
129
130
131
# File 'lib/puppeteer/isolated_world.rb', line 127

def extension
  return nil unless @world_id.is_a?(String)

  frame.page.browser.extensions[@world_id]
end

#focus(selector) ⇒ Object

Parameters:

  • selector (String)


410
411
412
413
414
# File 'lib/puppeteer/isolated_world.rb', line 410

def focus(selector)
  handle = query_selector(selector) or raise ElementNotFoundError.new(selector)
  handle.focus
  handle.dispose
end

#has_context?Boolean

Returns:

  • (Boolean)


105
106
107
# File 'lib/puppeteer/isolated_world.rb', line 105

def has_context?
  @context_promise.resolved?
end

#hover(selector) ⇒ Object

/**

* @param {string} selector
*/

async hover(selector)

const handle = await this.$(selector);
assert(handle, 'No node found for selector: ' + selector);
await handle.hover();
await handle.dispose();



425
426
427
428
429
# File 'lib/puppeteer/isolated_world.rb', line 425

def hover(selector)
  handle = query_selector(selector) or raise ElementNotFoundError.new(selector)
  handle.hover
  handle.dispose
end

#query_selector(selector) ⇒ !Promise<?Puppeteer.ElementHandle> Also known as: S

‘$()` in JavaScript.

Parameters:

  • selector (string)

Returns:



166
167
168
# File 'lib/puppeteer/isolated_world.rb', line 166

def query_selector(selector)
  document.query_selector(selector)
end

#query_selector_all(selector, isolate: nil) ⇒ !Promise<!Array<!Puppeteer.ElementHandle>> Also known as: SS

‘$$()` in JavaScript.

Parameters:

  • selector (string)

Returns:



219
220
221
# File 'lib/puppeteer/isolated_world.rb', line 219

def query_selector_all(selector, isolate: nil)
  document.query_selector_all(selector, isolate: isolate)
end

#select(selector, *values) ⇒ Array<String>

Parameters:

  • selector (String)

Returns:

  • (Array<String>)


433
434
435
436
437
438
439
# File 'lib/puppeteer/isolated_world.rb', line 433

def select(selector, *values)
  handle = query_selector(selector) or raise ElementNotFoundError.new(selector)
  result = handle.select(*values)
  handle.dispose

  result
end

#set_content(html, timeout: nil, wait_until: nil) ⇒ Object

Parameters:

  • html (String)
  • timeout (Integer) (defaults to: nil)
  • wait_until (String|Array<String>) (defaults to: nil)


247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
# File 'lib/puppeteer/isolated_world.rb', line 247

def set_content(html, timeout: nil, wait_until: nil)
  option_wait_until = [wait_until || 'load'].flatten
  option_timeout = timeout || @timeout_settings.navigation_timeout

  # We rely upon the fact that document.open() will reset frame lifecycle with "init"
  # lifecycle event. @see https://crrev.com/608658
  js = <<-JAVASCRIPT
  (html) => {
    document.open();
    document.write(html);
    document.close();
  }
  JAVASCRIPT
  evaluate(js, html)

  watcher = Puppeteer::LifecycleWatcher.new(@frame_manager, @frame, option_wait_until, option_timeout)
  begin
    Puppeteer::AsyncUtils.await_promise_race(
      watcher.timeout_or_termination_promise,
      watcher.lifecycle_promise,
    )
  ensure
    watcher.dispose
  end
end

#Sx(expression) ⇒ !Promise<!Array<!Puppeteer.ElementHandle>>

‘$x()` in JavaScript. $ is not allowed to use as a method name in Ruby.

Parameters:

  • expression (string)

Returns:



192
193
194
# File 'lib/puppeteer/isolated_world.rb', line 192

def Sx(expression)
  document.Sx(expression)
end

#tap(selector) ⇒ Object

Parameters:

  • selector (String)


442
443
444
445
446
# File 'lib/puppeteer/isolated_world.rb', line 442

def tap(selector)
  handle = query_selector(selector) or raise ElementNotFoundError.new(selector)
  handle.tap
  handle.dispose
end

#titleString

Returns:

  • (String)


585
586
587
# File 'lib/puppeteer/isolated_world.rb', line 585

def title
  evaluate('() => document.title')
end

#transfer_handle(element_handle) ⇒ Object



650
651
652
653
654
# File 'lib/puppeteer/isolated_world.rb', line 650

def transfer_handle(element_handle)
  result = adopt_handle(element_handle)
  element_handle.dispose
  result
end

#type_text(selector, text, delay: nil) ⇒ Object

Parameters:

  • selector (String)
  • text (String)
  • delay (Number) (defaults to: nil)


451
452
453
454
455
# File 'lib/puppeteer/isolated_world.rb', line 451

def type_text(selector, text, delay: nil)
  handle = query_selector(selector) or raise ElementNotFoundError.new(selector)
  handle.type_text(text, delay: delay)
  handle.dispose
end

#wait_for_function(page_function, args: [], polling: nil, timeout: nil) ⇒ Puppeteer::JSHandle

Parameters:

  • page_function (String)
  • args (Array) (defaults to: [])
  • polling (Integer|String) (defaults to: nil)
  • timeout (Integer) (defaults to: nil)

Returns:



569
570
571
572
573
574
575
576
577
578
579
580
581
# File 'lib/puppeteer/isolated_world.rb', line 569

def wait_for_function(page_function, args: [], polling: nil, timeout: nil)
  option_polling = polling || 'raf'
  option_timeout = timeout.nil? ? @timeout_settings.timeout : timeout

  Puppeteer::WaitTask.new(
    dom_world: self,
    predicate_body: page_function,
    title: 'function',
    polling: option_polling,
    timeout: option_timeout,
    args: args,
  ).await_promise
end