Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
4cd4df9
initial devtools extension setupContextMenu
dkrasner Oct 1, 2022
f0a6e78
adding d3 tree viewer to the devtools cells panel
dkrasner Oct 2, 2022
a4333ee
initial structuring of the d3 code into something reasonable
dkrasner Oct 3, 2022
d11bf67
adding info split-panel for cells
dkrasner Oct 10, 2022
93cf81b
adding background and content-script js files
dkrasner Oct 10, 2022
9eb8459
updating communication between background target and devtools
dkrasner Oct 10, 2022
019b704
adding a README
dkrasner Oct 12, 2022
259b75d
adding handleMessageFromBackground to devtools display
dkrasner Oct 16, 2022
2023e27
first pass at getting tree data to debugger
dkrasner Oct 16, 2022
233bf51
minor update to tree building for devtools
dkrasner Oct 17, 2022
0565251
wrapping up devtools cell tree data generator and msg method
dkrasner Oct 17, 2022
a23ff93
adding mouseover and leave for cell tree nodes
dkrasner Oct 17, 2022
ecc9d52
updaing mouseover and leave
dkrasner Oct 17, 2022
cc0608c
updating devtools readme
dkrasner Oct 17, 2022
2ec08b7
adding a better check (on the devtools side) to see if tree updated
dkrasner Oct 17, 2022
f830156
limiting the node name length in tree
dkrasner Nov 4, 2022
70969b2
template setup for tree component
dkrasner Nov 4, 2022
8f8fcf3
adding TreeNode and updating basic example setup
dkrasner Nov 4, 2022
7fbf00c
adding depths to trees and webpack
dkrasner Nov 5, 2022
947d46f
[WIP] rewriting Tree as regular object to dealing with leaderlines
dkrasner Nov 7, 2022
44676d4
[WIP] add tree.css and minor updates to leaderlines
dkrasner Nov 8, 2022
de7677b
v0 of working tree with conneiton lines
dkrasner Nov 15, 2022
a94d0cf
leadrlines use proper js document.createElementNS API
dkrasner Nov 15, 2022
64175ad
adding global css, better styling and window resize listener
dkrasner Nov 16, 2022
6f75f80
adding subtree zoom in/out and related css updates to tree and node
dkrasner Nov 30, 2022
1344b95
optimizing svg line rendering; adding findParentSubTree for nav
dkrasner Nov 30, 2022
993661d
adding keyboard events for better scrolling up tree
dkrasner Nov 30, 2022
9cd87ac
initial integration of tree [WIP]
dkrasner Nov 30, 2022
b872dd8
adding proper display depth and related
dkrasner Dec 1, 2022
0675f74
fixing up connector lines in tree
dkrasner Dec 5, 2022
e784196
updating id key for messages from CellHandler to devtools
dkrasner Dec 31, 2022
06620c1
adding handling of DOM invalid cell/node ids
dkrasner Dec 31, 2022
17c8426
addiung non-starting-root node handling
dkrasner Jan 2, 2023
098b492
fixing up vertical paths in tree and adding on hover highlight
dkrasner Jan 2, 2023
1d35ea0
adding forgotten setupDevtools() in CellHandler
dkrasner Jan 2, 2023
580767f
typo fix
dkrasner Jan 2, 2023
9006db8
hightlight query selector fix
dkrasner Jan 2, 2023
6db1ea7
adding better overlay cleanup for devtools
dkrasner Jan 2, 2023
35b3cb1
adding better hover devtools highlighting and related changes
dkrasner Jan 2, 2023
d8871ce
filtering out non cells from messager to devtools in CellHandler
dkrasner Jan 24, 2023
f04608c
removing old d3 tree js file
dkrasner Jan 25, 2023
9b0227b
adding node click and customization handlers
dkrasner Jan 25, 2023
4de7667
improving the bezier curves connecting trees
dkrasner May 1, 2023
a6ee429
updating cell info panel
dkrasner May 3, 2023
71d0049
temp removal of child node display in info panel
dkrasner May 4, 2023
6de8bd8
minor clean and improvement of cells panel
dkrasner May 25, 2023
80274df
adding devtools request for cells source code
dkrasner May 26, 2023
29f0330
adding print statement to cell
dkrasner May 26, 2023
3a80632
pausing cells messages
dkrasner May 26, 2023
f631598
setting up target window call to get cell source
dkrasner May 26, 2023
7e5b8a5
fixing up cell lookup
dkrasner May 26, 2023
bf7b43c
adding a quick install and a readme
dkrasner Jun 1, 2023
74f6b8f
testing setting panel to background communication in devtools
dkrasner Jun 11, 2023
931cd27
cleaning up ports in devtools
dkrasner Jun 16, 2023
15da70b
refactoring inspected window handling CS message handling
dkrasner Jun 19, 2023
3f18df3
setting up cell source code request via the window message api
dkrasner Jun 19, 2023
1dff900
typo correction
dkrasner Jun 19, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ object_database/web/content/dist
\#*
\.#*
*~
.projectile

