Module: Ferrum::Frame::DOM

Included in:
Ferrum::Frame
Defined in:
lib/ferrum/frame/dom.rb

Overview

Evaluates and executes JavaScript to query and manipulate a frame's DOM: reading the URL, title, doctype, and HTML, finding nodes by XPath or CSS selector, and injecting <script>, <style>, and <link> tags.

Constant Summary collapse

SCRIPT_SRC_TAG =
<<~JS
  const script = document.createElement("script");
  script.src = arguments[0];
  script.type = arguments[1];
  script.onload = arguments[2];
  document.head.appendChild(script);
JS
SCRIPT_TEXT_TAG =
<<~JS
  const script = document.createElement("script");
  script.text = arguments[0];
  script.type = arguments[1];
  document.head.appendChild(script);
  arguments[2]();
JS
STYLE_TAG =
<<~JS
  const style = document.createElement("style");
  style.type = "text/css";
  style.appendChild(document.createTextNode(arguments[0]));
  document.head.appendChild(style);
  arguments[1]();
JS
<<~JS
  const link = document.createElement("link");
  link.rel = "stylesheet";
  link.href = arguments[0];
  link.onload = arguments[1];
  document.head.appendChild(link);
JS

Instance Method Summary collapse

Instance Method Details

#add_script_tag(url: nil, path: nil, content: nil, type: "text/javascript") ⇒ Object

Adds a <script> tag to the document.

Examples:

browser.add_script_tag(url: "http://example.com/stylesheet.css") # => true

Parameters:

  • url (String, nil) (defaults to: nil)
  • path (String, nil) (defaults to: nil)
  • content (String, nil) (defaults to: nil)
  • type (String) (defaults to: "text/javascript")


260
261
262
263
264
265
266
267
268
269
270
271
272
# File 'lib/ferrum/frame/dom.rb', line 260

def add_script_tag(url: nil, path: nil, content: nil, type: "text/javascript")
  expr, *args = if url
                  [SCRIPT_SRC_TAG, url, type]
                elsif path || content
                  if path
                    content = File.read(path)
                    content += "\n//# sourceURL=#{path}"
                  end
                  [SCRIPT_TEXT_TAG, content, type]
                end

  evaluate_async(expr, @page.timeout, *args)
end

#add_style_tag(url: nil, path: nil, content: nil) ⇒ Object

Adds a <style> tag to the document.

Examples:

browser.add_style_tag(content: "h1 { font-size: 40px; }") # => true

Parameters:

  • url (String, nil) (defaults to: nil)
  • path (String, nil) (defaults to: nil)
  • content (String, nil) (defaults to: nil)


286
287
288
289
290
291
292
293
294
295
296
297
298
# File 'lib/ferrum/frame/dom.rb', line 286

def add_style_tag(url: nil, path: nil, content: nil)
  expr, *args = if url
                  [LINK_TAG, url]
                elsif path || content
                  if path
                    content = File.read(path)
                    content += "\n//# sourceURL=#{path}"
                  end
                  [STYLE_TAG, content]
                end

  evaluate_async(expr, @page.timeout, *args)
end

#at_css(selector, within: nil) ⇒ Node?

Finds a node by using a CSS path selector.

Examples:

browser.go_to("https://github.com/")
browser.at_css("a[aria-label='Issues you created']") # => Node

Parameters:

  • selector (String) —

    The CSS path selector.

  • within (Node, nil) (defaults to: nil) —

    The parent node to search within.

Returns:

  • (Node, nil) —

    The matching node.



235
236
237
238
239
240
241
242
243
244
# File 'lib/ferrum/frame/dom.rb', line 235

def at_css(selector, within: nil)
  expr = <<~JS
    function(selector, within) {
      within ||= document
      return within.querySelector(selector);
    }
  JS

  evaluate_func(expr, selector, within)
end

#at_xpath(selector, within: nil) ⇒ Node?

Finds a node by using a XPath selector.

Examples:

browser.go_to("https://github.com/")
browser.at_xpath("//a[@aria-label='Issues you created']") # => Node

