Home | History | Annotate | Download | only in front_end
      1 /*
      2  * Copyright (C) 2011 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  * @constructor
     33  * @extends {WebInspector.Object}
     34  */
     35 WebInspector.WorkerManager = function()
     36 {
     37     this._workerIdToWindow = {};
     38     InspectorBackend.registerWorkerDispatcher(new WebInspector.WorkerDispatcher(this));
     39 }
     40 
     41 WebInspector.WorkerManager.isWorkerFrontend = function()
     42 {
     43     return !!WebInspector.queryParamsObject["dedicatedWorkerId"] ||
     44            !!WebInspector.queryParamsObject["isSharedWorker"];
     45 }
     46 
     47 WebInspector.WorkerManager.isDedicatedWorkerFrontend = function()
     48 {
     49     return !!WebInspector.queryParamsObject["dedicatedWorkerId"];
     50 }
     51 
     52 WebInspector.WorkerManager.loaded = function()
     53 {
     54     var workerId = WebInspector.queryParamsObject["dedicatedWorkerId"];
     55     if (workerId)
     56         WebInspector.WorkerManager._initializeDedicatedWorkerFrontend(workerId);
     57     else
     58         WebInspector.workerManager = new WebInspector.WorkerManager();
     59 }
     60 
     61 WebInspector.WorkerManager.loadCompleted = function()
     62 {
     63     // Make sure script execution of dedicated worker is resumed and then paused
     64     // on the first script statement in case we autoattached to it.
     65     if (WebInspector.queryParamsObject["workerPaused"]) {
     66         DebuggerAgent.pause();
     67         RuntimeAgent.run(calculateTitle);
     68     } else if (WebInspector.WorkerManager.isWorkerFrontend())
     69         calculateTitle();
     70 
     71     function calculateTitle()
     72     {
     73         WebInspector.WorkerManager._calculateWorkerInspectorTitle();
     74     }
     75 
     76     if (WebInspector.workerManager)
     77         WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.MainFrameNavigated, WebInspector.workerManager._mainFrameNavigated, WebInspector.workerManager);
     78 }
     79 
     80 WebInspector.WorkerManager._initializeDedicatedWorkerFrontend = function(workerId)
     81 {
     82     function receiveMessage(event)
     83     {
     84         var message = event.data;
     85         InspectorBackend.dispatch(message);
     86     }
     87     window.addEventListener("message", receiveMessage, true);
     88 
     89 
     90     InspectorBackend.sendMessageObjectToBackend = function(message)
     91     {
     92         window.opener.postMessage({workerId: workerId, command: "sendMessageToBackend", message: message}, "*");
     93     }
     94 }
     95 
     96 WebInspector.WorkerManager._calculateWorkerInspectorTitle = function()
     97 {
     98     var expression = "location.href";
     99     if (WebInspector.queryParamsObject["isSharedWorker"])
    100         expression += " + (this.name ? ' (' + this.name + ')' : '')";
    101     RuntimeAgent.evaluate.invoke({expression:expression, doNotPauseOnExceptionsAndMuteConsole:true, returnByValue: true}, evalCallback.bind(this));
    102 
    103     /**
    104      * @param {?Protocol.Error} error
    105      * @param {!RuntimeAgent.RemoteObject} result
    106      * @param {boolean=} wasThrown
    107      */
    108     function evalCallback(error, result, wasThrown)
    109     {
    110         if (error || wasThrown) {
    111             console.error(error);
    112             return;
    113         }
    114         InspectorFrontendHost.inspectedURLChanged(result.value);
    115     }
    116 }
    117 
    118 WebInspector.WorkerManager.Events = {
    119     WorkerAdded: "worker-added",
    120     WorkerRemoved: "worker-removed",
    121     WorkersCleared: "workers-cleared",
    122 }
    123 
    124 WebInspector.WorkerManager.prototype = {
    125     _workerCreated: function(workerId, url, inspectorConnected)
    126      {
    127         if (inspectorConnected)
    128             this._openInspectorWindow(workerId, true);
    129         this.dispatchEventToListeners(WebInspector.WorkerManager.Events.WorkerAdded, {workerId: workerId, url: url, inspectorConnected: inspectorConnected});
    130      },
    131 
    132     _workerTerminated: function(workerId)
    133      {
    134         this.closeWorkerInspector(workerId);
    135         this.dispatchEventToListeners(WebInspector.WorkerManager.Events.WorkerRemoved, workerId);
    136      },
    137 
    138     _sendMessageToWorkerInspector: function(workerId, message)
    139     {
    140         var workerInspectorWindow = this._workerIdToWindow[workerId];
    141         if (workerInspectorWindow)
    142             workerInspectorWindow.postMessage(message, "*");
    143     },
    144 
    145     openWorkerInspector: function(workerId)
    146     {
    147         var existingInspector = this._workerIdToWindow[workerId];
    148         if (existingInspector) {
    149             existingInspector.focus();
    150             return;
    151         }
    152 
    153         this._openInspectorWindow(workerId, false);
    154         WorkerAgent.connectToWorker(workerId);
    155     },
    156 
    157     _openInspectorWindow: function(workerId, workerIsPaused)
    158     {
    159         var search = window.location.search;
    160         var hash = window.location.hash;
    161         var url = window.location.href;
    162         // Make sure hash is in rear
    163         url = url.replace(hash, "");
    164         url += (search ? "&dedicatedWorkerId=" : "?dedicatedWorkerId=") + workerId;
    165         if (workerIsPaused)
    166             url += "&workerPaused=true";
    167         url = url.replace("docked=true&", "");
    168         url = url.replace("can_dock=true&", "");
    169         url += hash;
    170         var width = WebInspector.settings.workerInspectorWidth.get();
    171         var height = WebInspector.settings.workerInspectorHeight.get();
    172         // Set location=0 just to make sure the front-end will be opened in a separate window, not in new tab.
    173         var workerInspectorWindow = window.open(url, undefined, "location=0,width=" + width + ",height=" + height);
    174         workerInspectorWindow.addEventListener("resize", this._onWorkerInspectorResize.bind(this, workerInspectorWindow), false);
    175         this._workerIdToWindow[workerId] = workerInspectorWindow;
    176         workerInspectorWindow.addEventListener("beforeunload", this._workerInspectorClosing.bind(this, workerId), true);
    177 
    178         // Listen to beforeunload in detached state and to the InspectorClosing event in case of attached inspector.
    179         window.addEventListener("unload", this._pageInspectorClosing.bind(this), true);
    180     },
    181 
    182     closeWorkerInspector: function(workerId)
    183     {
    184         var workerInspectorWindow = this._workerIdToWindow[workerId];
    185         if (workerInspectorWindow)
    186             workerInspectorWindow.close();
    187     },
    188 
    189     _mainFrameNavigated: function(event)
    190     {
    191         for (var workerId in this._workerIdToWindow)
    192             this.closeWorkerInspector(workerId);
    193         this.dispatchEventToListeners(WebInspector.WorkerManager.Events.WorkersCleared);
    194     },
    195 
    196     _pageInspectorClosing: function()
    197     {
    198         this._ignoreWorkerInspectorClosing = true;
    199         for (var workerId in this._workerIdToWindow) {
    200             this._workerIdToWindow[workerId].close();
    201             WorkerAgent.disconnectFromWorker(parseInt(workerId, 10));
    202         }
    203     },
    204 
    205     _onWorkerInspectorResize: function(workerInspectorWindow)
    206     {
    207         var doc = workerInspectorWindow.document;
    208         WebInspector.settings.workerInspectorWidth.set(doc.width);
    209         WebInspector.settings.workerInspectorHeight.set(doc.height);
    210     },
    211 
    212     _workerInspectorClosing: function(workerId, event)
    213     {
    214         if (event.target.location.href === "about:blank")
    215             return;
    216         if (this._ignoreWorkerInspectorClosing)
    217             return;
    218         delete this._workerIdToWindow[workerId];
    219         WorkerAgent.disconnectFromWorker(workerId);
    220     },
    221 
    222     _disconnectedFromWorker: function()
    223     {
    224         var screen = new WebInspector.WorkerTerminatedScreen();
    225         WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.GlobalObjectCleared, screen.hide, screen);
    226         screen.showModal();
    227     },
    228 
    229     __proto__: WebInspector.Object.prototype
    230 }
    231 
    232 /**
    233  * @constructor
    234  * @implements {WorkerAgent.Dispatcher}
    235  */
    236 WebInspector.WorkerDispatcher = function(workerManager)
    237 {
    238     this._workerManager = workerManager;
    239     window.addEventListener("message", this._receiveMessage.bind(this), true);
    240 }
    241 
    242 WebInspector.WorkerDispatcher.prototype = {
    243     _receiveMessage: function(event)
    244     {
    245         var workerId = event.data["workerId"];
    246         workerId = parseInt(workerId, 10);
    247         var command = event.data.command;
    248         var message = event.data.message;
    249 
    250         if (command == "sendMessageToBackend")
    251             WorkerAgent.sendMessageToWorker(workerId, message);
    252     },
    253 
    254     workerCreated: function(workerId, url, inspectorConnected)
    255     {
    256         this._workerManager._workerCreated(workerId, url, inspectorConnected);
    257     },
    258 
    259     workerTerminated: function(workerId)
    260     {
    261         this._workerManager._workerTerminated(workerId);
    262     },
    263 
    264     dispatchMessageFromWorker: function(workerId, message)
    265     {
    266         this._workerManager._sendMessageToWorkerInspector(workerId, message);
    267     },
    268 
    269     disconnectedFromWorker: function()
    270     {
    271         this._workerManager._disconnectedFromWorker();
    272     }
    273 }
    274 
    275 /**
    276  * @constructor
    277  * @extends {WebInspector.HelpScreen}
    278  */
    279 WebInspector.WorkerTerminatedScreen = function()
    280 {
    281     WebInspector.HelpScreen.call(this, WebInspector.UIString("Inspected worker terminated"));
    282     var p = this.contentElement.createChild("p");
    283     p.classList.add("help-section");
    284     p.textContent = WebInspector.UIString("Inspected worker has terminated. Once it restarts we will attach to it automatically.");
    285 }
    286 
    287 WebInspector.WorkerTerminatedScreen.prototype = {
    288     /**
    289      * @override
    290      */
    291     willHide: function()
    292     {
    293         WebInspector.debuggerModel.removeEventListener(WebInspector.DebuggerModel.Events.GlobalObjectCleared, this.hide, this);
    294         WebInspector.HelpScreen.prototype.willHide.call(this);
    295     },
    296 
    297     __proto__: WebInspector.HelpScreen.prototype
    298 }
    299