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 */
36WebInspector.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
55WebInspector.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
75WebInspector.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 */
466WebInspector.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
495WebInspector.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 */
692WebInspector.PageDispatcher = function(resourceTreeModel)
693{
694    this._resourceTreeModel = resourceTreeModel;
695}
696
697WebInspector.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 */
778WebInspector.resourceTreeModel;
779