Class: Puppeteer::WaitTask

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

Defined Under Namespace

Classes: TerminatedError, TimeoutError

Constant Summary collapse

WAIT_FOR_PREDICATE_PAGE_FUNCTION =
<<~JAVASCRIPT
async function _(predicateBody, polling, timeout, ...args) {
    const predicate = new Function('...args', predicateBody);
    let timedOut = false;
    if (timeout)
        setTimeout(() => (timedOut = true), timeout);
    if (polling === 'raf')
        return await pollRaf();
    if (polling === 'mutation')
        return await pollMutation();
    if (typeof polling === 'number')
        return await pollInterval(polling);
    /**
     * @return {!Promise<*>}
     */
    function pollMutation() {
        const success = predicate(...args);
        if (success)
            return Promise.resolve(success);
        let fulfill;
        const result = new Promise((x) => (fulfill = x));
        const observer = new MutationObserver(() => {
            if (timedOut) {
                observer.disconnect();
                fulfill();
            }
            const success = predicate(...args);
            if (success) {
                observer.disconnect();
                fulfill(success);
            }
        });
        observer.observe(document, {
            childList: true,
            subtree: true,
            attributes: true,
        });
        return result;
    }
    function pollRaf() {
        let fulfill;
        const result = new Promise((x) => (fulfill = x));
        onRaf();
        return result;
        function onRaf() {
            if (timedOut) {
                fulfill();
                return;
            }
            const success = predicate(...args);
            if (success)
                fulfill(success);
            else
                requestAnimationFrame(onRaf);
        }
    }
    function pollInterval(pollInterval) {
        let fulfill;
        const result = new Promise((x) => (fulfill = x));
        onTimeout();
        return result;
        function onTimeout() {
            if (timedOut) {
                fulfill();
                return;
            }
            const success = predicate(...args);
            if (success)
                fulfill(success);
            else
                setTimeout(onTimeout, pollInterval);
        }
    }
}
JAVASCRIPT

Instance Method Summary collapse

Constructor Details

#initialize(dom_world:, predicate_body:, title:, polling:, timeout:, args: []) ⇒ WaitTask

Returns a new instance of WaitTask.



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/puppeteer/wait_task.rb', line 12

def initialize(dom_world:, predicate_body:, title:, polling:, timeout:, args: [])
  if polling.is_a?(String)
    if polling != 'raf' && polling != 'mutation'
      raise ArgumentError.new("Unknown polling option: #{polling}")
    end
  elsif polling.is_a?(Numeric)
    unless polling.positive?
      raise ArgumentError.new("Cannot poll with non-positive interval: #{polling}")
    end
  else
    raise ArgumentError.new("Unknown polling options: #{polling}")
  end

  @dom_world = dom_world
  @polling = polling
  @timeout = timeout
  @predicate_body = "return (#{predicate_body})(...args);"
  @args = args
  @run_count = 0
  @dom_world._wait_tasks.add(self)
  @promise = resolvable_future

  # Since page navigation requires us to re-install the pageScript, we should track
  # timeout on our end.
  if timeout
    timeout_error = TimeoutError.new(title: title, timeout: timeout)
    Concurrent::Promises.schedule(timeout / 1000.0) { terminate(timeout_error) unless @timeout_cleared }
  end
  async_rerun
end

Instance Method Details

#await_promisePuppeteer::JSHandle

Returns:



44
45
46
# File 'lib/puppeteer/wait_task.rb', line 44

def await_promise
  @promise.value!
end

#rerunObject



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/puppeteer/wait_task.rb', line 54

def rerun
  run_count = (@run_count += 1)
  begin
    success = @dom_world.execution_context.evaluate_handle(
      WAIT_FOR_PREDICATE_PAGE_FUNCTION,
      @predicate_body,
      @polling,
      @timeout,
      *@args,
    )
  rescue => err
    error = err
  end

  if @terminated || run_count != @run_count
    if success
      success.dispose
    end
    return
  end

  # Ignore timeouts in pageScript - we track timeouts ourselves.
  # If the frame's execution context has already changed, `frame.evaluate` will
  # throw an error - ignore this predicate run altogether.
  if !error && (@dom_world.evaluate("s => !s", success) rescue true)
    success.dispose
    return
  end

  # When the page is navigated, the promise is rejected.
  # We will try again in the new execution context.
  if error && error.message.include?('Execution context was destroyed')
    return
  end

  # We could have tried to evaluate in a context which was already
  # destroyed.
  if error && error.message.include?('Cannot find context with specified id')
    return
  end

  if error
    @promise.reject(error)
  else
    @promise.fulfill(success)
  end

  cleanup
end

#terminate(error) ⇒ Object



48
49
50
51
52
# File 'lib/puppeteer/wait_task.rb', line 48

def terminate(error)
  @terminated = true
  @promise.reject(error)
  cleanup
end