• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (C) 2010 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 *     * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *     * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 *     * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31/**
32 * FIXME: change field naming style to use trailing underscore.
33 * @fileoverview Tools is a main class that wires all components of the
34 * DevTools frontend together. It is also responsible for overriding existing
35 * WebInspector functionality while it is getting upstreamed into WebCore.
36 */
37
38/**
39 * Dispatches raw message from the host.
40 * @param {string} remoteName
41 * @prama {string} methodName
42 * @param {string} param1, param2, param3 Arguments to dispatch.
43 */
44devtools$$dispatch = function(remoteName, methodName, param1, param2, param3)
45{
46    remoteName = "Remote" + remoteName.substring(0, remoteName.length - 8);
47    var agent = window[remoteName];
48    if (!agent) {
49        debugPrint("No remote agent '" + remoteName + "' found.");
50        return;
51    }
52    var method = agent[methodName];
53    if (!method) {
54        debugPrint("No method '" + remoteName + "." + methodName + "' found.");
55        return;
56    }
57    method.call(this, param1, param2, param3);
58};
59
60
61devtools.ToolsAgent = function()
62{
63    RemoteToolsAgent.didDispatchOn = WebInspector.Callback.processCallback;
64    RemoteToolsAgent.frameNavigate = this.frameNavigate_.bind(this);
65    RemoteToolsAgent.dispatchOnClient = this.dispatchOnClient_.bind(this);
66    this.debuggerAgent_ = new devtools.DebuggerAgent();
67    this.profilerAgent_ = new devtools.ProfilerAgent();
68};
69
70
71/**
72 * Resets tools agent to its initial state.
73 */
74devtools.ToolsAgent.prototype.reset = function()
75{
76    this.debuggerAgent_.reset();
77};
78
79
80/**
81 * @param {string} script Script exression to be evaluated in the context of the
82 *     inspected page.
83 * @param {function(Object|string, boolean):undefined} opt_callback Function to
84 *     call with the result.
85 */
86devtools.ToolsAgent.prototype.evaluateJavaScript = function(script, opt_callback)
87{
88    InspectorBackend.evaluate(script, opt_callback || function() {});
89};
90
91
92/**
93 * @return {devtools.DebuggerAgent} Debugger agent instance.
94 */
95devtools.ToolsAgent.prototype.getDebuggerAgent = function()
96{
97    return this.debuggerAgent_;
98};
99
100
101/**
102 * @return {devtools.ProfilerAgent} Profiler agent instance.
103 */
104devtools.ToolsAgent.prototype.getProfilerAgent = function()
105{
106    return this.profilerAgent_;
107};
108
109
110/**
111 * @param {string} url Url frame navigated to.
112 * @see tools_agent.h
113 * @private
114 */
115devtools.ToolsAgent.prototype.frameNavigate_ = function(url)
116{
117    this.reset();
118    // Do not reset Profiles panel.
119    var profiles = null;
120    if ("profiles" in WebInspector.panels) {
121        profiles = WebInspector.panels["profiles"];
122        delete WebInspector.panels["profiles"];
123    }
124    WebInspector.reset();
125    if (profiles !== null)
126        WebInspector.panels["profiles"] = profiles;
127};
128
129
130/**
131 * @param {string} message Serialized call to be dispatched on WebInspector.
132 * @private
133 */
134devtools.ToolsAgent.prototype.dispatchOnClient_ = function(message)
135{
136    var args = JSON.parse(message);
137    var methodName = args[0];
138    var parameters = args.slice(1);
139    WebInspector[methodName].apply(WebInspector, parameters);
140};
141
142
143/**
144 * Evaluates js expression.
145 * @param {string} expr
146 */
147devtools.ToolsAgent.prototype.evaluate = function(expr)
148{
149    RemoteToolsAgent.evaluate(expr);
150};
151
152
153/**
154 * Enables / disables resources panel in the ui.
155 * @param {boolean} enabled New panel status.
156 */
157WebInspector.setResourcesPanelEnabled = function(enabled)
158{
159    InspectorBackend._resourceTrackingEnabled = enabled;
160    WebInspector.panels.resources.reset();
161};
162
163
164/**
165 * Prints string  to the inspector console or shows alert if the console doesn't
166 * exist.
167 * @param {string} text
168 */
169function debugPrint(text) {
170    var console = WebInspector.console;
171    if (console) {
172        console.addMessage(new WebInspector.ConsoleMessage(
173            WebInspector.ConsoleMessage.MessageSource.JS,
174            WebInspector.ConsoleMessage.MessageType.Log,
175            WebInspector.ConsoleMessage.MessageLevel.Log,
176            1, "chrome://devtools/<internal>", undefined, -1, text));
177    } else
178        alert(text);
179}
180
181
182/**
183 * Global instance of the tools agent.
184 * @type {devtools.ToolsAgent}
185 */
186devtools.tools = null;
187
188
189var context = {};  // Used by WebCore's inspector routines.
190
191///////////////////////////////////////////////////////////////////////////////
192// Here and below are overrides to existing WebInspector methods only.
193// TODO(pfeldman): Patch WebCore and upstream changes.
194var oldLoaded = WebInspector.loaded;
195WebInspector.loaded = function()
196{
197    devtools.tools = new devtools.ToolsAgent();
198    devtools.tools.reset();
199
200    Preferences.ignoreWhitespace = false;
201    Preferences.samplingCPUProfiler = true;
202    Preferences.heapProfilerPresent = true;
203    oldLoaded.call(this);
204
205    InspectorFrontendHost.loaded();
206};
207
208
209(function()
210{
211
212    /**
213     * Handles an F3 keydown event to focus the Inspector search box.
214     * @param {KeyboardEvent} event Event to optionally handle
215     * @return {boolean} whether the event has been handled
216     */
217    function handleF3Keydown(event) {
218        if (event.keyIdentifier === "F3" && !event.altKey && !event.ctrlKey && !event.shiftKey && !event.metaKey) {
219            var searchField = document.getElementById("search");
220            searchField.focus();
221            searchField.select();
222            event.preventDefault();
223            return true;
224        }
225        return false;
226    }
227
228
229    var oldKeyDown = WebInspector.documentKeyDown;
230    /**
231     * This override allows to intercept keydown events we want to handle in a
232     * custom way. Some nested documents (iframes) delegate keydown handling to
233     * WebInspector.documentKeyDown (e.g. SourceFrame).
234     * @param {KeyboardEvent} event
235     * @override
236     */
237    WebInspector.documentKeyDown = function(event) {
238        var isHandled = handleF3Keydown(event);
239        if (!isHandled) {
240            // Mute refresh action.
241            if (event.keyIdentifier === "F5")
242                event.preventDefault();
243            else if (event.keyIdentifier === "U+0052" /* "R" */ && (event.ctrlKey || event.metaKey))
244                event.preventDefault();
245            else
246                oldKeyDown.call(this, event);
247        }
248    };
249})();
250
251
252/**
253 * This override is necessary for adding script source asynchronously.
254 * @override
255 */
256WebInspector.ScriptView.prototype.setupSourceFrameIfNeeded = function()
257{
258    if (!this._frameNeedsSetup)
259        return;
260
261    this.attach();
262
263    if (this.script.source)
264        this.didResolveScriptSource_();
265    else {
266        var self = this;
267        devtools.tools.getDebuggerAgent().resolveScriptSource(
268            this.script.sourceID,
269            function(source) {
270                self.script.source = source || WebInspector.UIString("<source is not available>");
271                self.didResolveScriptSource_();
272            });
273    }
274};
275
276
277/**
278 * Performs source frame setup when script source is aready resolved.
279 */
280WebInspector.ScriptView.prototype.didResolveScriptSource_ = function()
281{
282    this.sourceFrame.setContent("text/javascript", this.script.source);
283    this._sourceFrameSetup = true;
284    delete this._frameNeedsSetup;
285};
286
287
288/**
289 * @param {string} type Type of the the property value("object" or "function").
290 * @param {string} className Class name of the property value.
291 * @constructor
292 */
293WebInspector.UnresolvedPropertyValue = function(type, className)
294{
295    this.type = type;
296    this.className = className;
297};
298
299
300(function()
301{
302    var oldShow = WebInspector.ScriptsPanel.prototype.show;
303    WebInspector.ScriptsPanel.prototype.show =  function()
304    {
305        devtools.tools.getDebuggerAgent().initUI();
306        this.enableToggleButton.visible = false;
307        oldShow.call(this);
308    };
309})();
310
311
312(function InterceptProfilesPanelEvents()
313{
314    var oldShow = WebInspector.ProfilesPanel.prototype.show;
315    WebInspector.ProfilesPanel.prototype.show = function()
316    {
317        devtools.tools.getProfilerAgent().initializeProfiling();
318        this.enableToggleButton.visible = false;
319        oldShow.call(this);
320        // Show is called on every show event of a panel, so
321        // we only need to intercept it once.
322        WebInspector.ProfilesPanel.prototype.show = oldShow;
323    };
324})();
325
326
327/*
328 * @override
329 * TODO(mnaganov): Restore l10n when it will be agreed that it is needed.
330 */
331WebInspector.UIString = function(string)
332{
333    return String.vsprintf(string, Array.prototype.slice.call(arguments, 1));
334};
335
336
337// There is no clear way of setting frame title yet. So sniffing main resource
338// load.
339(function OverrideUpdateResource() {
340    var originalUpdateResource = WebInspector.updateResource;
341    WebInspector.updateResource = function(identifier, payload)
342    {
343        originalUpdateResource.call(this, identifier, payload);
344        var resource = this.resources[identifier];
345        if (resource && resource.mainResource && resource.finished)
346            document.title = WebInspector.UIString("Developer Tools - %s", resource.url);
347    };
348})();
349
350
351// Highlight extension content scripts in the scripts list.
352(function () {
353    var original = WebInspector.ScriptsPanel.prototype._addScriptToFilesMenu;
354    WebInspector.ScriptsPanel.prototype._addScriptToFilesMenu = function(script)
355    {
356        var result = original.apply(this, arguments);
357        var debuggerAgent = devtools.tools.getDebuggerAgent();
358        var type = debuggerAgent.getScriptContextType(script.sourceID);
359        var option = script.filesSelectOption;
360        if (type === "injected" && option)
361            option.addStyleClass("injected");
362        return result;
363    };
364})();
365
366
367/** Pending WebKit upstream by apavlov). Fixes iframe vs drag problem. */
368(function()
369{
370    var originalDragStart = WebInspector.elementDragStart;
371    WebInspector.elementDragStart = function(element)
372    {
373        if (element) {
374            var glassPane = document.createElement("div");
375            glassPane.style.cssText = "position:absolute;width:100%;height:100%;opacity:0;z-index:1";
376            glassPane.id = "glass-pane-for-drag";
377            element.parentElement.appendChild(glassPane);
378        }
379
380        originalDragStart.apply(this, arguments);
381    };
382
383    var originalDragEnd = WebInspector.elementDragEnd;
384    WebInspector.elementDragEnd = function()
385    {
386        originalDragEnd.apply(this, arguments);
387
388        var glassPane = document.getElementById("glass-pane-for-drag");
389        if (glassPane)
390            glassPane.parentElement.removeChild(glassPane);
391    };
392})();
393
394
395(function () {
396var orig = InjectedScriptAccess.prototype.getProperties;
397InjectedScriptAccess.prototype.getProperties = function(objectProxy, ignoreHasOwnProperty, abbreviate, callback)
398{
399    if (objectProxy.isScope)
400        devtools.tools.getDebuggerAgent().resolveScope(objectProxy.objectId, callback);
401    else if (objectProxy.isV8Ref)
402        devtools.tools.getDebuggerAgent().resolveChildren(objectProxy.objectId, callback, false);
403    else
404        orig.apply(this, arguments);
405};
406})();
407
408
409(function()
410{
411InjectedScriptAccess.prototype.evaluateInCallFrame = function(callFrameId, code, objectGroup, callback)
412{
413    //TODO(pfeldman): remove once 49084 is rolled.
414    if (!callback)
415        callback = objectGroup;
416    devtools.tools.getDebuggerAgent().evaluateInCallFrame(callFrameId, code, callback);
417};
418})();
419
420
421WebInspector.resourceTrackingWasEnabled = function()
422{
423      InspectorBackend._resourceTrackingEnabled = true;
424      this.panels.resources.resourceTrackingWasEnabled();
425};
426
427WebInspector.resourceTrackingWasDisabled = function()
428{
429      InspectorBackend._resourceTrackingEnabled = false;
430      this.panels.resources.resourceTrackingWasDisabled();
431};
432
433(function()
434{
435var orig = WebInspector.ConsoleMessage.prototype.setMessageBody;
436WebInspector.ConsoleMessage.prototype.setMessageBody = function(args)
437{
438    for (var i = 0; i < args.length; ++i) {
439        if (typeof args[i] === "string")
440            args[i] = WebInspector.ObjectProxy.wrapPrimitiveValue(args[i]);
441    }
442    orig.call(this, args);
443};
444})();
445
446
447(function()
448{
449var orig = InjectedScriptAccess.prototype.getCompletions;
450InjectedScriptAccess.prototype.getCompletions = function(expressionString, includeInspectorCommandLineAPI, callFrameId, reportCompletions)
451{
452    if (typeof callFrameId === "number")
453        devtools.tools.getDebuggerAgent().resolveCompletionsOnFrame(expressionString, callFrameId, reportCompletions);
454    else
455        return orig.apply(this, arguments);
456};
457})();
458
459
460(function()
461{
462WebInspector.ElementsPanel.prototype._nodeSearchButtonClicked = function( event)
463{
464    InspectorBackend.toggleNodeSearch();
465    this.nodeSearchButton.toggled = !this.nodeSearchButton.toggled;
466};
467})();
468
469
470// We need to have a place for postponed tasks
471// which should be executed when all the messages between agent and frontend
472// are processed.
473
474WebInspector.runAfterPendingDispatchesQueue = [];
475
476WebInspector.TestController.prototype.runAfterPendingDispatches = function(callback)
477{
478    WebInspector.runAfterPendingDispatchesQueue.push(callback);
479};
480
481WebInspector.queuesAreEmpty = function()
482{
483    var copy = this.runAfterPendingDispatchesQueue.slice();
484    this.runAfterPendingDispatchesQueue = [];
485    for (var i = 0; i < copy.length; ++i)
486        copy[i].call(this);
487};
488
489(function()
490{
491var originalAddToFrame = InspectorFrontendHost.addResourceSourceToFrame;
492InspectorFrontendHost.addResourceSourceToFrame = function(identifier, element)
493{
494    var resource = WebInspector.resources[identifier];
495    if (!resource)
496        return;
497    originalAddToFrame.call(this, identifier, resource.mimeType, element);
498};
499})();
500
501WebInspector.pausedScript = function(callFrames)
502{
503    this.panels.scripts.debuggerPaused(callFrames);
504};
505