browser/devtools/profiler/cleopatra.js

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:c7ca8d4bdc81
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 let { Cu } = require("chrome");
8 let { defer } = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise;
9 let EventEmitter = require("devtools/toolkit/event-emitter");
10
11 const { PROFILE_IDLE, PROFILE_COMPLETED, PROFILE_RUNNING } = require("devtools/profiler/consts");
12
13 /**
14 * An implementation of a profile visualization that uses Cleopatra.
15 * It consists of an iframe with Cleopatra loaded in it and some
16 * surrounding meta-data (such as UIDs).
17 *
18 * Cleopatra is also an event emitter. It emits the following events:
19 * - ready, when Cleopatra is done loading (you can also check the isReady
20 * property to see if a particular instance has been loaded yet.
21 *
22 * @param number uid
23 * Unique ID for this profile.
24 * @param ProfilerPanel panel
25 * A reference to the container panel.
26 */
27 function Cleopatra(panel, opts) {
28 let doc = panel.document;
29 let win = panel.window;
30 let { uid, name } = opts;
31 let spd = opts.showPlatformData;
32 let ext = opts.external;
33
34 EventEmitter.decorate(this);
35
36 this.isReady = false;
37 this.isStarted = false;
38 this.isFinished = false;
39
40 this.panel = panel;
41 this.uid = uid;
42 this.name = name;
43
44 this.iframe = doc.createElement("iframe");
45 this.iframe.setAttribute("flex", "1");
46 this.iframe.setAttribute("id", "profiler-cleo-" + uid);
47 this.iframe.setAttribute("src", "cleopatra.html?uid=" + uid + "&spd=" + spd + "&ext=" + ext);
48 this.iframe.setAttribute("hidden", "true");
49
50 // Append our iframe and subscribe to postMessage events.
51 // They'll tell us when the underlying page is done loading
52 // or when user clicks on start/stop buttons.
53
54 doc.getElementById("profiler-report").appendChild(this.iframe);
55 win.addEventListener("message", function (event) {
56 if (parseInt(event.data.uid, 10) !== parseInt(this.uid, 10)) {
57 return;
58 }
59
60 switch (event.data.status) {
61 case "loaded":
62 this.isReady = true;
63 this.emit("ready");
64 break;
65 case "displaysource":
66 this.panel.displaySource(event.data.data);
67 }
68 }.bind(this));
69 }
70
71 Cleopatra.prototype = {
72 /**
73 * Returns a contentWindow of the iframe pointing to Cleopatra
74 * if it exists and can be accessed. Otherwise returns null.
75 */
76 get contentWindow() {
77 if (!this.iframe) {
78 return null;
79 }
80
81 try {
82 return this.iframe.contentWindow;
83 } catch (err) {
84 return null;
85 }
86 },
87
88 show: function () {
89 this.iframe.removeAttribute("hidden");
90 },
91
92 hide: function () {
93 this.iframe.setAttribute("hidden", true);
94 },
95
96 /**
97 * Send raw profiling data to Cleopatra for parsing.
98 *
99 * @param object data
100 * Raw profiling data from the SPS Profiler.
101 * @param function onParsed
102 * A callback to be called when Cleopatra finishes
103 * parsing and displaying results.
104 *
105 */
106 parse: function (data, onParsed) {
107 if (!this.isReady) {
108 return void this.on("ready", this.parse.bind(this, data, onParsed));
109 }
110
111 this.message({ task: "receiveProfileData", rawProfile: data }).then(() => {
112 let poll = () => {
113 let wait = this.panel.window.setTimeout.bind(null, poll, 100);
114 let trail = this.contentWindow.gBreadcrumbTrail;
115
116 if (!trail) {
117 return wait();
118 }
119
120 if (!trail._breadcrumbs || !trail._breadcrumbs.length) {
121 return wait();
122 }
123
124 onParsed();
125 };
126
127 poll();
128 });
129 },
130
131 /**
132 * Send a message to Cleopatra instance. If a message cannot be
133 * sent, this method queues it for later.
134 *
135 * @param object data JSON data to send (must be serializable)
136 * @return promise
137 */
138 message: function (data) {
139 let deferred = defer();
140 data = JSON.stringify(data);
141
142 let send = () => {
143 if (!this.contentWindow)
144 setTimeout(send, 50);
145
146 this.contentWindow.postMessage(data, "*");
147 deferred.resolve();
148 };
149
150 send();
151 return deferred.promise;
152 },
153
154 /**
155 * Destroys the ProfileUI instance.
156 */
157 destroy: function () {
158 this.isReady = null;
159 this.panel = null;
160 this.uid = null;
161 this.iframe = null;
162 this.messages = null;
163 }
164 };
165
166 module.exports = Cleopatra;

mercurial