Class: Puppeteer::DOMWorld

Inherits:
Object
  • Object
show all
Defined in:
lib/puppeteer/dom_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) ⇒ DOMWorld

Returns a new instance of DOMWorld.

Parameters:



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/puppeteer/dom_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/dom_world.rb', line 68

def frame
  @frame
end

Instance Method Details

#add_binding_to_context(context, binding_function) ⇒ Object



434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
# File 'lib/puppeteer/dom_world.rb', line 434

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/dom_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/dom_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

#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/dom_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/dom_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/dom_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/dom_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/dom_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

#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/dom_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/dom_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/dom_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/dom_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/dom_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/dom_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/dom_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/dom_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/dom_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/dom_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/dom_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/dom_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/dom_world.rb', line 404

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

#titleString

Returns:

  • (String)


606
607
608
# File 'lib/puppeteer/dom_world.rb', line 606

def title
  evaluate('() => document.title')
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/dom_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:



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

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

#wait_for_selector(selector, visible: nil, hidden: nil, timeout: nil, root: nil) ⇒ Object

Parameters:

  • selector (String)
  • visible (Boolean) (defaults to: nil)

    Wait for element visible (not ‘display: none’ nor ‘visibility: hidden’) on true. default to false.

  • hidden (Boolean) (defaults to: nil)

    Wait for element invisible (‘display: none’ nor ‘visibility: hidden’) on true. default to false.

  • timeout (Integer) (defaults to: nil)


423
424
425
426
427
# File 'lib/puppeteer/dom_world.rb', line 423

def wait_for_selector(selector, visible: nil, hidden: nil, timeout: nil, root: nil)
  # call wait_for_selector_in_page with custom query selector.
  query_selector_manager = Puppeteer::QueryHandlerManager.instance
  query_selector_manager.detect_query_handler(selector).wait_for(self, visible: visible, hidden: hidden, timeout: timeout, root: root)
end

#wait_for_xpath(xpath, visible: nil, hidden: nil, timeout: nil, root: nil) ⇒ Object

Parameters:

  • xpath (String)
  • visible (Boolean) (defaults to: nil)

    Wait for element visible (not ‘display: none’ nor ‘visibility: hidden’) on true. default to false.

  • hidden (Boolean) (defaults to: nil)

    Wait for element invisible (‘display: none’ nor ‘visibility: hidden’) on true. default to false.

  • timeout (Integer) (defaults to: nil)


546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
# File 'lib/puppeteer/dom_world.rb', line 546

def wait_for_xpath(xpath, visible: nil, hidden: nil, timeout: nil, root: nil)
  option_wait_for_visible = visible || false
  option_wait_for_hidden = hidden || false
  option_timeout = timeout || @timeout_settings.timeout
  option_root = root

  polling =
    if option_wait_for_visible || option_wait_for_hidden
      'raf'
    else
      'mutation'
    end
  title = "XPath #{xpath}#{option_wait_for_hidden ? 'to be hidden' : ''}"

  xpath_predicate = make_predicate_string(
    predicate_arg_def: '(root, selector, waitForVisible, waitForHidden)',
    predicate_body: "      const node = document.evaluate(selector, root, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;\n      return checkWaitForOptions(node, waitForVisible, waitForHidden);\n    JAVASCRIPT\n  )\n\n  wait_task = Puppeteer::WaitTask.new(\n    dom_world: self,\n    predicate_body: xpath_predicate,\n    title: title,\n    polling: polling,\n    timeout: option_timeout,\n    args: [xpath, option_wait_for_visible, option_wait_for_hidden],\n    root: option_root,\n  )\n  handle = wait_task.await_promise\n  unless handle.as_element\n    handle.dispose\n    return nil\n  end\n  handle.as_element\nend\n"