/*
 * Copyright (C) 2007, 2008 Apple Inc.  All rights reserved.
 * Copyright (C) 2008, 2009 Anthony Ricaud <rik@webkit.org>
 * Copyright (C) 2011 Google Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1.  Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 * 2.  Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
 *     its contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * @constructor
 * @implements {WebInspector.ContextMenu.Provider}
 * @implements {WebInspector.Searchable}
 * @extends {WebInspector.Panel}
 */
WebInspector.NetworkPanel = function()
{
    WebInspector.Panel.call(this, "network");
    this.registerRequiredCSS("network/networkPanel.css");

    this._networkLogShowOverviewSetting = WebInspector.settings.createSetting("networkLogShowOverview", true);
    this._networkLogLargeRowsSetting = WebInspector.settings.createSetting("networkLogLargeRows", false);
    this._networkRecordFilmStripSetting = WebInspector.settings.createSetting("networkRecordFilmStripSetting", false);
    this._toggleRecordAction = /** @type {!WebInspector.Action }*/(WebInspector.actionRegistry.action("network.toggle-recording"));

    /** @type {?WebInspector.FilmStripView} */
    this._filmStripView = null;
    /** @type {?WebInspector.NetworkPanel.FilmStripRecorder} */
    this._filmStripRecorder = null;

    this._panelToolbar = new WebInspector.Toolbar("", this.element);
    this._filterBar = new WebInspector.FilterBar("networkPanel", true);
    this._filterBar.show(this.element);

    // Create top overview component.
    this._overviewPane = new WebInspector.TimelineOverviewPane("network");
    this._overviewPane.addEventListener(WebInspector.TimelineOverviewPane.Events.WindowChanged, this._onWindowChanged.bind(this));
    this._overviewPane.element.id = "network-overview-panel";
    this._networkOverview = new WebInspector.NetworkOverview();
    this._overviewPane.setOverviewControls([this._networkOverview]);
    this._calculator = new WebInspector.NetworkTransferTimeCalculator();

    this._splitWidget = new WebInspector.SplitWidget(true, false, "networkPanelSplitViewState");
    this._splitWidget.hideMain();

    this._splitWidget.show(this.element);

    this._progressBarContainer = createElement("div");
    this._createToolbarButtons();

    this._searchableView = new WebInspector.SearchableView(this);
    this._searchableView.setPlaceholder(WebInspector.UIString("Find by filename or path"));

    /** @type {!WebInspector.NetworkLogView} */
    this._networkLogView = new WebInspector.NetworkLogView(this._filterBar, this._progressBarContainer, this._networkLogLargeRowsSetting);
    this._networkLogView.show(this._searchableView.element);

    this._splitWidget.setSidebarWidget(this._searchableView);

    this._detailsWidget = new WebInspector.VBox();
    this._detailsWidget.element.classList.add("network-details-view");
    this._splitWidget.setMainWidget(this._detailsWidget);

    this._closeButtonElement = createElementWithClass("div", "network-close-button", "dt-close-button");
    this._closeButtonElement.addEventListener("click", this._showRequest.bind(this, null), false);

    this._networkLogShowOverviewSetting.addChangeListener(this._toggleShowOverview, this);
    this._networkLogLargeRowsSetting.addChangeListener(this._toggleLargerRequests, this);
    this._networkRecordFilmStripSetting.addChangeListener(this._toggleRecordFilmStrip, this);

    this._toggleRecord(true);
    this._toggleShowOverview();
    this._toggleLargerRequests();
    this._toggleRecordFilmStrip();
    this._updateUI();

    WebInspector.targetManager.addModelListener(WebInspector.ResourceTreeModel, WebInspector.ResourceTreeModel.EventTypes.WillReloadPage, this._willReloadPage, this);
    WebInspector.targetManager.addModelListener(WebInspector.ResourceTreeModel, WebInspector.ResourceTreeModel.EventTypes.Load, this._load, this);
    this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.RequestSelected, this._onRequestSelected, this);
    this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.SearchCountUpdated, this._onSearchCountUpdated, this);
    this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.SearchIndexUpdated, this._onSearchIndexUpdated, this);
    this._networkLogView.addEventListener(WebInspector.NetworkLogView.EventTypes.UpdateRequest, this._onUpdateRequest, this);

    WebInspector.DataSaverInfobar.maybeShowInPanel(this);
}

