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
# 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 = resolvable_future
  @task_manager = Puppeteer::TaskManager.new
  @bound_functions = {}
  @ctx_bindings = Set.new
  @detached = false

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

Instance Attribute Details

#frameObject (readonly)

Returns the value of attribute frame.



68
69
70
# File 'lib/puppeteer/isolated_world.rb', line 68

def frame
  @frame
end

#task_managerObject (readonly)

Returns the value of attribute task_manager.



68
69
70
# File 'lib/puppeteer/isolated_world.rb', line 68

def task_manager
  @task_manager
end

Instance Method Details

#add_binding_to_context(context, binding_function) ⇒ Object



417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
# File 'lib/puppeteer/isolated_world.rb', line 417

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)


235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
# File 'lib/puppeteer/isolated_world.rb', line 235

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 # for Chrome
      raise "Loading script from #{url} failed"
    rescue Puppeteer::Connection::ProtocolError # for Firefox
      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)


299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
# File 'lib/puppeteer/isolated_world.rb', line 299

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 # for Chrome
      raise "Loading style from #{url} failed"
    rescue Puppeteer::Connection::ProtocolError # for Firefox
      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:



583
584
585
586
587
588
589
590
591
592
# File 'lib/puppeteer/isolated_world.rb', line 583

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:



597
598
599
600
601
602
603
604
# File 'lib/puppeteer/isolated_world.rb', line 597

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

Parameters:

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

    “left”|“right”|“middle”

  • click_count (Number) (defaults to: nil)


363
364
365
366
367
# File 'lib/puppeteer/isolated_world.rb', line 363

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

#contentString

Returns:

  • (String)


188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/puppeteer/isolated_world.rb', line 188

def content
  evaluate(<<-JAVASCRIPT)
  () => {
    let retVal = '';
    if (document.doctype)
      retVal = new XMLSerializer().serializeToString(document.doctype);
    if (document.documentElement)
      retVal += document.documentElement.outerHTML;
    return retVal;
  }
  JAVASCRIPT
end

#context=(context) ⇒ Object

Parameters:



76
77
78
79
80
81
82
83
84
85
86
# File 'lib/puppeteer/isolated_world.rb', line 76

def context=(context)
  if context
    @ctx_bindings.clear
    unless @context_promise.resolved?
      @context_promise.fulfill(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(execution_context_id) ⇒ Object



88
89
90
91
# File 'lib/puppeteer/isolated_world.rb', line 88

def delete_context(execution_context_id)
  @document = nil
  @context_promise = resolvable_future
end

#detachObject



97
98
99
100
# File 'lib/puppeteer/isolated_world.rb', line 97

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

#documentObject



148
149
150
# File 'lib/puppeteer/isolated_world.rb', line 148

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


164
165
166
# File 'lib/puppeteer/isolated_world.rb', line 164

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


174
175
176
# File 'lib/puppeteer/isolated_world.rb', line 174

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


122
123
124
# File 'lib/puppeteer/isolated_world.rb', line 122

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:



115
116
117
# File 'lib/puppeteer/isolated_world.rb', line 115

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

#execution_context!Promise<!Puppeteer.ExecutionContext>

Returns:



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

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

#focus(selector) ⇒ Object

Parameters:

  • selector (String)


370
371
372
373
374
# File 'lib/puppeteer/isolated_world.rb', line 370

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

#has_context?Boolean

Returns:

  • (Boolean)


93
94
95
# File 'lib/puppeteer/isolated_world.rb', line 93

def has_context?
  @context_promise.resolved?
end

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

‘$()` in JavaScript.

Parameters:

  • selector (string)

Returns:



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

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

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

‘$$()` in JavaScript.

Parameters:

  • selector (string)

Returns:



182
183
184
# File 'lib/puppeteer/isolated_world.rb', line 182

def query_selector_all(selector)
  document.query_selector_all(selector)
end

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

Parameters:

  • selector (String)

Returns:

  • (Array<String>)


388
389
390
391
392
393
394
# File 'lib/puppeteer/isolated_world.rb', line 388

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)


204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/puppeteer/isolated_world.rb', line 204

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
    await_any(
      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:



155
156
157
# File 'lib/puppeteer/isolated_world.rb', line 155

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

#tap(selector) ⇒ Object

Parameters:

  • selector (String)


397
398
399
400
401
# File 'lib/puppeteer/isolated_world.rb', line 397

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

#titleString

Returns:

  • (String)


541
542
543
# File 'lib/puppeteer/isolated_world.rb', line 541

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

#transfer_handle(element_handle) ⇒ Object



606
607
608
609
610
# File 'lib/puppeteer/isolated_world.rb', line 606

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)


406
407
408
409
410
# File 'lib/puppeteer/isolated_world.rb', line 406

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:



525
526
527
528
529
530
531
532
533
534
535
536
537
# File 'lib/puppeteer/isolated_world.rb', line 525

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

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