# leading slash means only match at repo-root level
/.python-version
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ from the repo. The public PyPI release is out of date.
```shell
pip install -e .
```


(there is a [quick_install.sh])(./quick_install.sh) file for you)
# Major components

This repo has 3 major components and 1 notable major dependency:
Expand Down
19 changes: 19 additions & 0 deletions object_database/web/cells/cells.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
import traceback
import logging
import types
import textwrap

from inspect import getsourcelines

from object_database.web.cells.session_state import SessionState
from object_database.web.cells.cell import Cell
Expand Down Expand Up @@ -127,6 +130,22 @@ def onMessage(self, message):

if cell is not None:
cell.mostRecentFocusId = self.focusEventId
if message.get("event") == "devtoolsRequest":
cellId = str(message.get("cellId"))
cell = self._cells.get(cellId)
data = {}
if message.get("request") == "source":
print("GETTING DEVTOOLS MESSAGE")
print(cell)
"""
sourceString = textwrap.dedent(
"".join(getsourcelines(cell.__func__)[0])
)
data["source"] = sourceString
print("response %s" % data)
return data
"""


def _executeCallback(self, callback, withLogging=True):
context = DependencyContext(self, readOnly=False)
Expand Down
5 changes: 5 additions & 0 deletions object_database/web/content/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -1096,3 +1096,8 @@ pre {
overflow: auto;
pointer-events: auto;
}

// devtools helpers
.devtools-inspect{
background-color: lightblue!important;
}
81 changes: 80 additions & 1 deletion object_database/web/content/src/CellHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ class CellHandler {

// the server-assigned session
this.sessionId = null;

// devtools related messages
this.sendMessageToDevtools = this.sendMessageToDevtools.bind(this);
this.updateDevtools = this.updateDevtools.bind(this);
this.setupDevtools = this.setupDevtools.bind(this);
}

tearDownAllLiveCells() {
Expand Down Expand Up @@ -127,6 +132,10 @@ class CellHandler {
])
])
);
this.setupDevtools();
this.sendMessageToDevtools({
status: "initial load"
});
}

afterConnected() {
Expand Down Expand Up @@ -158,6 +167,9 @@ class CellHandler {
["Reconnecting in " + waitSeconds + " seconds"])
])
);
this.sendMessageToDevtools({
status: "reconnecting"
});
}

/**
Expand All @@ -167,7 +179,7 @@ class CellHandler {
* It will case out the appropriate handling
* method based on the `type` field in the
* message.
* Note taht we call `doesNotUnderstand()`
* Note that we call `doesNotUnderstand()`
* in the event of a message containing a
* message type that is unknown to the system.
* @param {Object} message - A JSON decoded
Expand Down Expand Up @@ -365,6 +377,7 @@ class CellHandler {
+ totalUpdateCount + " nodes created/updated."
)
}
this.updateDevtools();
}
}

Expand Down Expand Up @@ -495,6 +508,72 @@ class CellHandler {
this.socket.sendString(JSON.stringify(message));
}
}

/**
* Devtools
*/
setupDevtools(){
window.addEventListener("message", (event) => {
// filter on the target windows url
if (event.origin === window.location.origin) {
// listent to only devtools content-script messages
if (event.data.type == "cells_devtools_CS") {
console.log("receiving message from CS: ", event.data);
}
}
})
}

sendMessageToDevtools(msg){
// TODO perhaps this should be run by a worker
msg.type = "cells_devtools";
window.postMessage(msg);
}