Parameters:

  • selector (String) —

    The XPath selector.

  • within (Node, nil) (defaults to: nil) —

    The parent node to search within.

Returns:

  • (Node, nil) —

    The matching node.



181
182
183
184
185
186
187
188
189
190
# File 'lib/ferrum/frame/dom.rb', line 181

def at_xpath(selector, within: nil)
  expr = <<~JS
    function(selector, within) {
      within ||= document
      let xpath = document.evaluate(selector, within, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
      return xpath.snapshotItem(0);
    }
  JS
  evaluate_func(expr, selector, within)
end

#body ⇒ String

Returns current page's html.

Examples:

browser.go_to("https://google.com/")
browser.body # => '<html itemscope="" itemtype="http://schema.org/WebPage" lang="ru"><head>...

Returns:

  • (String) —

    The HTML source of the current page.



111
112
113
# File 'lib/ferrum/frame/dom.rb', line 111

def body
  evaluate("document.documentElement?.outerHTML") || ""
end

#css(selector, within: nil) ⇒ Array<Node>

Finds nodes by using a CSS path selector.

Examples:

browser.go_to("https://github.com/")
browser.css("a[aria-label='Issues you created']") # => [Node]

Parameters:

  • selector (String) —

    The CSS path selector.

  • within (Node, nil) (defaults to: nil) —

    The parent node to search within.

Returns:

  • (Array<Node>) —

    The matching nodes.



208
209
210
211
212
213
214
215
216
217
# File 'lib/ferrum/frame/dom.rb', line 208

def css(selector, within: nil)
  expr = <<~JS
    function(selector, within) {
      within ||= document
      return Array.from(within.querySelectorAll(selector));
    }
  JS

  evaluate_func(expr, selector, within)
end

#current_title ⇒ String

Returns current top window title.

Examples:

browser.go_to("https://google.com/")
browser.current_title # => "Google"

Returns:

  • (String) —

    The window's current title.



82
83
84
# File 'lib/ferrum/frame/dom.rb', line 82

def current_title
  evaluate("window.top.document.title")
end

#current_url ⇒ String

Returns current top window location href.

Examples:

browser.go_to("https://google.com/")
browser.current_url # => "https://www.google.com/"

Returns:

  • (String) —

    The window's current URL.



68
69
70
# File 'lib/ferrum/frame/dom.rb', line 68

def current_url
  evaluate("window.top.location.href")
end

#doctype ⇒ String?

Returns current document's doctype declaration.

Examples:

browser.go_to("https://example.com")
browser.doctype # => "<!DOCTYPE html>"

Returns:

  • (String, nil) —

    The serialized <!DOCTYPE ...> declaration, or nil if the document has none.



97
98
99
# File 'lib/ferrum/frame/dom.rb', line 97

def doctype
  evaluate("document.doctype && new XMLSerializer().serializeToString(document.doctype)")
end

#frame_element ⇒ Node?

Returns the element in which the window is embedded.

Examples:

browser.go_to("https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe")
frame = browser.frames.last
frame.frame_element # => [Node]
frame.parent.parent.parent.frame_element # => nil

Returns:

  • (Node, nil) —

    The element in which the window is embedded.



127
128
129
# File 'lib/ferrum/frame/dom.rb', line 127

def frame_element
  evaluate("window.frameElement")
end

#xpath(selector, within: nil) ⇒ Array<Node>

Finds nodes by using a XPath selector.

Examples:

browser.go_to("https://github.com/")
browser.xpath("//a[@aria-label='Issues you created']") # => [Node]

Parameters:

  • selector (String) —

    The XPath selector.

  • within (Node, nil) (defaults to: nil) —

    The parent node to search within.

Returns:

  • (Array<Node>) —

    The matching nodes.



147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/ferrum/frame/dom.rb', line 147

def xpath(selector, within: nil)
  expr = <<~JS
    function(selector, within) {
      let results = [];
      within ||= document

      let xpath = document.evaluate(selector, within, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
      for (let i = 0; i < xpath.snapshotLength; i++) {
        results.push(xpath.snapshotItem(i));
      }

      return results;
    }
  JS

  evaluate_func(expr, selector, within)
end