WebInspector.NetworkPanel.prototype = {
    /**
     * @param {!WebInspector.Event} event
     */
    _onWindowChanged: function(event)
    {
        var startTime = Math.max(this._calculator.minimumBoundary(), event.data.startTime / 1000);
        var endTime = Math.min(this._calculator.maximumBoundary(), event.data.endTime / 1000);
        this._networkLogView.setWindow(startTime, endTime);
    },

    _createToolbarButtons: function()
    {
        this._panelToolbar.appendToolbarItem(WebInspector.Toolbar.createActionButton(this._toggleRecordAction));

        this._clearButton = new WebInspector.ToolbarButton(WebInspector.UIString("Clear"), "clear-toolbar-item");
        this._clearButton.addEventListener("click", this._onClearButtonClicked, this);
        this._panelToolbar.appendToolbarItem(this._clearButton);
        this._panelToolbar.appendSeparator();
        var recordFilmStripButton = new WebInspector.ToolbarSettingToggle(this._networkRecordFilmStripSetting, "camera-toolbar-item", WebInspector.UIString("Capture screenshots"));
        this._panelToolbar.appendToolbarItem(recordFilmStripButton);

        this._panelToolbar.appendToolbarItem(this._filterBar.filterButton());
        this._panelToolbar.appendSeparator();

        this._panelToolbar.appendText(WebInspector.UIString("View:"));

        var largerRequestsButton = new WebInspector.ToolbarSettingToggle(this._networkLogLargeRowsSetting, "large-list-toolbar-item", WebInspector.UIString("Use large request rows"), WebInspector.UIString("Use small request rows"));
        this._panelToolbar.appendToolbarItem(largerRequestsButton);

        var showOverviewButton = new WebInspector.ToolbarSettingToggle(this._networkLogShowOverviewSetting, "waterfall-toolbar-item", WebInspector.UIString("Show overview"), WebInspector.UIString("Hide overview"));
        this._panelToolbar.appendToolbarItem(showOverviewButton);
        this._panelToolbar.appendSeparator();

        this._preserveLogCheckbox = new WebInspector.ToolbarCheckbox(WebInspector.UIString("Preserve log"), WebInspector.UIString("Do not clear log on page reload / navigation"));
        this._preserveLogCheckbox.inputElement.addEventListener("change", this._onPreserveLogCheckboxChanged.bind(this), false);
        this._panelToolbar.appendToolbarItem(this._preserveLogCheckbox);

        this._disableCacheCheckbox = new WebInspector.ToolbarCheckbox(WebInspector.UIString("Disable cache"), WebInspector.UIString("Disable cache (while DevTools is open)"), WebInspector.moduleSetting("cacheDisabled"));
        this._panelToolbar.appendToolbarItem(this._disableCacheCheckbox);

        this._panelToolbar.appendSeparator();
        this._panelToolbar.appendToolbarItem(this._createBlockedURLsButton());
        this._panelToolbar.appendToolbarItem(WebInspector.NetworkConditionsSelector.createOfflineToolbarCheckbox());
        this._panelToolbar.appendToolbarItem(this._createNetworkConditionsSelect());
        this._panelToolbar.appendToolbarItem(new WebInspector.ToolbarItem(this._progressBarContainer));
    },

    /**
     * @return {!WebInspector.ToolbarItem}
     */
    _createBlockedURLsButton: function()
    {
        var setting = WebInspector.moduleSetting("blockedURLs");
        setting.addChangeListener(updateAction);
        var action = /** @type {!WebInspector.Action }*/(WebInspector.actionRegistry.action("network.blocked-urls.show"));
        var button = WebInspector.Toolbar.createActionButton(action);
        button.setVisible(Runtime.experiments.isEnabled("requestBlocking"));
        updateAction();
        return button;

        function updateAction()
        {
            action.setToggled(!!setting.get().length);
        }
    },

    /**
     * @return {!WebInspector.ToolbarComboBox}
     */
    _createNetworkConditionsSelect: function()
    {
        var toolbarItem = new WebInspector.ToolbarComboBox(null);
        toolbarItem.setMaxWidth(140);
        WebInspector.NetworkConditionsSelector.decorateSelect(toolbarItem.selectElement());
        return toolbarItem;
    },

    _toggleRecording: function()
    {
        if (!this._preserveLogCheckbox.checked() && !this._toggleRecordAction.toggled())
            this._reset();
        this._toggleRecord(!this._toggleRecordAction.toggled());
    },

    /**
     * @param {boolean} toggled
     */
    _toggleRecord: function(toggled)
    {
        this._toggleRecordAction.setToggled(toggled);
        this._networkLogView.setRecording(toggled);
        if (!toggled && this._filmStripRecorder)
            this._filmStripRecorder.stopRecording(this._filmStripAvailable.bind(this));
    },

    /**
     * @param {?WebInspector.FilmStripModel} filmStripModel
     */
    _filmStripAvailable: function(filmStripModel)
    {
        if (!filmStripModel)
            return;
        var calculator = this._networkLogView.timeCalculator();
        this._filmStripView.setModel(filmStripModel, calculator.minimumBoundary() * 1000, calculator.boundarySpan() * 1000);
        this._networkOverview.setFilmStripModel(filmStripModel);
        var timestamps = filmStripModel.frames().map(mapTimestamp);

        /**
         * @param {!WebInspector.FilmStripModel.Frame} frame
         * @return {number}
         */
        function mapTimestamp(frame)
        {
            return frame.timestamp / 1000;
        }

        this._networkLogView.addFilmStripFrames(timestamps);
    },

    /**
     * @param {!Event} event
     */
    _onPreserveLogCheckboxChanged: function(event)
    {
        this._networkLogView.setPreserveLog(this._preserveLogCheckbox.checked());
    },

    /**
     * @param {!WebInspector.Event} event
     */
    _onClearButtonClicked: function(event)
    {
        this._reset();
    },

    _reset: function()
    {
        this._calculator.reset();
        this._overviewPane.reset();
        this._networkLogView.reset();
        WebInspector.BlockedURLsPane.reset();
        if (this._filmStripView)
            this._resetFilmStripView();
    },

    /**
     * @param {!WebInspector.Event} event
     */
    _willReloadPage: function(event)
    {
        if (!this._preserveLogCheckbox.checked())
            this._reset();
        this._toggleRecord(true);
        if (this._pendingStopTimer) {
            clearTimeout(this._pendingStopTimer);
            delete this._pendingStopTimer;
        }
        if (this.isShowing() && this._filmStripRecorder)
            this._filmStripRecorder.startRecording();
    },

    /**
     * @param {!WebInspector.Event} event
     */
    _load: function(event)
    {
        if (this._filmStripRecorder && this._filmStripRecorder.isRecording())
            this._pendingStopTimer = setTimeout(this._toggleRecord.bind(this, false), 1000);
    },

    _toggleLargerRequests: function()
    {
        this._updateUI();
    },

    _toggleShowOverview: function()
    {
        var toggled = this._networkLogShowOverviewSetting.get();
        if (toggled)
            this._overviewPane.show(this.element, this._splitWidget.element);
        else
            this._overviewPane.detach();
        this.doResize();
    },

    _toggleRecordFilmStrip: function()
    {
        var toggled = this._networkRecordFilmStripSetting.get();
        if (toggled && !this._filmStripRecorder) {
            this._filmStripView = new WebInspector.FilmStripView();
            this._filmStripView.setMode(WebInspector.FilmStripView.Modes.FrameBased);
            this._filmStripView.element.classList.add("network-film-strip");
            this._filmStripRecorder = new WebInspector.NetworkPanel.FilmStripRecorder(this._networkLogView.timeCalculator(), this._filmStripView);
            this._filmStripView.show(this.element, this.element.firstElementChild);
            this._filmStripView.addEventListener(WebInspector.FilmStripView.Events.FrameSelected, this._onFilmFrameSelected, this);
            this._filmStripView.addEventListener(WebInspector.FilmStripView.Events.FrameEnter, this._onFilmFrameEnter, this);
            this._filmStripView.addEventListener(WebInspector.FilmStripView.Events.FrameExit, this._onFilmFrameExit, this);
            this._resetFilmStripView();
        }

        if (!toggled && this._filmStripRecorder) {
            this._filmStripView.detach();
            this._filmStripView = null;
            this._filmStripRecorder = null;
        }
    },

    _resetFilmStripView: function()
    {
        this._filmStripView.reset();
        this._filmStripView.setStatusText(WebInspector.UIString("Hit %s to reload and capture filmstrip.", WebInspector.shortcutRegistry.shortcutDescriptorsForAction("main.reload")[0].name));
    },

    /**
     * @override
     * @return {!Array.<!Element>}
     */
    elementsToRestoreScrollPositionsFor: function()
    {
        return this._networkLogView.elementsToRestoreScrollPositionsFor();
    },

    /**
     * @override
     * @return {!WebInspector.SearchableView}
     */
    searchableView: function()
    {
        return this._searchableView;
    },

    /**
     * @override
     * @param {!KeyboardEvent} event
     */
    handleShortcut: function(event)
    {
        if (this._networkItemView && event.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code) {
            this._showRequest(null);
            event.handled = true;
            return;
        }

        WebInspector.Panel.prototype.handleShortcut.call(this, event);
    },

    wasShown: function()
    {
        WebInspector.context.setFlavor(WebInspector.NetworkPanel, this);
    },

    willHide: function()
    {
        WebInspector.context.setFlavor(WebInspector.NetworkPanel, null);
    },

    /**
     * @param {!WebInspector.NetworkRequest} request
     */
    revealAndHighlightRequest: function(request)
    {
        this._showRequest(null);
        if (request)
            this._networkLogView.revealAndHighlightRequest(request);
    },

    /**
     * @param {!WebInspector.Event} event
     */
    _onRowSizeChanged: function(event)
    {
        this._updateUI();
    },

    /**
     * @param {!WebInspector.Event} event
     */
    _onSearchCountUpdated: function(event)
    {
        var count = /** @type {number} */ (event.data);
        this._searchableView.updateSearchMatchesCount(count);
    },

    /**
     * @param {!WebInspector.Event} event
     */
    _onSearchIndexUpdated: function(event)
    {
        var index = /** @type {number} */ (event.data);
        this._searchableView.updateCurrentMatchIndex(index);
    },

    /**
     * @param {!WebInspector.Event} event
     */
    _onRequestSelected: function(event)
    {
        var request = /** @type {?WebInspector.NetworkRequest} */ (event.data);
        this._showRequest(request);
    },

    /**
     * @param {?WebInspector.NetworkRequest} request
     */
    _showRequest: function(request)
    {
        if (this._networkItemView) {
            this._networkItemView.detach();
            this._networkItemView = null;
        }

        if (request) {
            this._networkItemView = new WebInspector.NetworkItemView(request, this._networkLogView.timeCalculator());
            this._networkItemView.insertBeforeTabStrip(this._closeButtonElement);
            this._networkItemView.show(this._detailsWidget.element);
            this._splitWidget.showBoth();
        } else {
            this._splitWidget.hideMain();
            this._networkLogView.clearSelection();
        }
        this._updateUI();
    },

    _updateUI: function()
    {
        this._detailsWidget.element.classList.toggle("network-details-view-tall-header", this._networkLogLargeRowsSetting.get());
        this._networkLogView.switchViewMode(!this._splitWidget.isResizable());
    },

    /**
     * @override
     * @param {!WebInspector.SearchableView.SearchConfig} searchConfig
     * @param {boolean} shouldJump
     * @param {boolean=} jumpBackwards
     */
    performSearch: function(searchConfig, shouldJump, jumpBackwards)
    {
        this._networkLogView.performSearch(searchConfig, shouldJump, jumpBackwards);
    },

    /**
     * @override
     */
    jumpToPreviousSearchResult: function()
    {
        this._networkLogView.jumpToPreviousSearchResult();
    },

    /**
     * @override
     * @return {boolean}
     */
    supportsCaseSensitiveSearch: function()
    {
        return false;
    },

    /**
     * @override
     * @return {boolean}
     */
    supportsRegexSearch: function()
    {
        return false;
    },

    /**
     * @override
     */
    jumpToNextSearchResult: function()
    {
        this._networkLogView.jumpToNextSearchResult();
    },

    /**
     * @override
     */
    searchCanceled: function()
    {
        this._networkLogView.searchCanceled();
    },

    /**
     * @override
     * @param {!Event} event
     * @param {!WebInspector.ContextMenu} contextMenu
     * @param {!Object} target
     * @this {WebInspector.NetworkPanel}
     */
    appendApplicableItems: function(event, contextMenu, target)
    {
        /**
         * @this {WebInspector.NetworkPanel}
         */
        function reveal(request)
        {
            WebInspector.inspectorView.setCurrentPanel(this);
            this.revealAndHighlightRequest(request);
        }

        /**
         * @this {WebInspector.NetworkPanel}
         */
        function appendRevealItem(request)
        {
            contextMenu.appendItem(WebInspector.UIString.capitalize("Reveal in Network ^panel"), reveal.bind(this, request));
        }

        if (event.target.isSelfOrDescendant(this.element))
            return;

        if (target instanceof WebInspector.Resource) {
            var resource = /** @type {!WebInspector.Resource} */ (target);
            if (resource.request)
                appendRevealItem.call(this, resource.request);
            return;
        }
        if (target instanceof WebInspector.UISourceCode) {
            var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (target);
            var resource = WebInspector.resourceForURL(WebInspector.networkMapping.networkURL(uiSourceCode));
            if (resource && resource.request)
                appendRevealItem.call(this, resource.request);
            return;
        }

        if (!(target instanceof WebInspector.NetworkRequest))
            return;
        var request = /** @type {!WebInspector.NetworkRequest} */ (target);
        if (this._networkItemView && this._networkItemView.isShowing() && this._networkItemView.request() === request)
            return;

        appendRevealItem.call(this, request);
    },

    /**
     * @param {!WebInspector.Event} event
     */
    _onFilmFrameSelected: function(event)
    {
        var timestamp = /** @type {number} */ (event.data);
        this._overviewPane.requestWindowTimes(0, timestamp);
    },

    /**
     * @param {!WebInspector.Event} event
     */
    _onFilmFrameEnter: function(event)
    {
        var timestamp = /** @type {number} */ (event.data);
        this._networkOverview.selectFilmStripFrame(timestamp);
        this._networkLogView.selectFilmStripFrame(timestamp / 1000);
    },

    /**
     * @param {!WebInspector.Event} event
     */
    _onFilmFrameExit: function(event)
    {
        this._networkOverview.clearFilmStripFrame();
        this._networkLogView.clearFilmStripFrame();
    },

    /**
     * @param {!WebInspector.Event} event
     */
    _onUpdateRequest: function(event)
    {
        var request = /** @type {!WebInspector.NetworkRequest} */ (event.data);
        this._calculator.updateBoundaries(request);
        // FIXME: Unify all time units across the frontend!
        this._overviewPane.setBounds(this._calculator.minimumBoundary() * 1000, this._calculator.maximumBoundary() * 1000);
        this._networkOverview.updateRequest(request);
        this._overviewPane.scheduleUpdate();
    },

    __proto__: WebInspector.Panel.prototype
}