/**
* Send updated cells data to devtools
**/
updateDevtools(){
const buildTree = (cell) => {
if (cell.isCell) {
return {
name: cell.constructor.name,
id: cell.identity,
children: mapChildren(cell.namedChildren)
}
}
}
// NOTE: sometimes a named child is a cell, sometimes it's an array of cells
// so we need a helper function to deal with these cases
const mapChildren = (namedChildren) => {
const children = [];
if (namedChildren) {
Object.values(namedChildren).forEach((child) => {
if (child){
if (child instanceof Array){
child.forEach((subchild) => {
const subTree = buildTree(subchild);
if (subTree){
children.push(buildTree(subchild));
}
})
}
const subTree = buildTree(child);
if (subTree) {
children.push(buildTree(child));
}
}
})
}
return children;
}
const page_root = this.activeCells['page_root'];
const tree = buildTree(page_root);
this.sendMessageToDevtools({
status: "loaded",
cells: tree
});
}
}

export {CellHandler, CellHandler as default};
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
71 changes: 71 additions & 0 deletions object_database/web/devtools/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
## Chrome Devtools Extensions ##

The code here is devoted to building devtool extensions and related development.

**NOTE: as of writing this is tested only in Chrome 106.X.**

### Installation ###

Open the extension manager in chrome. Then press "Load Temporary Add-on," select the [manifest.json](./manifest.json) file and press "open." When loading one of our example applications and opening devtools you should see a "Cells" panel.

### Development ####

At its core the devtools extension is configured and defined by a [manifest.json](./manifest.json) file [NOTE: currently this is written for Manifest V2 which will no longer be supported in 2023]. The internals of the configuration is pretty self explanatory but the key thing to note is the presence of a `devtools_page` key. This specifies that the current is a "devtools extension" as opposed to a generic chrome extension. You can read more about manifests [here](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json).

After loading the extension as above you can see your changes by pressing the `reload` button. You can also load a devtools for the devtools by pressing `Inspect`, but the functionalities are limited (for example there is no view on the DOM, since there is no DOM here in the standard sense).

The rest of the story consists of understanding all the necessary components and how these communicate. This is summarized in the image below:

![Devtools Extension Setup](DevtoolsExtensions.png)

With the above "knowledge" you should already be able to guess that everything in the "Devtools" box will log to the devtool's devtool console, while everything in the "Document" box will log to the document's devtools console.

Lets go through these one at a time:

[devtools_init.html](./devtools_init.js) and [devtools_init.js](./devtools_init.js)
-------------------------------------------------------------------

With the extension loaded as above, when the devtools open it will load the `devools_page` specified in the manifest file. Even though [devtools.html](./devtools.html) is technically an html page, devtools pages **do not** have any visible DOM or access to WebExtension API's. What they do have is JS source code included via the `script` tag. In our case this is the [devtools_init.js](./devtools_init.js) file.

This bundled source file **does** have access to DOM API's through the global `window` object, same WebExtension API as content scripts (see below), and devtools APIs. The first thing this script does is use the latter to [create and configure the panel](./devtools_init.js#L9). Then subsequently it sets up global variables for holding data, callbacks for `panel.onShow` (fired for example when the panel icon is clicked) and `panel.onHidden`, as well as opening a `chrome.runtime.Port` which will allow for communication between the panel and the [background.js](./background.js) processes (see more on that below).

