Skip to content

Commit

Permalink
implant: add UI scraping subsystem via :scrape automation action
Browse files Browse the repository at this point in the history
  • Loading branch information
darwin committed Jun 3, 2016
1 parent 5f28a1d commit 4421519
Show file tree
Hide file tree
Showing 3 changed files with 171 additions and 1 deletion.
4 changes: 3 additions & 1 deletion src/implant/dirac/implant/automation.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
[cljs.reader :as reader]
[dirac.settings :refer-macros [get-automation-entry-point-key]]
[dirac.utils :as utils]
[dirac.implant.helpers :as helpers :refer [get-console-view get-inspector-view]]))
[dirac.implant.helpers :as helpers :refer [get-console-view get-inspector-view]]
[dirac.implant.automation.scrapers :refer [scrape]]))

; -- automation actions -----------------------------------------------------------------------------------------------------

Expand Down Expand Up @@ -110,6 +111,7 @@
:disable-console-feedback (disable-console-feedback!)
:get-suggest-box-representation (get-suggest-box-representation)
:get-prompt-representation (get-prompt-representation)
:scrape (scrape (:scraper command))
(warn "received unknown automation command:" (pr-str command))))

; -- automation -------------------------------------------------------------------------------------------------------------
Expand Down
103 changes: 103 additions & 0 deletions src/implant/dirac/implant/automation/scrapers.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
(ns dirac.implant.automation.scrapers
(:require-macros [com.rpl.specter.macros :refer [providepath declarepath transform select select-one select-first]])
(:require [chromex.support :refer-macros [oget oset ocall oapply]]
[chromex.logging :refer-macros [log warn error info]]
[cljs.pprint :refer [pprint]]
[com.rpl.specter :refer [must continue-then-stay multi-path if-path ALL STAY]]
[dirac.implant.automation.scraping :as scraping :refer [RepWalker select-subrep select-subreps scrape-rep]]
[clojure.string :as string]))

; -- helpers ----------------------------------------------------------------------------------------------------------------

(defn pp-rep [rep]
(with-out-str (pprint rep)))

(defn widget-rep? [rep]
(some? (re-find #"widget" (str (:class rep)))))

(defn title-rep? [rep]
(= "title" (:class rep)))

(defn subtitle-rep? [rep]
(= "subtitle" (:class rep)))

(defn print-list [list]
(if (empty? list)
"no items displayed"
(str "displayed " (count list) " items:\n"
(string/join "\n" (map #(str " * " (or % "<empty>")) list)))))

; -- call stack UI ----------------------------------------------------------------------------------------------------------

(defn is-callstack-title-el? [el]
(= (oget el "textContent") "Call Stack"))

(defn find-callstack-pane-element []
(let [title-els (scraping/query-selector "html /deep/ .sidebar-pane-title")]
(if-let [callstack-title-el (select-first [ALL is-callstack-title-el?] title-els)]
(oget callstack-title-el "nextElementSibling"))))

(defn get-callstack-pane-rep [callstack-pane-el]
(scrape-rep callstack-pane-el))

(defn select-callstack-widget-rep [rep]
; example output
(comment
{:tag "div",
:class "widget vbox",
:children
({:tag "div",
:class "list-item selected",
:children ({:tag "div",
:class "subtitle",
:content "core.cljs:10",
:title "http://localhost:9080/compiled/tests/dirac/tests/scenarios/breakpoint/core.cljs:10"}
{:tag "div",
:class "title",
:content "breakpoint-demo",
:title "dirac.tests.scenarios.breakpoint.core/breakpoint-demo"})}
; ...
{:tag "div",
:class "list-item",
:children ({:tag "div",
:class "subtitle",
:content "notifications.cljs:53",
:title "http://localhost:9080/compiled/tests/dirac/automation/notifications.cljs:53"}
{:tag "div",
:class "title",
:content "process-event!",
:title "dirac.automation.notifications/process-event!"})})})

(select-subrep widget-rep? rep))

(defn print-callstack-function [rep]
(let [{:keys [title content]} rep]
(str content (if title (str " / " title)))))

(defn print-callstack-location [rep]
(let [{:keys [title content]} rep]
(str content (if title (str " / " title)))))

; -- general interface for :scrape automation action ------------------------------------------------------------------------

(defmulti scrape (fn [name & _args]
(keyword name)))

(defmethod scrape :default [name & _]
(str "! scraper '" name "' has missing implementation in dirac.implant.automation.scrapers"))

(defmethod scrape :callstack-pane-functions [_ & _]
(->> (find-callstack-pane-element)
(get-callstack-pane-rep)
(select-callstack-widget-rep)
(select-subreps title-rep?)
(map print-callstack-function)
(print-list)))

(defmethod scrape :callstack-pane-locations [_ & _]
(->> (find-callstack-pane-element)
(get-callstack-pane-rep)
(select-callstack-widget-rep)
(select-subreps subtitle-rep?)
(map print-callstack-location)
(print-list)))
65 changes: 65 additions & 0 deletions src/implant/dirac/implant/automation/scraping.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
(ns dirac.implant.automation.scraping
(:require-macros [com.rpl.specter.macros :refer [providepath declarepath select select-first]])
(:require [chromex.support :refer-macros [oget oset ocall oapply]]
[chromex.logging :refer-macros [log warn error info]]
[com.rpl.specter :refer [must continue-then-stay multi-path if-path ALL]]
[dirac.dom.shim]
[clojure.string :as string]))

; -- specter walker ---------------------------------------------------------------------------------------------------------

(declarepath RepWalker)

(providepath RepWalker
(continue-then-stay
(multi-path
[(must :children) ALL]
(must :shadowRoot))
RepWalker))

; -- helpers ----------------------------------------------------------------------------------------------------------------

(defn clean-rep [rep]
(into {} (remove (comp empty? second) rep)))

(defn select-subrep [pred rep]
(select-first [RepWalker pred] rep))

(defn select-subreps [pred rep]
(select [RepWalker pred] rep))

; -- DOM reading ------------------------------------------------------------------------------------------------------------

(defn query-selector [selector]
(.querySelectorAll js/document selector))

(defn get-tag-name [el]
(if-let [tag-name (oget el "tagName")]
(string/lower-case tag-name)))

(defn get-class-name [el]
(oget el "className"))

(defn get-children [el]
(oget el "children"))

(defn get-shadow-root [el]
(oget el "shadowRoot"))

(defn get-own-text-content [el]
(if (empty? (get-children el))
(oget el "textContent")))

(defn get-title [el]
(oget el "title"))

; -- DOM scraping -----------------------------------------------------------------------------------------------------------

(defn scrape-rep [el]
(if (some? el)
(clean-rep {:tag (get-tag-name el)
:class (get-class-name el)
:content (get-own-text-content el)
:title (get-title el)
:children (doall (map scrape-rep (get-children el)))
:shadowRoot (scrape-rep (get-shadow-root el))})))

0 comments on commit 4421519

Please sign in to comment.