/**
 * @constructor
 * @implements {WebInspector.ContextMenu.Provider}
 */
WebInspector.NetworkPanel.ContextMenuProvider = function()
{
}

WebInspector.NetworkPanel.ContextMenuProvider.prototype = {
    /**
     * @override
     * @param {!Event} event
     * @param {!WebInspector.ContextMenu} contextMenu
     * @param {!Object} target
     */
    appendApplicableItems: function(event, contextMenu, target)
    {
        WebInspector.NetworkPanel._instance().appendApplicableItems(event, contextMenu, target);
    }
}

/**
 * @constructor
 * @implements {WebInspector.Revealer}
 */
WebInspector.NetworkPanel.RequestRevealer = function()
{
}

WebInspector.NetworkPanel.RequestRevealer.prototype = {
    /**
     * @override
     * @param {!Object} request
     * @return {!Promise}
     */
    reveal: function(request)
    {
        if (!(request instanceof WebInspector.NetworkRequest))
            return Promise.reject(new Error("Internal error: not a network request"));
        var panel = WebInspector.NetworkPanel._instance();
        WebInspector.inspectorView.setCurrentPanel(panel);
        panel.revealAndHighlightRequest(request);
        return Promise.resolve();
    }
}


WebInspector.NetworkPanel.show = function()
{
    WebInspector.inspectorView.setCurrentPanel(WebInspector.NetworkPanel._instance());
}

