Skip to content

Commit

Permalink
implement a subsystem for loading backend API specs from user's browser
Browse files Browse the repository at this point in the history
  • Loading branch information
darwin committed Aug 17, 2016
1 parent d68c99e commit 28995a0
Show file tree
Hide file tree
Showing 17 changed files with 364 additions and 30 deletions.
2 changes: 2 additions & 0 deletions resources/unpacked/devtools/front_end/dirac/dirac.js
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,8 @@ Object.assign(window.dirac, (function() {
_DEBUG_FEEDBACK: false,
_DEBUG_WATCHING: false,
_DEBUG_CACHES: false,
_DEBUG_BACKEND_API: false,
_DEBUG_BACKEND_CSS: false,

// -- feature toggles -----------------------------------------------------------------------------------------------
hasREPL: hasFeature("enable-repl"),
Expand Down
6 changes: 2 additions & 4 deletions resources/unpacked/devtools/front_end/dirac/module.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
"dependencies": [
"platform",
"common",
"host",
"sdk",
"ui"
"host"
],
"scripts": [
"keysim.js",
Expand All @@ -19,4 +17,4 @@
],
"resources": [
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
WebInspector.InspectorBackendExtensionMode = {};

WebInspector.InspectorBackendExtensionMode.loadFromExtensionIfNeeded = function() {
if (InspectorBackend.isInitialized()) {
return;
}

const evalAPI = (lines) => {
let lineNum = 0;
for (let line of lines) {
lineNum += 1;
if (dirac._DEBUG_BACKEND_API) {
console.log("BackendAPI: (eval) ", line);
}
try {
eval(line);
} catch (e) {
throw new Error("BackendAPI: unable to eval API at line #" + lineNum + ":\n" + " " + line + "\n" + e);
}
}
};

const backendAPI = Runtime.queryParam("backend_api");
if (backendAPI) {
const decodedBackendAPI = decodeURIComponent(backendAPI);
const lines = decodedBackendAPI.split("\n");
if (dirac._DEBUG_BACKEND_API) {
console.log("BackendAPI: backend_api url parameter present (" + lines.length + " registrations).");
}
evalAPI(lines);
WebInspector.BakedInspectorBackendMode = "external";
} else {
const lines = WebInspector.BakedInspectorBackendAPI.split("\n");
if (dirac._DEBUG_BACKEND_API) {
console.log("BackendAPI: backend_api url parameter not present. Using pre-baked backend API (" + lines.length + " registrations).");
}
evalAPI(lines);
WebInspector.BakedInspectorBackendMode = "internal";
}
};

WebInspector.InspectorBackendExtensionMode.loadFromExtensionIfNeeded();
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
WebInspector.SupportedCSSPropertiesExtensionMode = {};

WebInspector.SupportedCSSPropertiesExtensionMode.loadFromExtensionIfNeeded = function() {
const evalCSS = (definitions) => {
try {
eval("WebInspector.CSSMetadata._generatedProperties = " + definitions);
} catch (e) {
throw new Error("BackendCSS: unable to eval CSS" + e);
}
};

const encodedBackendCSS = Runtime.queryParam("backend_css");
if (encodedBackendCSS) {
const backendCSS = decodeURIComponent(encodedBackendCSS);
const lines = backendCSS.split("\n");
if (dirac._DEBUG_BACKEND_CSS) {
console.log("BackendCSS: backend_css url parameter present (" + lines.length + " definitions).");
}
evalCSS(backendCSS);
WebInspector.BakedSupportedCSSPropertiesMode = "external";
} else {
const backendCSS = WebInspector.BakedSupportedCSSProperties;
const lines = backendCSS.split("\n");
if (dirac._DEBUG_BACKEND_CSS) {
console.log("BackendCSS: backend_css url parameter not present. Using pre-baked backend CSS (" + lines.length + " definitions).");
}
evalCSS(backendCSS);
WebInspector.BakedSupportedCSSPropertiesMode = "internal";
}
};

WebInspector.SupportedCSSPropertiesExtensionMode.loadFromExtensionIfNeeded();
7 changes: 5 additions & 2 deletions resources/unpacked/devtools/front_end/sdk/module.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
"dependencies": [
"common",
"host",
"platform"
"platform",
"dirac"
],
"extensions": [
{
Expand Down Expand Up @@ -73,6 +74,7 @@
"scripts": [
"InspectorBackend.js",
"../InspectorBackendCommands.js",
"InspectorBackendExtensionMode.js",
"InspectorBackendHostedMode.js",
"Target.js",
"TargetManager.js",
Expand Down Expand Up @@ -113,7 +115,8 @@
"NetworkRequest.js",
"PaintProfiler.js",
"HeapProfilerModel.js",
"../SupportedCSSProperties.js"
"../SupportedCSSProperties.js",
"SupportedCSSPropertiesExtensionMode.js"
],
"skip_compilation": [
"../InspectorBackendCommands.js",
Expand Down
4 changes: 3 additions & 1 deletion resources/unpacked/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@
"storage",
"webRequest",
"http://*/json",
"<all_urls>",
"pageCapture",
"tabs"
],
"manifest_version": 2
}
}
2 changes: 1 addition & 1 deletion src/background/dirac/background/chrome.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
(:require-macros [cljs.core.async.macros :refer [go go-loop]]
[dirac.background.logging :refer [log info warn error]])
(:require [cljs.core.async :refer [<! chan put!]]
[chromex.support :refer-macros [oget ocall oapply]]
[chromex.support :refer-macros [oget ocall oapply oset]]
[chromex.chrome-event-channel :refer [make-chrome-event-channel]]
[chromex.protocols :refer [post-message! get-sender get-name]]
[chromex.ext.runtime :as runtime]
Expand Down
10 changes: 10 additions & 0 deletions src/background/dirac/background/core.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
[chromex.chrome-event-channel :refer [make-chrome-event-channel]]
[chromex.protocols :refer [post-message! get-sender get-name]]
[dirac.background.chrome :as chrome]
[dirac.background.state :as state]
[dirac.background.thief :as thief]
[dirac.options.model :as options]))

; -- main entry point -------------------------------------------------------------------------------------------------------
Expand All @@ -14,4 +16,12 @@
(log "init")
(go
(<! (options/init!))
(let [extract-api? (options/get-option :use-backend-supported-api)
extract-css? (options/get-option :use-backend-supported-css)]
(if (or extract-api? extract-css?)
(let [[backend-api backend-css] (<! (thief/scrape-bundled-devtools!))]
(if (and extract-api? (some? backend-api))
(state/set-backend-api! backend-api))
(if (and extract-css? (some? backend-css))
(state/set-backend-css! backend-css)))))
(chrome/start-chrome-event-loop!)))
19 changes: 13 additions & 6 deletions src/background/dirac/background/helpers.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
[dirac.background.action :as action]
[dirac.background.state :as state]
[dirac.utils :as utils]
[cljs.reader :as reader])
(:import goog.Uri
goog.Uri.QueryData))
[cljs.reader :as reader]
[clojure.string :as string])
(:import goog.Uri))

