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