/**
 * @param {!Array<{filterType: !WebInspector.NetworkLogView.FilterType, filterValue: string}>} filters
 */
WebInspector.NetworkPanel.revealAndFilter = function(filters)
{
    var panel = WebInspector.NetworkPanel._instance();
    var filterString = "";
    for (var filter of filters)
        filterString += `${filter.filterType}:${filter.filterValue} `;
    panel._networkLogView.setTextFilterValue(filterString);
    WebInspector.inspectorView.setCurrentPanel(panel);
}

/**
 * @return {!WebInspector.NetworkPanel}
 */
WebInspector.NetworkPanel._instance = function()
{
    if (!WebInspector.NetworkPanel._instanceObject)
        WebInspector.NetworkPanel._instanceObject = new WebInspector.NetworkPanel();
    return WebInspector.NetworkPanel._instanceObject;
}

/**
 * @constructor
 * @implements {WebInspector.PanelFactory}
 */
WebInspector.NetworkPanelFactory = function()
{
}

WebInspector.NetworkPanelFactory.prototype = {
    /**
     * @override
     * @return {!WebInspector.Panel}
     */
    createPanel: function()
    {
        return WebInspector.NetworkPanel._instance();
    }
}

/**
 * @constructor
 * @implements {WebInspector.TracingManagerClient}
 * @param {!WebInspector.NetworkTimeCalculator} timeCalculator
 * @param {!WebInspector.FilmStripView} filmStripView
 */