Note: since `devtools_init.js` has access to the `window` object, which is the context that panel lives in, we can call functions or access variable between the two.
For example, [window.handleMessageFromBackground()](./devtools_init.js#L25) is defined in [cell_panel.js](./js/cell_panel.js) but we can still access it through `_window`.

Note also, the var `_window` is used here to deal with the possibility that at times the message inspector panel is closed (for example you are looking at console or something else) but there is still message passing going on and data needs to be stored. In this case the panel collects data which will be subsequently processed when the panel opens. This is the core of what is defined in the `devtools_init.js` file.

[panels](./js)
------------------

The files you find in [js](./js) are what you expect of every normal web application: there is html, js and css. As of writing there are two interesting pieces here:

* the `handleMessageFromBackground()` function, already seen above called in `devtools_init.js`, is the callback for a message coming in from [background.js](background.js) via the panel port connection set up in `devtools_init.js`. The function handles the incoming message and updates in the display in the panel accordingly. As of writing it cases on `msg.status` (initial load, reconnecting, loaded) and calls for corresponding display functions,
* Note the `mouseover` and `mouseleave` event handlers added to the tree in [tree.js](./js/tree.js). This callback uses the `chrome.devtools.inspectedWindow.eval` API to insert a raw script and execute it on the `document`, i.e. on the inspected window. This allows for a way to by-pass the "standard" extension communication protocol and directly interact with the target window, although in a somewhat limited way. Is it safe to call `eval()` on raw scripts strings - depends... so use with caution.

In short, the code in panels is what you see when you click on the devtools message inspector icon and it communicates with the rest of the world either via `backround.js` or via direct script insertions.


[background.js](./background.js)
--------------------------------

[background](background.js) is the bridge communication between the devtools panel and the target application (see more on this in the content-script description below).

It
* listens for `chrome.runtime.Port` communications from both the `content-script.js` and the `cell_panel.js`,
* routes messages as needed via the various open ports.


[content-script.js](./content-script.js)
----------------------------------------

This script is injected into the target document window and runs each time devtools is open, i.e. everything here is essentially the same as any js code that you import via the `script` tag into your application. The key exception is that **content scripts have access to the background script** defined processes and code via the `chrome.runtime.Port` API **and** it can communicate with the your application (in our case via the [window.postMessage()](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) API.

Since devtools doesn't have direct access to the target window API, `background.js` becomes the bridge for all communication.

Now it's pretty clear what content scripts should do:
* set up a port to communication with background,
* listen to messages coming via the `window.postMessage` interface and forward whatever is needed to background.

The messages that content script here is waiting on are sent in `System.sendMessage()` in our application.

And that's more or less it.
63 changes: 63 additions & 0 deletions object_database/web/devtools/background.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* The background script handles communication to and from
* the content script, embedded in the document, and
* the panel scripts, living in devtools.
* These communiccation are handled by chrome.runtime connection
* ports.
* The connections are initialized int he content and panel scripts,
* respectively. Here we listen for these connection and create
* connection/port specific handlers.
*/
var portCSBackground;
var portPanelBackground;

function connected(port) {
// handle all communication to and from the panel
if (port.name === "port-panel-background"){
portPanelBackground = port;
// at the moment we don't do anything with messages coming
// from the panels
portPanelBackground.onMessage.addListener(function(msg) {
console.log("recieved message from panel", msg);
if (msg.action == "notifyCS") {
notifyCS(msg);
}
});
};
// handle all communication to and from the content script
if (port.name === "port-cs-background"){
portCSBackground = port;
portCSBackground.onMessage.addListener(function(msg) {
// Having received a message from the content script, i.e.
// from the target window we forward this message to the panels
// if the connection is not alive we log this in the devtools's
// debugger console
notifyDevtoolsPanel(msg.data);
});
}
// notify if the port has disconnected
port.onDisconnect.addListener(function(port) {
if (port.name === "port-panel-background" || port.name === "port-cs-background"){
console.log(`${port.name} has disconnected`);
};
});
}

chrome.runtime.onConnect.addListener(connected);

function notifyDevtoolsPanel(msg){
if (portPanelBackground){
portPanelBackground.postMessage(msg);
} else {
console.log(msg);
console.log("failed to send message to devtools panel: port disconnected");
}
}

function notifyCS(msg) {
if (portCSBackground) {
portCSBackground.postMessage(msg);
} else {
console.log("failed to send message to content script: port disconnected", msg);
}
}
49 changes: 49 additions & 0 deletions object_database/web/devtools/cells_panel.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
html {
height: 100% !important;
}

body {
height: 100% !important;
display: flex;
flex-direction: row;
margin: 0px;
}

div#main {
display: flex;
justify-content: center;
min-width: 70%;
border-right: solid 2px;
align-items: center;
}

div#cell-info {
display: flex;
justify-content: center;
align-items: center;
min-width: 30%;
text-align: center;
font-family: Roboto;
font-size: 20px;
}

.node {
cursor: pointer;
}
.node circle {
fill: #fff;
stroke: steelblue;
stroke-width: 1.5px;
}
.node text {
font: 10px sans-serif;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 1.5px;
}

body {
overflow: hidden;
}
16 changes: 16 additions & 0 deletions object_database/web/devtools/cells_panel.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Cells</title>
<!--<script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>-->
<script src="tree/dist/tree.bundle.js"></script>
<link rel="stylesheet" type="text/css" href="cells_panel.css">
<link rel="stylesheet" href="tree/tree.css" />
</head>
<body>
<div id="main"></div>
<div id="cell-info"> some cells data here</div>
</body>
<script type="module" src="js/cell_panel.js"></script>
</html>
Loading