Skip to content

Commit 113b474

Browse files
committed
Retrieve jQuery.data as grips from the backend
1 parent f1db8b7 commit 113b474

10 files changed

+457
-47
lines changed

data/grip-store.js

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
/* See license.txt for terms of usage */
2+
3+
define(function(require, exports, module) {
4+
5+
// FireQuery
6+
const { Dispatcher } = require("./dispatcher");
7+
8+
// Implementation
9+
function GripStore() {
10+
this.cache = new Map();
11+
12+
addEventListener("firequery/chrome/message",
13+
this.onContentMessage.bind(this));
14+
15+
this.requests = new Map();
16+
}
17+
18+
/**
19+
* Stores properties for grips. If specified grip properties are not
20+
* in the store yet they are asynchronously requested from the backend.
21+
*/
22+
GripStore.prototype =
23+
/** @lends GripStore */
24+
{
25+
// Grip Properties Provider
26+
27+
getPrototypeAndProperties: function(grip) {
28+
Trace.sysout("GripStore.getPrototypeAndProperties; for: " +
29+
grip.actor, grip);
30+
31+
if (!grip || !grip.actor) {
32+
Trace.sysout("GripStore.getPrototypeAndProperties; " +
33+
" ERROR no such actor!", grip);
34+
throw(new Error("ERROR no such actor!"));
35+
}
36+
37+
var entry = this.requests.get(grip.actor);
38+
if (entry) {
39+
return entry.promise;
40+
}
41+
42+
// If the response is already in the cache resolve immediately.
43+
var response = this.cache.get(grip.actor);
44+
if (response) {
45+
return response.ownProperties;
46+
}
47+
48+
// Allocate new entry in the cache and fetch properties from the
49+
// server side (asynchronously).
50+
grip = JSON.parse(JSON.stringify(grip));
51+
this.cache.set(grip.actor, grip);
52+
this.postChromeMessage("getPrototypeAndProperties", grip);
53+
54+
var promise = new Promise((resolve, reject) => {
55+
this.requests.set(grip.actor, {
56+
resolve: resolve,
57+
reject: reject
58+
});
59+
});
60+
61+
this.requests.get(grip.actor).promise = promise;
62+
63+
return promise;
64+
},
65+
66+
onPrototypeAndProperties: function(response) {
67+
response = JSON.parse(response);
68+
69+
// The cache entry should be already allocated.
70+
var grip = this.cache.get(response.from);
71+
if (!grip) {
72+
return;
73+
}
74+
75+
// Properly deal with getters.
76+
mergeProperties(response);
77+
78+
// Copy all response props into the cache entry.
79+
var props = Object.getOwnPropertyNames(response);
80+
for (var name of props) {
81+
grip[name] = response[name];
82+
}
83+
84+
Trace.sysout("GripStore.onPrototypeAndProperties; PACKET from: " +
85+
response.from, grip);
86+
87+
var entry = this.requests.get(response.from);
88+
this.requests.delete(response.from);
89+
90+
entry.promise.then(props => {
91+
Dispatcher.dispatch("update", {
92+
from: response.from,
93+
grip: grip,
94+
});
95+
})
96+
97+
entry.resolve(grip.ownProperties);
98+
},
99+
100+
// Communication Channel
101+
102+
onContentMessage: function(event) {
103+
var { data } = event;
104+
105+
switch (data.type) {
106+
case "prototypeAndProperties":
107+
this.onPrototypeAndProperties(data.args);
108+
break;
109+
}
110+
},
111+
112+
postChromeMessage: function(type, args) {
113+
var data = {
114+
type: type,
115+
args: args
116+
};
117+
118+
var event = new MessageEvent("firequery/content/message", {
119+
bubbles: true,
120+
cancelable: true,
121+
data: data,
122+
});
123+
124+
dispatchEvent(event);
125+
},
126+
};
127+
128+
function mergeProperties(response) {
129+
var { ownProperties, prototype } = response;
130+
131+
// 'safeGetterValues' is new and isn't necessary defined on old actors.
132+
var safeGetterValues = response.safeGetterValues || {};
133+
134+
// Merge the safe getter values into one object such that we can use it
135+
// in VariablesView.
136+
for (var name of Object.keys(safeGetterValues)) {
137+
if (name in ownProperties) {
138+
var { getterValue, getterPrototypeLevel } = safeGetterValues[name];
139+
ownProperties[name].getterValue = getterValue;
140+
ownProperties[name].getterPrototypeLevel = getterPrototypeLevel;
141+
} else {
142+
ownProperties[name] = safeGetterValues[name];
143+
}
144+
}
145+
}
146+
147+
// Exports from this module
148+
exports.GripStore = GripStore;
149+
});

data/markup-tooltip.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
<head>
44
<meta charset="utf-8">
55
<link href="chrome://firequery-firebug.sdk/skin/domTree.css" rel="stylesheet">
6+
<link href="chrome://firequery/skin/console.css" rel="stylesheet">
67
<link href="markup-tooltip.css" rel="stylesheet">
78
<script data-main="markup-config" src="lib/require.js"></script>
89
</head>

data/markup-tooltip.js