(defn ^:dynamic warn-about-unexpected-number-views [devtools-id views]
(warn (str "found unexpected number views with enabled automation support for devtools #" devtools-id "\n")
Expand All @@ -30,10 +30,15 @@
(let [uri (make-uri-object url)]
(.getParameterValue uri param)))

(defn build-query [params]
(let [encode js/encodeURIComponent
items (map (fn [[k v]] (str (encode k) "=" (encode v))) params)]
(string/join "&" items)))

(defn make-relative-url [path params]
{:pre [(map? params)]}
(let [non-empty-params (into {} (filter second params))]
(str path "?" (.toDecodedString (.createFromMap QueryData (clj->js non-empty-params))))))
(let [non-empty-params (into {} (filter #(some? (second %)) params))]
(str path "?" (build-query non-empty-params))))

; -- dirac frontend url -----------------------------------------------------------------------------------------------------

Expand All @@ -47,7 +52,7 @@
; chrome-extension://mjdnckdilfjoenmikegbbenflgjcmbid/devtools/front_end/inspector.html?devtools_id=1&dirac_flags=11111&ws=localhost:9222/devtools/page/76BE0A6D-412C-4592-BC3C-ED3ECB5DFF8C
(defn make-dirac-frontend-url [devtools-id options]
{:pre [devtools-id]}
(let [{:keys [backend-url flags reset-settings automate extra-url-params]} options]
(let [{:keys [backend-url flags reset-settings automate extra-url-params backend-api backend-css]} options]
(assert backend-url)
(assert flags)
(let [html-file-path (get-dirac-main-html-file-path)
Expand All @@ -58,6 +63,8 @@
; add optional params
reset-settings (assoc "reset_settings" 1)
automate (assoc "dirac_automate" 1)
backend-api (assoc "backend_api" backend-api)
backend-css (assoc "backend_css" backend-css)
extra-url-params (merge extra-url-params))]
(runtime/get-url (make-relative-url html-file-path all-params)))))

Expand Down
27 changes: 25 additions & 2 deletions src/background/dirac/background/state.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@
[chromex.logging :refer-macros [log info warn error group group-end]]
[chromex.protocols :refer [post-message! get-sender get-name]]
[devtools.toolbox :as toolbox]
[dirac.utils :as utils]))
[dirac.utils :as utils]
[clojure.string :as string]))

