|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 "use strict"; |
|
6 |
|
7 var gInstanceUID; |
|
8 var gParsedQS; |
|
9 var gHideSourceLinks; |
|
10 |
|
11 function getParam(key) { |
|
12 if (gParsedQS) |
|
13 return gParsedQS[key]; |
|
14 |
|
15 var query = window.location.search.substring(1); |
|
16 gParsedQS = {}; |
|
17 |
|
18 query.split("&").forEach(function (pair) { |
|
19 pair = pair.split("="); |
|
20 gParsedQS[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]); |
|
21 }); |
|
22 |
|
23 return gParsedQS[key]; |
|
24 } |
|
25 |
|
26 /** |
|
27 * Sends a message to the parent window with a status |
|
28 * update. |
|
29 * |
|
30 * @param string status |
|
31 * Status to send to the parent page: |
|
32 * - loaded, when page is loaded. |
|
33 * - displaysource, when user wants to display source |
|
34 * @param object data (optional) |
|
35 * Additional data to send to the parent page. |
|
36 */ |
|
37 function notifyParent(status, data={}) { |
|
38 if (!gInstanceUID) { |
|
39 gInstanceUID = getParam("uid"); |
|
40 } |
|
41 |
|
42 window.parent.postMessage({ |
|
43 uid: gInstanceUID, |
|
44 status: status, |
|
45 data: data |
|
46 }, "*"); |
|
47 } |
|
48 |
|
49 /** |
|
50 * A listener for incoming messages from the parent |
|
51 * page. All incoming messages must be stringified |
|
52 * JSON objects to be compatible with Cleopatra's |
|
53 * format: |
|
54 * |
|
55 * { |
|
56 * task: string, |
|
57 * ... |
|
58 * } |
|
59 * |
|
60 * This listener recognizes two tasks: onStarted and |
|
61 * onStopped. |
|
62 * |
|
63 * @param object event |
|
64 * PostMessage event object. |
|
65 */ |
|
66 function onParentMessage(event) { |
|
67 var start = document.getElementById("startWrapper"); |
|
68 var stop = document.getElementById("stopWrapper"); |
|
69 var profilerMessage = document.getElementById("profilerMessage"); |
|
70 var msg = JSON.parse(event.data); |
|
71 |
|
72 if (msg.task !== "receiveProfileData" && !msg.isCurrent) { |
|
73 return; |
|
74 } |
|
75 |
|
76 switch (msg.task) { |
|
77 case "onStarted": |
|
78 start.style.display = "none"; |
|
79 start.querySelector("button").removeAttribute("disabled"); |
|
80 stop.style.display = "inline"; |
|
81 break; |
|
82 case "onStopped": |
|
83 stop.style.display = "none"; |
|
84 stop.querySelector("button").removeAttribute("disabled"); |
|
85 start.style.display = "inline"; |
|
86 break; |
|
87 case "receiveProfileData": |
|
88 loadProfile(JSON.stringify(msg.rawProfile)); |
|
89 } |
|
90 } |
|
91 |
|
92 window.addEventListener("message", onParentMessage); |
|
93 |
|
94 /** |
|
95 * Main entry point. This function initializes Cleopatra |
|
96 * in the light mode and creates all the UI we need. |
|
97 */ |
|
98 function initUI() { |
|
99 gHideSourceLinks = getParam("ext") === "true"; |
|
100 gFileList = { profileParsingFinished: function () {} }; |
|
101 gInfoBar = { display: function () {} }; |
|
102 |
|
103 var container = document.createElement("div"); |
|
104 container.id = "ui"; |
|
105 |
|
106 gMainArea = document.createElement("div"); |
|
107 gMainArea.id = "mainarea"; |
|
108 |
|
109 container.appendChild(gMainArea); |
|
110 document.body.appendChild(container); |
|
111 } |
|
112 |
|
113 /** |
|
114 * Modified copy of Cleopatra's enterFinishedProfileUI. |
|
115 * By overriding the function we don't need to modify ui.js which helps |
|
116 * with updating from upstream. |
|
117 */ |
|
118 function enterFinishedProfileUI() { |
|
119 var cover = document.createElement("div"); |
|
120 cover.className = "finishedProfilePaneBackgroundCover"; |
|
121 |
|
122 var pane = document.createElement("table"); |
|
123 var rowIndex = 0; |
|
124 var currRow; |
|
125 |
|
126 pane.style.width = "100%"; |
|
127 pane.style.height = "100%"; |
|
128 pane.border = "0"; |
|
129 pane.cellPadding = "0"; |
|
130 pane.cellSpacing = "0"; |
|
131 pane.borderCollapse = "collapse"; |
|
132 pane.className = "finishedProfilePane"; |
|
133 |
|
134 gBreadcrumbTrail = new BreadcrumbTrail(); |
|
135 currRow = pane.insertRow(rowIndex++); |
|
136 currRow.insertCell(0).appendChild(gBreadcrumbTrail.getContainer()); |
|
137 |
|
138 gHistogramView = new HistogramView(); |
|
139 currRow = pane.insertRow(rowIndex++); |
|
140 currRow.insertCell(0).appendChild(gHistogramView.getContainer()); |
|
141 |
|
142 if (gMeta && gMeta.videoCapture) { |
|
143 gVideoPane = new VideoPane(gMeta.videoCapture); |
|
144 gVideoPane.onTimeChange(videoPaneTimeChange); |
|
145 currRow = pane.insertRow(rowIndex++); |
|
146 currRow.insertCell(0).appendChild(gVideoPane.getContainer()); |
|
147 } |
|
148 |
|
149 var tree = document.createElement("div"); |
|
150 tree.className = "treeContainer"; |
|
151 tree.style.width = "100%"; |
|
152 tree.style.height = "100%"; |
|
153 |
|
154 gTreeManager = new ProfileTreeManager(); |
|
155 gTreeManager.treeView.setColumns([ |
|
156 { name: "sampleCount", title: gStrings["Running Time"] }, |
|
157 { name: "selfSampleCount", title: gStrings["Self"] }, |
|
158 { name: "resource", title: "" } |
|
159 ]); |
|
160 |
|
161 currRow = pane.insertRow(rowIndex++); |
|
162 currRow.style.height = "100%"; |
|
163 |
|
164 var cell = currRow.insertCell(0); |
|
165 cell.appendChild(tree); |
|
166 tree.appendChild(gTreeManager.getContainer()); |
|
167 |
|
168 gPluginView = new PluginView(); |
|
169 tree.appendChild(gPluginView.getContainer()); |
|
170 |
|
171 gMainArea.appendChild(cover); |
|
172 gMainArea.appendChild(pane); |
|
173 |
|
174 var currentBreadcrumb = gSampleFilters; |
|
175 gBreadcrumbTrail.add({ |
|
176 title: gStrings["Complete Profile"], |
|
177 enterCallback: function () { |
|
178 gSampleFilters = []; |
|
179 filtersChanged(); |
|
180 } |
|
181 }); |
|
182 |
|
183 if (currentBreadcrumb == null || currentBreadcrumb.length == 0) { |
|
184 gTreeManager.restoreSerializedSelectionSnapshot(gRestoreSelection); |
|
185 viewOptionsChanged(); |
|
186 } |
|
187 |
|
188 for (var i = 0; i < currentBreadcrumb.length; i++) { |
|
189 var filter = currentBreadcrumb[i]; |
|
190 var forceSelection = null; |
|
191 if (gRestoreSelection != null && i == currentBreadcrumb.length - 1) { |
|
192 forceSelection = gRestoreSelection; |
|
193 } |
|
194 switch (filter.type) { |
|
195 case "FocusedFrameSampleFilter": |
|
196 focusOnSymbol(filter.name, filter.symbolName); |
|
197 gBreadcrumbTrail.enterLastItem(forceSelection); |
|
198 case "FocusedCallstackPrefixSampleFilter": |
|
199 focusOnCallstack(filter.focusedCallstack, filter.name, false); |
|
200 gBreadcrumbTrail.enterLastItem(forceSelection); |
|
201 case "FocusedCallstackPostfixSampleFilter": |
|
202 focusOnCallstack(filter.focusedCallstack, filter.name, true); |
|
203 gBreadcrumbTrail.enterLastItem(forceSelection); |
|
204 case "RangeSampleFilter": |
|
205 gHistogramView.selectRange(filter.start, filter.end); |
|
206 gBreadcrumbTrail.enterLastItem(forceSelection); |
|
207 } |
|
208 } |
|
209 |
|
210 // Show platform data? |
|
211 if (getParam("spd") !== "true") |
|
212 toggleJavascriptOnly(); |
|
213 } |
|
214 |
|
215 function enterProgressUI() { |
|
216 var pane = document.createElement("div"); |
|
217 var label = document.createElement("a"); |
|
218 var bar = document.createElement("progress"); |
|
219 var string = gStrings.getStr("profiler.loading"); |
|
220 |
|
221 pane.className = "profileProgressPane"; |
|
222 pane.appendChild(label); |
|
223 pane.appendChild(bar); |
|
224 |
|
225 var reporter = new ProgressReporter(); |
|
226 reporter.addListener(function (rep) { |
|
227 var progress = rep.getProgress(); |
|
228 |
|
229 if (label.textContent !== string) { |
|
230 label.textContent = string; |
|
231 } |
|
232 |
|
233 if (isNaN(progress)) { |
|
234 bar.removeAttribute("value"); |
|
235 } else { |
|
236 bar.value = progress; |
|
237 } |
|
238 }); |
|
239 |
|
240 gMainArea.appendChild(pane); |
|
241 Parser.updateLogSetting(); |
|
242 |
|
243 return reporter; |
|
244 } |