Home | History | Annotate | Download | only in sdk
      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.SDKModel}
     34  * @param {!WebInspector.Target} target
     35  */
     36 WebInspector.ResourceTreeModel = function(target)
     37 {
     38     WebInspector.SDKModel.call(this, WebInspector.ResourceTreeModel, target);
     39 
     40     target.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.RequestFinished, this._onRequestFinished, this);
     41     target.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.RequestUpdateDropped, this._onRequestUpdateDropped, this);
     42 
     43     target.consoleModel.addEventListener(WebInspector.ConsoleModel.Events.MessageAdded, this._consoleMessageAdded, this);
     44     target.consoleModel.addEventListener(WebInspector.ConsoleModel.Events.ConsoleCleared, this._consoleCleared, this);
     45 
     46     this._agent = target.pageAgent();
     47     this._agent.enable();
     48 
     49     this._fetchResourceTree();
     50 
     51     target.registerPageDispatcher(new WebInspector.PageDispatcher(this));
     52 
     53     this._pendingConsoleMessages = {};
     54     this._securityOriginFrameCount = {};
     55     this._inspectedPageURL = "";
     56 }
     57 
     58 WebInspector.ResourceTreeModel.EventTypes = {
     59     FrameAdded: "FrameAdded",
     60     FrameNavigated: "FrameNavigated",
     61     FrameDetached: "FrameDetached",
     62     FrameResized: "FrameResized",
     63     MainFrameNavigated: "MainFrameNavigated",
     64     ResourceAdded: "ResourceAdded",
     65     WillLoadCachedResources: "WillLoadCachedResources",
     66     CachedResourcesLoaded: "CachedResourcesLoaded",
     67     DOMContentLoaded: "DOMContentLoaded",
     68     Load: "Load",
     69     WillReloadPage: "WillReloadPage",
     70     InspectedURLChanged: "InspectedURLChanged",
     71     SecurityOriginAdded: "SecurityOriginAdded",
     72     SecurityOriginRemoved: "SecurityOriginRemoved",
     73     ScreencastFrame: "ScreencastFrame",
     74     ScreencastVisibilityChanged: "ScreencastVisibilityChanged",
     75     ViewportChanged: "ViewportChanged",
     76     ColorPicked: "ColorPicked"
     77 }
     78 
     79 WebInspector.ResourceTreeModel.prototype = {
     80     _fetchResourceTree: function()
     81     {
     82         /** @type {!Object.<string, !WebInspector.ResourceTreeFrame>} */
     83         this._frames = {};
     84         delete this._cachedResourcesProcessed;
     85         this._agent.getResourceTree(this._processCachedResources.bind(this));
     86     },
     87 
     88     _processCachedResources: function(error, mainFramePayload)
     89     {
     90         if (error) {
     91             //FIXME: remove resourceTreeModel from worker
     92             if (!this.target().isWorkerTarget())
     93                 console.error(JSON.stringify(error));
     94             return;
     95         }
     96 
     97         this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.WillLoadCachedResources);
     98         this._inspectedPageURL = mainFramePayload.frame.url;
     99         this._addFramesRecursively(null, mainFramePayload);
    100         this._dispatchInspectedURLChanged();
    101         this._cachedResourcesProcessed = true;
    102         this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.CachedResourcesLoaded);
    103     },
    104 
    105     /**
    106      * @return {string}
    107      */
    108     inspectedPageURL: function()
    109     {
    110         return this._inspectedPageURL;
    111     },
    112 
    113     /**
    114      * @return {string}
    115      */
    116     inspectedPageDomain: function()
    117     {
    118         var parsedURL = this._inspectedPageURL ? this._inspectedPageURL.asParsedURL() : null;
    119         return parsedURL ? parsedURL.host : "";
    120     },
    121 
    122     /**
    123      * @return {boolean}
    124      */
    125     cachedResourcesLoaded: function()
    126     {
    127         return this._cachedResourcesProcessed;
    128     },
    129 
    130     _dispatchInspectedURLChanged: function()
    131     {
    132         InspectorFrontendHost.inspectedURLChanged(this._inspectedPageURL);
    133         this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.InspectedURLChanged, this._inspectedPageURL);
    134     },
    135 
    136     /**
    137      * @param {!WebInspector.ResourceTreeFrame} frame
    138      * @param {boolean=} aboutToNavigate
    139      */
    140     _addFrame: function(frame, aboutToNavigate)
    141     {
    142         this._frames[frame.id] = frame;
    143         if (frame.isMainFrame())
    144             this.mainFrame = frame;
    145         this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.FrameAdded, frame);
    146         if (!aboutToNavigate)
    147             this._addSecurityOrigin(frame.securityOrigin);
    148     },
    149 
    150     /**
    151      * @param {string} securityOrigin
    152      */
    153     _addSecurityOrigin: function(securityOrigin)
    154     {
    155         if (!this._securityOriginFrameCount[securityOrigin]) {
    156             this._securityOriginFrameCount[securityOrigin] = 1;
    157             this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.SecurityOriginAdded, securityOrigin);
    158             return;
    159         }
    160         this._securityOriginFrameCount[securityOrigin] += 1;
    161     },
    162 
    163     /**
    164      * @param {string|undefined} securityOrigin
    165      */
    166     _removeSecurityOrigin: function(securityOrigin)
    167     {
    168         if (typeof securityOrigin === "undefined")
    169             return;
    170         if (this._securityOriginFrameCount[securityOrigin] === 1) {
    171             delete this._securityOriginFrameCount[securityOrigin];
    172             this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.SecurityOriginRemoved, securityOrigin);
    173             return;
    174         }
    175         this._securityOriginFrameCount[securityOrigin] -= 1;
    176     },
    177 
    178     /**
    179      * @return {!Array.<string>}
    180      */
    181     securityOrigins: function()
    182     {
    183         return Object.keys(this._securityOriginFrameCount);
    184     },
    185 
    186     /**
    187      * @param {!WebInspector.ResourceTreeFrame} mainFrame
    188      */
    189     _handleMainFrameDetached: function(mainFrame)
    190     {
    191         /**
    192          * @param {!WebInspector.ResourceTreeFrame} frame
    193          * @this {WebInspector.ResourceTreeModel}
    194          */
    195         function removeOriginForFrame(frame)
    196         {
    197             for (var i = 0; i < frame.childFrames.length; ++i)
    198                 removeOriginForFrame.call(this, frame.childFrames[i]);
    199             if (!frame.isMainFrame())
    200                 this._removeSecurityOrigin(frame.securityOrigin);
    201         }
    202         removeOriginForFrame.call(this, mainFrame);
    203     },
    204 
    205     /**
    206      * @param {!PageAgent.FrameId} frameId
    207      * @param {?PageAgent.FrameId} parentFrameId
    208      * @return {?WebInspector.ResourceTreeFrame}
    209      */
    210     _frameAttached: function(frameId, parentFrameId)
    211     {
    212         // Do nothing unless cached resource tree is processed - it will overwrite everything.
    213         if (!this._cachedResourcesProcessed)
    214             return null;
    215         if (this._frames[frameId])
    216             return null;
    217 
    218         var parentFrame = parentFrameId ? this._frames[parentFrameId] : null;
    219         var frame = new WebInspector.ResourceTreeFrame(this, parentFrame, frameId);
    220         if (frame.isMainFrame() && this.mainFrame) {
    221             this._handleMainFrameDetached(this.mainFrame);
    222             // Navigation to the new backend process.
    223             this._frameDetached(this.mainFrame.id);
    224         }
    225         this._addFrame(frame, true);
    226         return frame;
    227     },
    228 
    229     /**
    230      * @param {!PageAgent.Frame} framePayload
    231      */
    232     _frameNavigated: function(framePayload)
    233     {
    234         // Do nothing unless cached resource tree is processed - it will overwrite everything.
    235         if (!this._cachedResourcesProcessed)
    236             return;
    237         var frame = this._frames[framePayload.id];
    238         if (!frame) {
    239             // Simulate missed "frameAttached" for a main frame navigation to the new backend process.
    240             console.assert(!framePayload.parentId, "Main frame shouldn't have parent frame id.");
    241             frame = this._frameAttached(framePayload.id, framePayload.parentId || "");
    242             console.assert(frame);
    243         }
    244         this._removeSecurityOrigin(frame.securityOrigin);
    245         frame._navigate(framePayload);
    246         var addedOrigin = frame.securityOrigin;
    247 
    248         if (frame.isMainFrame())
    249             this._inspectedPageURL = frame.url;
    250 
    251         this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.FrameNavigated, frame);
    252         if (frame.isMainFrame())
    253             this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.MainFrameNavigated, frame);
    254         if (addedOrigin)
    255             this._addSecurityOrigin(addedOrigin);
    256 
    257         // Fill frame with retained resources (the ones loaded using new loader).
    258         var resources = frame.resources();
    259         for (var i = 0; i < resources.length; ++i)
    260             this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.ResourceAdded, resources[i]);
    261 
    262         if (frame.isMainFrame())
    263             this._dispatchInspectedURLChanged();
    264     },
    265 
    266     /**
    267      * @param {!PageAgent.FrameId} frameId
    268      */
    269     _frameDetached: function(frameId)
    270     {
    271         // Do nothing unless cached resource tree is processed - it will overwrite everything.
    272         if (!this._cachedResourcesProcessed)
    273             return;
    274 
    275         var frame = this._frames[frameId];
    276         if (!frame)
    277             return;
    278 
    279         this._removeSecurityOrigin(frame.securityOrigin);
    280         if (frame.parentFrame)
    281             frame.parentFrame._removeChildFrame(frame);
    282         else
    283             frame._remove();
    284     },
    285 
    286     /**
    287      * @param {!WebInspector.Event} event
    288      */
    289     _onRequestFinished: function(event)
    290     {
    291         if (!this._cachedResourcesProcessed)
    292             return;
    293 
    294         var request = /** @type {!WebInspector.NetworkRequest} */ (event.data);
    295         if (request.failed || request.type === WebInspector.resourceTypes.XHR)
    296             return;
    297 
    298         var frame = this._frames[request.frameId];
    299         if (frame) {
    300             var resource = frame._addRequest(request);
    301             this._addPendingConsoleMessagesToResource(resource);
    302         }
    303     },
    304 
    305     /**
    306      * @param {!WebInspector.Event} event
    307      */
    308     _onRequestUpdateDropped: function(event)
    309     {
    310         if (!this._cachedResourcesProcessed)
    311             return;
    312 
    313         var frameId = event.data.frameId;
    314         var frame = this._frames[frameId];
    315         if (!frame)
    316             return;
    317 
    318         var url = event.data.url;
    319         if (frame._resourcesMap[url])
    320             return;
    321 
    322         var resource = new WebInspector.Resource(this.target(), null, url, frame.url, frameId, event.data.loaderId, WebInspector.resourceTypes[event.data.resourceType], event.data.mimeType);
    323         frame.addResource(resource);
    324     },
    325 
    326     /**
    327      * @param {!PageAgent.FrameId} frameId
    328      * @return {!WebInspector.ResourceTreeFrame}
    329      */
    330     frameForId: function(frameId)
    331     {
    332         return this._frames[frameId];
    333     },
    334 
    335     /**
    336      * @param {function(!WebInspector.Resource)} callback
    337      * @return {boolean}
    338      */
    339     forAllResources: function(callback)
    340     {
    341         if (this.mainFrame)
    342             return this.mainFrame._callForFrameResources(callback);
    343         return false;
    344     },
    345 
    346     /**
    347      * @return {!Array.<!WebInspector.ResourceTreeFrame>}
    348      */
    349     frames: function()
    350     {
    351         return Object.values(this._frames);
    352     },
    353 
    354     /**
    355      * @param {!WebInspector.Event} event
    356      */
    357     _consoleMessageAdded: function(event)
    358     {
    359         var msg = /** @type {!WebInspector.ConsoleMessage} */ (event.data);
    360         var resource = msg.url ? this.resourceForURL(msg.url) : null;
    361         if (resource)
    362             this._addConsoleMessageToResource(msg, resource);
    363         else
    364             this._addPendingConsoleMessage(msg);
    365     },
    366 
    367     /**
    368      * @param {!WebInspector.ConsoleMessage} msg
    369      */
    370     _addPendingConsoleMessage: function(msg)
    371     {
    372         if (!msg.url)
    373             return;
    374         if (!this._pendingConsoleMessages[msg.url])
    375             this._pendingConsoleMessages[msg.url] = [];
    376         this._pendingConsoleMessages[msg.url].push(msg);
    377     },
    378 
    379     /**
    380      * @param {!WebInspector.Resource} resource
    381      */
    382     _addPendingConsoleMessagesToResource: function(resource)
    383     {
    384         var messages = this._pendingConsoleMessages[resource.url];
    385         if (messages) {
    386             for (var i = 0; i < messages.length; i++)
    387                 this._addConsoleMessageToResource(messages[i], resource);
    388             delete this._pendingConsoleMessages[resource.url];
    389         }
    390     },
    391 
    392     /**
    393      * @param {!WebInspector.ConsoleMessage} msg
    394      * @param {!WebInspector.Resource} resource
    395      */
    396     _addConsoleMessageToResource: function(msg, resource)
    397     {
    398         switch (msg.level) {
    399         case WebInspector.ConsoleMessage.MessageLevel.Warning:
    400             resource.warnings++;
    401             break;
    402         case WebInspector.ConsoleMessage.MessageLevel.Error:
    403             resource.errors++;
    404             break;
    405         }
    406         resource.addMessage(msg);
    407     },
    408 
    409     _consoleCleared: function()
    410     {
    411         function callback(resource)
    412         {
    413             resource.clearErrorsAndWarnings();
    414         }
    415 
    416         this._pendingConsoleMessages = {};
    417         this.forAllResources(callback);
    418     },
    419 
    420     /**
    421      * @param {string} url
    422      * @return {?WebInspector.Resource}
    423      */
    424     resourceForURL: function(url)
    425     {
    426         // Workers call into this with no frames available.
    427         return this.mainFrame ? this.mainFrame.resourceForURL(url) : null;
    428     },
    429 
    430     /**
    431      * @param {?WebInspector.ResourceTreeFrame} parentFrame
    432      * @param {!PageAgent.FrameResourceTree} frameTreePayload
    433      */
    434     _addFramesRecursively: function(parentFrame, frameTreePayload)
    435     {
    436         var framePayload = frameTreePayload.frame;
    437         var frame = new WebInspector.ResourceTreeFrame(this, parentFrame, framePayload.id, framePayload);
    438         this._addFrame(frame);
    439 
    440         var frameResource = this._createResourceFromFramePayload(framePayload, framePayload.url, WebInspector.resourceTypes.Document, framePayload.mimeType);
    441         if (frame.isMainFrame())
    442             this._inspectedPageURL = frameResource.url;
    443         frame.addResource(frameResource);
    444 
    445         for (var i = 0; frameTreePayload.childFrames && i < frameTreePayload.childFrames.length; ++i)
    446             this._addFramesRecursively(frame, frameTreePayload.childFrames[i]);
    447 
    448         for (var i = 0; i < frameTreePayload.resources.length; ++i) {
    449             var subresource = frameTreePayload.resources[i];
    450             var resource = this._createResourceFromFramePayload(framePayload, subresource.url, WebInspector.resourceTypes[subresource.type], subresource.mimeType);
    451             frame.addResource(resource);
    452         }
    453     },
    454 
    455     /**
    456      * @param {!PageAgent.Frame} frame
    457      * @param {string} url
    458      * @param {!WebInspector.ResourceType} type
    459      * @param {string} mimeType
    460      * @return {!WebInspector.Resource}
    461      */
    462     _createResourceFromFramePayload: function(frame, url, type, mimeType)
    463     {
    464         return new WebInspector.Resource(this.target(), null, url, frame.url, frame.id, frame.loaderId, type, mimeType);
    465     },
    466 
    467     /**
    468      * @param {boolean=} ignoreCache
    469      * @param {string=} scriptToEvaluateOnLoad
    470      * @param {string=} scriptPreprocessor
    471      */
    472     reloadPage: function(ignoreCache, scriptToEvaluateOnLoad, scriptPreprocessor)
    473     {
    474         this.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.WillReloadPage);
    475         this._agent.reload(ignoreCache, scriptToEvaluateOnLoad, scriptPreprocessor);
    476     },
    477 
    478     __proto__: WebInspector.SDKModel.prototype
    479 }
    480 
    481 /**
    482  * @constructor
    483  * @param {!WebInspector.ResourceTreeModel} model
    484  * @param {?WebInspector.ResourceTreeFrame} parentFrame
    485  * @param {!PageAgent.FrameId} frameId
    486  * @param {!PageAgent.Frame=} payload
    487  */
    488 WebInspector.ResourceTreeFrame = function(model, parentFrame, frameId, payload)
    489 {
    490     this._model = model;
    491     this._parentFrame = parentFrame;
    492     this._id = frameId;
    493     this._url = "";
    494 
    495     if (payload) {
    496         this._loaderId = payload.loaderId;
    497         this._name = payload.name;
    498         this._url = payload.url;
    499         this._securityOrigin = payload.securityOrigin;
    500         this._mimeType = payload.mimeType;
    501     }
    502 
    503     /**
    504      * @type {!Array.<!WebInspector.ResourceTreeFrame>}
    505      */
    506     this._childFrames = [];
    507 
    508     /**
    509      * @type {!Object.<string, !WebInspector.Resource>}
    510      */
    511     this._resourcesMap = {};
    512 
    513     if (this._parentFrame)
    514         this._parentFrame._childFrames.push(this);
    515 }
    516 
    517 WebInspector.ResourceTreeFrame.prototype = {
    518     /**
    519      * @return {!WebInspector.Target}
    520      */
    521     target: function()
    522     {
    523         return this._model.target();
    524     },
    525 
    526     /**
    527      * @return {string}
    528      */
    529     get id()
    530     {
    531         return this._id;
    532     },
    533 
    534     /**
    535      * @return {string}
    536      */
    537     get name()
    538     {
    539         return this._name || "";
    540     },
    541 
    542     /**
    543      * @return {string}
    544      */
    545     get url()
    546     {
    547         return this._url;
    548     },
    549 
    550     /**
    551      * @return {string}
    552      */
    553     get securityOrigin()
    554     {
    555         return this._securityOrigin;
    556     },
    557 
    558     /**
    559      * @return {string}
    560      */
    561     get loaderId()
    562     {
    563         return this._loaderId;
    564     },
    565 
    566     /**
    567      * @return {?WebInspector.ResourceTreeFrame}
    568      */
    569     get parentFrame()
    570     {
    571         return this._parentFrame;
    572     },
    573 
    574     /**
    575      * @return {!Array.<!WebInspector.ResourceTreeFrame>}
    576      */
    577     get childFrames()
    578     {
    579         return this._childFrames;
    580     },
    581 
    582     /**
    583      * @return {boolean}
    584      */
    585     isMainFrame: function()
    586     {
    587         return !this._parentFrame;
    588     },
    589 
    590     /**
    591      * @param {!PageAgent.Frame} framePayload
    592      */
    593     _navigate: function(framePayload)
    594     {
    595         this._loaderId = framePayload.loaderId;
    596         this._name = framePayload.name;
    597         this._url = framePayload.url;
    598         this._securityOrigin = framePayload.securityOrigin;
    599         this._mimeType = framePayload.mimeType;
    600 
    601         var mainResource = this._resourcesMap[this._url];
    602         this._resourcesMap = {};
    603         this._removeChildFrames();
    604         if (mainResource && mainResource.loaderId === this._loaderId)
    605             this.addResource(mainResource);
    606     },
    607 
    608     /**
    609      * @return {!WebInspector.Resource}
    610      */
    611     get mainResource()
    612     {
    613         return this._resourcesMap[this._url];
    614     },
    615 
    616     /**
    617      * @param {!WebInspector.ResourceTreeFrame} frame
    618      */
    619     _removeChildFrame: function(frame)
    620     {
    621         this._childFrames.remove(frame);
    622         frame._remove();
    623     },
    624 
    625     _removeChildFrames: function()
    626     {
    627         var frames = this._childFrames;
    628         this._childFrames = [];
    629         for (var i = 0; i < frames.length; ++i)
    630             frames[i]._remove();
    631     },
    632 
    633     _remove: function()
    634     {
    635         this._removeChildFrames();
    636         delete this._model._frames[this.id];
    637         this._model.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.FrameDetached, this);
    638     },
    639 
    640     /**
    641      * @param {!WebInspector.Resource} resource
    642      */
    643     addResource: function(resource)
    644     {
    645         if (this._resourcesMap[resource.url] === resource) {
    646             // Already in the tree, we just got an extra update.
    647             return;
    648         }
    649         this._resourcesMap[resource.url] = resource;
    650         this._model.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.ResourceAdded, resource);
    651     },
    652 
    653     /**
    654      * @param {!WebInspector.NetworkRequest} request
    655      * @return {!WebInspector.Resource}
    656      */
    657     _addRequest: function(request)
    658     {
    659         var resource = this._resourcesMap[request.url];
    660         if (resource && resource.request === request) {
    661             // Already in the tree, we just got an extra update.
    662             return resource;
    663         }
    664         resource = new WebInspector.Resource(this.target(), request, request.url, request.documentURL, request.frameId, request.loaderId, request.type, request.mimeType);
    665         this._resourcesMap[resource.url] = resource;
    666         this._model.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.ResourceAdded, resource);
    667         return resource;
    668     },
    669 
    670     /**
    671      * @return {!Array.<!WebInspector.Resource>}
    672      */
    673     resources: function()
    674     {
    675         var result = [];
    676         for (var url in this._resourcesMap)
    677             result.push(this._resourcesMap[url]);
    678         return result;
    679     },
    680 
    681     /**
    682      * @param {string} url
    683      * @return {?WebInspector.Resource}
    684      */
    685     resourceForURL: function(url)
    686     {
    687         var result;
    688         function filter(resource)
    689         {
    690             if (resource.url === url) {
    691                 result = resource;
    692                 return true;
    693             }
    694         }
    695         this._callForFrameResources(filter);
    696         return result || null;
    697     },
    698 
    699     /**
    700      * @param {function(!WebInspector.Resource)} callback
    701      * @return {boolean}
    702      */
    703     _callForFrameResources: function(callback)
    704     {
    705         for (var url in this._resourcesMap) {
    706             if (callback(this._resourcesMap[url]))
    707                 return true;
    708         }
    709 
    710         for (var i = 0; i < this._childFrames.length; ++i) {
    711             if (this._childFrames[i]._callForFrameResources(callback))
    712                 return true;
    713         }
    714         return false;
    715     },
    716 
    717     /**
    718      * @return {string}
    719      */
    720     displayName: function()
    721     {
    722         if (!this._parentFrame)
    723             return WebInspector.UIString("<top frame>");
    724         var subtitle = new WebInspector.ParsedURL(this._url).displayName;
    725         if (subtitle) {
    726             if (!this._name)
    727                 return subtitle;
    728             return this._name + "( " + subtitle + " )";
    729         }
    730         return WebInspector.UIString("<iframe>");
    731     }
    732 }
    733 
    734 /**
    735  * @constructor
    736  * @implements {PageAgent.Dispatcher}
    737  */
    738 WebInspector.PageDispatcher = function(resourceTreeModel)
    739 {
    740     this._resourceTreeModel = resourceTreeModel;
    741 }
    742 
    743 WebInspector.PageDispatcher.prototype = {
    744     /**
    745      * @override
    746      * @param {number} time
    747      */
    748     domContentEventFired: function(time)
    749     {
    750         this._resourceTreeModel.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.DOMContentLoaded, time);
    751     },
    752 
    753     /**
    754      * @override
    755      * @param {number} time
    756      */
    757     loadEventFired: function(time)
    758     {
    759         this._resourceTreeModel.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.Load, time);
    760     },
    761 
    762     frameAttached: function(frameId, parentFrameId)
    763     {
    764         this._resourceTreeModel._frameAttached(frameId, parentFrameId);
    765     },
    766 
    767     frameNavigated: function(frame)
    768     {
    769         this._resourceTreeModel._frameNavigated(frame);
    770     },
    771 
    772     frameDetached: function(frameId)
    773     {
    774         this._resourceTreeModel._frameDetached(frameId);
    775     },
    776 
    777     frameStartedLoading: function(frameId)
    778     {
    779     },
    780 
    781     frameStoppedLoading: function(frameId)
    782     {
    783     },
    784 
    785     frameScheduledNavigation: function(frameId, delay)
    786     {
    787     },
    788 
    789     frameClearedScheduledNavigation: function(frameId)
    790     {
    791     },
    792 
    793     frameResized: function()
    794     {
    795         this._resourceTreeModel.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.FrameResized, null);
    796     },
    797 
    798     javascriptDialogOpening: function(message)
    799     {
    800     },
    801 
    802     javascriptDialogClosed: function()
    803     {
    804     },
    805 
    806     scriptsEnabled: function(isEnabled)
    807     {
    808         WebInspector.settings.javaScriptDisabled.set(!isEnabled);
    809     },
    810 
    811     /**
    812      * @param {string} data
    813      * @param {!PageAgent.ScreencastFrameMetadata=} metadata
    814      */
    815     screencastFrame: function(data, metadata)
    816     {
    817         this._resourceTreeModel.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.ScreencastFrame, {data:data, metadata:metadata});
    818     },
    819 
    820     /**
    821      * @param {boolean} visible
    822      */
    823     screencastVisibilityChanged: function(visible)
    824     {
    825         this._resourceTreeModel.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.ScreencastVisibilityChanged, {visible:visible});
    826     },
    827 
    828     /**
    829      * @param {!PageAgent.Viewport=} viewport
    830      */
    831     viewportChanged: function(viewport)
    832     {
    833         this._resourceTreeModel.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.ViewportChanged, viewport);
    834     },
    835 
    836     /**
    837      * @param {!DOMAgent.RGBA} color
    838      */
    839     colorPicked: function(color)
    840     {
    841         this._resourceTreeModel.dispatchEventToListeners(WebInspector.ResourceTreeModel.EventTypes.ColorPicked, color);
    842     },
    843 
    844     interstitialShown: function()
    845     {
    846         // Frontend is not interested in interstitials.
    847     },
    848 
    849     interstitialHidden: function()
    850     {
    851         // Frontend is not interested in interstitials.
    852     }
    853 }
    854 
    855 /**
    856  * @type {!WebInspector.ResourceTreeModel}
    857  */
    858 WebInspector.resourceTreeModel;
    859