WebInspector.NetworkPanel.FilmStripRecorder = function(timeCalculator, filmStripView)
{
    this._timeCalculator = timeCalculator;
    this._filmStripView = filmStripView;
}

WebInspector.NetworkPanel.FilmStripRecorder.prototype = {
    /**
     * @override
     */
    tracingStarted: function()
    {
    },

    /**
     * @override
     * @param {!Array.<!WebInspector.TracingManager.EventPayload>} events
     */
    traceEventsCollected: function(events)
    {
        if (this._tracingModel)
            this._tracingModel.addEvents(events);
    },

    /**
     * @override
     */
    tracingComplete: function()
    {
        if (!this._tracingModel)
            return;
        this._tracingModel.tracingComplete();
        var resourceTreeModel = this._target.resourceTreeModel;
        this._target = null;
        setImmediate(resourceTreeModel.resumeReload.bind(resourceTreeModel));
        this._callback(new WebInspector.FilmStripModel(this._tracingModel, this._timeCalculator.minimumBoundary() * 1000));
        delete this._callback;
    },

    /**
     * @override
     */
    tracingBufferUsage: function()
    {
    },

    /**
     * @override
     * @param {number} progress
     */
    eventsRetrievalProgress: function(progress)
    {
    },

    startRecording: function()
    {
        this._filmStripView.reset();
        this._filmStripView.setStatusText(WebInspector.UIString("Recording frames..."));
        if (this._target)
            return;

        this._target = WebInspector.targetManager.mainTarget();
        if (this._tracingModel)
            this._tracingModel.reset();
        else
            this._tracingModel = new WebInspector.TracingModel(new WebInspector.TempFileBackingStorage("tracing"));
        this._target.tracingManager.start(this, "-*,disabled-by-default-devtools.screenshot", "");
    },

    /**
     * @return {boolean}
     */
    isRecording: function()
    {
        return !!this._target;
    },

    /**
     * @param {function(?WebInspector.FilmStripModel)} callback
     */
    stopRecording: function(callback)
    {
        if (!this._target)
            return;

        this._target.tracingManager.stop();
        this._target.resourceTreeModel.suspendReload();
        this._callback = callback;
        this._filmStripView.setStatusText(WebInspector.UIString("Fetching frames..."));
    }
}

/**
 * @constructor
 * @implements {WebInspector.ActionDelegate}
 */
WebInspector.NetworkPanel.RecordActionDelegate = function()
{
}
WebInspector.NetworkPanel.RecordActionDelegate.prototype = {
    /**
     * @override
     * @param {!WebInspector.Context} context
     * @param {string} actionId
     * @return {boolean}
     */
    handleAction: function(context, actionId)
    {
        var panel = WebInspector.context.flavor(WebInspector.NetworkPanel);
        console.assert(panel && panel instanceof WebInspector.NetworkPanel);
        panel._toggleRecording();
        return true;
    }
}
