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

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
  @wait_tasks = Set.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

Instance Method Details

#add_binding_to_context(context, binding_function) ⇒ Object



424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
# File 'lib/puppeteer/isolated_world.rb', line 424

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)


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

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)


306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
# File 'lib/puppeteer/isolated_world.rb', line 306

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:



592
593
594
595
596
597
598
599
600
601
# File 'lib/puppeteer/isolated_world.rb', line 592

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:



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

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)


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

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)


195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/puppeteer/isolated_world.rb', line 195

def content
  evaluate("  () => {\n    let retVal = '';\n    if (document.doctype)\n      retVal = new XMLSerializer().serializeToString(document.doctype);\n    if (document.documentElement)\n      retVal += document.documentElement.outerHTML;\n    return retVal;\n  }\n  JAVASCRIPT\nend\n")

#context=(context) ⇒ Object

Parameters:



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

def context=(context)
  if context
    @ctx_bindings.clear
    unless @context_promise.resolved?
      @context_promise.fulfill(context)
    end
    @wait_tasks.each(&:async_rerun)
  else
    raise ArgumentError.new("context should now be nil. Use #delete_context for clearing document.")
  end
end

#delete_context(execution_context_id) ⇒ Object



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

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

#detachObject



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

def detach
  @detached = true
  @wait_tasks.each do |wait_task|
    wait_task.terminate(Puppeteer::WaitTask::TerminatedError.new('waitForFunction failed: frame got detached.'))
  end
end

#documentObject



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

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


171
172
173
# File 'lib/puppeteer/isolated_world.rb', line 171

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


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

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


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

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:



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

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

#execution_context!Promise<!Puppeteer.ExecutionContext>

Returns:



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

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)


377
378
379
380
381
# File 'lib/puppeteer/isolated_world.rb', line 377

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

#has_context?Boolean

Returns:

  • (Boolean)


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

def has_context?
  @context_promise.resolved?
end

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

‘$()` in JavaScript.

Parameters:

  • selector (string)

Returns:



136
137
138
# File 'lib/puppeteer/isolated_world.rb', line 136

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:



189
190
191
# File 'lib/puppeteer/isolated_world.rb', line 189

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

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

Parameters:

  • selector (String)

Returns:

  • (Array<String>)


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

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)


211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
# File 'lib/puppeteer/isolated_world.rb', line 211

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 = "  (html) => {\n    document.open();\n    document.write(html);\n    document.close();\n  }\n  JAVASCRIPT\n  evaluate(js, html)\n\n  watcher = Puppeteer::LifecycleWatcher.new(@frame_manager, @frame, option_wait_until, option_timeout)\n  begin\n    await_any(\n      watcher.timeout_or_termination_promise,\n      watcher.lifecycle_promise,\n    )\n  ensure\n    watcher.dispose\n  end\nend\n"

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

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

Parameters:

  • expression (string)

Returns:



162
163
164
# File 'lib/puppeteer/isolated_world.rb', line 162

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

#tap(selector) ⇒ Object

Parameters:

  • selector (String)


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

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

#titleString

Returns:

  • (String)


548
549
550
# File 'lib/puppeteer/isolated_world.rb', line 548

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

#transfer_handle(element_handle) ⇒ Object



615
616
617
618
619
# File 'lib/puppeteer/isolated_world.rb', line 615

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)


413
414
415
416
417
# File 'lib/puppeteer/isolated_world.rb', line 413

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:



532
533
534
535
536
537
538
539
540
541
542
543
544
# File 'lib/puppeteer/isolated_world.rb', line 532

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