Home | History | Annotate | Download | only in js
      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  */
     44 devtools$$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 
     61 devtools.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  */
     74 devtools.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  */
     86 devtools.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  */
     95 devtools.ToolsAgent.prototype.getDebuggerAgent = function()
     96 {
     97     return this.debuggerAgent_;
     98 };
     99 
    100 
    101 /**
    102  * @return {devtools.ProfilerAgent} Profiler agent instance.
    103  */
    104 devtools.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  */
    115 devtools.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  */
    134 devtools.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  */
    147 devtools.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  */
    157 WebInspector.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  */
    169 function 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  */
    186 devtools.tools = null;
    187 
    188 
    189 var 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.
    194 var oldLoaded = WebInspector.loaded;
    195 WebInspector.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  */
    256 WebInspector.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  */
    280 WebInspector.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  */
    293 WebInspector.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  */
    331 WebInspector.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 () {
    396 var orig = InjectedScriptAccess.prototype.getProperties;
    397 InjectedScriptAccess.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 {
    411 InjectedScriptAccess.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 
    421 WebInspector.resourceTrackingWasEnabled = function()
    422 {
    423       InspectorBackend._resourceTrackingEnabled = true;
    424       this.panels.resources.resourceTrackingWasEnabled();
    425 };
    426 
    427 WebInspector.resourceTrackingWasDisabled = function()
    428 {
    429       InspectorBackend._resourceTrackingEnabled = false;
    430       this.panels.resources.resourceTrackingWasDisabled();
    431 };
    432 
    433 (function()
    434 {
    435 var orig = WebInspector.ConsoleMessage.prototype.setMessageBody;
    436 WebInspector.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 {
    449 var orig = InjectedScriptAccess.prototype.getCompletions;
    450 InjectedScriptAccess.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 {
    462 WebInspector.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 
    474 WebInspector.runAfterPendingDispatchesQueue = [];
    475 
    476 WebInspector.TestController.prototype.runAfterPendingDispatches = function(callback)
    477 {
    478     WebInspector.runAfterPendingDispatchesQueue.push(callback);
    479 };
    480 
    481 WebInspector.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 {
    491 var originalAddToFrame = InspectorFrontendHost.addResourceSourceToFrame;
    492 InspectorFrontendHost.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 
    501 WebInspector.pausedScript = function(callFrames)
    502 {
    503     this.panels.scripts.debuggerPaused(callFrames);
    504 };
    505