Age | Commit message (Collapse) | Author |
|
|
|
A bit crude, but it covers us when extensions are in multiple Chrome
profiles. This ensures the desired tab is reloaded because all current
tabs in all profiles that have requested extensions are reloaded.
Not very intelligent, but a simple approach.
|
|
Didn't reload the active tab in my test. But the previous way doesn't
work when multiple copies of the same extension appear in the list of
targets.
Think we might want to always reload the current tab after reloading an
extension because the extension targets could be in different Chrome
profiles. Thus, you couldn't be sure which tab would be reloaded.
|
|
If we reload the active tab and it fails, we'll get an error response
back. Keep trying to reload the page until we no longer get the error.
|
|
Tried using manually-incremented `*reloaded-count*` but that didn't
quite work as I didn't have the right conditions to start the reload.
Then tried setting up the condition such that we reload when the
response comes back from the extension reload message. We can tell this
when we get a `result` response that includes a `sessionId` field. To
execute the reload a single time, store the most recent session ID, and
compare against that.
Using conditions on both the reloaded count (which needs to be changed
to handle multiple copies of the same extension) and the last session ID
in the message contents, we have enough to set up the reload in the tab.
Added the `sleep` call back in because otherwise I got this error:
reloading NOW
Response: (OBJ (id . 2)
(result OBJ (sessionId . 106D182E44C641B22EC65E9F6458B245)))
#<WAIT-GROUP :counter 2>
Response: (OBJ (id . 1)
(result OBJ
(result OBJ (type . object) (subtype . error)
(className . TypeError)
(description
. TypeError: Cannot read property 'reload' of undefined
at <anonymous>:1:16)
(objectId . 8548451452974044825.19.1))
(exceptionDetails OBJ (exceptionId . 2) (text . Uncaught)
(lineNumber . 0) (columnNumber . 15) (scriptId . 147)
(exception OBJ (type . object) (subtype . error)
(className . TypeError)
(description
. TypeError: Cannot read property 'reload' of undefined
at <anonymous>:1:16)
(objectId . 8548451452974044825.19.2))))
(sessionId . 106D182E44C641B22EC65E9F6458B245))
#<WAIT-GROUP :counter 1>
Response: (OBJ (id . 2)
(result OBJ
(result OBJ (type . object) (subtype . error)
(className . TypeError)
(description
. TypeError: Cannot read property 'reload' of undefined
at <anonymous>:1:13)
(objectId . 8548451452974044825.19.3))
(exceptionDetails OBJ (exceptionId . 3) (text . Uncaught)
(lineNumber . 0) (columnNumber . 12) (scriptId . 156)
(exception OBJ (type . object) (subtype . error)
(className . TypeError)
(description
. TypeError: Cannot read property 'reload' of undefined
at <anonymous>:1:13)
(objectId . 8548451452974044825.19.4))))
(sessionId . 106D182E44C641B22EC65E9F6458B245))
That tells us that the tab reload message was sent to the extension's
background page before it had a chance to fully reload. We thus need to
wait until the extension is fully reloaded before being able to send the
tab reload message.
Don't like the sleep call here. Would be nice to have a more robust
solution that didn't wait an arbitrary amount of time. Maybe we can keep
sending the tab reload message until we get a response that's not an
error.
|
|
Trying to set up the reload on the active tab after all extensions are
reloaded. It's only working half the time and I can't figure out what
I'm doing wrong. Something wrong with the wait group I think.
Committing what I have and might try other ideas.
|
|
|
|
|
|
|
|
Previously, we'd drop into the debugger on uncaught errors. Instead, we
should just exit with an error message.
|
|
|
|
Remove the hard-coded call IDs and replace them with a class that keeps
track of the current call ID and allows for easy incrementing to get the
next ID.
This should allow us to give multiple extension IDs on the command line
and send messages with properly incrementing call IDs.
Didn't touch the `runtime-evaluate-msg` message call ID because that one
is local to the target it's attached to, so we can keep it at ID "1".
|
|
|
|
|
|
Reload the extension IDs given on the command line now that we have them
from the `config` object. Replace the hard-coded extension ID that I had
been using for testing.
|
|
|
|
Keep the `*client*` global variable, but use the client in `config`,
constructed from the WebSocket URL passed from the command line.
Not a huge fan of the global variable, but it's way easier to keep it
like this rather than passing the client down to the `attach-to-target`
and `reload-extension` functions.
|
|
Add a new `make-config` function to construct a `config` object. This
creates a new websocket-driver client and stores it in the `ws-client`
slot in the `config`.
Before this, I thought about using a writer method on the `socket-url`
slot that creates a new client. This didn't work in `make-instance`
though. Perhaps there's a way to have the `:initarg` use the writer, but
I'm not sure.
|
|
The `main.lisp` file was getting crowded. Move DevTools Protocol-related
functions into a new file.
|
|
|
|
|
|
Make a new function `parse-options` that parses the command line options
and returns a `config` object. We'll use that object instead of
`options` in `main`. Cleans up the `main` function a bit.
Currently, we just print the `config` object to ensure we're storing the
proper values.
Followed Practical Common Lisp's example to implement `print-object` so
we can see the contents of its slots:
http://www.gigamonkeys.com/book/practical-a-spam-filter.html#the-heart-of-a-spam-filter
Still need to implement error checking for a missing `--socket-url`
option.
|
|
We'll use this as a storage container for the command line options.
|
|
|
|
Add an error handler that just prints the error messages from
'unix-opts' to standard error and exits with EX_USAGE.
Inspired by:
- http://lispcookbook.github.io/cl-cookbook/scripting.html#handling-malformed-or-missing-arguments
- https://github.com/libre-man/unix-opts/blob/0e61f34b2ecf62288437810d4abb31e572048b04/example/example.lisp
|
|
Add `:arg-parser` to the `:socket-url` definition because 'unix-opts'
requires it in order to display the "SOCKET_URL" `:meta-var` in the help
output.
|
|
Include the 'unix-opts' library described in
http://lispcookbook.github.io/cl-cookbook/scripting.html#parsing-command-line-arguments
for command line option parsing.
Define the options I need. We want a `--socket-url` option, and a list
of extension IDs as free arguments.
Implement the `-V` version command line argument. Thanks to JJJ
(https://stackoverflow.com/users/1337941/jjj) on Stack Overflow for
describing how to get the version number of an ASDF system:
https://stackoverflow.com/questions/11084339/getting-the-version-of-an-asdf-system/11088022#11088022
Add a new `options.lisp` file where we'll add the option parsing restart
error handling functions required by 'unix-opts'.
|
|
Ignore FASL files.
|
|
|
|
|
|
Create a new `with-websocket-connection` decorator that starts and
closes a WebSocket connection to the given `client` around the body
forms.
Thanks to Practical Common Lisp's "The Special Operators" chapter
(http://www.gigamonkeys.com/book/the-special-operators.html#unwinding-the-stack)
for introducing me to `unwind-protect`.
Couldn't figure out how to get the new macro to auto-indent properly
with Vlime, so ended up manually indenting it.
|
|
Must have copy-pasted that s-expression from elsewhere in the file.
|
|
|
|
|
|
The `sleep` call allowed me to test the behaviour of the program, since
without it, it would exit before the WebSocket messages had a chance to
be sent and received.
But we shouldn't be waiting a fixed number of seconds for the program to
execute. Instead, we should only keep the program alive as long as there
are messages to be sent and received.
This adds a Go-style wait group using my wait-group library that
increments the wait group when we send a WebSocket message, and
decrements it when we receive a WebSocket response. That allows us to
keep the program alive only for the amount of time necessary for the
messages to be exchanged.
|
|
|
|
Doesn't make a big difference, just for fun. I like the idea of a
compile-time version of this since it's essentially just renaming
`remove-if-not`.
Move it to a new file so we can include it before it's used in
`main.lisp`.
|
|
|
|
I've been reading Practical Common Lisp's "Collections" chapter
(http://www.gigamonkeys.com/book/collections.html) and it seemed like
`find-if` would be nicer here than what I wrote before.
|
|
Just learned about `remove-if-not`. Really cleans up this function. Not
even really necessary to keep `filter`, but I guess I'll hold on to it
for now.
|
|
Just learned that `string=` exists, and that seems to describe the
intent better than `equal`.
|
|
|
|
|
|
|
|
Send DevTools Protocol messages to reload extensions. Not easy to do
things sequentially since the responses have to be handled in
`ws-on-message`. Once we filter the list of extensions wanted to reload,
attach to their DevTools targets, then send them JavaScript evaluation
messages that tells the extensions to reload.
|
|
Prefix the function name with the name of the DevTools domain to
distinguish Target message functions from Runtime functions.
|
|
|
|
Filter a list of extension background page targets.
The extension IDs should come from the command line, but I've hard-coded
the list here.
Increased the `sleep` time to allow time for the messages to be sent &
received before the program exits. Will need to figure out a proper way
to do this later.
|
|
Use `jsown` to parse the response from the `Target.getTargets` message.
Get a list of `targetInfos` from the response.
Extracting keys from the JSON result with `jsown:val` raises an `error`
exception when the key is not present. Turn the exception into `nil`
with the `json-obj-get` function.
|
|
Build an executable binary by dumping an SBCL image, using the method
described in:
https://lispcookbook.github.io/cl-cookbook/scripting.html#with-asdf
|