browser/devtools/profiler/cleopatra.js

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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/. */
     5 "use strict";
     7 let { Cu }       = require("chrome");
     8 let { defer }    = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise;
     9 let EventEmitter = require("devtools/toolkit/event-emitter");
    11 const { PROFILE_IDLE, PROFILE_COMPLETED, PROFILE_RUNNING } = require("devtools/profiler/consts");
    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;
    34   EventEmitter.decorate(this);
    36   this.isReady = false;
    37   this.isStarted = false;
    38   this.isFinished = false;
    40   this.panel = panel;
    41   this.uid = uid;
    42   this.name = name;
    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");
    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.
    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     }
    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 }
    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     }
    81     try {
    82       return this.iframe.contentWindow;
    83     } catch (err) {
    84       return null;
    85     }
    86   },
    88   show: function () {
    89     this.iframe.removeAttribute("hidden");
    90   },
    92   hide: function () {
    93     this.iframe.setAttribute("hidden", true);
    94   },
    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     }
   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;
   116         if (!trail) {
   117           return wait();
   118         }
   120         if (!trail._breadcrumbs || !trail._breadcrumbs.length) {
   121           return wait();
   122         }
   124         onParsed();
   125       };
   127       poll();
   128     });
   129   },
   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);
   142     let send = () => {
   143       if (!this.contentWindow)
   144         setTimeout(send, 50);
   146       this.contentWindow.postMessage(data, "*");
   147       deferred.resolve();
   148     };
   150     send();
   151     return deferred.promise;
   152   },
   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 };
   166 module.exports = Cleopatra;

mercurial