| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405 | /* * Copyright (C) 2008 Apple 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. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR * 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. */const UserInitiatedProfileName = "org.webkit.profiles.user-initiated";/** * @constructor * @extends {WebInspector.Object} * @param {string} id * @param {string} name */WebInspector.ProfileType = function(id, name){    this._id = id;    this._name = name;    /** @type {!Array.<!WebInspector.ProfileHeader>} */    this._profiles = [];    this._profilesIdMap = {};    /** @type {WebInspector.SidebarSectionTreeElement} */    this.treeElement = null;}WebInspector.ProfileType.Events = {    AddProfileHeader: "add-profile-header",    RemoveProfileHeader: "remove-profile-header",    ProgressUpdated: "progress-updated",    ViewUpdated: "view-updated"}WebInspector.ProfileType.prototype = {    /**     * @return {boolean}     */    hasTemporaryView: function()    {        return false;    },    /**     * @return {string|null}     */    fileExtension: function()    {        return null;    },    get statusBarItems()    {        return [];    },    get buttonTooltip()    {        return "";    },    get id()    {        return this._id;    },    get treeItemTitle()    {        return this._name;    },    get name()    {        return this._name;    },    /**     * @return {boolean}     */    buttonClicked: function()    {        return false;    },    get description()    {        return "";    },    /**     * @return {boolean}     */    isInstantProfile: function()    {        return false;    },    /**     * @return {boolean}     */    isEnabled: function()    {        return true;    },    /**     * @return {!Array.<!WebInspector.ProfileHeader>}     */    getProfiles: function()    {        return this._profiles.filter(function(profile) { return !profile.isTemporary; });    },    /**     * @return {Element}     */    decorationElement: function()    {        return null;    },    /**     * @nosideeffects     * @param {number} uid     * @return {WebInspector.ProfileHeader}     */    getProfile: function(uid)    {        return this._profilesIdMap[this._makeKey(uid)];    },    // Must be implemented by subclasses.    /**     * @param {string=} title     * @return {!WebInspector.ProfileHeader}     */    createTemporaryProfile: function(title)    {        throw new Error("Needs implemented.");    },    /**     * @param {ProfilerAgent.ProfileHeader} profile     * @return {!WebInspector.ProfileHeader}     */    createProfile: function(profile)    {        throw new Error("Not supported for " + this._name + " profiles.");    },    /**     * @nosideeffects     * @param {number} id     * @return {string}     */    _makeKey: function(id)    {        return id + '/' + escape(this.id);    },    /**     * @param {!WebInspector.ProfileHeader} profile     */    addProfile: function(profile)    {        this._profiles.push(profile);        // FIXME: uid only based key should be enough.        this._profilesIdMap[this._makeKey(profile.uid)] = profile;        this.dispatchEventToListeners(WebInspector.ProfileType.Events.AddProfileHeader, profile);    },    /**     * @param {!WebInspector.ProfileHeader} profile     */    removeProfile: function(profile)    {        for (var i = 0; i < this._profiles.length; ++i) {            if (this._profiles[i].uid === profile.uid) {                this._profiles.splice(i, 1);                break;            }        }        delete this._profilesIdMap[this._makeKey(profile.uid)];    },    /**     * @nosideeffects     * @return {WebInspector.ProfileHeader}     */    findTemporaryProfile: function()    {        for (var i = 0; i < this._profiles.length; ++i) {            if (this._profiles[i].isTemporary)                return this._profiles[i];        }        return null;    },    _reset: function()    {        var profiles = this._profiles.slice(0);        for (var i = 0; i < profiles.length; ++i) {            var profile = profiles[i];            var view = profile.existingView();            if (view) {                view.detach();                if ("dispose" in view)                    view.dispose();            }            this.dispatchEventToListeners(WebInspector.ProfileType.Events.RemoveProfileHeader, profile);        }        this.treeElement.removeChildren();        this._profiles = [];        this._profilesIdMap = {};    },    /**     * @param {function(this:WebInspector.ProfileType, ?string, !Array.<!ProfilerAgent.ProfileHeader>)} populateCallback     */    _requestProfilesFromBackend: function(populateCallback)    {    },    _populateProfiles: function()    {        /**         * @param {?string} error         * @param {!Array.<!ProfilerAgent.ProfileHeader>} profileHeaders         */        function populateCallback(error, profileHeaders) {            if (error)                return;            profileHeaders.sort(function(a, b) { return a.uid - b.uid; });            var count = profileHeaders.length;            for (var i = 0; i < count; ++i)                this.addProfile(this.createProfile(profileHeaders[i]));        }        this._requestProfilesFromBackend(populateCallback.bind(this));    },    __proto__: WebInspector.Object.prototype}/** * @constructor * @param {!WebInspector.ProfileType} profileType * @param {string} title * @param {number=} uid */WebInspector.ProfileHeader = function(profileType, title, uid){    this._profileType = profileType;    this.title = title;    this.isTemporary = uid === undefined;    this.uid = this.isTemporary ? -1 : uid;    this._fromFile = false;}WebInspector.ProfileHeader.prototype = {    /**     * @return {!WebInspector.ProfileType}     */    profileType: function()    {        return this._profileType;    },    /**     * Must be implemented by subclasses.     * @return {WebInspector.ProfileSidebarTreeElement}     */    createSidebarTreeElement: function()    {        throw new Error("Needs implemented.");    },    /**     * @return {?WebInspector.View}     */    existingView: function()    {        return this._view;    },    /**     * @param {!WebInspector.ProfilesPanel} panel     * @return {!WebInspector.View}     */    view: function(panel)    {        if (!this._view)            this._view = this.createView(panel);        return this._view;    },    /**     * @param {!WebInspector.ProfilesPanel} panel     * @return {!WebInspector.View}     */    createView: function(panel)    {        throw new Error("Not implemented.");    },    dispose: function()    {    },    /**     * @param {Function} callback     */    load: function(callback)    {    },    /**     * @return {boolean}     */    canSaveToFile: function()    {        return false;    },    saveToFile: function()    {        throw new Error("Needs implemented");    },    /**     * @param {File} file     */    loadFromFile: function(file)    {        throw new Error("Needs implemented");    },    /**     * @return {boolean}     */    fromFile: function()    {        return this._fromFile;    },    setFromFile: function()    {        this._fromFile = true;        this.uid = -2;    }}/** * @constructor * @extends {WebInspector.Panel} * @implements {WebInspector.ContextMenu.Provider} * @param {string=} name * @param {WebInspector.ProfileType=} type */WebInspector.ProfilesPanel = function(name, type){    // If the name is not specified the ProfilesPanel works in multi-profile mode.    var singleProfileMode = typeof name !== "undefined";    name = name || "profiles";    WebInspector.Panel.call(this, name);    this.registerRequiredCSS("panelEnablerView.css");    this.registerRequiredCSS("heapProfiler.css");    this.registerRequiredCSS("profilesPanel.css");    this.createSidebarViewWithTree();    this.profilesItemTreeElement = new WebInspector.ProfilesSidebarTreeElement(this);    this.sidebarTree.appendChild(this.profilesItemTreeElement);    this._singleProfileMode = singleProfileMode;    this._profileTypesByIdMap = {};    this.profileViews = document.createElement("div");    this.profileViews.id = "profile-views";    this.splitView.mainElement.appendChild(this.profileViews);    this._statusBarButtons = [];    this.recordButton = new WebInspector.StatusBarButton("", "record-profile-status-bar-item");    this.recordButton.addEventListener("click", this.toggleRecordButton, this);    this._statusBarButtons.push(this.recordButton);    this.clearResultsButton = new WebInspector.StatusBarButton(WebInspector.UIString("Clear all profiles."), "clear-status-bar-item");    this.clearResultsButton.addEventListener("click", this._clearProfiles, this);    this._statusBarButtons.push(this.clearResultsButton);    this._profileTypeStatusBarItemsContainer = document.createElement("div");    this._profileTypeStatusBarItemsContainer.className = "status-bar-items";    this._profileViewStatusBarItemsContainer = document.createElement("div");    this._profileViewStatusBarItemsContainer.className = "status-bar-items";    if (singleProfileMode) {        this._launcherView = this._createLauncherView();        this._registerProfileType(/** @type {!WebInspector.ProfileType} */ (type));        this._selectedProfileType = type;        this._updateProfileTypeSpecificUI();    } else {        this._launcherView = new WebInspector.MultiProfileLauncherView(this);        this._launcherView.addEventListener(WebInspector.MultiProfileLauncherView.EventTypes.ProfileTypeSelected, this._onProfileTypeSelected, this);        this._registerProfileType(new WebInspector.CPUProfileType());        this._registerProfileType(new WebInspector.HeapSnapshotProfileType());        this._registerProfileType(new WebInspector.TrackingHeapSnapshotProfileType(this));        if (!WebInspector.WorkerManager.isWorkerFrontend() && WebInspector.experimentsSettings.canvasInspection.isEnabled())            this._registerProfileType(new WebInspector.CanvasProfileType());    }    this._profilesWereRequested = false;    this._reset();    this._createFileSelectorElement();    this.element.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this), true);    this._registerShortcuts();    WebInspector.ContextMenu.registerProvider(this);}WebInspector.ProfilesPanel.prototype = {    _createFileSelectorElement: function()    {        if (this._fileSelectorElement)            this.element.removeChild(this._fileSelectorElement);        this._fileSelectorElement = WebInspector.createFileSelectorElement(this._loadFromFile.bind(this));        this.element.appendChild(this._fileSelectorElement);    },    /**     * @return {!WebInspector.ProfileLauncherView}     */    _createLauncherView: function()    {        return new WebInspector.ProfileLauncherView(this);    },    _findProfileTypeByExtension: function(fileName)    {        for (var id in this._profileTypesByIdMap) {            var type = this._profileTypesByIdMap[id];            var extension = type.fileExtension();            if (!extension)                continue;            if (fileName.endsWith(type.fileExtension()))                return type;        }        return null;    },    _registerShortcuts: function()    {        this.registerShortcuts(WebInspector.ProfilesPanelDescriptor.ShortcutKeys.StartStopRecording, this.toggleRecordButton.bind(this));    },    /**     * @param {!File} file     */    _loadFromFile: function(file)    {        this._createFileSelectorElement();        var profileType = this._findProfileTypeByExtension(file.name);        if (!profileType) {            var extensions = [];            for (var id in this._profileTypesByIdMap) {                var extension = this._profileTypesByIdMap[id].fileExtension();                if (!extension)                    continue;                extensions.push(extension);            }            WebInspector.log(WebInspector.UIString("Can't load file. Only files with extensions '%s' can be loaded.", extensions.join("', '")));            return;        }        if (!!profileType.findTemporaryProfile()) {            WebInspector.log(WebInspector.UIString("Can't load profile when other profile is recording."));            return;        }        var temporaryProfile = profileType.createTemporaryProfile(WebInspector.ProfilesPanelDescriptor.UserInitiatedProfileName + "." + file.name);        temporaryProfile.setFromFile();        profileType.addProfile(temporaryProfile);        temporaryProfile.loadFromFile(file);    },    get statusBarItems()    {        return this._statusBarButtons.select("element").concat(this._profileTypeStatusBarItemsContainer, this._profileViewStatusBarItemsContainer);    },    /**     * @param {WebInspector.Event|Event=} event     * @return {boolean}     */    toggleRecordButton: function(event)    {        var isProfiling = this._selectedProfileType.buttonClicked();        this.setRecordingProfile(this._selectedProfileType.id, isProfiling);        return true;    },    _populateAllProfiles: function()    {        if (this._profilesWereRequested)            return;        this._profilesWereRequested = true;        for (var typeId in this._profileTypesByIdMap)            this._profileTypesByIdMap[typeId]._populateProfiles();    },    wasShown: function()    {        WebInspector.Panel.prototype.wasShown.call(this);        this._populateAllProfiles();    },    /**     * @param {WebInspector.Event} event     */    _onProfileTypeSelected: function(event)    {        this._selectedProfileType = /** @type {!WebInspector.ProfileType} */ (event.data);        this._updateProfileTypeSpecificUI();    },    _updateProfileTypeSpecificUI: function()    {        this.recordButton.title = this._selectedProfileType.buttonTooltip;        this._launcherView.updateProfileType(this._selectedProfileType);        this._profileTypeStatusBarItemsContainer.removeChildren();        var statusBarItems = this._selectedProfileType.statusBarItems;        if (statusBarItems) {            for (var i = 0; i < statusBarItems.length; ++i)                this._profileTypeStatusBarItemsContainer.appendChild(statusBarItems[i]);        }        this._resize(this.splitView.sidebarWidth());    },    _reset: function()    {        WebInspector.Panel.prototype.reset.call(this);        for (var typeId in this._profileTypesByIdMap)            this._profileTypesByIdMap[typeId]._reset();        delete this.visibleView;        delete this.currentQuery;        this.searchCanceled();        this._profileGroups = {};        this.recordButton.toggled = false;        if (this._selectedProfileType)            this.recordButton.title = this._selectedProfileType.buttonTooltip;        this._launcherView.profileFinished();        this.sidebarTreeElement.removeStyleClass("some-expandable");        this.profileViews.removeChildren();        this._profileViewStatusBarItemsContainer.removeChildren();        this.removeAllListeners();        this.recordButton.visible = true;        this._profileViewStatusBarItemsContainer.removeStyleClass("hidden");        this.clearResultsButton.element.removeStyleClass("hidden");        this.profilesItemTreeElement.select();        this._showLauncherView();    },    _showLauncherView: function()    {        this.closeVisibleView();        this._profileViewStatusBarItemsContainer.removeChildren();        this._launcherView.show(this.splitView.mainElement);        this.visibleView = this._launcherView;    },    _clearProfiles: function()    {        ProfilerAgent.clearProfiles();        HeapProfilerAgent.clearProfiles();        this._reset();    },    _garbageCollectButtonClicked: function()    {        HeapProfilerAgent.collectGarbage();    },    /**     * @param {!WebInspector.ProfileType} profileType     */    _registerProfileType: function(profileType)    {        this._profileTypesByIdMap[profileType.id] = profileType;        this._launcherView.addProfileType(profileType);        profileType.treeElement = new WebInspector.SidebarSectionTreeElement(profileType.treeItemTitle, null, true);        profileType.treeElement.hidden = !this._singleProfileMode;        this.sidebarTree.appendChild(profileType.treeElement);        profileType.treeElement.childrenListElement.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this), true);        function onAddProfileHeader(event)        {            this._addProfileHeader(event.data);        }        function onRemoveProfileHeader(event)        {            this._removeProfileHeader(event.data);        }        function onProgressUpdated(event)        {            this._reportProfileProgress(event.data.profile, event.data.done, event.data.total);        }        profileType.addEventListener(WebInspector.ProfileType.Events.ViewUpdated, this._updateProfileTypeSpecificUI, this);        profileType.addEventListener(WebInspector.ProfileType.Events.AddProfileHeader, onAddProfileHeader, this);        profileType.addEventListener(WebInspector.ProfileType.Events.RemoveProfileHeader, onRemoveProfileHeader, this);        profileType.addEventListener(WebInspector.ProfileType.Events.ProgressUpdated, onProgressUpdated, this);    },    /**     * @param {Event} event     */    _handleContextMenuEvent: function(event)    {        var element = event.srcElement;        while (element && !element.treeElement && element !== this.element)            element = element.parentElement;        if (!element)            return;        if (element.treeElement && element.treeElement.handleContextMenuEvent) {            element.treeElement.handleContextMenuEvent(event, this);            return;        }        var contextMenu = new WebInspector.ContextMenu(event);        if (this.visibleView instanceof WebInspector.HeapSnapshotView) {            this.visibleView.populateContextMenu(contextMenu, event);        }        if (element !== this.element || event.srcElement === this.sidebarElement) {            contextMenu.appendItem(WebInspector.UIString("Load\u2026"), this._fileSelectorElement.click.bind(this._fileSelectorElement));        }        contextMenu.show();    },    /**     * @nosideeffects     * @param {string} text     * @param {string} profileTypeId     * @return {string}     */    _makeTitleKey: function(text, profileTypeId)    {        return escape(text) + '/' + escape(profileTypeId);    },    /**     * @param {!WebInspector.ProfileHeader} profile     */    _addProfileHeader: function(profile)    {        var profileType = profile.profileType();        var typeId = profileType.id;        var sidebarParent = profileType.treeElement;        sidebarParent.hidden = false;        var small = false;        var alternateTitle;        if (!WebInspector.ProfilesPanelDescriptor.isUserInitiatedProfile(profile.title) && !profile.isTemporary) {            var profileTitleKey = this._makeTitleKey(profile.title, typeId);            if (!(profileTitleKey in this._profileGroups))                this._profileGroups[profileTitleKey] = [];            var group = this._profileGroups[profileTitleKey];            group.push(profile);            if (group.length === 2) {                // Make a group TreeElement now that there are 2 profiles.                group._profilesTreeElement = new WebInspector.ProfileGroupSidebarTreeElement(this, profile.title);                // Insert at the same index for the first profile of the group.                var index = sidebarParent.children.indexOf(group[0]._profilesTreeElement);                sidebarParent.insertChild(group._profilesTreeElement, index);                // Move the first profile to the group.                var selected = group[0]._profilesTreeElement.selected;                sidebarParent.removeChild(group[0]._profilesTreeElement);                group._profilesTreeElement.appendChild(group[0]._profilesTreeElement);                if (selected)                    group[0]._profilesTreeElement.revealAndSelect();                group[0]._profilesTreeElement.small = true;                group[0]._profilesTreeElement.mainTitle = WebInspector.UIString("Run %d", 1);                this.sidebarTreeElement.addStyleClass("some-expandable");            }            if (group.length >= 2) {                sidebarParent = group._profilesTreeElement;                alternateTitle = WebInspector.UIString("Run %d", group.length);                small = true;            }        }        var profileTreeElement = profile.createSidebarTreeElement();        profile.sidebarElement = profileTreeElement;        profileTreeElement.small = small;        if (alternateTitle)            profileTreeElement.mainTitle = alternateTitle;        profile._profilesTreeElement = profileTreeElement;        var temporaryProfile = profileType.findTemporaryProfile();        if (profile.isTemporary || !temporaryProfile)            sidebarParent.appendChild(profileTreeElement);        else {            if (temporaryProfile) {                sidebarParent.insertBeforeChild(profileTreeElement, temporaryProfile._profilesTreeElement);                this._removeTemporaryProfile(profile.profileType().id);            }            if (!this.visibleView || this.visibleView === this._launcherView)                this._showProfile(profile);            this.dispatchEventToListeners("profile added", {                type: typeId            });        }    },    /**     * @param {!WebInspector.ProfileHeader} profile     */    _removeProfileHeader: function(profile)    {        profile.dispose();        profile.profileType().removeProfile(profile);        var sidebarParent = profile.profileType().treeElement;        var profileTitleKey = this._makeTitleKey(profile.title, profile.profileType().id);        var group = this._profileGroups[profileTitleKey];        if (group) {            group.splice(group.indexOf(profile), 1);            if (group.length === 1) {                // Move the last profile out of its group and remove the group.                var index = sidebarParent.children.indexOf(group._profilesTreeElement);                sidebarParent.insertChild(group[0]._profilesTreeElement, index);                group[0]._profilesTreeElement.small = false;                group[0]._profilesTreeElement.mainTitle = group[0].title;                sidebarParent.removeChild(group._profilesTreeElement);            }            if (group.length !== 0)                sidebarParent = group._profilesTreeElement;            else                delete this._profileGroups[profileTitleKey];        }        sidebarParent.removeChild(profile._profilesTreeElement);        // No other item will be selected if there aren't any other profiles, so        // make sure that view gets cleared when the last profile is removed.        if (!sidebarParent.children.length) {            this.profilesItemTreeElement.select();            this._showLauncherView();            sidebarParent.hidden = !this._singleProfileMode;        }    },    /**     * @param {!WebInspector.ProfileHeader} profile     * @return {WebInspector.View}     */    _showProfile: function(profile)    {        if (!profile || (profile.isTemporary && !profile.profileType().hasTemporaryView()))            return null;        var view = profile.view(this);        if (view === this.visibleView)            return view;        this.closeVisibleView();        view.show(this.profileViews);        profile._profilesTreeElement._suppressOnSelect = true;        profile._profilesTreeElement.revealAndSelect();        delete profile._profilesTreeElement._suppressOnSelect;        this.visibleView = view;        this._profileViewStatusBarItemsContainer.removeChildren();        var statusBarItems = view.statusBarItems;        if (statusBarItems)            for (var i = 0; i < statusBarItems.length; ++i)                this._profileViewStatusBarItemsContainer.appendChild(statusBarItems[i]);        return view;    },    /**     * @param {HeapProfilerAgent.HeapSnapshotObjectId} snapshotObjectId     * @param {string} viewName     */    showObject: function(snapshotObjectId, viewName)    {        var heapProfiles = this.getProfileType(WebInspector.HeapSnapshotProfileType.TypeId).getProfiles();        for (var i = 0; i < heapProfiles.length; i++) {            var profile = heapProfiles[i];            // FIXME: allow to choose snapshot if there are several options.            if (profile.maxJSObjectId >= snapshotObjectId) {                this._showProfile(profile);                var view = profile.view(this);                view.changeView(viewName, function() {                    view.dataGrid.highlightObjectByHeapSnapshotId(snapshotObjectId);                });                break;            }        }    },    /**     * @param {string} typeId     */    _createTemporaryProfile: function(typeId)    {        var type = this.getProfileType(typeId);        if (!type.findTemporaryProfile())            type.addProfile(type.createTemporaryProfile());    },    /**     * @param {string} typeId     */    _removeTemporaryProfile: function(typeId)    {        var temporaryProfile = this.getProfileType(typeId).findTemporaryProfile();        if (!!temporaryProfile)            this._removeProfileHeader(temporaryProfile);    },    /**     * @param {string} typeId     * @param {number} uid     */    getProfile: function(typeId, uid)    {        return this.getProfileType(typeId).getProfile(uid);    },    /**     * @param {WebInspector.View} view     */    showView: function(view)    {        this._showProfile(view.profile);    },    /**     * @param {string} typeId     */    getProfileType: function(typeId)    {        return this._profileTypesByIdMap[typeId];    },    /**     * @param {string} typeId     * @param {string} uid     * @return {WebInspector.View}     */    showProfile: function(typeId, uid)    {        return this._showProfile(this.getProfile(typeId, Number(uid)));    },    closeVisibleView: function()    {        if (this.visibleView)            this.visibleView.detach();        delete this.visibleView;    },    /**     * @param {string} query     * @param {boolean} shouldJump     */    performSearch: function(query, shouldJump)    {        this.searchCanceled();        var searchableViews = this._searchableViews();        if (!searchableViews || !searchableViews.length)            return;        var visibleView = this.visibleView;        var matchesCountUpdateTimeout = null;        function updateMatchesCount()        {            WebInspector.searchController.updateSearchMatchesCount(this._totalSearchMatches, this);            WebInspector.searchController.updateCurrentMatchIndex(this._currentSearchResultIndex, this);            matchesCountUpdateTimeout = null;        }        function updateMatchesCountSoon()        {            if (matchesCountUpdateTimeout)                return;            // Update the matches count every half-second so it doesn't feel twitchy.            matchesCountUpdateTimeout = setTimeout(updateMatchesCount.bind(this), 500);        }        function finishedCallback(view, searchMatches)        {            if (!searchMatches)                return;            this._totalSearchMatches += searchMatches;            this._searchResults.push(view);            this.searchMatchFound(view, searchMatches);            updateMatchesCountSoon.call(this);            if (shouldJump && view === visibleView)                view.jumpToFirstSearchResult();        }        var i = 0;        var panel = this;        var boundFinishedCallback = finishedCallback.bind(this);        var chunkIntervalIdentifier = null;        // Split up the work into chunks so we don't block the        // UI thread while processing.        function processChunk()        {            var view = searchableViews[i];            if (++i >= searchableViews.length) {                if (panel._currentSearchChunkIntervalIdentifier === chunkIntervalIdentifier)                    delete panel._currentSearchChunkIntervalIdentifier;                clearInterval(chunkIntervalIdentifier);            }            if (!view)                return;            view.currentQuery = query;            view.performSearch(query, boundFinishedCallback);        }        processChunk();        chunkIntervalIdentifier = setInterval(processChunk, 25);        this._currentSearchChunkIntervalIdentifier = chunkIntervalIdentifier;    },    jumpToNextSearchResult: function()    {        if (!this.showView || !this._searchResults || !this._searchResults.length)            return;        var showFirstResult = false;        this._currentSearchResultIndex = this._searchResults.indexOf(this.visibleView);        if (this._currentSearchResultIndex === -1) {            this._currentSearchResultIndex = 0;            showFirstResult = true;        }        var currentView = this._searchResults[this._currentSearchResultIndex];        if (currentView.showingLastSearchResult()) {            if (++this._currentSearchResultIndex >= this._searchResults.length)                this._currentSearchResultIndex = 0;            currentView = this._searchResults[this._currentSearchResultIndex];            showFirstResult = true;        }        WebInspector.searchController.updateCurrentMatchIndex(this._currentSearchResultIndex, this);        if (currentView !== this.visibleView) {            this.showView(currentView);            WebInspector.searchController.showSearchField();        }        if (showFirstResult)            currentView.jumpToFirstSearchResult();        else            currentView.jumpToNextSearchResult();    },    jumpToPreviousSearchResult: function()    {        if (!this.showView || !this._searchResults || !this._searchResults.length)            return;        var showLastResult = false;        this._currentSearchResultIndex = this._searchResults.indexOf(this.visibleView);        if (this._currentSearchResultIndex === -1) {            this._currentSearchResultIndex = 0;            showLastResult = true;        }        var currentView = this._searchResults[this._currentSearchResultIndex];        if (currentView.showingFirstSearchResult()) {            if (--this._currentSearchResultIndex < 0)                this._currentSearchResultIndex = (this._searchResults.length - 1);            currentView = this._searchResults[this._currentSearchResultIndex];            showLastResult = true;        }        WebInspector.searchController.updateCurrentMatchIndex(this._currentSearchResultIndex, this);        if (currentView !== this.visibleView) {            this.showView(currentView);            WebInspector.searchController.showSearchField();        }        if (showLastResult)            currentView.jumpToLastSearchResult();        else            currentView.jumpToPreviousSearchResult();    },    /**     * @return {!Array.<!WebInspector.ProfileHeader>}     */    _getAllProfiles: function()    {        var profiles = [];        for (var typeId in this._profileTypesByIdMap)            profiles = profiles.concat(this._profileTypesByIdMap[typeId].getProfiles());        return profiles;    },    /**     * @return {!Array.<!WebInspector.View>}     */    _searchableViews: function()    {        var profiles = this._getAllProfiles();        var searchableViews = [];        for (var i = 0; i < profiles.length; ++i) {            var view = profiles[i].view(this);            if (view.performSearch)                searchableViews.push(view)        }        var index = searchableViews.indexOf(this.visibleView);        if (index > 0) {            // Move visibleView to the first position.            searchableViews[index] = searchableViews[0];            searchableViews[0] = this.visibleView;        }        return searchableViews;    },    searchMatchFound: function(view, matches)    {        view.profile._profilesTreeElement.searchMatches = matches;    },    searchCanceled: function()    {        if (this._searchResults) {            for (var i = 0; i < this._searchResults.length; ++i) {                var view = this._searchResults[i];                if (view.searchCanceled)                    view.searchCanceled();                delete view.currentQuery;            }        }        WebInspector.Panel.prototype.searchCanceled.call(this);        if (this._currentSearchChunkIntervalIdentifier) {            clearInterval(this._currentSearchChunkIntervalIdentifier);            delete this._currentSearchChunkIntervalIdentifier;        }        this._totalSearchMatches = 0;        this._currentSearchResultIndex = 0;        this._searchResults = [];        var profiles = this._getAllProfiles();        for (var i = 0; i < profiles.length; ++i)            profiles[i]._profilesTreeElement.searchMatches = 0;    },    /**     * @param {!WebInspector.Event} event     */    sidebarResized: function(event)    {        var sidebarWidth = /** @type {number} */ (event.data);        this._resize(sidebarWidth);    },    onResize: function()    {        this._resize(this.splitView.sidebarWidth());    },    /**     * @param {number} sidebarWidth     */    _resize: function(sidebarWidth)    {        var lastItemElement = this._statusBarButtons[this._statusBarButtons.length - 1].element;        var left = lastItemElement.totalOffsetLeft() + lastItemElement.offsetWidth;        this._profileTypeStatusBarItemsContainer.style.left = left + "px";        left += this._profileTypeStatusBarItemsContainer.offsetWidth - 1;        this._profileViewStatusBarItemsContainer.style.left = Math.max(left, sidebarWidth) + "px";    },    /**     * @param {string} profileType     * @param {boolean} isProfiling     */    setRecordingProfile: function(profileType, isProfiling)    {        var profileTypeObject = this.getProfileType(profileType);        this.recordButton.toggled = isProfiling;        this.recordButton.title = profileTypeObject.buttonTooltip;        if (isProfiling) {            this._launcherView.profileStarted();            this._createTemporaryProfile(profileType);            if (profileTypeObject.hasTemporaryView())                this._showProfile(profileTypeObject.findTemporaryProfile());        } else            this._launcherView.profileFinished();    },    /**     * @param {!WebInspector.ProfileHeader} profile     * @param {number} done     * @param {number} total     */    _reportProfileProgress: function(profile, done, total)    {        profile.sidebarElement.subtitle = WebInspector.UIString("%.0f%", (done / total) * 100);        profile.sidebarElement.wait = true;    },    /**      * @param {WebInspector.ContextMenu} contextMenu     * @param {Object} target     */    appendApplicableItems: function(event, contextMenu, target)    {        if (WebInspector.inspectorView.currentPanel() !== this)            return;        var object = /** @type {WebInspector.RemoteObject} */ (target);        var objectId = object.objectId;        if (!objectId)            return;        var heapProfiles = this.getProfileType(WebInspector.HeapSnapshotProfileType.TypeId).getProfiles();        if (!heapProfiles.length)            return;        function revealInView(viewName)        {            HeapProfilerAgent.getHeapObjectId(objectId, didReceiveHeapObjectId.bind(this, viewName));        }        function didReceiveHeapObjectId(viewName, error, result)        {            if (WebInspector.inspectorView.currentPanel() !== this)                return;            if (!error)                this.showObject(result, viewName);        }        contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Dominators view" : "Reveal in Dominators View"), revealInView.bind(this, "Dominators"));        contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Summary view" : "Reveal in Summary View"), revealInView.bind(this, "Summary"));    },    __proto__: WebInspector.Panel.prototype}/** * @constructor * @extends {WebInspector.SidebarTreeElement} * @param {!WebInspector.ProfileHeader} profile * @param {string} titleFormat * @param {string} className */WebInspector.ProfileSidebarTreeElement = function(profile, titleFormat, className){    this.profile = profile;    this._titleFormat = titleFormat;    if (WebInspector.ProfilesPanelDescriptor.isUserInitiatedProfile(this.profile.title))        this._profileNumber = WebInspector.ProfilesPanelDescriptor.userInitiatedProfileIndex(this.profile.title);    WebInspector.SidebarTreeElement.call(this, className, "", "", profile, false);    this.refreshTitles();}WebInspector.ProfileSidebarTreeElement.prototype = {    onselect: function()    {        if (!this._suppressOnSelect)            this.treeOutline.panel._showProfile(this.profile);    },    ondelete: function()    {        this.treeOutline.panel._removeProfileHeader(this.profile);        return true;    },    get mainTitle()    {        if (this._mainTitle)            return this._mainTitle;        if (WebInspector.ProfilesPanelDescriptor.isUserInitiatedProfile(this.profile.title))            return WebInspector.UIString(this._titleFormat, this._profileNumber);        return this.profile.title;    },    set mainTitle(x)    {        this._mainTitle = x;        this.refreshTitles();    },    set searchMatches(matches)    {        if (!matches) {            if (!this.bubbleElement)                return;            this.bubbleElement.removeStyleClass("search-matches");            this.bubbleText = "";            return;        }        this.bubbleText = matches;        this.bubbleElement.addStyleClass("search-matches");    },    /**     * @param {!Event} event     * @param {!WebInspector.ProfilesPanel} panel     */    handleContextMenuEvent: function(event, panel)    {        var profile = this.profile;        var contextMenu = new WebInspector.ContextMenu(event);        // FIXME: use context menu provider        contextMenu.appendItem(WebInspector.UIString("Load\u2026"), panel._fileSelectorElement.click.bind(panel._fileSelectorElement));        if (profile.canSaveToFile())            contextMenu.appendItem(WebInspector.UIString("Save\u2026"), profile.saveToFile.bind(profile));        contextMenu.appendItem(WebInspector.UIString("Delete"), this.ondelete.bind(this));        contextMenu.show();    },    __proto__: WebInspector.SidebarTreeElement.prototype}/** * @constructor * @extends {WebInspector.SidebarTreeElement} * @param {WebInspector.ProfilesPanel} panel * @param {string} title * @param {string=} subtitle */WebInspector.ProfileGroupSidebarTreeElement = function(panel, title, subtitle){    WebInspector.SidebarTreeElement.call(this, "profile-group-sidebar-tree-item", title, subtitle, null, true);    this._panel = panel;}WebInspector.ProfileGroupSidebarTreeElement.prototype = {    onselect: function()    {        if (this.children.length > 0)            this._panel._showProfile(this.children[this.children.length - 1].profile);    },    __proto__: WebInspector.SidebarTreeElement.prototype}/** * @constructor * @extends {WebInspector.SidebarTreeElement} * @param {!WebInspector.ProfilesPanel} panel */WebInspector.ProfilesSidebarTreeElement = function(panel){    this._panel = panel;    this.small = false;    WebInspector.SidebarTreeElement.call(this, "profile-launcher-view-tree-item", WebInspector.UIString("Profiles"), "", null, false);}WebInspector.ProfilesSidebarTreeElement.prototype = {    onselect: function()    {        this._panel._showLauncherView();    },    get selectable()    {        return true;    },    __proto__: WebInspector.SidebarTreeElement.prototype}/** * @constructor * @extends {WebInspector.ProfilesPanel} */WebInspector.CPUProfilerPanel = function(){    WebInspector.ProfilesPanel.call(this, "cpu-profiler", new WebInspector.CPUProfileType());}WebInspector.CPUProfilerPanel.prototype = {    __proto__: WebInspector.ProfilesPanel.prototype}/** * @constructor * @extends {WebInspector.ProfilesPanel} */WebInspector.HeapProfilerPanel = function(){    var heapSnapshotProfileType = new WebInspector.HeapSnapshotProfileType();    WebInspector.ProfilesPanel.call(this, "heap-profiler", heapSnapshotProfileType);    this._singleProfileMode = false;    this._registerProfileType(new WebInspector.TrackingHeapSnapshotProfileType(this));    this._launcherView.addEventListener(WebInspector.MultiProfileLauncherView.EventTypes.ProfileTypeSelected, this._onProfileTypeSelected, this);    this._launcherView._profileTypeChanged(heapSnapshotProfileType);}WebInspector.HeapProfilerPanel.prototype = {    _createLauncherView: function()    {        return new WebInspector.MultiProfileLauncherView(this);    },    __proto__: WebInspector.ProfilesPanel.prototype}/** * @constructor * @extends {WebInspector.ProfilesPanel} */WebInspector.CanvasProfilerPanel = function(){    WebInspector.ProfilesPanel.call(this, "canvas-profiler", new WebInspector.CanvasProfileType());}WebInspector.CanvasProfilerPanel.prototype = {    __proto__: WebInspector.ProfilesPanel.prototype}importScript("ProfileDataGridTree.js");importScript("BottomUpProfileDataGridTree.js");importScript("CPUProfileView.js");importScript("FlameChart.js");importScript("HeapSnapshot.js");importScript("HeapSnapshotDataGrids.js");importScript("HeapSnapshotGridNodes.js");importScript("HeapSnapshotLoader.js");importScript("HeapSnapshotProxy.js");importScript("HeapSnapshotView.js");importScript("HeapSnapshotWorkerDispatcher.js");importScript("JSHeapSnapshot.js");importScript("ProfileLauncherView.js");importScript("TopDownProfileDataGridTree.js");importScript("CanvasProfileView.js");importScript("CanvasReplayStateView.js");
 |