(defonce initial-state
{:last-devtools-id 0
:devtools-descriptors {} ; pairings between dirac devtools instances and connected backend tabs
:chrome-event-channel nil
:marion-port nil})
:marion-port nil
:backend-api nil
:backend-css nil})

(defonce state (atom initial-state))

Expand Down Expand Up @@ -95,3 +98,23 @@
([message-id] (post-reply! message-id nil))
([message-id value]
(post-raw-reply! message-id (make-reply value))))

; -- backend-api ------------------------------------------------------------------------------------------------------------

(defn set-backend-api! [api]
{:pre [(string? api)]}
(log (str "initialized backend-api with " (count (string/split-lines api)) " calls"))
(swap! state assoc :backend-api api))

(defn get-backend-api []
(:backend-api @state))

; -- backend-css ------------------------------------------------------------------------------------------------------------

(defn set-backend-css! [css]
{:pre [(string? css)]}
(log (str "initialized backend-css with " (count (string/split-lines css)) " definitions"))
(swap! state assoc :backend-css css))

(defn get-backend-css []
(:backend-css @state))
75 changes: 75 additions & 0 deletions src/background/dirac/background/thief.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
(ns dirac.background.thief
(:require-macros [cljs.core.async.macros :refer [go go-loop]])
(:require [cljs.core.async :refer [<! chan timeout]]
[chromex.support :refer-macros [oget oset ocall oapply]]
[chromex.logging :refer-macros [log info warn error group group-end]]
[chromex.ext.page-capture :as page-capture]
[dirac.mime :as mime]
[dirac.quoted-printable :as qp]
[goog.string :as gstring]
[dirac.utils :as utils]
[dirac.background.tools :as tools]
[clojure.string :as string]))

; -- inspector-js -----------------------------------------------------------------------------------------------------------

(defn extract-inspector-js [source]
(let [multipart (mime/parse-multipart-mime source)
_ (assert (= (count (:parts multipart)) 1))
parsed-first-part (mime/parse-mime (first (:parts multipart)))
content-type (get (:headers parsed-first-part) "Content-Type")
_ (assert (= content-type "text/html"))
content-transfer-encoding (get (:headers parsed-first-part) "Content-Transfer-Encoding")
_ (assert (= content-transfer-encoding "quoted-printable"))
encoded-body (:body parsed-first-part)]
(case content-transfer-encoding
"quoted-printable" (qp/decode-quoted-printable encoded-body))))