Lines changed: 86 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,91 @@ define(function(require, exports, module) {
44

55
// Dependencies
66
var React = require("react");
7-
const { Reps } = require("reps/repository");
7+
8+
// Firebug SDK
89
const { TreeView } = require("reps/tree-view");
910

11+
// FireQuery
12+
const { TooltipProvider } = require("./tooltip-provider");
13+
const { GripStore } = require("./grip-store");
14+
const { Dispatcher } = require("./dispatcher");
15+
const { TooltipContent } = require("./tooltip-content");
16+
17+
// Grip based reps
18+
// TODO: move into Firebug.SDK as soon as it's stable.
19+
require("./reps/regexp.js");
20+
require("./reps/stylesheet.js");
21+
require("./reps/event.js");
22+
require("./reps/date-time.js");
23+
require("./reps/css-rule.js");
24+
require("./reps/text-node.js");
25+
require("./reps/named-node-map.js");
26+
require("./reps/attribute.js");
27+
require("./reps/function.js");
28+
require("./reps/array.js");
29+
require("./reps/element.js");
30+
require("./reps/document.js");
31+
require("./reps/window.js");
32+
require("./reps/grip.js");
33+
34+
var theApp;
35+
1036
// xxxHonza: API in this file implements mostly the communication
1137
// between content and chrome scope. It duplicates API already
1238
// presented in markup-view-content.js
1339
// It would be great to have common module that can be included
1440
// in a content scope and installing the communication channel
1541
// automatically.
1642

43+
var rootGrip;
44+
45+
/**
46+
* Render tooltip content (expandable tree)
47+
*/
48+
function initialize(grip) {
49+
rootGrip = JSON.parse(grip);
50+
51+
Trace.sysout("MarkupTooltip; initialize " + rootGrip.actor, rootGrip);
52+
53+
var store = new GripStore();
54+
var content = TooltipContent({
55+
provider: new TooltipProvider(store),
56+
data: rootGrip,
57+
mode: "tiny"
58+
});
59+
60+
theApp = React.render(content, document.querySelector("#content"));
61+
}
62+
63+
/**
64+
* Update content
65+
*/
66+
Dispatcher.on("update", event => {
67+
Trace.sysout("MarkupTooltip; Update " + event.from, {
68+
event: event,
69+
theApp: theApp
70+
});
71+
72+
theApp.setState({
73+
forceUpdate: (event.grip.actor == rootGrip.actor),
74+
data: theApp.state.data
75+
});
76+
77+
updateSize();
78+
});
79+
80+
/**
81+
* Listen for messages from the Inspector panel (chrome scope).
82+
*/
83+
addEventListener("firequery/chrome/message", event => {
84+
var data = event.data;
85+
switch (data.type) {
86+
case "initialize":
87+
initialize(data.args);
88+
break;
89+
}
90+
}, true);
91+
1792
/**
1893
* Post message to the chrome through DOM event dispatcher.
1994
* (there is no message manager for the markupview.xhtml frame).
@@ -33,6 +108,14 @@ function postChromeMessage(type, args) {
33108
dispatchEvent(event);
34109
}
35110

111+
/**
112+
* Send a message to the chrome to update size of the tooltip.
113+
*/
114+
function updateSize() {
115+
var size = getRequiredSize();
116+
postChromeMessage("resize", size);
117+
}
118+
36119
/**
37120
* Returns ideal size of the tooltip required by the content.
38121
*/
@@ -44,37 +127,10 @@ function getRequiredSize() {
44127
}
45128
}
46129

47-
addEventListener("click", event => {
48-
var size = getRequiredSize();
49-
postChromeMessage("resize", size);
50-
});
51-
52-
/**
53-
* Listen for messages from the Inspector panel (chrome scope).
54-
*/
55-
addEventListener("firequery/chrome/message", event => {
56-
var data = event.data;
57-
switch (data.type) {
58-
case "render":
59-
renderContent(data.args);
60-
break;
61-
}
62-
}, true);
63-
64130
/**
65-
* Render tooltip content (expandable tree)
131+
* Update window size on click.
66132
*/
67-
function renderContent(value) {
68-
var tree = TreeView({
69-
data: JSON.parse(value),
70-
mode: "tiny"
71-
});
72-
73-
React.render(tree, document.querySelector("#content"));
74-
75-
var size = getRequiredSize();
76-
postChromeMessage("resize", size);
77-
}
133+
addEventListener("click", updateSize);
78134

79135
// Final initialize message posted to the chrome indicating that
80136
// all content modules has been successfully loaded.

data/tooltip-content.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/* See license.txt for terms of usage */
2+
3+
define(function(require, exports, module) {
4+
5+
"use strict";
6+
7+
// Dependencies
8+
const React = require("react");
9+
10+
// Firebug SDK
11+
const { Reps } = require("reps/repository");
12+
const { TreeView } = require("reps/tree-view");
13+
14+
// Shortcuts
15+
const { DIV } = Reps.DOM;
16+
17+
/**
18+
* @template This template represents content of the tooltip frame.
19+
*/
20+
var TooltipContent = React.createClass(
21+
/** @lends TooltipContent */
22+
{
23+
displayName: "TooltipContent",
24+
25+
getInitialState: function() {
26+
return {};
27+
},
28+
29+
render: function() {
30+
var grip = this.state.data || this.props.data;
31+
return (
32+
DIV({},
33+
TreeView({
34+
forceUpdate: this.state.forceUpdate,
35+
key: "content",
36+
data: grip,
37+
provider: this.props.provider,
38+
mode: this.props.mode
39+
})
40+
)
41+
);
42+
}
43+
});
44+
45+
// Exports from this module
46+
exports.TooltipContent = React.createFactory(TooltipContent);
47+
});

0 commit comments

Comments
 (0)