OLD | NEW |
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 /** | 5 /** |
6 * @fileoverview Tools is a main class that wires all components of the | 6 * @fileoverview Tools is a main class that wires all components of the |
7 * DevTools frontend together. It is also responsible for overriding existing | 7 * DevTools frontend together. It is also responsible for overriding existing |
8 * WebInspector functionality while it is getting upstreamed into WebCore. | 8 * WebInspector functionality while it is getting upstreamed into WebCore. |
9 */ | 9 */ |
10 goog.provide('devtools.Tools'); | 10 goog.provide('devtools.Tools'); |
11 | 11 |
12 goog.require('devtools.DebuggerAgent'); | 12 goog.require('devtools.DebuggerAgent'); |
13 goog.require('devtools.DomAgent'); | |
14 goog.require('devtools.NetAgent'); | |
15 | 13 |
16 | 14 |
17 /** | 15 /** |
18 * Dispatches raw message from the host. | 16 * Dispatches raw message from the host. |
19 * @param {Object} msg Message to dispatch. | 17 * @param {string} remoteName |
| 18 * @prama {string} methodName |
| 19 * @param {string} param1, param2, param3 Arguments to dispatch. |
20 */ | 20 */ |
21 devtools.dispatch = function(msg) { | 21 devtools$$dispatch = function(remoteName, methodName, param1, param2, param3) { |
22 var delegate = msg[0]; | 22 remoteName = 'Remote' + remoteName.substring(0, remoteName.length - 8); |
23 var methodName = msg[1]; | |
24 var remoteName = 'Remote' + delegate.substring(0, delegate.length - 8); | |
25 var agent = window[remoteName]; | 23 var agent = window[remoteName]; |
26 if (!agent) { | 24 if (!agent) { |
27 debugPrint('No remote agent "' + remoteName + '" found.'); | 25 debugPrint('No remote agent "' + remoteName + '" found.'); |
28 return; | 26 return; |
29 } | 27 } |
30 var method = agent[methodName]; | 28 var method = agent[methodName]; |
31 if (!method) { | 29 if (!method) { |
32 debugPrint('No method "' + remoteName + '.' + methodName + '" found.'); | 30 debugPrint('No method "' + remoteName + '.' + methodName + '" found.'); |
33 return; | 31 return; |
34 } | 32 } |
35 method.apply(this, msg.slice(2)); | 33 method.call(this, param1, param2, param3); |
36 }; | 34 }; |
37 | 35 |
38 | 36 |
39 devtools.ToolsAgent = function() { | 37 devtools.ToolsAgent = function() { |
40 RemoteToolsAgent.DidEvaluateJavaScript = devtools.Callback.processCallback; | |
41 RemoteToolsAgent.DidExecuteUtilityFunction = | 38 RemoteToolsAgent.DidExecuteUtilityFunction = |
42 devtools.Callback.processCallback; | 39 devtools.Callback.processCallback; |
43 RemoteToolsAgent.UpdateFocusedNode = | |
44 goog.bind(this.updateFocusedNode, this); | |
45 RemoteToolsAgent.FrameNavigate = | 40 RemoteToolsAgent.FrameNavigate = |
46 goog.bind(this.frameNavigate, this); | 41 goog.bind(this.frameNavigate_, this); |
47 RemoteToolsAgent.AddMessageToConsole = | 42 RemoteToolsAgent.DispatchOnClient = |
48 goog.bind(this.addMessageToConsole, this); | 43 goog.bind(this.dispatchOnClient_, this); |
49 this.debuggerAgent_ = new devtools.DebuggerAgent(); | 44 this.debuggerAgent_ = new devtools.DebuggerAgent(); |
50 this.domAgent_ = new devtools.DomAgent(); | |
51 this.netAgent_ = new devtools.NetAgent(); | |
52 }; | 45 }; |
53 | 46 |
54 | 47 |
55 /** | 48 /** |
56 * Resets tools agent to its initial state. | 49 * Resets tools agent to its initial state. |
57 */ | 50 */ |
58 devtools.ToolsAgent.prototype.reset = function() { | 51 devtools.ToolsAgent.prototype.reset = function() { |
59 this.domAgent_.reset(); | 52 DevToolsHost.reset(); |
60 this.netAgent_.reset(); | |
61 this.debuggerAgent_.reset(); | 53 this.debuggerAgent_.reset(); |
62 | |
63 this.domAgent_.getDocumentElementAsync(); | |
64 }; | 54 }; |
65 | 55 |
66 | 56 |
67 /** | 57 /** |
68 * @param {string} script Script exression to be evaluated in the context of the | 58 * @param {string} script Script exression to be evaluated in the context of the |
69 * inspected page. | 59 * inspected page. |
70 * @param {function(string):undefined} callback Function to call with the | 60 * @param {function(Object|string, boolean):undefined} opt_callback Function to |
71 * result. | 61 * call with the result. |
72 */ | 62 */ |
73 devtools.ToolsAgent.prototype.evaluateJavaScript = function(script, callback) { | 63 devtools.ToolsAgent.prototype.evaluateJavaScript = function(script, |
74 var callbackId = devtools.Callback.wrap(callback); | 64 opt_callback) { |
75 RemoteToolsAgent.EvaluateJavaScript(callbackId, script); | 65 InspectorController.evaluate(script, opt_callback || function() {}); |
76 }; | 66 }; |
77 | 67 |
78 | 68 |
79 /** | 69 /** |
80 * @return {devtools.DebuggerAgent} Debugger agent instance. | 70 * @return {devtools.DebuggerAgent} Debugger agent instance. |
81 */ | 71 */ |
82 devtools.ToolsAgent.prototype.getDebuggerAgent = function() { | 72 devtools.ToolsAgent.prototype.getDebuggerAgent = function() { |
83 return this.debuggerAgent_; | 73 return this.debuggerAgent_; |
84 }; | 74 }; |
85 | 75 |
86 /** | |
87 * DomAgent accessor. | |
88 * @return {devtools.DomAgent} Dom agent instance. | |
89 */ | |
90 devtools.ToolsAgent.prototype.getDomAgent = function() { | |
91 return this.domAgent_; | |
92 }; | |
93 | |
94 | |
95 /** | |
96 * NetAgent accessor. | |
97 * @return {devtools.NetAgent} Net agent instance. | |
98 */ | |
99 devtools.ToolsAgent.prototype.getNetAgent = function() { | |
100 return this.netAgent_; | |
101 }; | |
102 | |
103 | |
104 /** | |
105 * @see tools_agent.h | |
106 */ | |
107 devtools.ToolsAgent.prototype.updateFocusedNode = function(nodeId) { | |
108 var node = this.domAgent_.getNodeForId(nodeId); | |
109 WebInspector.updateFocusedNode(node); | |
110 }; | |
111 | |
112 | 76 |
113 /** | 77 /** |
114 * @param {string} url Url frame navigated to. | 78 * @param {string} url Url frame navigated to. |
115 * @param {bool} topLevel True iff top level navigation occurred. | |
116 * @see tools_agent.h | 79 * @see tools_agent.h |
| 80 * @private |
117 */ | 81 */ |
118 devtools.ToolsAgent.prototype.frameNavigate = function(url, topLevel) { | 82 devtools.ToolsAgent.prototype.frameNavigate_ = function(url) { |
119 if (topLevel) { | 83 this.reset(); |
120 this.reset(); | 84 // Do not reset Profiles panel. |
121 WebInspector.reset(); | 85 var profiles = null; |
| 86 if ('profiles' in WebInspector.panels) { |
| 87 profiles = WebInspector.panels['profiles']; |
| 88 delete WebInspector.panels['profiles']; |
| 89 } |
| 90 WebInspector.reset(); |
| 91 if (profiles != null) { |
| 92 WebInspector.panels['profiles'] = profiles; |
122 } | 93 } |
123 }; | 94 }; |
124 | 95 |
125 | 96 |
126 /** | 97 /** |
127 * @param {Object} message Message object to add. | 98 * @param {string} message Serialized call to be dispatched on WebInspector. |
128 * @see tools_agent.h | 99 * @private |
129 */ | 100 */ |
130 devtools.ToolsAgent.prototype.addMessageToConsole = function(message) { | 101 devtools.ToolsAgent.prototype.dispatchOnClient_ = function(message) { |
131 var console = WebInspector.console; | 102 WebInspector.dispatch.apply(WebInspector, JSON.parse(message)); |
132 if (console) { | |
133 console.addMessage(new WebInspector.ConsoleMessage( | |
134 message.source, message.level, message.line, message.sourceId, | |
135 undefined, 1, message.text)); | |
136 } | |
137 }; | 103 }; |
138 | 104 |
139 | 105 |
140 /** | 106 /** |
141 * Evaluates js expression. | 107 * Evaluates js expression. |
142 * @param {string} expr | 108 * @param {string} expr |
143 */ | 109 */ |
144 devtools.ToolsAgent.prototype.evaluate = function(expr) { | 110 devtools.ToolsAgent.prototype.evaluate = function(expr) { |
145 RemoteToolsAgent.evaluate(expr); | 111 RemoteToolsAgent.evaluate(expr); |
146 }; | 112 }; |
147 | 113 |
148 | 114 |
149 /** | 115 /** |
| 116 * Enables / disables resources panel in the ui. |
| 117 * @param {boolean} enabled New panel status. |
| 118 */ |
| 119 WebInspector.setResourcesPanelEnabled = function(enabled) { |
| 120 InspectorController.resourceTrackingEnabled_ = enabled; |
| 121 WebInspector.panels.resources.reset(); |
| 122 }; |
| 123 |
| 124 |
| 125 /** |
150 * Prints string to the inspector console or shows alert if the console doesn't | 126 * Prints string to the inspector console or shows alert if the console doesn't |
151 * exist. | 127 * exist. |
152 * @param {string} text | 128 * @param {string} text |
153 */ | 129 */ |
154 function debugPrint(text) { | 130 function debugPrint(text) { |
155 var console = WebInspector.console; | 131 var console = WebInspector.console; |
156 if (console) { | 132 if (console) { |
157 console.addMessage(new WebInspector.ConsoleMessage( | 133 console.addMessage(new WebInspector.ConsoleMessage( |
158 '', undefined, 1, '', undefined, 1, text)); | 134 WebInspector.ConsoleMessage.MessageSource.JS, |
| 135 WebInspector.ConsoleMessage.MessageType.Log, |
| 136 WebInspector.ConsoleMessage.MessageLevel.Log, |
| 137 1, 'chrome://devtools/<internal>', undefined, -1, text)); |
159 } else { | 138 } else { |
160 alert(text); | 139 alert(text); |
161 } | 140 } |
162 } | 141 } |
163 | 142 |
164 | 143 |
165 /** | 144 /** |
166 * Global instance of the tools agent. | 145 * Global instance of the tools agent. |
167 * @type {devtools.ToolsAgent} | 146 * @type {devtools.ToolsAgent} |
168 */ | 147 */ |
169 devtools.tools = null; | 148 devtools.tools = null; |
170 | 149 |
171 | 150 |
172 var context = {}; // Used by WebCore's inspector routines. | 151 var context = {}; // Used by WebCore's inspector routines. |
173 | 152 |
174 | |
175 /////////////////////////////////////////////////////////////////////////////// | 153 /////////////////////////////////////////////////////////////////////////////// |
176 // Here and below are overrides to existing WebInspector methods only. | 154 // Here and below are overrides to existing WebInspector methods only. |
177 // TODO(pfeldman): Patch WebCore and upstream changes. | 155 // TODO(pfeldman): Patch WebCore and upstream changes. |
178 var oldLoaded = WebInspector.loaded; | 156 var oldLoaded = WebInspector.loaded; |
179 WebInspector.loaded = function() { | 157 WebInspector.loaded = function() { |
180 devtools.tools = new devtools.ToolsAgent(); | 158 devtools.tools = new devtools.ToolsAgent(); |
181 devtools.tools.reset(); | 159 devtools.tools.reset(); |
182 | 160 |
183 Preferences.ignoreWhitespace = false; | 161 Preferences.ignoreWhitespace = false; |
184 oldLoaded.call(this); | 162 oldLoaded.call(this); |
185 | 163 |
| 164 // Hide dock button on Mac OS. |
| 165 // TODO(pfeldman): remove once Mac OS docking is implemented. |
| 166 if (InspectorController.platform().indexOf('mac') == 0) { |
| 167 document.getElementById('dock-status-bar-item').addStyleClass('hidden'); |
| 168 } |
| 169 |
| 170 // Mute refresh action. |
| 171 document.addEventListener("keydown", function(event) { |
| 172 if (event.keyIdentifier == 'F5') { |
| 173 event.preventDefault(); |
| 174 } else if (event.keyIdentifier == 'U+0052' /* 'R' */ && |
| 175 (event.ctrlKey || event.metaKey)) { |
| 176 event.preventDefault(); |
| 177 } |
| 178 }, true); |
| 179 |
186 DevToolsHost.loaded(); | 180 DevToolsHost.loaded(); |
187 }; | 181 }; |
188 | 182 |
189 | 183 |
190 var webkitUpdateChildren = | |
191 WebInspector.ElementsTreeElement.prototype.updateChildren; | |
192 | |
193 | |
194 /** | |
195 * @override | |
196 */ | |
197 WebInspector.ElementsTreeElement.prototype.updateChildren = function() { | |
198 var self = this; | |
199 devtools.tools.getDomAgent().getChildNodesAsync(this.representedObject, | |
200 function() { | |
201 webkitUpdateChildren.call(self); | |
202 }); | |
203 }; | |
204 | |
205 | |
206 /** | |
207 * @override | |
208 */ | |
209 WebInspector.ElementsPanel.prototype.performSearch = function(query) { | |
210 this.searchCanceled(); | |
211 devtools.tools.getDomAgent().performSearch(query, | |
212 goog.bind(this.performSearchCallback_, this)); | |
213 }; | |
214 | |
215 | |
216 WebInspector.ElementsPanel.prototype.performSearchCallback_ = function(nodes) { | |
217 for (var i = 0; i < nodes.length; ++i) { | |
218 var treeElement = this.treeOutline.findTreeElement(nodes[i]); | |
219 if (treeElement) | |
220 treeElement.highlighted = true; | |
221 } | |
222 | |
223 if (nodes.length) { | |
224 this.currentSearchResultIndex_ = 0; | |
225 this.focusedDOMNode = nodes[0]; | |
226 } | |
227 | |
228 this.searchResultCount_ = nodes.length; | |
229 }; | |
230 | |
231 | |
232 /** | |
233 * @override | |
234 */ | |
235 WebInspector.ElementsPanel.prototype.searchCanceled = function() { | |
236 this.currentSearchResultIndex_ = 0; | |
237 this.searchResultCount_ = 0; | |
238 devtools.tools.getDomAgent().searchCanceled( | |
239 goog.bind(this.searchCanceledCallback_, this)); | |
240 }; | |
241 | |
242 | |
243 WebInspector.ElementsPanel.prototype.searchCanceledCallback_ = function(nodes) { | |
244 for (var i = 0; i < nodes.length; i++) { | |
245 var treeElement = this.treeOutline.findTreeElement(nodes[i]); | |
246 if (treeElement) | |
247 treeElement.highlighted = false; | |
248 } | |
249 }; | |
250 | |
251 | |
252 /** | |
253 * @override | |
254 */ | |
255 WebInspector.ElementsPanel.prototype.jumpToNextSearchResult = function() { | |
256 if (!this.searchResultCount_) | |
257 return; | |
258 | |
259 if (++this.currentSearchResultIndex_ >= this.searchResultCount_) | |
260 this.currentSearchResultIndex_ = 0; | |
261 | |
262 this.focusedDOMNode = devtools.tools.getDomAgent(). | |
263 getSearchResultNode(this.currentSearchResultIndex_); | |
264 }; | |
265 | |
266 | |
267 /** | |
268 * @override | |
269 */ | |
270 WebInspector.ElementsPanel.prototype.jumpToPreviousSearchResult = function() { | |
271 if (!this.searchResultCount_) | |
272 return; | |
273 | |
274 if (--this.currentSearchResultIndex_ < 0) | |
275 this.currentSearchResultIndex_ = this.searchResultCount_ - 1; | |
276 | |
277 this.focusedDOMNode = devtools.tools.getDomAgent(). | |
278 getSearchResultNode(this.currentSearchResultIndex_); | |
279 }; | |
280 | |
281 | |
282 /** | |
283 * @override | |
284 */ | |
285 WebInspector.Console.prototype._evalInInspectedWindow = function(expr) { | |
286 return devtools.tools.evaluate(expr); | |
287 }; | |
288 | |
289 | |
290 /** | |
291 * Disable autocompletion in the console. | |
292 * TODO(yurys): change WebKit implementation to allow asynchronous completion. | |
293 * @override | |
294 */ | |
295 WebInspector.Console.prototype.completions = function( | |
296 wordRange, bestMatchOnly) { | |
297 return null; | |
298 }; | |
299 | |
300 | |
301 /** | |
302 * @override | |
303 */ | |
304 WebInspector.ElementsPanel.prototype.updateStyles = function(forceUpdate) { | |
305 var stylesSidebarPane = this.sidebarPanes.styles; | |
306 if (!stylesSidebarPane.expanded || !stylesSidebarPane.needsUpdate) { | |
307 return; | |
308 } | |
309 this.invokeWithStyleSet_(function(node) { | |
310 stylesSidebarPane.needsUpdate = !!node; | |
311 stylesSidebarPane.update(node, null, forceUpdate); | |
312 }); | |
313 }; | |
314 | |
315 | |
316 /** | |
317 * @override | |
318 */ | |
319 WebInspector.ElementsPanel.prototype.updateMetrics = function() { | |
320 var metricsSidebarPane = this.sidebarPanes.metrics; | |
321 if (!metricsSidebarPane.expanded || !metricsSidebarPane.needsUpdate) { | |
322 return; | |
323 } | |
324 this.invokeWithStyleSet_(function(node) { | |
325 metricsSidebarPane.needsUpdate = !!node; | |
326 metricsSidebarPane.update(node); | |
327 }); | |
328 }; | |
329 | |
330 | |
331 /** | |
332 * Temporarily sets style fetched from the inspectable tab to the currently | |
333 * focused node, invokes updateUI callback and clears the styles. | |
334 * @param {function(Node):undefined} updateUI Callback to call while styles are | |
335 * set. | |
336 */ | |
337 WebInspector.ElementsPanel.prototype.invokeWithStyleSet_ = | |
338 function(updateUI) { | |
339 var node = this.focusedDOMNode; | |
340 if (node && node.nodeType === Node.TEXT_NODE && node.parentNode) | |
341 node = node.parentNode; | |
342 | |
343 if (node && node.nodeType == Node.ELEMENT_NODE) { | |
344 var callback = function(stylesStr) { | |
345 var styles = JSON.parse(stylesStr); | |
346 if (!styles.computedStyle) { | |
347 return; | |
348 } | |
349 node.setStyles(styles.computedStyle, styles.inlineStyle, | |
350 styles.styleAttributes, styles.matchedCSSRules); | |
351 updateUI(node); | |
352 node.clearStyles(); | |
353 }; | |
354 devtools.tools.getDomAgent().getNodeStylesAsync( | |
355 node, | |
356 !Preferences.showUserAgentStyles, | |
357 callback); | |
358 } else { | |
359 updateUI(null); | |
360 } | |
361 }; | |
362 | |
363 | |
364 /** | |
365 * @override | |
366 */ | |
367 WebInspector.MetricsSidebarPane.prototype.editingCommitted = | |
368 function(element, userInput, previousContent, context) { | |
369 if (userInput === previousContent) { | |
370 // nothing changed, so cancel | |
371 return this.editingCancelled(element, context); | |
372 } | |
373 | |
374 if (context.box !== "position" && (!userInput || userInput === "\u2012")) { | |
375 userInput = "0px"; | |
376 } else if (context.box === "position" && | |
377 (!userInput || userInput === "\u2012")) { | |
378 userInput = "auto"; | |
379 } | |
380 | |
381 // Append a "px" unit if the user input was just a number. | |
382 if (/^\d+$/.test(userInput)) { | |
383 userInput += "px"; | |
384 } | |
385 devtools.tools.getDomAgent().setStylePropertyAsync( | |
386 this.node, | |
387 context.styleProperty, | |
388 userInput, | |
389 WebInspector.updateStylesAndMetrics_); | |
390 }; | |
391 | |
392 | |
393 /** | |
394 * @override | |
395 */ | |
396 WebInspector.PropertiesSidebarPane.prototype.update = function(object) { | |
397 var body = this.bodyElement; | |
398 body.removeChildren(); | |
399 | |
400 this.sections = []; | |
401 | |
402 if (!object) { | |
403 return; | |
404 } | |
405 | |
406 | |
407 var self = this; | |
408 devtools.tools.getDomAgent().getNodePrototypesAsync(object.id_, | |
409 function(json) { | |
410 // Get array of prototype user-friendly names. | |
411 var prototypes = JSON.parse(json); | |
412 for (var i = 0; i < prototypes.length; ++i) { | |
413 var prototype = {}; | |
414 prototype.id_ = object.id_; | |
415 prototype.protoDepth_ = i; | |
416 var section = new WebInspector.SidebarObjectPropertiesSection( | |
417 prototype, | |
418 prototypes[i]); | |
419 self.sections.push(section); | |
420 body.appendChild(section.element); | |
421 } | |
422 }); | |
423 }; | |
424 | |
425 | |
426 /** | |
427 * Our implementation of ObjectPropertiesSection for Elements tab. | |
428 * @constructor | |
429 */ | |
430 WebInspector.SidebarObjectPropertiesSection = function(object, title) { | |
431 WebInspector.ObjectPropertiesSection.call(this, object, title, | |
432 null /* subtitle */, null /* emptyPlaceholder */, | |
433 null /* ignoreHasOwnProperty */, null /* extraProperties */, | |
434 WebInspector.SidebarObjectPropertyTreeElement /* treeElementConstructor */ | |
435 ); | |
436 }; | |
437 goog.inherits(WebInspector.SidebarObjectPropertiesSection, | |
438 WebInspector.ObjectPropertiesSection); | |
439 | |
440 | |
441 /** | |
442 * @override | |
443 */ | |
444 WebInspector.SidebarObjectPropertiesSection.prototype.onpopulate = function() { | |
445 var nodeId = this.object.id_; | |
446 var protoDepth = this.object.protoDepth_; | |
447 var path = []; | |
448 devtools.tools.getDomAgent().getNodePropertiesAsync(nodeId, path, protoDepth, | |
449 goog.partial(WebInspector.didGetNodePropertiesAsync_, | |
450 this.propertiesTreeOutline, | |
451 this.treeElementConstructor, | |
452 nodeId, | |
453 path)); | |
454 }; | |
455 | |
456 | |
457 /** | |
458 * Our implementation of ObjectPropertyTreeElement for Elements tab. | |
459 * @constructor | |
460 */ | |
461 WebInspector.SidebarObjectPropertyTreeElement = function(parentObject, | |
462 propertyName) { | |
463 WebInspector.ObjectPropertyTreeElement.call(this, parentObject, | |
464 propertyName); | |
465 }; | |
466 goog.inherits(WebInspector.SidebarObjectPropertyTreeElement, | |
467 WebInspector.ObjectPropertyTreeElement); | |
468 | |
469 | |
470 /** | |
471 * @override | |
472 */ | |
473 WebInspector.SidebarObjectPropertyTreeElement.prototype.onpopulate = | |
474 function() { | |
475 var nodeId = this.parentObject.devtools$$nodeId_; | |
476 var path = this.parentObject.devtools$$path_.slice(0); | |
477 path.push(this.propertyName); | |
478 devtools.tools.getDomAgent().getNodePropertiesAsync(nodeId, path, -1, | |
479 goog.partial( | |
480 WebInspector.didGetNodePropertiesAsync_, | |
481 this, | |
482 this.treeOutline.section.treeElementConstructor, | |
483 nodeId, path)); | |
484 }; | |
485 | |
486 | |
487 /** | |
488 * This override is necessary for starting highlighting after the resource | |
489 * was added into the frame. | |
490 * @override | |
491 */ | |
492 WebInspector.SourceView.prototype.setupSourceFrameIfNeeded = function() { | |
493 if (!this._frameNeedsSetup) { | |
494 return; | |
495 } | |
496 | |
497 this.attach(); | |
498 | |
499 var self = this; | |
500 var identifier = this.resource.identifier; | |
501 var element = this.sourceFrame.element; | |
502 var netAgent = devtools.tools.getNetAgent(); | |
503 | |
504 netAgent.getResourceContentAsync(identifier, function(source) { | |
505 var resource = WebInspector.resources[identifier]; | |
506 if (InspectorController.addSourceToFrame(resource.mimeType, source, | |
507 element)) { | |
508 delete self._frameNeedsSetup; | |
509 if (resource.type === WebInspector.Resource.Type.Script) { | |
510 self.sourceFrame.addEventListener('syntax highlighting complete', | |
511 self._syntaxHighlightingComplete, self); | |
512 self.sourceFrame.syntaxHighlightJavascript(); | |
513 } else { | |
514 self._sourceFrameSetupFinished(); | |
515 } | |
516 } | |
517 }); | |
518 return true; | |
519 }; | |
520 | |
521 | |
522 /** | 184 /** |
523 * This override is necessary for adding script source asynchronously. | 185 * This override is necessary for adding script source asynchronously. |
524 * @override | 186 * @override |
525 */ | 187 */ |
526 WebInspector.ScriptView.prototype.setupSourceFrameIfNeeded = function() { | 188 WebInspector.ScriptView.prototype.setupSourceFrameIfNeeded = function() { |
527 if (!this._frameNeedsSetup) { | 189 if (!this._frameNeedsSetup) { |
528 return; | 190 return; |
529 } | 191 } |
530 | 192 |
531 this.attach(); | 193 this.attach(); |
532 | 194 |
533 if (this.script.source) { | 195 if (this.script.source) { |
534 this.didResolveScriptSource_(); | 196 this.didResolveScriptSource_(); |
535 } else { | 197 } else { |
536 var self = this; | 198 var self = this; |
537 devtools.tools.getDebuggerAgent().resolveScriptSource( | 199 devtools.tools.getDebuggerAgent().resolveScriptSource( |
538 this.script.sourceID, | 200 this.script.sourceID, |
539 function(source) { | 201 function(source) { |
540 self.script.source = source || '<source is not available>'; | 202 self.script.source = source || |
| 203 WebInspector.UIString('<source is not available>'); |
541 self.didResolveScriptSource_(); | 204 self.didResolveScriptSource_(); |
542 }); | 205 }); |
543 } | 206 } |
544 }; | 207 }; |
545 | 208 |
546 | 209 |
547 /** | 210 /** |
548 * Performs source frame setup when script source is aready resolved. | 211 * Performs source frame setup when script source is aready resolved. |
549 */ | 212 */ |
550 WebInspector.ScriptView.prototype.didResolveScriptSource_ = function() { | 213 WebInspector.ScriptView.prototype.didResolveScriptSource_ = function() { |
551 if (!InspectorController.addSourceToFrame( | 214 if (!InspectorController.addSourceToFrame( |
552 "text/javascript", this.script.source, this.sourceFrame.element)) { | 215 "text/javascript", this.script.source, this.sourceFrame.element)) { |
553 return; | 216 return; |
554 } | 217 } |
555 | 218 |
556 delete this._frameNeedsSetup; | 219 delete this._frameNeedsSetup; |
557 | 220 |
558 this.sourceFrame.addEventListener( | 221 this.sourceFrame.addEventListener( |
559 "syntax highlighting complete", this._syntaxHighlightingComplete, this); | 222 "syntax highlighting complete", this._syntaxHighlightingComplete, this); |
560 this.sourceFrame.syntaxHighlightJavascript(); | 223 this.sourceFrame.syntaxHighlightJavascript(); |
561 }; | 224 }; |
562 | 225 |
563 | 226 |
564 /** | 227 /** |
565 * Dummy object used during properties inspection. | 228 * @param {string} type Type of the the property value('object' or 'function'). |
566 * @see WebInspector.didGetNodePropertiesAsync_ | 229 * @param {string} className Class name of the property value. |
| 230 * @constructor |
567 */ | 231 */ |
568 WebInspector.dummyObject_ = { 'foo' : 'bar' }; | 232 WebInspector.UnresolvedPropertyValue = function(type, className) { |
569 | 233 this.type = type; |
570 | 234 this.className = className; |
571 /** | |
572 * Dummy function used during properties inspection. | |
573 * @see WebInspector.didGetNodePropertiesAsync_ | |
574 */ | |
575 WebInspector.dummyFunction_ = function() {}; | |
576 | |
577 | |
578 /** | |
579 * Callback function used with the getNodeProperties. | |
580 */ | |
581 WebInspector.didGetNodePropertiesAsync_ = function(treeOutline, constructor, | |
582 nodeId, path, json) { | |
583 var props = JSON.parse(json); | |
584 var properties = []; | |
585 var obj = {}; | |
586 obj.devtools$$nodeId_ = nodeId; | |
587 obj.devtools$$path_ = path; | |
588 for (var i = 0; i < props.length; i += 3) { | |
589 var type = props[i]; | |
590 var name = props[i + 1]; | |
591 var value = props[i + 2]; | |
592 properties.push(name); | |
593 if (type == 'object') { | |
594 // fake object is going to be replaced on expand. | |
595 obj[name] = WebInspector.dummyObject_; | |
596 } else if (type == 'function') { | |
597 // fake function is going to be replaced on expand. | |
598 obj[name] = WebInspector.dummyFunction_; | |
599 } else { | |
600 obj[name] = value; | |
601 } | |
602 } | |
603 properties.sort(); | |
604 | |
605 treeOutline.removeChildren(); | |
606 | |
607 for (var i = 0; i < properties.length; ++i) { | |
608 var propertyName = properties[i]; | |
609 treeOutline.appendChild(new constructor(obj, propertyName)); | |
610 } | |
611 }; | 235 }; |
612 | 236 |
613 | 237 |
614 /** | |
615 * Replace WebKit method with our own implementation to use our call stack | |
616 * representation. Original method uses Object.prototype.toString.call to | |
617 * learn if scope object is a JSActivation which doesn't work in Chrome. | |
618 */ | |
619 WebInspector.ScopeChainSidebarPane.prototype.update = function(callFrame) { | |
620 this.bodyElement.removeChildren(); | |
621 | |
622 this.sections = []; | |
623 this.callFrame = callFrame; | |
624 | |
625 if (!callFrame) { | |
626 var infoElement = document.createElement('div'); | |
627 infoElement.className = 'info'; | |
628 infoElement.textContent = WebInspector.UIString('Not Paused'); | |
629 this.bodyElement.appendChild(infoElement); | |
630 return; | |
631 } | |
632 | |
633 if (!callFrame._expandedProperties) { | |
634 callFrame._expandedProperties = {}; | |
635 } | |
636 | |
637 var scopeObject = callFrame.localScope; | |
638 var title = WebInspector.UIString('Local'); | |
639 var subtitle = Object.describe(scopeObject, true); | |
640 var emptyPlaceholder = null; | |
641 var extraProperties = null; | |
642 | |
643 var section = new WebInspector.ObjectPropertiesSection(scopeObject, title, | |
644 subtitle, emptyPlaceholder, true, extraProperties, | |
645 WebInspector.ScopeChainSidebarPane.TreeElement); | |
646 section.editInSelectedCallFrameWhenPaused = true; | |
647 section.pane = this; | |
648 | |
649 section.expanded = true; | |
650 | |
651 this.sections.push(section); | |
652 this.bodyElement.appendChild(section.element); | |
653 }; | |
654 | |
655 | |
656 /** | |
657 * Custom implementation of TreeElement that asynchronously resolves children | |
658 * using the debugger agent. | |
659 * @constructor | |
660 */ | |
661 WebInspector.ScopeChainSidebarPane.TreeElement = function(parentObject, | |
662 propertyName) { | |
663 WebInspector.ScopeVariableTreeElement.call(this, parentObject, propertyName); | |
664 } | |
665 WebInspector.ScopeChainSidebarPane.TreeElement.inherits( | |
666 WebInspector.ScopeVariableTreeElement); | |
667 | |
668 | |
669 /** | |
670 * @override | |
671 */ | |
672 WebInspector.ScopeChainSidebarPane.TreeElement.prototype.onpopulate = | |
673 function() { | |
674 var obj = this.parentObject[this.propertyName]; | |
675 devtools.tools.getDebuggerAgent().resolveChildren(obj, | |
676 goog.bind(this.didResolveChildren_, this)); | |
677 }; | |
678 | |
679 | |
680 /** | |
681 * Callback function used with the resolveChildren. | |
682 */ | |
683 WebInspector.ScopeChainSidebarPane.TreeElement.prototype.didResolveChildren_ = | |
684 function(object) { | |
685 this.removeChildren(); | |
686 var constructor = this.treeOutline.section.treeElementConstructor; | |
687 object = object.resolvedValue; | |
688 for (var name in object) { | |
689 this.appendChild(new constructor(object, name)); | |
690 } | |
691 }; | |
692 | |
693 | |
694 /** | |
695 * @override | |
696 */ | |
697 WebInspector.StylePropertyTreeElement.prototype.toggleEnabled = | |
698 function(event) { | |
699 var enabled = event.target.checked; | |
700 devtools.tools.getDomAgent().toggleNodeStyleAsync( | |
701 this.style, | |
702 enabled, | |
703 this.name, | |
704 WebInspector.updateStylesAndMetrics_); | |
705 }; | |
706 | |
707 | |
708 /** | |
709 * @override | |
710 */ | |
711 WebInspector.StylePropertyTreeElement.prototype.applyStyleText = function( | |
712 styleText, updateInterface) { | |
713 devtools.tools.getDomAgent().applyStyleTextAsync(this.style, this.name, | |
714 styleText, | |
715 function() { | |
716 if (updateInterface) { | |
717 WebInspector.updateStylesAndMetrics_(); | |
718 } | |
719 }); | |
720 }; | |
721 | |
722 | |
723 /** | |
724 * Forces update of styles and metrics sidebar panes. | |
725 */ | |
726 WebInspector.updateStylesAndMetrics_ = function() { | |
727 WebInspector.panels.elements.sidebarPanes.metrics.needsUpdate = true; | |
728 WebInspector.panels.elements.updateMetrics(); | |
729 WebInspector.panels.elements.sidebarPanes.styles.needsUpdate = true; | |
730 WebInspector.panels.elements.updateStyles(true); | |
731 }; | |
732 | |
733 | |
734 /** | 238 /** |
735 * This function overrides standard searchableViews getters to perform search | 239 * This function overrides standard searchableViews getters to perform search |
736 * only in the current view (other views are loaded asynchronously, no way to | 240 * only in the current view (other views are loaded asynchronously, no way to |
737 * search them yet). | 241 * search them yet). |
738 */ | 242 */ |
739 WebInspector.searchableViews_ = function() { | 243 WebInspector.searchableViews_ = function() { |
740 var views = []; | 244 var views = []; |
741 const visibleView = this.visibleView; | 245 const visibleView = this.visibleView; |
742 if (visibleView && visibleView.performSearch) { | 246 if (visibleView && visibleView.performSearch) { |
743 views.push(visibleView); | 247 views.push(visibleView); |
(...skipping 11 matching lines...) Expand all Loading... |
755 | 259 |
756 | 260 |
757 /** | 261 /** |
758 * @override | 262 * @override |
759 */ | 263 */ |
760 WebInspector.ScriptsPanel.prototype.__defineGetter__( | 264 WebInspector.ScriptsPanel.prototype.__defineGetter__( |
761 'searchableViews', | 265 'searchableViews', |
762 WebInspector.searchableViews_); | 266 WebInspector.searchableViews_); |
763 | 267 |
764 | 268 |
765 /** | |
766 * @override | |
767 */ | |
768 WebInspector.Console.prototype._evalInInspectedWindow = function(expression) { | |
769 if (WebInspector.panels.scripts.paused) | |
770 return WebInspector.panels.scripts.evaluateInSelectedCallFrame(expression); | |
771 | |
772 var console = this; | |
773 devtools.tools.evaluateJavaScript(expression, function(response) { | |
774 // TODO(yurys): send exception information along with the response | |
775 var exception = false; | |
776 console.addMessage(new WebInspector.ConsoleCommandResult( | |
777 response, exception, null /* commandMessage */)); | |
778 }); | |
779 // TODO(yurys): refactor WebInspector.Console so that the result is added into | |
780 // the command log message. | |
781 return 'evaluating...'; | |
782 }; | |
783 | |
784 | |
785 (function() { | 269 (function() { |
786 var oldShow = WebInspector.ScriptsPanel.prototype.show; | 270 var oldShow = WebInspector.ScriptsPanel.prototype.show; |
787 WebInspector.ScriptsPanel.prototype.show = function() { | 271 WebInspector.ScriptsPanel.prototype.show = function() { |
788 devtools.tools.getDebuggerAgent().initializeScriptsCache(); | 272 devtools.tools.getDebuggerAgent().initUI(); |
| 273 this.enableToggleButton.visible = false; |
789 oldShow.call(this); | 274 oldShow.call(this); |
790 }; | 275 }; |
791 })(); | 276 })(); |
792 | 277 |
793 | 278 |
794 /** | |
795 * We don't use WebKit's BottomUpProfileDataGridTree, instead using | |
796 * our own (because BottomUpProfileDataGridTree's functionality is | |
797 * implemented in profile_view.js for V8's Tick Processor). | |
798 * | |
799 * @param {WebInspector.ProfileView} profileView Profile view. | |
800 * @param {devtools.profiler.ProfileView} profile Profile. | |
801 */ | |
802 WebInspector.BottomUpProfileDataGridTree = function(profileView, profile) { | |
803 return WebInspector.buildProfileDataGridTree_( | |
804 profileView, profile.heavyProfile); | |
805 }; | |
806 | |
807 | |
808 /** | |
809 * We don't use WebKit's TopDownProfileDataGridTree, instead using | |
810 * our own (because TopDownProfileDataGridTree's functionality is | |
811 * implemented in profile_view.js for V8's Tick Processor). | |
812 * | |
813 * @param {WebInspector.ProfileView} profileView Profile view. | |
814 * @param {devtools.profiler.ProfileView} profile Profile. | |
815 */ | |
816 WebInspector.TopDownProfileDataGridTree = function(profileView, profile) { | |
817 return WebInspector.buildProfileDataGridTree_( | |
818 profileView, profile.treeProfile); | |
819 }; | |
820 | |
821 | |
822 /** | |
823 * A helper function, checks whether a profile node has visible children. | |
824 * | |
825 * @param {devtools.profiler.ProfileView.Node} profileNode Profile node. | |
826 * @return {boolean} Whether a profile node has visible children. | |
827 */ | |
828 WebInspector.nodeHasChildren_ = function(profileNode) { | |
829 var children = profileNode.children; | |
830 for (var i = 0, n = children.length; i < n; ++i) { | |
831 if (children[i].visible) { | |
832 return true; | |
833 } | |
834 } | |
835 return false; | |
836 }; | |
837 | |
838 | |
839 /** | |
840 * Common code for populating a profiler grid node or a tree with | |
841 * given profile nodes. | |
842 * | |
843 * @param {WebInspector.ProfileDataGridNode| | |
844 * WebInspector.ProfileDataGridTree} viewNode Grid node or a tree. | |
845 * @param {WebInspector.ProfileView} profileView Profile view. | |
846 * @param {Array<devtools.profiler.ProfileView.Node>} children Profile nodes. | |
847 * @param {WebInspector.ProfileDataGridTree} owningTree Grid tree. | |
848 */ | |
849 WebInspector.populateNode_ = function( | |
850 viewNode, profileView, children, owningTree) { | |
851 for (var i = 0, n = children.length; i < n; ++i) { | |
852 var child = children[i]; | |
853 if (child.visible) { | |
854 viewNode.appendChild( | |
855 new WebInspector.ProfileDataGridNode( | |
856 profileView, child, owningTree, | |
857 WebInspector.nodeHasChildren_(child))); | |
858 } | |
859 } | |
860 }; | |
861 | |
862 | |
863 /** | |
864 * A helper function for building a profile grid tree. | |
865 * | |
866 * @param {WebInspector.ProfileView} profileview Profile view. | |
867 * @param {devtools.profiler.ProfileView} profile Profile. | |
868 * @return {WebInspector.ProfileDataGridTree} Profile grid tree. | |
869 */ | |
870 WebInspector.buildProfileDataGridTree_ = function(profileView, profile) { | |
871 var children = profile.head.children; | |
872 var dataGridTree = new WebInspector.ProfileDataGridTree( | |
873 profileView, profile.head); | |
874 WebInspector.populateNode_(dataGridTree, profileView, children, dataGridTree); | |
875 return dataGridTree; | |
876 }; | |
877 | |
878 | |
879 /** | |
880 * @override | |
881 */ | |
882 WebInspector.ProfileDataGridNode.prototype._populate = function(event) { | |
883 var children = this.profileNode.children; | |
884 WebInspector.populateNode_(this, this.profileView, children, this.tree); | |
885 this.removeEventListener("populate", this._populate, this); | |
886 }; | |
887 | |
888 | |
889 // As columns in data grid can't be changed after initialization, | 279 // As columns in data grid can't be changed after initialization, |
890 // we need to intercept the constructor and modify columns upon creation. | 280 // we need to intercept the constructor and modify columns upon creation. |
891 (function InterceptDataGridForProfiler() { | 281 (function InterceptDataGridForProfiler() { |
892 var originalDataGrid = WebInspector.DataGrid; | 282 var originalDataGrid = WebInspector.DataGrid; |
893 WebInspector.DataGrid = function(columns) { | 283 WebInspector.DataGrid = function(columns) { |
894 if (('average' in columns) && ('calls' in columns)) { | 284 if (('average' in columns) && ('calls' in columns)) { |
895 delete columns['average']; | 285 delete columns['average']; |
896 delete columns['calls']; | 286 delete columns['calls']; |
897 } | 287 } |
898 return new originalDataGrid(columns); | 288 return new originalDataGrid(columns); |
899 }; | 289 }; |
900 })(); | 290 })(); |
901 | 291 |
902 | 292 |
903 /** | 293 // WebKit's profiler displays milliseconds with high resolution (shows |
904 * @override | 294 // three digits after the decimal point). We never have such resolution, |
905 * TODO(pfeldman): Add l10n. | 295 // as our minimal sampling rate is 1 ms. So we are disabling high resolution |
906 */ | 296 // to avoid visual clutter caused by meaningless ".000" parts. |
907 WebInspector.UIString = function(string) | 297 (function InterceptTimeDisplayInProfiler() { |
| 298 var originalDataGetter = |
| 299 WebInspector.ProfileDataGridNode.prototype.__lookupGetter__('data'); |
| 300 WebInspector.ProfileDataGridNode.prototype.__defineGetter__('data', |
| 301 function() { |
| 302 var oldNumberSecondsToString = Number.secondsToString; |
| 303 Number.secondsToString = function(seconds, formatterFunction) { |
| 304 return oldNumberSecondsToString(seconds, formatterFunction, false); |
| 305 }; |
| 306 var data = originalDataGetter.call(this); |
| 307 Number.secondsToString = oldNumberSecondsToString; |
| 308 return data; |
| 309 }); |
| 310 })(); |
| 311 |
| 312 |
| 313 (function InterceptProfilesPanelEvents() { |
| 314 var oldShow = WebInspector.ProfilesPanel.prototype.show; |
| 315 WebInspector.ProfilesPanel.prototype.show = function() { |
| 316 devtools.tools.getDebuggerAgent().initializeProfiling(); |
| 317 this.enableToggleButton.visible = false; |
| 318 oldShow.call(this); |
| 319 // Show is called on every show event of a panel, so |
| 320 // we only need to intercept it once. |
| 321 WebInspector.ProfilesPanel.prototype.show = oldShow; |
| 322 }; |
| 323 })(); |
| 324 |
| 325 |
| 326 /* |
| 327 * @override» |
| 328 * TODO(mnaganov): Restore l10n when it will be agreed that it is needed.» |
| 329 */» |
| 330 WebInspector.UIString = function(string) {» |
| 331 return String.vsprintf(string, Array.prototype.slice.call(arguments, 1));» |
| 332 }; |
| 333 |
| 334 |
| 335 // There is no clear way of setting frame title yet. So sniffing main resource |
| 336 // load. |
| 337 (function OverrideUpdateResource() { |
| 338 var originalUpdateResource = WebInspector.updateResource; |
| 339 WebInspector.updateResource = function(identifier, payload) { |
| 340 originalUpdateResource.call(this, identifier, payload); |
| 341 var resource = this.resources[identifier]; |
| 342 if (resource && resource.mainResource && resource.finished) { |
| 343 document.title = |
| 344 WebInspector.UIString('Developer Tools - %s', resource.url); |
| 345 } |
| 346 }; |
| 347 })(); |
| 348 |
| 349 |
| 350 // Highlight extension content scripts in the scripts list. |
| 351 (function () { |
| 352 var original = WebInspector.ScriptsPanel.prototype._addScriptToFilesMenu; |
| 353 WebInspector.ScriptsPanel.prototype._addScriptToFilesMenu = function(script) { |
| 354 var result = original.apply(this, arguments); |
| 355 var debuggerAgent = devtools.tools.getDebuggerAgent(); |
| 356 var type = debuggerAgent.getScriptContextType(script.sourceID); |
| 357 var option = script.filesSelectOption; |
| 358 if (type == 'injected' && option) { |
| 359 option.addStyleClass('injected'); |
| 360 } |
| 361 return result; |
| 362 }; |
| 363 })(); |
| 364 |
| 365 |
| 366 /** Pending WebKit upstream by apavlov). Fixes iframe vs drag problem. */ |
| 367 (function() { |
| 368 var originalDragStart = WebInspector.elementDragStart; |
| 369 WebInspector.elementDragStart = function(element) { |
| 370 var glassPane = document.createElement("div"); |
| 371 glassPane.style.cssText = |
| 372 'position:absolute;width:100%;height:100%;opacity:0;z-index:1'; |
| 373 glassPane.id = 'glass-pane-for-drag'; |
| 374 element.parentElement.appendChild(glassPane); |
| 375 |
| 376 originalDragStart.apply(this, arguments); |
| 377 }; |
| 378 |
| 379 var originalDragEnd = WebInspector.elementDragEnd; |
| 380 WebInspector.elementDragEnd = function() { |
| 381 originalDragEnd.apply(this, arguments); |
| 382 |
| 383 var glassPane = document.getElementById('glass-pane-for-drag'); |
| 384 glassPane.parentElement.removeChild(glassPane); |
| 385 }; |
| 386 })(); |
| 387 |
| 388 |
| 389 (function() { |
| 390 var originalCreatePanels = WebInspector._createPanels; |
| 391 WebInspector._createPanels = function() { |
| 392 originalCreatePanels.apply(this, arguments); |
| 393 this.panels.heap = new WebInspector.HeapProfilerPanel(); |
| 394 }; |
| 395 })(); |
| 396 |
| 397 |
| 398 WebInspector.resourceTrackingWasEnabled = function() |
908 { | 399 { |
909 return String.vsprintf(string, Array.prototype.slice.call(arguments, 1)); | 400 InspectorController.resourceTrackingEnabled_ = true; |
| 401 this.panels.resources.resourceTrackingWasEnabled(); |
910 } | 402 } |
| 403 |
| 404 WebInspector.resourceTrackingWasDisabled = function() |
| 405 { |
| 406 InspectorController.resourceTrackingEnabled_ = false; |
| 407 this.panels.resources.resourceTrackingWasDisabled(); |
| 408 } |
OLD | NEW |