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