home *** CD-ROM | disk | FTP | other *** search
/ Computer Active Guide 2009 July / CAG7.ISO / Internetas / SafariSetup.exe / AppleApplicationSupport.msi / WebKit.resources_inspector_ProfilesPanel.js < prev    next >
Encoding:
Text File  |  2010-06-03  |  19.7 KB  |  604 lines

  1. /*
  2.  * Copyright (C) 2008 Apple 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
  6.  * are met:
  7.  * 1. Redistributions of source code must retain the above copyright
  8.  *    notice, this list of conditions and the following disclaimer.
  9.  * 2. Redistributions in binary form must reproduce the above copyright
  10.  *    notice, this list of conditions and the following disclaimer in the
  11.  *    documentation and/or other materials provided with the distribution.
  12.  *
  13.  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
  14.  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  15.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  16.  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
  17.  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  18.  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  19.  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  20.  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
  21.  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  22.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  23.  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24.  */
  25.  
  26. const UserInitiatedProfileName = "org.webkit.profiles.user-initiated";
  27.  
  28. WebInspector.ProfileType = function(id, name)
  29. {
  30.     this._id = id;
  31.     this._name = name;
  32. }
  33.  
  34. WebInspector.ProfileType.URLRegExp = /webkit-profile:\/\/(.+)\/(.+)#([0-9]+)/;
  35.  
  36. WebInspector.ProfileType.prototype = {
  37.     get buttonTooltip()
  38.     {
  39.         return "";
  40.     },
  41.  
  42.     get buttonStyle()
  43.     {
  44.         return undefined;
  45.     },
  46.  
  47.     get buttonCaption()
  48.     {
  49.         return this.name;
  50.     },
  51.  
  52.     get id()
  53.     {
  54.         return this._id;
  55.     },
  56.  
  57.     get name()
  58.     {
  59.         return this._name;
  60.     },
  61.  
  62.     buttonClicked: function()
  63.     {
  64.     },
  65.  
  66.     viewForProfile: function(profile)
  67.     {
  68.         if (!profile._profileView)
  69.             profile._profileView = this.createView(profile);
  70.         return profile._profileView;
  71.     },
  72.  
  73.     get welcomeMessage()
  74.     {
  75.         return "";
  76.     },
  77.  
  78.     // Must be implemented by subclasses.
  79.     createView: function(profile)
  80.     {
  81.         throw new Error("Needs implemented.");
  82.     },
  83.  
  84.     // Must be implemented by subclasses.
  85.     createSidebarTreeElementForProfile: function(profile)
  86.     {
  87.         throw new Error("Needs implemented.");
  88.     }
  89. }
  90.  
  91. WebInspector.ProfilesPanel = function()
  92. {
  93.     WebInspector.Panel.call(this);
  94.  
  95.     this.createSidebar();
  96.  
  97.     this.element.addStyleClass("profiles");
  98.     this._profileTypesByIdMap = {};
  99.     this._profileTypeButtonsByIdMap = {};
  100.  
  101.     var panelEnablerHeading = WebInspector.UIString("You need to enable profiling before you can use the Profiles panel.");
  102.     var panelEnablerDisclaimer = WebInspector.UIString("Enabling profiling will make scripts run slower.");
  103.     var panelEnablerButton = WebInspector.UIString("Enable Profiling");
  104.     this.panelEnablerView = new WebInspector.PanelEnablerView("profiles", panelEnablerHeading, panelEnablerDisclaimer, panelEnablerButton);
  105.     this.panelEnablerView.addEventListener("enable clicked", this._enableProfiling, this);
  106.  
  107.     this.element.appendChild(this.panelEnablerView.element);
  108.  
  109.     this.profileViews = document.createElement("div");
  110.     this.profileViews.id = "profile-views";
  111.     this.element.appendChild(this.profileViews);
  112.  
  113.     this.enableToggleButton = new WebInspector.StatusBarButton("", "enable-toggle-status-bar-item");
  114.     this.enableToggleButton.addEventListener("click", this._toggleProfiling.bind(this), false);
  115.  
  116.     this.profileViewStatusBarItemsContainer = document.createElement("div");
  117.     this.profileViewStatusBarItemsContainer.id = "profile-view-status-bar-items";
  118.  
  119.     this.welcomeView = new WebInspector.WelcomeView("profiles", WebInspector.UIString("Welcome to the Profiles panel"));
  120.     this.element.appendChild(this.welcomeView.element);
  121.  
  122.     this._profiles = [];
  123.     this._profilerEnabled = Preferences.profilerAlwaysEnabled;
  124.     this.reset();
  125. }
  126.  
  127. WebInspector.ProfilesPanel.prototype = {
  128.     toolbarItemClass: "profiles",
  129.  
  130.     get toolbarItemLabel()
  131.     {
  132.         return WebInspector.UIString("Profiles");
  133.     },
  134.  
  135.     get statusBarItems()
  136.     {
  137.         function clickHandler(profileType, buttonElement)
  138.         {
  139.             profileType.buttonClicked.call(profileType);
  140.             this.updateProfileTypeButtons();
  141.         }
  142.  
  143.         var items = [this.enableToggleButton.element];
  144.         // FIXME: Generate a single "combo-button".
  145.         for (var typeId in this._profileTypesByIdMap) {
  146.             var profileType = this.getProfileType(typeId);
  147.             if (profileType.buttonStyle) {
  148.                 var button = new WebInspector.StatusBarButton(profileType.buttonTooltip, profileType.buttonStyle, profileType.buttonCaption);
  149.                 this._profileTypeButtonsByIdMap[typeId] = button.element;
  150.                 button.element.addEventListener("click", clickHandler.bind(this, profileType, button.element), false);
  151.                 items.push(button.element);
  152.             }
  153.         }
  154.         items.push(this.profileViewStatusBarItemsContainer);
  155.         return items;
  156.     },
  157.  
  158.     show: function()
  159.     {
  160.         WebInspector.Panel.prototype.show.call(this);
  161.         if (this._shouldPopulateProfiles)
  162.             this._populateProfiles();
  163.     },
  164.  
  165.     populateInterface: function()
  166.     {
  167.         this.reset();
  168.         if (this.visible)
  169.             this._populateProfiles();
  170.         else
  171.             this._shouldPopulateProfiles = true;
  172.     },
  173.  
  174.     profilerWasEnabled: function()
  175.     {
  176.         if (this._profilerEnabled)
  177.             return;
  178.  
  179.         this._profilerEnabled = true;
  180.         this.populateInterface();
  181.     },
  182.  
  183.     profilerWasDisabled: function()
  184.     {
  185.         if (!this._profilerEnabled)
  186.             return;
  187.  
  188.         this._profilerEnabled = false;
  189.         this.reset();
  190.     },
  191.  
  192.     reset: function()
  193.     {
  194.         for (var i = 0; i < this._profiles.length; ++i)
  195.             delete this._profiles[i]._profileView;
  196.         delete this.visibleView;
  197.  
  198.         delete this.currentQuery;
  199.         this.searchCanceled();
  200.  
  201.         this._profiles = [];
  202.         this._profilesIdMap = {};
  203.         this._profileGroups = {};
  204.         this._profileGroupsForLinks = {}
  205.  
  206.         this.sidebarTreeElement.removeStyleClass("some-expandable");
  207.  
  208.         for (var typeId in this._profileTypesByIdMap)
  209.             this.getProfileType(typeId).treeElement.removeChildren();
  210.  
  211.         this.profileViews.removeChildren();
  212.  
  213.         this.profileViewStatusBarItemsContainer.removeChildren();
  214.  
  215.         this._updateInterface();
  216.         this.welcomeView.show();
  217.     },
  218.  
  219.     registerProfileType: function(profileType)
  220.     {
  221.         this._profileTypesByIdMap[profileType.id] = profileType;
  222.         profileType.treeElement = new WebInspector.SidebarSectionTreeElement(profileType.name, null, true);
  223.         this.sidebarTree.appendChild(profileType.treeElement);
  224.         profileType.treeElement.expand();
  225.         this._addWelcomeMessage(profileType);
  226.     },
  227.  
  228.     _addWelcomeMessage: function(profileType)
  229.     {
  230.         var message = profileType.welcomeMessage;
  231.         // Message text is supposed to have a '%s' substring as a placeholder
  232.         // for a status bar button. If it is there, we split the message in two
  233.         // parts, and insert the button between them.
  234.         var buttonPos = message.indexOf("%s");
  235.         if (buttonPos > -1) {
  236.             var container = document.createDocumentFragment();
  237.             var part1 = document.createElement("span");
  238.             part1.innerHTML = message.substr(0, buttonPos);
  239.             container.appendChild(part1);
  240.      
  241.             var button = new WebInspector.StatusBarButton(profileType.buttonTooltip, profileType.buttonStyle, profileType.buttonCaption);
  242.             container.appendChild(button.element);
  243.        
  244.             var part2 = document.createElement("span");
  245.             part2.innerHTML = message.substr(buttonPos + 2);
  246.             container.appendChild(part2);
  247.             this.welcomeView.addMessage(container);
  248.         } else
  249.             this.welcomeView.addMessage(message);
  250.     },
  251.  
  252.     _makeKey: function(text, profileTypeId)
  253.     {
  254.         return escape(text) + '/' + escape(profileTypeId);
  255.     },
  256.  
  257.     addProfileHeader: function(profile)
  258.     {
  259.         var typeId = profile.typeId;
  260.         var profileType = this.getProfileType(typeId);
  261.         var sidebarParent = profileType.treeElement;
  262.         var small = false;
  263.         var alternateTitle;
  264.  
  265.         profile.__profilesPanelProfileType = profileType;
  266.         this._profiles.push(profile);
  267.         this._profilesIdMap[this._makeKey(profile.uid, typeId)] = profile;
  268.  
  269.         if (profile.title.indexOf(UserInitiatedProfileName) !== 0) {
  270.             var profileTitleKey = this._makeKey(profile.title, typeId);
  271.             if (!(profileTitleKey in this._profileGroups))
  272.                 this._profileGroups[profileTitleKey] = [];
  273.  
  274.             var group = this._profileGroups[profileTitleKey];
  275.             group.push(profile);
  276.  
  277.             if (group.length === 2) {
  278.                 // Make a group TreeElement now that there are 2 profiles.
  279.                 group._profilesTreeElement = new WebInspector.ProfileGroupSidebarTreeElement(profile.title);
  280.  
  281.                 // Insert at the same index for the first profile of the group.
  282.                 var index = sidebarParent.children.indexOf(group[0]._profilesTreeElement);
  283.                 sidebarParent.insertChild(group._profilesTreeElement, index);
  284.  
  285.                 // Move the first profile to the group.
  286.                 var selected = group[0]._profilesTreeElement.selected;
  287.                 sidebarParent.removeChild(group[0]._profilesTreeElement);
  288.                 group._profilesTreeElement.appendChild(group[0]._profilesTreeElement);
  289.                 if (selected) {
  290.                     group[0]._profilesTreeElement.select();
  291.                     group[0]._profilesTreeElement.reveal();
  292.                 }
  293.  
  294.                 group[0]._profilesTreeElement.small = true;
  295.                 group[0]._profilesTreeElement.mainTitle = WebInspector.UIString("Run %d", 1);
  296.  
  297.                 this.sidebarTreeElement.addStyleClass("some-expandable");
  298.             }
  299.  
  300.             if (group.length >= 2) {
  301.                 sidebarParent = group._profilesTreeElement;
  302.                 alternateTitle = WebInspector.UIString("Run %d", group.length);
  303.                 small = true;
  304.             }
  305.         }
  306.  
  307.         var profileTreeElement = profileType.createSidebarTreeElementForProfile(profile);
  308.         profileTreeElement.small = small;
  309.         if (alternateTitle)
  310.             profileTreeElement.mainTitle = alternateTitle;
  311.         profile._profilesTreeElement = profileTreeElement;
  312.  
  313.         sidebarParent.appendChild(profileTreeElement);
  314.         if (!profile.isTemporary) {
  315.             this.welcomeView.hide();
  316.             if (!this.visibleView)
  317.                 this.showProfile(profile);
  318.         }
  319.     },
  320.  
  321.     removeProfileHeader: function(profile)
  322.     {
  323.         var typeId = profile.typeId;
  324.         var profileType = this.getProfileType(typeId);
  325.         var sidebarParent = profileType.treeElement;
  326.  
  327.         for (var i = 0; i < this._profiles.length; ++i) {
  328.             if (this._profiles[i].uid === profile.uid) {
  329.                 profile = this._profiles[i];
  330.                 this._profiles.splice(i, 1);
  331.                 break;
  332.             }
  333.         }
  334.         delete this._profilesIdMap[this._makeKey(profile.uid, typeId)];
  335.  
  336.         var profileTitleKey = this._makeKey(profile.title, typeId);
  337.         delete this._profileGroups[profileTitleKey];
  338.  
  339.         sidebarParent.removeChild(profile._profilesTreeElement);
  340.     },
  341.  
  342.     showProfile: function(profile)
  343.     {
  344.         if (!profile || profile.isTemporary)
  345.             return;
  346.  
  347.         this.closeVisibleView();
  348.  
  349.         var view = profile.__profilesPanelProfileType.viewForProfile(profile);
  350.  
  351.         view.show(this.profileViews);
  352.  
  353.         profile._profilesTreeElement.select(true);
  354.         profile._profilesTreeElement.reveal();
  355.  
  356.         this.visibleView = view;
  357.  
  358.         this.profileViewStatusBarItemsContainer.removeChildren();
  359.  
  360.         var statusBarItems = view.statusBarItems;
  361.         for (var i = 0; i < statusBarItems.length; ++i)
  362.             this.profileViewStatusBarItemsContainer.appendChild(statusBarItems[i]);
  363.     },
  364.  
  365.     showView: function(view)
  366.     {
  367.         this.showProfile(view.profile);
  368.     },
  369.  
  370.     getProfileType: function(typeId)
  371.     {
  372.         return this._profileTypesByIdMap[typeId];
  373.     },
  374.  
  375.     showProfileForURL: function(url)
  376.     {
  377.         var match = url.match(WebInspector.ProfileType.URLRegExp);
  378.         if (!match)
  379.             return;
  380.         this.showProfile(this._profilesIdMap[this._makeKey(match[3], match[1])]);
  381.     },
  382.  
  383.     updateProfileTypeButtons: function()
  384.     {
  385.         for (var typeId in this._profileTypeButtonsByIdMap) {
  386.             var buttonElement = this._profileTypeButtonsByIdMap[typeId];
  387.             var profileType = this.getProfileType(typeId);
  388.             buttonElement.className = profileType.buttonStyle;
  389.             buttonElement.title = profileType.buttonTooltip;
  390.             // FIXME: Apply profileType.buttonCaption once captions are added to button controls.
  391.         }
  392.     },
  393.  
  394.     closeVisibleView: function()
  395.     {
  396.         if (this.visibleView)
  397.             this.visibleView.hide();
  398.         delete this.visibleView;
  399.     },
  400.  
  401.     displayTitleForProfileLink: function(title, typeId)
  402.     {
  403.         title = unescape(title);
  404.         if (title.indexOf(UserInitiatedProfileName) === 0) {
  405.             title = WebInspector.UIString("Profile %d", title.substring(UserInitiatedProfileName.length + 1));
  406.         } else {
  407.             var titleKey = this._makeKey(title, typeId);
  408.             if (!(titleKey in this._profileGroupsForLinks))
  409.                 this._profileGroupsForLinks[titleKey] = 0;
  410.  
  411.             groupNumber = ++this._profileGroupsForLinks[titleKey];
  412.  
  413.             if (groupNumber > 2)
  414.                 // The title is used in the console message announcing that a profile has started so it gets
  415.                 // incremented twice as often as it's displayed
  416.                 title += " " + WebInspector.UIString("Run %d", groupNumber / 2);
  417.         }
  418.         
  419.         return title;
  420.     },
  421.  
  422.     get searchableViews()
  423.     {
  424.         var views = [];
  425.  
  426.         const visibleView = this.visibleView;
  427.         if (visibleView && visibleView.performSearch)
  428.             views.push(visibleView);
  429.  
  430.         var profilesLength = this._profiles.length;
  431.         for (var i = 0; i < profilesLength; ++i) {
  432.             var profile = this._profiles[i];
  433.             var view = profile.__profilesPanelProfileType.viewForProfile(profile);
  434.             if (!view.performSearch || view === visibleView)
  435.                 continue;
  436.             views.push(view);
  437.         }
  438.  
  439.         return views;
  440.     },
  441.  
  442.     searchMatchFound: function(view, matches)
  443.     {
  444.         view.profile._profilesTreeElement.searchMatches = matches;
  445.     },
  446.  
  447.     searchCanceled: function(startingNewSearch)
  448.     {
  449.         WebInspector.Panel.prototype.searchCanceled.call(this, startingNewSearch);
  450.  
  451.         if (!this._profiles)
  452.             return;
  453.  
  454.         for (var i = 0; i < this._profiles.length; ++i) {
  455.             var profile = this._profiles[i];
  456.             profile._profilesTreeElement.searchMatches = 0;
  457.         }
  458.     },
  459.  
  460.     _updateInterface: function()
  461.     {
  462.         // FIXME: Replace ProfileType-specific button visibility changes by a single ProfileType-agnostic "combo-button" visibility change.
  463.         if (this._profilerEnabled) {
  464.             this.enableToggleButton.title = WebInspector.UIString("Profiling enabled. Click to disable.");
  465.             this.enableToggleButton.toggled = true;
  466.             for (var typeId in this._profileTypeButtonsByIdMap)
  467.                 this._profileTypeButtonsByIdMap[typeId].removeStyleClass("hidden");
  468.             this.profileViewStatusBarItemsContainer.removeStyleClass("hidden");
  469.             this.panelEnablerView.visible = false;
  470.         } else {
  471.             this.enableToggleButton.title = WebInspector.UIString("Profiling disabled. Click to enable.");
  472.             this.enableToggleButton.toggled = false;
  473.             for (var typeId in this._profileTypeButtonsByIdMap)
  474.                 this._profileTypeButtonsByIdMap[typeId].addStyleClass("hidden");
  475.             this.profileViewStatusBarItemsContainer.addStyleClass("hidden");
  476.             this.panelEnablerView.visible = true;
  477.         }
  478.     },
  479.  
  480.     _enableProfiling: function()
  481.     {
  482.         if (this._profilerEnabled)
  483.             return;
  484.         this._toggleProfiling(this.panelEnablerView.alwaysEnabled);
  485.     },
  486.  
  487.     _toggleProfiling: function(optionalAlways)
  488.     {
  489.         if (this._profilerEnabled)
  490.             InspectorBackend.disableProfiler(true);
  491.         else
  492.             InspectorBackend.enableProfiler(!!optionalAlways);
  493.     },
  494.  
  495.     _populateProfiles: function()
  496.     {
  497.         var sidebarTreeChildrenCount = this.sidebarTree.children.length;
  498.         for (var i = 0; i < sidebarTreeChildrenCount; ++i) {
  499.             var treeElement = this.sidebarTree.children[i];
  500.             if (treeElement.children.length)
  501.                 return;
  502.         }
  503.  
  504.         function populateCallback(profileHeaders) {
  505.             profileHeaders.sort(function(a, b) { return a.uid - b.uid; });
  506.             var profileHeadersLength = profileHeaders.length;
  507.             for (var i = 0; i < profileHeadersLength; ++i)
  508.                 WebInspector.addProfileHeader(profileHeaders[i]);
  509.         }
  510.  
  511.         var callId = WebInspector.Callback.wrap(populateCallback);
  512.         InspectorBackend.getProfileHeaders(callId);
  513.  
  514.         delete this._shouldPopulateProfiles;
  515.     },
  516.  
  517.     updateMainViewWidth: function(width)
  518.     {
  519.         this.welcomeView.element.style.left = width + "px";
  520.         this.profileViews.style.left = width + "px";
  521.         this.profileViewStatusBarItemsContainer.style.left = width + "px";
  522.         this.resize();
  523.     }
  524. }
  525.  
  526. WebInspector.ProfilesPanel.prototype.__proto__ = WebInspector.Panel.prototype;
  527.  
  528. WebInspector.ProfileSidebarTreeElement = function(profile)
  529. {
  530.     this.profile = profile;
  531.  
  532.     if (this.profile.title.indexOf(UserInitiatedProfileName) === 0)
  533.         this._profileNumber = this.profile.title.substring(UserInitiatedProfileName.length + 1);
  534.  
  535.     WebInspector.SidebarTreeElement.call(this, "profile-sidebar-tree-item", "", "", profile, false);
  536.  
  537.     this.refreshTitles();
  538. }
  539.  
  540. WebInspector.ProfileSidebarTreeElement.prototype = {
  541.     onselect: function()
  542.     {
  543.         WebInspector.panels.profiles.showProfile(this.profile);
  544.     },
  545.  
  546.     get mainTitle()
  547.     {
  548.         if (this._mainTitle)
  549.             return this._mainTitle;
  550.         if (this.profile.title.indexOf(UserInitiatedProfileName) === 0)
  551.             return WebInspector.UIString("Profile %d", this._profileNumber);
  552.         return this.profile.title;
  553.     },
  554.  
  555.     set mainTitle(x)
  556.     {
  557.         this._mainTitle = x;
  558.         this.refreshTitles();
  559.     },
  560.  
  561.     get subtitle()
  562.     {
  563.         // There is no subtitle.
  564.     },
  565.  
  566.     set subtitle(x)
  567.     {
  568.         // Can't change subtitle.
  569.     },
  570.  
  571.     set searchMatches(matches)
  572.     {
  573.         if (!matches) {
  574.             if (!this.bubbleElement)
  575.                 return;
  576.             this.bubbleElement.removeStyleClass("search-matches");
  577.             this.bubbleText = "";
  578.             return;
  579.         }
  580.  
  581.         this.bubbleText = matches;
  582.         this.bubbleElement.addStyleClass("search-matches");
  583.     }
  584. }
  585.  
  586. WebInspector.ProfileSidebarTreeElement.prototype.__proto__ = WebInspector.SidebarTreeElement.prototype;
  587.  
  588. WebInspector.ProfileGroupSidebarTreeElement = function(title, subtitle)
  589. {
  590.     WebInspector.SidebarTreeElement.call(this, "profile-group-sidebar-tree-item", title, subtitle, null, true);
  591. }
  592.  
  593. WebInspector.ProfileGroupSidebarTreeElement.prototype = {
  594.     onselect: function()
  595.     {
  596.         WebInspector.panels.profiles.showProfile(this.children[this.children.length - 1].profile);
  597.     }
  598. }
  599.  
  600. WebInspector.ProfileGroupSidebarTreeElement.prototype.__proto__ = WebInspector.SidebarTreeElement.prototype;
  601.  
  602. WebInspector.didGetProfileHeaders = WebInspector.Callback.processCallback;
  603. WebInspector.didGetProfile = WebInspector.Callback.processCallback;
  604.