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

#contextObject

Returns the value of attribute context.



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

def context
  @context
end

#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



468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
# File 'lib/puppeteer/isolated_world.rb', line 468

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)


285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
# File 'lib/puppeteer/isolated_world.rb', line 285

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)


347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
# File 'lib/puppeteer/isolated_world.rb', line 347

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:



633
634
635
636
637
638
639
640
641
642
# File 'lib/puppeteer/isolated_world.rb', line 633

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:



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

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, count: nil) ⇒ Object

Parameters:

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

    "left"|"right"|"middle"

  • count (Number) (defaults to: nil)


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

def click(selector, delay: nil, button: nil, count: nil)
  handle = query_selector(selector) or raise ElementNotFoundError.new(selector)
  handle.click(delay: delay, button: button, 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

#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)


416
417
418
419
420
# File 'lib/puppeteer/isolated_world.rb', line 416

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(); }


431
432
433
434
435
# File 'lib/puppeteer/isolated_world.rb', line 431

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>)


439
440
441
442
443
444
445
# File 'lib/puppeteer/isolated_world.rb', line 439

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
272
273
274
275
276
277
278
# File 'lib/puppeteer/isolated_world.rb', line 247

def set_content(html, timeout: nil, wait_until: nil)
  option_wait_until = [wait_until || 'load'].flatten
  unsupported_events = option_wait_until & ['networkidle0', 'networkidle2']
  unless unsupported_events.empty?
    raise ArgumentError.new(
      "set_content does not support #{unsupported_events.join(', ')}; " \
      'compose set_content with wait_for_network_idle instead.',
    )
  end
  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)


448
449
450
451
452
# File 'lib/puppeteer/isolated_world.rb', line 448

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

#titleString

Returns:

  • (String)


591
592
593
# File 'lib/puppeteer/isolated_world.rb', line 591

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

#transfer_handle(element_handle) ⇒ Object



656
657
658
659
660
# File 'lib/puppeteer/isolated_world.rb', line 656

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)


457
458
459
460
461
# File 'lib/puppeteer/isolated_world.rb', line 457

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:



575
576
577
578
579
580
581
582
583
584
585
586
587
# File 'lib/puppeteer/isolated_world.rb', line 575

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