[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] externals/firefox-javascript-repl f0ed5fbf81 03/24: Get ready for
From: |
Thomas Fitzsimmons |
Subject: |
[elpa] externals/firefox-javascript-repl f0ed5fbf81 03/24: Get ready for asynchronous JavaScript evaluation |
Date: |
Thu, 1 Jun 2023 00:41:51 -0400 (EDT) |
branch: externals/firefox-javascript-repl
commit f0ed5fbf8191864b423d34bab07835454647bead
Author: Thomas Fitzsimmons <fitzsim@fitzsim.org>
Commit: Thomas Fitzsimmons <fitzsim@fitzsim.org>
Get ready for asynchronous JavaScript evaluation
* firefox-javascript-repl.el (fjrepl--message)
(fjrepl--error, fjrepl--create-profile-directory)
(fjrepl--send-string): Shorten name.
(fjrepl--parse-prior-message)
(fjrepl--get-result, fjrepl--handle-done)
(fjrepl--handle-console, fjrepl--format-message)
(fjrepl--handle-target, fjrepl--handle-tab)
(fjrepl--handle-first): New functions.
(firefox-javascript-repl): Simplify using new function.
---
firefox-javascript-repl.el | 201 +++++++++++++++++++++++++++++++++------------
1 file changed, 147 insertions(+), 54 deletions(-)
diff --git a/firefox-javascript-repl.el b/firefox-javascript-repl.el
index bab00de84d..072b7af5d7 100644
--- a/firefox-javascript-repl.el
+++ b/firefox-javascript-repl.el
@@ -36,16 +36,16 @@
:group 'external
:type 'string)
-(defun firefox-javascript-repl--message (format &rest arguments)
+(defun fjrepl--message (format &rest arguments)
"Print to *Messages* a package herald and FORMAT.
ARGUMENTS will be used for FORMAT, like `messages'."
(apply #'message (concat "firefox-javascript-repl: " format) arguments))
-(defun firefox-javascript-repl--error (message)
+(defun fjrepl--error (message)
"Throw an error with a package herald and MESSAGE."
(error "firefox-javascript-repl: %s" message))
-(defun firefox-javascript-repl--create-profile-directory ()
+(defun fjrepl--create-profile-directory ()
"Create a profile directory."
(let ((profile-directory (make-temp-file "firefox-javascript-repl-" t)))
(with-current-buffer
@@ -57,37 +57,147 @@ ARGUMENTS will be used for FORMAT, like `messages'."
(kill-buffer))
profile-directory))
-(defun firefox-javascript-repl--get-first-tab-actor (network-buffer)
- "Get the actor of the first browser tab.
-NETWORK-BUFFER is where Firefox responses go."
- (gethash
- "actor"
- (elt
- (gethash
- "tabs"
- (with-current-buffer network-buffer
- (firefox-javascript-repl--message "BUF: %S" (buffer-string))
- (goto-char (point-max))
- (unless (bolp)
- (insert "\n"))
- (search-backward "{\"tabs\":")
- (let ((start (point)))
- (forward-sexp)
- (prog1
- (json-parse-string (buffer-substring start (point)))
- (goto-char (point-max))))))
- 0)))
-
-(defun firefox-javascript-repl--accept-input (network)
- "Wait up to ten sections to see if packets are received from NETWORK."
- (unless (accept-process-output network 10 0 t)
- (firefox-javascript-repl--error
- "Failed to receive network packets from Firefox")))
-
-(defun firefox-javascript-repl--send-string (network message)
+(defun fjrepl--parse-prior-message (buffer)
+ "Parse the JSON message before in BUFFER."
+ (with-current-buffer buffer
+ (when (re-search-backward "[0-9]+:{" nil t)
+ (forward-char 2)
+ (let ((start (point)))
+ (forward-sexp)
+ (prog1
+ (json-parse-string (buffer-substring start (point)))
+ (backward-sexp))))) )
+
+(defun fjrepl--get-result (buffer depth &rest arguments)
+ "Return a field from BUFFER.
+BUFFER holds Firefox's Remote Debug Protocol response messages.
+DEPTH is non-nil if ARGUMENTS represent a decending path through
+the data structure. DEPTH is nil if ARGUMENTS should match
+fields at the current depth level. Search from the most recent
+to the least recent messages. Return the filtered value based on
+the matching arguments in the hierarchy. ARGUMENTS is a list of
+numbers and strings. A number is used to pick out an element in
+a JSON list, a string is used to pick out an element from a JSON
+map."
+ (with-current-buffer buffer
+ (goto-char (point-max))
+ (unless (bolp) (insert "\n")) ; avoid one long line
+ (let (result)
+ (catch 'match
+ (while (setq result (fjrepl--parse-prior-message buffer))
+ (catch 'fail
+ (dolist (argument arguments) ; check that all arguments match
+ (cond ((numberp argument)
+ (setq result (when (vectorp result)
+ (elt result argument))))
+ ((stringp argument)
+ (if depth
+ (setq result (when (hash-table-p result)
+ (gethash argument result)))
+ (setq result (and (gethash argument result)
+ result)))))
+ (unless result (throw 'fail nil))))
+ (when result (throw 'match result)))))))
+
+(defun fjrepl--accept-input (&optional network)
+ "Wait up to ten sections to see if packets are received.
+If NETWORK is non-nil, check for output from it. Otherwise check
+for output from any processes on which Emacs is waiting.."
+ (dotimes (_count 20)
+ (accept-process-output network 0.1)))
+
+(defun fjrepl--send-string (network message)
"Send to NETWORK a MESSAGE then wait for the response."
(process-send-string network message)
- (firefox-javascript-repl--accept-input network))
+ (fjrepl--accept-input))
+
+(defun fjrepl--handle-done (name buffer network)
+ "Process the startListeners response.
+NAME is a string, the process name to use, BUFFER is a string,
+the name of the buffer in which to put process output, NETWORK is
+the debugger-server connection to Firefox."
+ (fjrepl--message "Ready for asynchronous JavaScript evaluation %s %s %s"
+ name buffer network))
+
+(defun fjrepl--handle-console (name buffer network)
+ "Process the startListeners response.
+NAME is a string, the process name to use, BUFFER is a string,
+the name of the buffer in which to put process output, NETWORK is
+the debugger-server connection to Firefox."
+ (fjrepl--message "Handling console message")
+ (when (get-buffer buffer)
+ (let ((result (fjrepl--get-result buffer nil "startedListeners" "from")))
+ (when result
+ (let ((console-actor (gethash "from" result)))
+ (fjrepl--message "Ready for asynchronous JavaScript evaluation on %s"
+ console-actor)))
+ (run-at-time (unless result 1) nil
+ (if result
+ 'fjrepl--handle-done 'fjrepl--handle-console)
+ name buffer network))))
+
+(defun fjrepl--format-message (format &rest arguments)
+ "Format a message for the Firefox Remote Debug Protocol wire.
+FORMAT is a format string taken by `format', and ARGUMENTS are
+the arguments for the format string."
+ (let ((message (apply 'format format arguments)))
+ (format "%d:%s" (length message) message)))
+
+(defun fjrepl--handle-target (name buffer network)
+ "Process the getTarget response.
+NAME is a string, the process name to use, BUFFER is a string,
+the name of the buffer in which to put process output, NETWORK is
+the debugger-server connection to Firefox."
+ (fjrepl--message "Handling target message")
+ (when (get-buffer buffer)
+ (let ((console-actor (fjrepl--get-result buffer t "frame" "consoleActor")))
+ (when console-actor
+ (process-send-string network
+ (fjrepl--format-message
+ (concat
+ "{\"type\":\"startListeners\","
+ "\"to\":\"%s\","
+ "\"listeners\":[\"eventListener\"]}")
+ console-actor)))
+ (run-at-time (unless console-actor 1) nil
+ (if console-actor
+ 'fjrepl--handle-console 'fjrepl--handle-target)
+ name buffer network))))
+
+(defun fjrepl--handle-tab (name buffer network)
+ "Process the listTabs response.
+NAME is a string, the process name to use, BUFFER is a string,
+the name of the buffer in which to put process output, NETWORK is
+the debugger-server connection to Firefox."
+ (fjrepl--message "Handling tab message")
+ (when (get-buffer buffer)
+ (let ((actor (fjrepl--get-result buffer t "tabs" 0 "actor")))
+ (when actor
+ (process-send-string network
+ (fjrepl--format-message
+ "{\"type\":\"getTarget\",\"to\":\"%s\"}" actor)))
+ (run-at-time (unless actor 1) nil
+ (if actor 'fjrepl--handle-target 'fjrepl--handle-tab)
+ name buffer network))))
+
+(defun fjrepl--handle-first (name buffer network)
+ "Connect to Firefox process and process the first message from Firefox.
+NAME is a string, the process name to use, BUFFER is a string,
+the name of the buffer in which to put process output, NETWORK is
+the debugger-server connection to Firefox."
+ (fjrepl--message "Handling first message")
+ (let ((network network))
+ (unwind-protect
+ (setq network (ignore-errors
+ (open-network-stream name buffer "127.0.0.1" 6000)))
+ (let ((nextp
+ (and network (fjrepl--get-result buffer t "applicationType"))))
+ (when nextp
+ (process-send-string
+ network "31:{\"to\":\"root\",\"type\":\"listTabs\"}"))
+ (run-at-time (unless nextp 1) nil
+ (if nextp 'fjrepl--handle-tab 'fjrepl--handle-first)
+ name buffer network)))))
(defun firefox-javascript-repl ()
"Run a new instance of Firefox in a new profile.
@@ -99,7 +209,7 @@ localhost (127.0.0.1) TCP port 6000."
(interactive)
(when (not (process-status "firefox-javascript-repl"))
(let* ((profile-directory
- (firefox-javascript-repl--create-profile-directory))
+ (fjrepl--create-profile-directory))
(temporary-name (file-name-nondirectory profile-directory))
(process-name (concat "out-" temporary-name))
(process-buffer-name (concat "*" process-name "*"))
@@ -116,31 +226,14 @@ localhost (127.0.0.1) TCP port 6000."
(lambda (process event)
(when (or (string= event "killed\n")
(string= event "finished\n"))
- (firefox-javascript-repl--message
+ (fjrepl--message
"%s %s; deleting %s"
process (string-trim event) profile-directory)
- (ignore-errors (kill-buffer
network-buffer-name))
+ (ignore-errors
+ (kill-buffer network-buffer-name))
(kill-buffer process-buffer-name)
(delete-directory profile-directory t))))
- (let (network)
- (catch 'connected
- (dotimes (_count 50)
- (when
- (setq network
- (ignore-errors
- (open-network-stream process-name network-buffer-name
- "127.0.0.1" 6000)))
- (throw 'connected network))
- (sit-for 2))
- (firefox-javascript-repl--error
- "Failed to connect to Firefox network port"))
- (firefox-javascript-repl--accept-input network)
- (firefox-javascript-repl--message "Established Firefox connection")
- (firefox-javascript-repl--send-string
- network "31:{\"to\":\"root\",\"type\":\"listTabs\"}")
- (let ((actor (firefox-javascript-repl--get-first-tab-actor
- network-buffer-name)))
- (message "Actor: %s" actor)))
+ (fjrepl--handle-first network-name network-buffer-name nil)
nil)))
(defun firefox-javascript-repl-stop ()
- [elpa] branch externals/firefox-javascript-repl created (now 7b288e2dba), Thomas Fitzsimmons, 2023/06/01
- [elpa] externals/firefox-javascript-repl 18fa0a257b 07/24: Enable font lock, Thomas Fitzsimmons, 2023/06/01
- [elpa] externals/firefox-javascript-repl f601354463 08/24: Show JavaScript quirk on startup, Thomas Fitzsimmons, 2023/06/01
- [elpa] externals/firefox-javascript-repl 7c8f747bba 10/24: Add a JavaScript example with which to test, Thomas Fitzsimmons, 2023/06/01
- [elpa] externals/firefox-javascript-repl 76fe78566b 11/24: Polish process management, debugging and naming, Thomas Fitzsimmons, 2023/06/01
- [elpa] externals/firefox-javascript-repl 075bd87281 15/24: Adjust commentary wording, Thomas Fitzsimmons, 2023/06/01
- [elpa] externals/firefox-javascript-repl 2016b0eafa 22/24: Make firefox-javascript-repl--mode private, Thomas Fitzsimmons, 2023/06/01
- [elpa] externals/firefox-javascript-repl c2a0f7f44a 04/24: Get basic REPL working, Thomas Fitzsimmons, 2023/06/01
- [elpa] externals/firefox-javascript-repl f0ed5fbf81 03/24: Get ready for asynchronous JavaScript evaluation,
Thomas Fitzsimmons <=
- [elpa] externals/firefox-javascript-repl 77c9907e3e 18/24: Move usage to start of commentary, Thomas Fitzsimmons, 2023/06/01
- [elpa] externals/firefox-javascript-repl 1e0638d411 17/24: Mention throwaway profile in commentary, Thomas Fitzsimmons, 2023/06/01
- [elpa] externals/firefox-javascript-repl 7b288e2dba 24/24: Bump version to 0.9.0, Thomas Fitzsimmons, 2023/06/01
- [elpa] externals/firefox-javascript-repl 7bed24ae8e 23/24: Ignore errors when showing quirk, Thomas Fitzsimmons, 2023/06/01
- [elpa] externals/firefox-javascript-repl b0695f591d 02/24: Complete actor retrieval, Thomas Fitzsimmons, 2023/06/01
- [elpa] externals/firefox-javascript-repl 3d5c11b468 05/24: Add .gitignore file, Thomas Fitzsimmons, 2023/06/01
- [elpa] externals/firefox-javascript-repl 563ae0984c 01/24: firefox-javascript-repl: New package, Thomas Fitzsimmons, 2023/06/01
- [elpa] externals/firefox-javascript-repl 9644f919dd 06/24: Fix string quoting, Thomas Fitzsimmons, 2023/06/01
- [elpa] externals/firefox-javascript-repl 5c47a29fd8 09/24: Remove newlines before sending JavaScript, Thomas Fitzsimmons, 2023/06/01
- [elpa] externals/firefox-javascript-repl b4186a5762 12/24: Backport to Emacs 26.1, Thomas Fitzsimmons, 2023/06/01