; -- backend api ------------------------------------------------------------------------------------------------------------

(defn extract-backend-api [inspector-js-source]
(let [re (js/RegExp. ";InspectorBackend\\.register(.*?)\\)" "gm")
res #js []]
(loop []
(when-let [m (.exec re inspector-js-source)]
(.push res (.substring (first m) 1))
(recur)))
(.join res "\n")))

(defn steal-inspector-js [multipart-mime]
(-> multipart-mime
(gstring/canonicalizeNewlines)
(extract-inspector-js)))

(defn steal-backend-api [inspector-js]
(-> inspector-js
(extract-backend-api)))

; -- backend css ------------------------------------------------------------------------------------------------------------

(defn extract-backend-css [inspector-js-source]
(let [re (js/RegExp. ";WebInspector\\.CSSMetadata\\._generatedProperties=(\\[.*\\])" "g")]
(if-let [m (re-find re inspector-js-source)]
(second m))))

(defn insert-css-newlines [source]
(string/replace source "}," "},\n"))

(defn steal-backend-css [inspector-js]
(-> inspector-js
(extract-backend-css)
(insert-css-newlines)))

; -- robbery entry point ----------------------------------------------------------------------------------------------------

(defn scrape-bundled-devtools! []
(go
(let [tab-id (<! (tools/create-bundled-devtools-inspector-window!))
[mhtml] (<! (page-capture/save-as-mhtml (js-obj "tabId" tab-id)))
_ (tools/close-tab-with-id! tab-id)
multipart-mime (<! (utils/convert-blob-to-string mhtml))
inspector-js (steal-inspector-js multipart-mime)
backend-api (steal-backend-api inspector-js)
backend-css (steal-backend-css inspector-js)]
[backend-api backend-css])))

34 changes: 33 additions & 1 deletion src/background/dirac/background/tools.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,18 @@
first-tab (aget tabs 0)]
(sugar/get-tab-id first-tab))))))

(defn create-bundled-devtools-inspector-window! []
(let [window-params #js {:url "chrome-devtools://devtools/bundled/inspector.js"
:type "normal"
:state "minimized"}]
(go
(if-let [[window] (<! (windows/create window-params))]
(let [tabs (oget window "tabs")
first-tab (aget tabs 0)
tab-id (sugar/get-tab-id first-tab)]
(<! (timeout 1000))
tab-id)))))

(defn create-dirac-tab! []
(go
(if-let [[tab] (<! (tabs/create #js {:url (helpers/make-blank-page-url)}))]
Expand Down Expand Up @@ -84,9 +96,29 @@
(assoc options :automate true)
options))

(defn provide-backend-api-if-available [options]
(or
(if (options/get-option :use-backend-supported-api)
(if-let [backend-api (state/get-backend-api)]
(assoc options :backend-api backend-api)))
options))

(defn provide-backend-css-if-available [options]
(or
(if (options/get-option :use-backend-supported-css)
(if-let [backend-css (state/get-backend-css)]
(assoc options :backend-css backend-css)))
options))

(defn prepare-options [initial-options]
(-> initial-options
(automate-if-marion-present)
(provide-backend-api-if-available)
(provide-backend-css-if-available)))

(defn connect-and-navigate-dirac-devtools! [frontend-tab-id backend-tab-id options]
(let [devtools-id (devtools/register! frontend-tab-id backend-tab-id)
dirac-frontend-url (helpers/make-dirac-frontend-url devtools-id (automate-if-marion-present options))]
dirac-frontend-url (helpers/make-dirac-frontend-url devtools-id (prepare-options options))]
(go
(<! (tabs/update frontend-tab-id #js {:url dirac-frontend-url}))
(<! (timeout 500)) ; give the page some time load the document
Expand Down
Loading

0 comments on commit 28995a0

Please sign in to comment.