Merge m-c to inbound.
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 17 Jan 2013 21:21:24 -0500
changeset 119226 75d9c44ed26750c62aec5d68656077458d60bac1
parent 119225 6c9d47ecf49c21815748d636b804d9bdbc7e9ddf (current diff)
parent 119127 b52c02f77cf5b76027fea25412e629cd3d551cb3 (diff)
child 119227 93794651309a89b0bb8ec6f7a9cfb72c970c4b14
push id24195
push userMs2ger@gmail.com
push dateSat, 19 Jan 2013 16:10:11 +0000
treeherdermozilla-central@02e12a80aef9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone21.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to inbound.
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -734,17 +734,18 @@ var AlertsHelper = {
     function send(appName, appIcon) {
       shell.sendChromeEvent({
         type: "desktop-notification",
         id: uid,
         icon: imageUrl,
         title: title,
         text: text,
         appName: appName,
-        appIcon: appIcon
+        appIcon: appIcon,
+        manifestURL: manifestUrl
       });
     }
 
     if (!manifestUrl || !manifestUrl.length) {
       send(null, null);
     }
 
     // If we have a manifest URL, get the icon and title from the manifest
--- a/browser/components/migration/src/IEProfileMigrator.js
+++ b/browser/components/migration/src/IEProfileMigrator.js
@@ -248,17 +248,17 @@ Bookmarks.prototype = {
         if (entry.isReadable()) {
           // Recursively import the folder.
           this._migrateFolder(entry, destFolderId);
         }
       }
       else {
         // Strip the .url extension, to both check this is a valid link file,
         // and get the associated title.
-        let matches = entry.leafName.match(/(.+)\.url$/);
+        let matches = entry.leafName.match(/(.+)\.url$/i);
         if (matches) {
           let fileHandler = Cc["@mozilla.org/network/protocol;1?name=file"].
                             getService(Ci.nsIFileProtocolHandler);
           let uri = fileHandler.readURLFile(entry);
           let title = matches[1];
 
           PlacesUtils.bookmarks.insertBookmark(aDestFolderId,
                                                uri,
--- a/browser/components/thumbnails/test/browser_thumbnails_bug726727.js
+++ b/browser/components/thumbnails/test/browser_thumbnails_bug726727.js
@@ -2,17 +2,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * These tests ensure that capturing a sites's thumbnail, saving it and
  * retrieving it from the cache works.
  */
 function runTests() {
   // Create a tab that shows an error page.
-  let tab = gBrowser.addTab("http://non-existant.url/");
+  let tab = gBrowser.addTab("http://127.0.0.1:1/");
   let browser = tab.linkedBrowser;
 
   yield browser.addEventListener("DOMContentLoaded", function onLoad() {
     browser.removeEventListener("DOMContentLoaded", onLoad, false);
     executeSoon(next);
   }, false);
 
   ok(!gBrowserThumbnails._shouldCapture(browser), "we're not going to capture an error page");
--- a/browser/devtools/profiler/ProfilerPanel.jsm
+++ b/browser/devtools/profiler/ProfilerPanel.jsm
@@ -272,17 +272,17 @@ ProfilerPanel.prototype = {
    *
    * @param ProfileUI profile
    *   A profile instance to switch to.
    * @param function onLoad
    *   A function to call when profile instance is ready.
    *   If the instance is already loaded, onLoad will be
    *   called synchronously.
    */
-  switchToProfile: function PP_switchToProfile(profile, onLoad) {
+  switchToProfile: function PP_switchToProfile(profile, onLoad=function() {}) {
     let doc = this.document;
 
     if (this.activeProfile) {
       this.activeProfile.hide();
     }
 
     let active = doc.querySelector("#profiles-list > li.splitview-active");
     if (active) {
--- a/browser/devtools/profiler/cleopatra/js/devtools.js
+++ b/browser/devtools/profiler/cleopatra/js/devtools.js
@@ -42,36 +42,43 @@ function notifyParent(status) {
  * @param object event
  *   PostMessage event object.
  */
 function onParentMessage(event) {
   var start = document.getElementById("startWrapper");
   var stop = document.getElementById("stopWrapper");
   var msg = JSON.parse(event.data);
 
-  if (msg.task === "onStarted") {
-    start.style.display = "none";
-    start.querySelector("button").removeAttribute("disabled");
-    stop.style.display = "inline";
-  } else if (msg.task === "onStopped") {
-    stop.style.display = "none";
-    stop.querySelector("button").removeAttribute("disabled");
-    start.style.display = "inline";
+  switch (msg.task) {
+    case "onStarted":
+      start.style.display = "none";
+      start.querySelector("button").removeAttribute("disabled");
+      stop.style.display = "inline";
+      break;
+    case "onStopped":
+      stop.style.display = "none";
+      stop.querySelector("button").removeAttribute("disabled");
+      start.style.display = "inline";
+      break;
+    case "receiveProfileData":
+      loadProfile(JSON.stringify(msg.rawProfile));
   }
 }
 
 window.addEventListener("message", onParentMessage);
 
 /**
  * Main entry point. This function initializes Cleopatra
  * in the light mode and creates all the UI we need.
  */
 function initUI() {
   gLightMode = true;
-  gJavaScriptOnly = true;
+
+  gFileList = { profileParsingFinished: function () {} };
+  gInfoBar = { display: function () {} };
 
   var container = document.createElement("div");
   container.id = "ui";
 
   gMainArea = document.createElement("div");
   gMainArea.id = "mainarea";
 
   container.appendChild(gMainArea);
@@ -96,9 +103,109 @@ function initUI() {
   controlPane.innerHTML =
     "<p id='startWrapper'>Click <span class='btn'></span> to start profiling.</p>" +
     "<p id='stopWrapper'>Click <span class='btn'></span> to stop profiling.</p>";
 
   controlPane.querySelector("#startWrapper > span.btn").appendChild(startButton);
   controlPane.querySelector("#stopWrapper > span.btn").appendChild(stopButton);
 
   gMainArea.appendChild(controlPane);
+}
+
+/**
+ * Modified copy of Cleopatra's enterFinishedProfileUI.
+ * By overriding the function we don't need to modify ui.js which helps
+ * with updating from upstream.
+ */
+function enterFinishedProfileUI() {
+  var cover = document.createElement("div");
+  cover.className = "finishedProfilePaneBackgroundCover";
+
+  var pane = document.createElement("table");
+  var rowIndex = 0;
+  var currRow;
+
+  pane.style.width = "100%";
+  pane.style.height = "100%";
+  pane.border = "0";
+  pane.cellPadding = "0";
+  pane.cellSpacing = "0";
+  pane.borderCollapse = "collapse";
+  pane.className = "finishedProfilePane";
+
+  gBreadcrumbTrail = new BreadcrumbTrail();
+  currRow = pane.insertRow(rowIndex++);
+  currRow.insertCell(0).appendChild(gBreadcrumbTrail.getContainer());
+
+  gHistogramView = new HistogramView();
+  currRow = pane.insertRow(rowIndex++);
+  currRow.insertCell(0).appendChild(gHistogramView.getContainer());
+
+  if (gMeta && gMeta.videoCapture) {
+    gVideoPane = new VideoPane(gMeta.videoCapture);
+    gVideoPane.onTimeChange(videoPaneTimeChange);
+    currRow = pane.insertRow(rowIndex++);
+    currRow.insertCell(0).appendChild(gVideoPane.getContainer());
+  }
+
+  var tree = document.createElement("div");
+  tree.className = "treeContainer";
+  tree.style.width = "100%";
+  tree.style.height = "100%";
+
+  gTreeManager = new ProfileTreeManager();
+  gTreeManager.treeView.setColumns([
+    { name: "sampleCount", title: "Running time" },
+    { name: "selfSampleCount", title: "Self" },
+    { name: "resource", title: "" },
+  ]);
+
+  currRow = pane.insertRow(rowIndex++);
+  currRow.style.height = "100%";
+
+  var cell = currRow.insertCell(0);
+  cell.appendChild(tree);
+  tree.appendChild(gTreeManager.getContainer());
+
+  gPluginView = new PluginView();
+  tree.appendChild(gPluginView.getContainer());
+
+  gMainArea.appendChild(cover);
+  gMainArea.appendChild(pane);
+
+  var currentBreadcrumb = gSampleFilters;
+  gBreadcrumbTrail.add({
+    title: "Complete Profile",
+    enterCallback: function () {
+      gSampleFilters = [];
+      filtersChanged();
+    }
+  });
+
+  if (currentBreadcrumb == null || currentBreadcrumb.length == 0) {
+    gTreeManager.restoreSerializedSelectionSnapshot(gRestoreSelection);
+    viewOptionsChanged();
+  }
+
+  for (var i = 0; i < currentBreadcrumb.length; i++) {
+    var filter = currentBreadcrumb[i];
+    var forceSelection = null;
+    if (gRestoreSelection != null && i == currentBreadcrumb.length - 1) {
+      forceSelection = gRestoreSelection;
+    }
+    switch (filter.type) {
+      case "FocusedFrameSampleFilter":
+        focusOnSymbol(filter.name, filter.symbolName);
+        gBreadcrumbTrail.enterLastItem(forceSelection);
+      case "FocusedCallstackPrefixSampleFilter":
+        focusOnCallstack(filter.focusedCallstack, filter.name, false);
+        gBreadcrumbTrail.enterLastItem(forceSelection);
+      case "FocusedCallstackPostfixSampleFilter":
+        focusOnCallstack(filter.focusedCallstack, filter.name, true);
+        gBreadcrumbTrail.enterLastItem(forceSelection);
+      case "RangeSampleFilter":
+        gHistogramView.selectRange(filter.start, filter.end);
+        gBreadcrumbTrail.enterLastItem(forceSelection);
+    }
+  }
+
+  toggleJavascriptOnly();
 }
\ No newline at end of file
--- a/browser/devtools/profiler/cleopatra/js/parserWorker.js
+++ b/browser/devtools/profiler/cleopatra/js/parserWorker.js
@@ -234,27 +234,36 @@ function parseRawProfile(requestID, para
   progressReporter.addListener(function (r) {
     sendProgress(requestID, r.getProgress());
   });
   progressReporter.begin("Parsing...");
 
   var symbolicationTable = {};
   var symbols = [];
   var symbolIndices = {};
+  var resources = {};
   var functions = [];
   var functionIndices = {};
   var samples = [];
   var meta = {};
   var armIncludePCIndex = {};
 
+  if (rawProfile == null) {
+    throw "rawProfile is null";
+  }
+
   if (typeof rawProfile == "string" && rawProfile[0] == "{") {
     // rawProfile is a JSON string.
     rawProfile = JSON.parse(rawProfile);
+    if (rawProfile === null) {
+      throw "rawProfile couldn't not successfully be parsed using JSON.parse. Make sure that the profile is a valid JSON encoding.";
+    }
   }
 
+
   if (rawProfile.profileJSON && !rawProfile.profileJSON.meta && rawProfile.meta) {
     rawProfile.profileJSON.meta = rawProfile.meta;
   }
 
   if (typeof rawProfile == "object") {
     switch (rawProfile.format) {
       case "profileStringWithSymbolicationTable,1":
         symbolicationTable = rawProfile.symbolicationTable;
@@ -266,171 +275,240 @@ function parseRawProfile(requestID, para
         break;
       default:
         parseProfileJSON(rawProfile);
     }
   } else {
     parseProfileString(rawProfile);
   }
 
+  if (params.profileId) {
+    meta.profileId = params.profileId;
+  }
+
   function cleanFunctionName(functionName) {
     var ignoredPrefix = "non-virtual thunk to ";
-    if (functionName.substr(0, ignoredPrefix.length) == ignoredPrefix)
+    if (functionName.startsWith(ignoredPrefix))
       return functionName.substr(ignoredPrefix.length);
     return functionName;
   }
 
-  function resourceNameForAddon(addonID) {
-    for (var i in meta.addons) {
-      var addon = meta.addons[i];
-      if (addon.id.toLowerCase() == addonID.toLowerCase()) {
-        var iconHTML = "";
-        if (addon.iconURL)
-          iconHTML = "<img src=\"" + addon.iconURL + "\" style='width:12px; height:12px;'> "
-        return iconHTML + " " + (/@jetpack$/.exec(addonID) ? "Jetpack: " : "") + addon.name;
-      }
-    }
-    return "";
+  function resourceNameForAddon(addon) {
+    if (!addon)
+      return "";
+
+    var iconHTML = "";
+    if (addon.iconURL)
+      iconHTML = "<img src=\"" + addon.iconURL + "\" style='width:12px; height:12px;'> "
+    return iconHTML + " " + (/@jetpack$/.exec(addon.id) ? "Jetpack: " : "") + addon.name;
+  }
+
+  function addonWithID(addonID) {
+    return firstMatch(meta.addons, function addonHasID(addon) {
+      return addon.id.toLowerCase() == addonID.toLowerCase();
+    })
+  }
+
+  function resourceNameForAddonWithID(addonID) {
+    return resourceNameForAddon(addonWithID(addonID));
   }
 
-  function parseResourceName(url) {
-    if (!url) {
-      return "No URL";
+  function findAddonForChromeURIHost(host) {
+    return firstMatch(meta.addons, function addonUsesChromeURIHost(addon) {
+      return addon.chromeURIHosts && addon.chromeURIHosts.indexOf(host) != -1;
+    });
+  }
+
+  function ensureResource(name, resourceDescription) {
+    if (!(name in resources)) {
+      resources[name] = resourceDescription;
     }
-    if (url.startsWith("resource:///")) {
-      // Take the last URL from a chained list of URLs.
-      var urls = url.split(" -> ");
-      url = urls[urls.length - 1];
+    return name;
+  }
+
+  function resourceNameFromLibrary(library) {
+    return ensureResource("lib_" + library, {
+      type: "library",
+      name: library
+    });
+  }
+
+  function getAddonForScriptURI(url, host) {
+    if (!meta || !meta.addons)
+      return null;
+
+    if (url.startsWith("resource:") && endsWith(host, "-at-jetpack")) {
+      // Assume this is a jetpack url
+      var jetpackID = host.substring(0, host.length - 11) + "@jetpack";
+      return addonWithID(jetpackID);
     }
 
-    // TODO Fix me, this certainly doesn't handle all URLs formats
-    var match = /^.*:\/\/(.*?)\/.*$/.exec(url);
-
-    if (!match)
-      return url;
-
-    var host = match[1];
+    if (url.startsWith("file:///") && url.indexOf("/extensions/") != -1) {
+      var unpackedAddonNameMatch = /\/extensions\/(.*?)\//.exec(url);
+      if (unpackedAddonNameMatch)
+        return addonWithID(decodeURIComponent(unpackedAddonNameMatch[1]));
+      return null;
+    }
 
-    if (meta && meta.addons) {
-      if (url.startsWith("resource:") && endsWith(host, "-at-jetpack")) {
-        // Assume this is a jetpack url
-        var jetpackID = host.substring(0, host.length - 11) + "@jetpack";
-        var resName = resourceNameForAddon(jetpackID);
-        if (resName)
-          return resName;
-      }
-      if (url.startsWith("file:///") && url.indexOf("/extensions/") != -1) {
-        var unpackedAddonNameMatch = /\/extensions\/(.*?)\//.exec(url);
-        if (unpackedAddonNameMatch) {
-          var resName = resourceNameForAddon(decodeURIComponent(unpackedAddonNameMatch[1]));
-          if (resName)
-            return resName;
-        }
-      }
-      if (url.startsWith("jar:file:///") && url.indexOf("/extensions/") != -1) {
-        var packedAddonNameMatch = /\/extensions\/(.*?).xpi/.exec(url);
-        if (packedAddonNameMatch) {
-          var resName = resourceNameForAddon(decodeURIComponent(packedAddonNameMatch[1]));
-          if (resName)
-            return resName;
-        }
-      }
+    if (url.startsWith("jar:file:///") && url.indexOf("/extensions/") != -1) {
+      var packedAddonNameMatch = /\/extensions\/(.*?).xpi/.exec(url);
+      if (packedAddonNameMatch)
+        return addonWithID(decodeURIComponent(packedAddonNameMatch[1]));
+      return null;
+    }
+
+    if (url.startsWith("chrome://")) {
+      var chromeURIMatch = /chrome\:\/\/(.*?)\//.exec(url);
+      if (chromeURIMatch)
+        return findAddonForChromeURIHost(chromeURIMatch[1]);
+      return null;
     }
 
-    var iconHTML = "";
-    if (url.indexOf("http://") == 0) {
-      iconHTML = "<img src=\"http://" + host + "/favicon.ico\" style='width:12px; height:12px;'> ";
-    } else if (url.indexOf("https://") == 0) {
-      iconHTML = "<img src=\"https://" + host + "/favicon.ico\" style='width:12px; height:12px;'> ";
+    return null;
+  }
+
+  function resourceNameFromURI(url) {
+    if (!url)
+      return ensureResource("unknown", {type: "unknown", name: "<unknown>"});
+
+    var match = /^(.*):\/\/(.*?)\//.exec(url);
+
+    if (!match) {
+      // Can this happen? If so, we should change the regular expression above.
+      return ensureResource("url_" + url, {type: "url", name: url});
     }
-    return iconHTML + host;
+
+    var urlRoot = match[0];
+    var protocol = match[1];
+    var host = match[2];
+
+    var addon = getAddonForScriptURI(url, host);
+    if (addon) {
+      return ensureResource("addon_" + addon.id, {
+        type: "addon",
+        name: addon.name,
+        addonID: addon.id,
+        icon: addon.iconURL
+      });
+    }
+
+    if (protocol.startsWith("http")) {
+      return ensureResource("webhost_" + host, {
+        type: "webhost",
+        name: host,
+        icon: urlRoot + "favicon.ico"
+      });
+    }
+
+    return ensureResource("otherhost_" + host, {
+      type: "otherhost",
+      name: host
+    });
   }
 
   function parseScriptFile(url) {
-     // TODO Fix me, this certainly doesn't handle all URLs formats
-     var match = /^.*\/(.*)\.js$/.exec(url);
+     var match = /([^\/]*)$/.exec(url);
+     if (match && match[1])
+       return match[1];
 
-     if (!match)
-       return url;
-
-     return match[1] + ".js";
+     return url;
   }
 
-  function parseScriptURI(url) {
+  // JS File information sometimes comes with multiple URIs which are chained
+  // with " -> ". We only want the last URI in this list.
+  function getRealScriptURI(url) {
     if (url) {
-      var urlTokens = url.split(" ");
-      url = urlTokens[urlTokens.length-1];
+      var urls = url.split(" -> ");
+      return urls[urls.length - 1];
     }
     return url;
   }
 
   function getFunctionInfo(fullName) {
-    var isJSFrame = false;
-    var match =
-      /^(.*) \(in ([^\)]*)\) (\+ [0-9]+)$/.exec(fullName) ||
-      /^(.*) \(in ([^\)]*)\) (\(.*:.*\))$/.exec(fullName) ||
-      /^(.*) \(in ([^\)]*)\)$/.exec(fullName);
-      // Try to parse a JS frame
-    var scriptLocation = null;
-    var jsMatch1 = match ||
-      /^(.*) \((.*):([0-9]+)\)$/.exec(fullName);
-    if (!match && jsMatch1) {
-      scriptLocation = {
-        scriptURI: parseScriptURI(jsMatch1[2]),
-        lineInformation: jsMatch1[3]
+
+    function getCPPFunctionInfo(fullName) {
+      var match =
+        /^(.*) \(in ([^\)]*)\) (\+ [0-9]+)$/.exec(fullName) ||
+        /^(.*) \(in ([^\)]*)\) (\(.*:.*\))$/.exec(fullName) ||
+        /^(.*) \(in ([^\)]*)\)$/.exec(fullName);
+
+      if (!match)
+        return null;
+
+      return {
+        functionName: cleanFunctionName(match[1]),
+        libraryName: resourceNameFromLibrary(match[2]),
+        lineInformation: match[3] || "",
+        isRoot: false,
+        isJSFrame: false
       };
-      match = [0, jsMatch1[1]+"() @ "+parseScriptFile(jsMatch1[2]) + ":" + jsMatch1[3], parseResourceName(jsMatch1[2]), ""];
-      isJSFrame = true;
     }
-    var jsMatch2 = match ||
-      /^(.*):([0-9]+)$/.exec(fullName);
-    if (!match && jsMatch2) {
-      scriptLocation = {
-        scriptURI: parseScriptURI(jsMatch2[1]),
-        lineInformation: jsMatch2[2]
+
+    function getJSFunctionInfo(fullName) {
+      var jsMatch =
+        /^(.*) \((.*):([0-9]+)\)$/.exec(fullName) ||
+        /^()(.*):([0-9]+)$/.exec(fullName);
+
+      if (!jsMatch)
+        return null;
+
+      var functionName = jsMatch[1] || "<Anonymous>";
+      var scriptURI = getRealScriptURI(jsMatch[2]);
+      var lineNumber = jsMatch[3];
+      var scriptFile = parseScriptFile(scriptURI);
+      var resourceName = resourceNameFromURI(scriptURI);
+
+      return {
+        functionName: functionName + "() @ " + scriptFile + ":" + lineNumber,
+        libraryName: resourceName,
+        lineInformation: "",
+        isRoot: false,
+        isJSFrame: true,
+        scriptLocation: {
+          scriptURI: scriptURI,
+          lineInformation: lineNumber
+        }
       };
-      match = [0, "<Anonymous> @ "+parseScriptFile(jsMatch2[1]) + ":" + jsMatch2[2], parseResourceName(jsMatch2[1]), ""];
-      isJSFrame = true;
     }
-    if (!match) {
-      match = [fullName, fullName];
+
+    function getFallbackFunctionInfo(fullName) {
+      return {
+        functionName: cleanFunctionName(fullName),
+        libraryName: "",
+        lineInformation: "",
+        isRoot: fullName == "(root)",
+        isJSFrame: false
+      };
     }
-    return {
-      functionName: cleanFunctionName(match[1]),
-      libraryName: match[2] || "",
-      lineInformation: match[3] || "",
-      isJSFrame: isJSFrame,
-      scriptLocation: scriptLocation
-    };
+
+    return getCPPFunctionInfo(fullName) ||
+           getJSFunctionInfo(fullName) ||
+           getFallbackFunctionInfo(fullName);
   }
 
-  function indexForFunction(symbol, functionName, libraryName, isJSFrame, scriptLocation) {
-    var resolve = functionName+"_LIBNAME_"+libraryName;
+  function indexForFunction(symbol, info) {
+    var resolve = info.functionName + "__" + info.libraryName;
     if (resolve in functionIndices)
       return functionIndices[resolve];
     var newIndex = functions.length;
-    functions[newIndex] = {
-      symbol: symbol,
-      functionName: functionName,
-      libraryName: libraryName,
-      isJSFrame: isJSFrame,
-      scriptLocation: scriptLocation
-    };
+    info.symbol = symbol;
+    functions[newIndex] = info;
     functionIndices[resolve] = newIndex;
     return newIndex;
   }
 
   function parseSymbol(symbol) {
     var info = getFunctionInfo(symbol);
     //dump("Parse symbol: " + symbol + "\n");
     return {
       symbolName: symbol,
       functionName: info.functionName,
-      functionIndex: indexForFunction(symbol, info.functionName, info.libraryName, info.isJSFrame, info.scriptLocation),
+      functionIndex: indexForFunction(symbol, info),
       lineInformation: info.lineInformation,
+      isRoot: info.isRoot,
       isJSFrame: info.isJSFrame,
       scriptLocation: info.scriptLocation
     };
   }
 
   function translatedSymbol(symbol) {
     return symbolicationTable[symbol] || symbol;
   }
@@ -577,16 +655,19 @@ function parseRawProfile(requestID, para
         rootSymbol = rootSymbol || indicedFrames[0];
       }
       if (sample.extraInfo == null) {
         sample.extraInfo = {};
       }
       if (sample.responsiveness) {
         sample.extraInfo["responsiveness"] = sample.responsiveness;
       }
+      if (sample.marker) {
+        sample.extraInfo["marker"] = sample.marker;
+      }
       if (sample.time) {
         sample.extraInfo["time"] = sample.time;
       }
       if (sample.frameNumber) {
         sample.extraInfo["frameNumber"] = sample.frameNumber;
         //dump("Got frame number: " + sample.frameNumber + "\n");
         frameStart[sample.frameNumber] = samples.length;
       }
@@ -595,36 +676,42 @@ function parseRawProfile(requestID, para
     }
     if (insertCommonRoot) {
       var rootIndex = indexForSymbol("(root)");
       for (var i = 0; i < samples.length; i++) {
         var sample = samples[i];
         if (!sample) continue;
         // If length == 0 then the sample was filtered when saving the profile
         if (sample.frames.length >= 1 && sample.frames[0] != rootIndex)
-          sample.frames.splice(0, 0, rootIndex)
+          sample.frames.unshift(rootIndex)
       }
     }
   }
 
   progressReporter.finish();
-  var profileID = gNextProfileID++;
+  // Don't increment the profile ID now because (1) it's buggy
+  // and (2) for now there's no point in storing each profile
+  // here if we're storing them in the local storage.
+  //var profileID = gNextProfileID++;
+  var profileID = gNextProfileID;
   gProfiles[profileID] = JSON.parse(JSON.stringify({
     meta: meta,
     symbols: symbols,
     functions: functions,
+    resources: resources,
     allSamples: samples
   }));
   clearRegExpLastMatch();
   sendFinished(requestID, {
     meta: meta,
     numSamples: samples.length,
     profileID: profileID,
     symbols: symbols,
-    functions: functions
+    functions: functions,
+    resources: resources
   });
 }
 
 function getSerializedProfile(requestID, profileID, complete) {
   var profile = gProfiles[profileID];
   var symbolicationTable = {};
   if (complete || !profile.filterSettings.mergeFunctions) {
     for (var symbolIndex in profile.symbols) {
@@ -683,17 +770,16 @@ TreeNode.prototype.incrementCountersInPa
   if (this.parent)
     this.parent.incrementCountersInParentChain();
 };
 
 function convertToCallTree(samples, isReverse) {
   function areSamplesMultiroot(samples) {
     var previousRoot;
     for (var i = 0; i < samples.length; ++i) {
-      if (!samples[i].frames) continue;
       if (!previousRoot) {
         previousRoot = samples[i].frames[0];
         continue;
       }
       if (previousRoot != samples[i].frames[0]) {
         return true;
       }
     }
@@ -701,31 +787,26 @@ function convertToCallTree(samples, isRe
   }
   samples = samples.filter(function noNullSamples(sample) {
     return sample != null;
   });
   if (samples.length == 0)
     return new TreeNode("(empty)", null, 0);
   var firstRoot = null;
   for (var i = 0; i < samples.length; ++i) {
-    if (!samples[i].frames) continue;
-    sendError(null, "got root: " + samples[i].frames[0]);
     firstRoot = samples[i].frames[0];
     break;
   }
   if (firstRoot == null) {
     return new TreeNode("(all filtered)", null, 0);
   }
   var multiRoot = areSamplesMultiroot(samples);
   var treeRoot = new TreeNode((isReverse || multiRoot) ? "(total)" : firstRoot, null, 0);
   for (var i = 0; i < samples.length; ++i) {
     var sample = samples[i];
-    if (!sample.frames) {
-      continue;
-    }
     var callstack = sample.frames.slice(0);
     callstack.shift();
     if (isReverse)
       callstack.reverse();
     var deepestExistingNode = treeRoot.followPath(callstack);
     var remainingCallstack = callstack.slice(deepestExistingNode.getDepth());
     deepestExistingNode.incrementCountersInParentChain();
     var node = deepestExistingNode;
@@ -759,78 +840,82 @@ function filterBySymbol(samples, symbolO
         sample.frames = sample.frames.slice(i);
         return sample;
       }
     }
     return null; // no frame matched; filter out complete sample
   });
 }
 
-function filterByCallstackPrefix(samples, callstack) {
-  return samples.map(function filterSample(origSample) {
-    if (!origSample)
+function filterByCallstackPrefix(samples, symbols, functions, callstack, appliesToJS, useFunctions) {
+  var isJSFrameOrRoot = useFunctions ? function isJSFunctionOrRoot(functionIndex) {
+      return (functionIndex in functions) && (functions[functionIndex].isJSFrame || functions[functionIndex].isRoot);
+    } : function isJSSymbolOrRoot(symbolIndex) {
+      return (symbolIndex in symbols) && (symbols[symbolIndex].isJSFrame || symbols[symbolIndex].isRoot);
+    };
+  return samples.map(function filterSample(sample) {
+    if (!sample)
       return null;
-    if (origSample.frames.length < callstack.length)
+    if (sample.frames.length < callstack.length)
       return null;
-    var sample = cloneSample(origSample);
-    for (var i = 0; i < callstack.length; i++) {
-      if (sample.frames[i] != callstack[i])
+    for (var i = 0, j = 0; j < callstack.length; i++) {
+      if (i >= sample.frames.length)
         return null;
+      if (appliesToJS && !isJSFrameOrRoot(sample.frames[i]))
+        continue;
+      if (sample.frames[i] != callstack[j])
+        return null;
+      j++;
     }
-    sample.frames = sample.frames.slice(callstack.length - 1);
-    return sample;
+    return makeSample(sample.frames.slice(i - 1), sample.extraInfo);
   });
 }
 
-function filterByCallstackPostfix(samples, callstack) {
-  return samples.map(function filterSample(origSample) {
-    if (!origSample)
+function filterByCallstackPostfix(samples, symbols, functions, callstack, appliesToJS, useFunctions) {
+  var isJSFrameOrRoot = useFunctions ? function isJSFunctionOrRoot(functionIndex) {
+      return (functionIndex in functions) && (functions[functionIndex].isJSFrame || functions[functionIndex].isRoot);
+    } : function isJSSymbolOrRoot(symbolIndex) {
+      return (symbolIndex in symbols) && (symbols[symbolIndex].isJSFrame || symbols[symbolIndex].isRoot);
+    };
+  return samples.map(function filterSample(sample) {
+    if (!sample)
       return null;
-    if (origSample.frames.length < callstack.length)
+    if (sample.frames.length < callstack.length)
       return null;
-    var sample = cloneSample(origSample);
-    for (var i = 0; i < callstack.length; i++) {
-      if (sample.frames[sample.frames.length - i - 1] != callstack[i])
+    for (var i = 0, j = 0; j < callstack.length; i++) {
+      if (i >= sample.frames.length)
         return null;
+      if (appliesToJS && !isJSFrameOrRoot(sample.frames[sample.frames.length - i - 1]))
+        continue;
+      if (sample.frames[sample.frames.length - i - 1] != callstack[j])
+        return null;
+      j++;
     }
-    sample.frames = sample.frames.slice(0, sample.frames.length - callstack.length + 1);
-    return sample;
+    var newFrames = sample.frames.slice(0, sample.frames.length - i + 1);
+    return makeSample(newFrames, sample.extraInfo);
   });
 }
 
 function chargeNonJSToCallers(samples, symbols, functions, useFunctions) {
-  function isJSFrame(index, useFunction) {
-    if (useFunctions) {
-      if (!(index in functions))
-        return "";
-      return functions[index].isJSFrame;
-    }
-    if (!(index in symbols))
-      return "";
-    return symbols[index].isJSFrame;
-  }
+  var isJSFrameOrRoot = useFunctions ? function isJSFunctionOrRoot(functionIndex) {
+      return (functionIndex in functions) && (functions[functionIndex].isJSFrame || functions[functionIndex].isRoot);
+    } : function isJSSymbolOrRoot(symbolIndex) {
+      return (symbolIndex in symbols) && (symbols[symbolIndex].isJSFrame || symbols[symbolIndex].isRoot);
+    };
   samples = samples.slice(0);
   for (var i = 0; i < samples.length; ++i) {
     var sample = samples[i];
     if (!sample)
       continue;
-    var callstack = sample.frames;
-    var newFrames = [];
-    for (var j = 0; j < callstack.length; ++j) {
-      if (isJSFrame(callstack[j], useFunctions)) {
-        // Record Javascript frames
-        newFrames.push(callstack[j]);
-      }
+    var newFrames = sample.frames.filter(isJSFrameOrRoot);
+    if (!newFrames.length) {
+      samples[i] = null;
+    } else {
+      samples[i].frames = newFrames;
     }
-    if (!newFrames.length) {
-      newFrames = null;
-    } else {
-      newFrames.splice(0, 0, "(total)");
-    }
-    samples[i].frames = newFrames;
   }
   return samples;
 }
 
 function filterByName(samples, symbols, functions, filterName, useFunctions) {
   function getSymbolOrFunctionName(index, useFunctions) {
     if (useFunctions) {
       if (!(index in functions))
@@ -907,36 +992,38 @@ function mergeUnbranchedCallPaths(root) 
     mergeUnbranchedCallPaths(root.children[i]);
   }
 }
 
 function FocusedFrameSampleFilter(focusedSymbol) {
   this._focusedSymbol = focusedSymbol;
 }
 FocusedFrameSampleFilter.prototype = {
-  filter: function FocusedFrameSampleFilter_filter(samples, symbols, functions) {
+  filter: function FocusedFrameSampleFilter_filter(samples, symbols, functions, useFunctions) {
     return filterBySymbol(samples, this._focusedSymbol);
   }
 };
 
-function FocusedCallstackPrefixSampleFilter(focusedCallstack) {
+function FocusedCallstackPrefixSampleFilter(focusedCallstack, appliesToJS) {
   this._focusedCallstackPrefix = focusedCallstack;
+  this._appliesToJS = appliesToJS;
 }
 FocusedCallstackPrefixSampleFilter.prototype = {
-  filter: function FocusedCallstackPrefixSampleFilter_filter(samples, symbols, functions) {
-    return filterByCallstackPrefix(samples, this._focusedCallstackPrefix);
+  filter: function FocusedCallstackPrefixSampleFilter_filter(samples, symbols, functions, useFunctions) {
+    return filterByCallstackPrefix(samples, symbols, functions, this._focusedCallstackPrefix, this._appliesToJS, useFunctions);
   }
 };
 
-function FocusedCallstackPostfixSampleFilter(focusedCallstack) {
+function FocusedCallstackPostfixSampleFilter(focusedCallstack, appliesToJS) {
   this._focusedCallstackPostfix = focusedCallstack;
+  this._appliesToJS = appliesToJS;
 }
 FocusedCallstackPostfixSampleFilter.prototype = {
-  filter: function FocusedCallstackPostfixSampleFilter_filter(samples, symbols, functions) {
-    return filterByCallstackPostfix(samples, this._focusedCallstackPostfix);
+  filter: function FocusedCallstackPostfixSampleFilter_filter(samples, symbols, functions, useFunctions) {
+    return filterByCallstackPostfix(samples, symbols, functions, this._focusedCallstackPostfix, this._appliesToJS, useFunctions);
   }
 };
 
 function RangeSampleFilter(start, end) {
   this._start = start;
   this._end = end;
 }
 RangeSampleFilter.prototype = {
@@ -946,19 +1033,19 @@ RangeSampleFilter.prototype = {
 }
 
 function unserializeSampleFilters(filters) {
   return filters.map(function (filter) {
     switch (filter.type) {
       case "FocusedFrameSampleFilter":
         return new FocusedFrameSampleFilter(filter.focusedSymbol);
       case "FocusedCallstackPrefixSampleFilter":
-        return new FocusedCallstackPrefixSampleFilter(filter.focusedCallstack);
+        return new FocusedCallstackPrefixSampleFilter(filter.focusedCallstack, filter.appliesToJS);
       case "FocusedCallstackPostfixSampleFilter":
-        return new FocusedCallstackPostfixSampleFilter(filter.focusedCallstack);
+        return new FocusedCallstackPostfixSampleFilter(filter.focusedCallstack, filter.appliesToJS);
       case "RangeSampleFilter":
         return new RangeSampleFilter(filter.start, filter.end);
       case "PluginView":
         return null;
       default:
         throw new Error("Unknown filter");
     }
   })
@@ -970,43 +1057,38 @@ function updateFilters(requestID, profil
   var profile = gProfiles[profileID];
   var samples = profile.allSamples;
   var symbols = profile.symbols;
   var functions = profile.functions;
 
   if (filters.mergeFunctions) {
     samples = discardLineLevelInformation(samples, symbols, functions);
   }
-  if (filters.javascriptOnly) {
-    try {
-      //samples = filterByName(samples, symbols, functions, "runScript", filters.mergeFunctions);
-      samples = chargeNonJSToCallers(samples, symbols, functions, filters.mergeFunctions);
-    } catch (e) {
-      dump("Could not filer by javascript: " + e + "\n");
-    }
-  }
   if (filters.nameFilter) {
     try {
       samples = filterByName(samples, symbols, functions, filters.nameFilter, filters.mergeFunctions);
     } catch (e) {
       dump("Could not filer by name: " + e + "\n");
     }
   }
   samples = unserializeSampleFilters(filters.sampleFilters).reduce(function (filteredSamples, currentFilter) {
     if (currentFilter===null) return filteredSamples;
-    return currentFilter.filter(filteredSamples, symbols, functions);
+    return currentFilter.filter(filteredSamples, symbols, functions, filters.mergeFunctions);
   }, samples);
   if (filters.jankOnly) {
     samples = filterByJank(samples, gJankThreshold);
   }
+  if (filters.javascriptOnly) {
+    samples = chargeNonJSToCallers(samples, symbols, functions, filters.mergeFunctions);
+  }
 
   gProfiles[profileID].filterSettings = filters;
   gProfiles[profileID].filteredSamples = samples;
   sendFinishedInChunks(requestID, samples, 40000,
-                       function (sample) { return (sample && sample.frames) ? sample.frames.length : 1; });
+                       function (sample) { return sample ? sample.frames.length : 1; });
 }
 
 function updateViewOptions(requestID, profileID, options) {
   var profile = gProfiles[profileID];
   var samples = profile.filteredSamples;
   var symbols = profile.symbols;
   var functions = profile.functions;
 
@@ -1034,31 +1116,31 @@ function calculateHistogramData(requestI
 
   var profile = gProfiles[profileID];
   var data = profile.filteredSamples;
   var histogramData = [];
   var maxHeight = 0;
   for (var i = 0; i < data.length; ++i) {
     if (!data[i])
       continue;
-    var value = data[i].frames ? data[i].frames.length : 0;
+    var value = data[i].frames.length;
     if (maxHeight < value)
       maxHeight = value;
   }
   maxHeight += 1;
   var nextX = 0;
   // The number of data items per histogramData rects.
   // Except when seperated by a marker.
   // This is used to cut down the number of rects, since
   // there's no point in having more rects then pixels
   var samplesPerStep = Math.max(1, Math.floor(data.length / 2000));
   var frameStart = {};
   for (var i = 0; i < data.length; i++) {
     var step = data[i];
-    if (!step || !step.frames) {
+    if (!step) {
       // Add a gap for the sample that was filtered out.
       nextX += 1 / samplesPerStep;
       continue;
     }
     nextX = Math.ceil(nextX);
     var value = step.frames.length / maxHeight;
     var frames = step.frames;
     var currHistogramData = histogramData[histogramData.length-1];
@@ -1135,27 +1217,85 @@ var diagnosticList = [
     check: function(frames, symbols, meta) {
 
       return stepContains('PaintGradient', frames, symbols)
           && stepContains('BasicTiledLayerBuffer::PaintThebesSingleBufferDraw', frames, symbols)
           ;
     },
   },
   {
+    image: "cache.png",
+    title: "Bug 717761 - Main thread can be blocked by IO on the cache thread",
+    bugNumber: "717761",
+    check: function(frames, symbols, meta) {
+
+      return stepContains('nsCacheEntryDescriptor::GetStoragePolicy', frames, symbols)
+          ;
+    },
+  },
+  {
+    image: "js.png",
+    title: "Web Content Shutdown Notification",
+    check: function(frames, symbols, meta) {
+
+      return stepContains('nsAppStartup::Quit', frames, symbols)
+          && stepContains('nsDocShell::FirePageHideNotification', frames, symbols)
+          ;
+    },
+  },
+  {
     image: "js.png",
     title: "Bug 789193 - AMI_startup() takes 200ms on startup",
     bugNumber: "789193",
     check: function(frames, symbols, meta) {
 
       return stepContains('AMI_startup()', frames, symbols)
           ;
     },
   },
   {
     image: "js.png",
+    title: "Bug 818296 - [Shutdown] js::NukeCrossCompartmentWrappers takes up 300ms on shutdown",
+    bugNumber: "818296",
+    check: function(frames, symbols, meta) {
+      return stepContains('js::NukeCrossCompartmentWrappers', frames, symbols)
+          && (stepContains('WindowDestroyedEvent', frames, symbols) || stepContains('DoShutdown', frames, symbols))
+          ;
+    },
+  },
+  {
+    image: "js.png",
+    title: "Bug 818274 - [Shutdown] Telemetry takes ~10ms on shutdown",
+    bugNumber: "818274",
+    check: function(frames, symbols, meta) {
+      return stepContains('TelemetryPing.js', frames, symbols)
+          ;
+    },
+  },
+  {
+    image: "plugin.png",
+    title: "Bug 818265 - [Shutdown] Plug-in shutdown takes ~90ms on shutdown",
+    bugNumber: "818265",
+    check: function(frames, symbols, meta) {
+      return stepContains('PluginInstanceParent::Destroy', frames, symbols)
+          ;
+    },
+  },
+  {
+    image: "snapshot.png",
+    title: "Bug 720575 - Make thumbnailing faster and/or asynchronous",
+    bugNumber: "720575",
+    check: function(frames, symbols, meta) {
+      return stepContains('Thumbnails_capture()', frames, symbols)
+          ;
+    },
+  },
+
+  {
+    image: "js.png",
     title: "Bug 789185 - LoginManagerStorage_mozStorage.init() takes 300ms on startup ",
     bugNumber: "789185",
     check: function(frames, symbols, meta) {
 
       return stepContains('LoginManagerStorage_mozStorage.prototype.init()', frames, symbols)
           ;
     },
   },
@@ -1233,17 +1373,16 @@ var diagnosticList = [
   },
   {
     image: "cc.png",
     title: "Cycle Collect",
     check: function(frames, symbols, meta, step) {
       var ccEvent = findCCEvent(frames, symbols, meta, step);
 
       if (ccEvent) {
-        dump("Found\n");
         return true;
       }
       return false;
     },
     details: function(frames, symbols, meta, step) {
       var ccEvent = findCCEvent(frames, symbols, meta, step);
       if (ccEvent) {
         return "" +
@@ -1263,21 +1402,32 @@ var diagnosticList = [
   },
   {
     image: "gc.png",
     title: "Garbage Collection",
     canMergeWithGC: false,
     check: function(frames, symbols, meta) {
       return stepContainsRegEx(/.*Collect.*Runtime.*Invocation.*/, frames, symbols)
           || stepContains('GarbageCollectNow', frames, symbols) // Label
+          || stepContains('JS_GC(', frames, symbols) // Label
           || stepContains('CycleCollect__', frames, symbols) // Label
           ;
     },
   },
   {
+    image: "cc.png",
+    title: "Cycle Collect",
+    check: function(frames, symbols, meta) {
+      return stepContains('nsCycleCollector::Collect', frames, symbols)
+          || stepContains('CycleCollect__', frames, symbols) // Label
+          || stepContains('nsCycleCollectorRunner::Collect', frames, symbols) // Label
+          ;
+    },
+  },
+  {
     image: "plugin.png",
     title: "Sync Plugin Constructor",
     check: function(frames, symbols, meta) {
       return stepContains('CallPPluginInstanceConstructor', frames, symbols) 
           || stepContains('CallPCrashReporterConstructor', frames, symbols) 
           || stepContains('PPluginModuleParent::CallNP_Initialize', frames, symbols)
           || stepContains('GeckoChildProcessHost::SyncLaunch', frames, symbols)
           ;
@@ -1291,16 +1441,17 @@ var diagnosticList = [
     },
   },
   {
     image: "io.png",
     title: "Main Thread IO!",
     check: function(frames, symbols, meta) {
       return stepContains('__getdirentries64', frames, symbols) 
           || stepContains('__open', frames, symbols) 
+          || stepContains('NtFlushBuffersFile', frames, symbols) 
           || stepContains('storage:::Statement::ExecuteStep', frames, symbols) 
           || stepContains('__unlink', frames, symbols) 
           || stepContains('fsync', frames, symbols) 
           || stepContains('stat$INODE64', frames, symbols)
           ;
     },
   },
 ];
@@ -1366,35 +1517,41 @@ function findGCSlice(frames, symbols, me
       }
     }
   }
 
   return null;
 }
 function stepContains(substring, frames, symbols) {
   for (var i = 0; frames && i < frames.length; i++) {
+    if (!(frames[i] in symbols))
+      continue;
     var frameSym = symbols[frames[i]].functionName || symbols[frames[i]].symbolName;
     if (frameSym.indexOf(substring) != -1) {
       return true;
     }
   }
   return false;
 }
 function stepContainsRegEx(regex, frames, symbols) {
   for (var i = 0; frames && i < frames.length; i++) {
+    if (!(frames[i] in symbols))
+      continue;
     var frameSym = symbols[frames[i]].functionName || symbols[frames[i]].symbolName;
     if (regex.exec(frameSym)) {
       return true;
     }
   }
   return false;
 }
 function symbolSequence(symbolsOrder, frames, symbols) {
   var symbolIndex = 0;
   for (var i = 0; frames && i < frames.length; i++) {
+    if (!(frames[i] in symbols))
+      continue;
     var frameSym = symbols[frames[i]].functionName || symbols[frames[i]].symbolName;
     var substring = symbolsOrder[symbolIndex];
     if (frameSym.indexOf(substring) != -1) {
       symbolIndex++;
       if (symbolIndex == symbolsOrder.length) {
         return true;
       }
     }
@@ -1453,24 +1610,23 @@ function calculateDiagnosticItems(reques
 /*
   dump("meta: " + meta.gcStats + "\n");
   if (meta && meta.gcStats) {
     dump("GC Stats: " + JSON.stringify(meta.gcStats) + "\n");
   }
 */
 
   data.forEach(function diagnoseStep(step, x) {
-    if (!step)
-      return;
+    if (step) {
+      var frames = step.frames;
 
-    var frames = step.frames;
-
-    var diagnostic = firstMatch(diagnosticList, function (diagnostic) {
-      return diagnostic.check(frames, symbols, meta, step);
-    });
+      var diagnostic = firstMatch(diagnosticList, function (diagnostic) {
+        return diagnostic.check(frames, symbols, meta, step);
+      });
+    }
 
     if (!diagnostic) {
       finishPendingDiagnostic(x);
       return;
     }
 
     var details = diagnostic.details ? diagnostic.details(frames, symbols, meta, step) : null;
 
--- a/browser/devtools/profiler/cleopatra/js/tree.js
+++ b/browser/devtools/profiler/cleopatra/js/tree.js
@@ -15,34 +15,30 @@ function unescapeHTML(html) {
   escape.innerHTML = html;
   return escape.value;
 }
 
 RegExp.escape = function(text) {
     return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
 }
 
-var requestAnimationFrame_timeout = null;
 var requestAnimationFrame = window.webkitRequestAnimationFrame ||
                             window.mozRequestAnimationFrame ||
                             window.oRequestAnimationFrame ||
                             window.msRequestAnimationFrame ||
                             function(callback, element) {
-                              window.setTimeout(callback, 1000 / 60);
+                              return window.setTimeout(callback, 1000 / 60);
                             };
 
 var cancelAnimationFrame = window.webkitCancelAnimationFrame ||
                            window.mozCancelAnimationFrame ||
                            window.oCancelAnimationFrame ||
                            window.msCancelAnimationFrame ||
-                           function(callback, element) {
-                             if (requestAnimationFrame_timeout) {
-                               window.clearTimeout(requestAnimationFrame_timeout);
-                               requestAnimationFrame_timeout = null;
-                             }
+                           function(req) {
+                             window.clearTimeout(req);
                            };
 
 function TreeView() {
   this._eventListeners = {};
   this._pendingActions = [];
   this._pendingActionsProcessingCallback = null;
 
   this._container = document.createElement("div");
@@ -60,30 +56,40 @@ function TreeView() {
   this._leftColumnBackground = document.createElement("div");
   this._leftColumnBackground.className = "leftColumnBackground";
   this._verticalScrollbox.appendChild(this._leftColumnBackground);
 
   this._horizontalScrollbox = document.createElement("div");
   this._horizontalScrollbox.className = "treeViewHorizontalScrollbox";
   this._verticalScrollbox.appendChild(this._horizontalScrollbox);
 
+  this._styleElement = document.createElement("style");
+  this._styleElement.setAttribute("type", "text/css");
+  this._container.appendChild(this._styleElement);
+
   this._contextMenu = document.createElement("menu");
   this._contextMenu.setAttribute("type", "context");
   this._contextMenu.id = "contextMenuForTreeView" + TreeView.instanceCounter++;
   this._container.appendChild(this._contextMenu);
 
   this._busyCover = document.createElement("div");
   this._busyCover.className = "busyCover";
   this._container.appendChild(this._busyCover);
   this._abortToggleAll = false;
+  this.initSelection = true;
 
   var self = this;
   this._container.onkeydown = function (e) {
     self._onkeypress(e);
   };
+  this._container.onkeypress = function (e) {
+    // on key down gives us '8' and mapping shift+8='*' may not be portable.
+    if (String.fromCharCode(e.charCode) == '*')
+      self._onkeypress(e);
+  };
   this._container.onclick = function (e) {
     self._onclick(e);
   };
   this._verticalScrollbox.addEventListener("contextmenu", function(event) {
     self._populateContextMenu(event);
   }, true);
   this._setUpScrolling();
 };
@@ -101,19 +107,21 @@ TreeView.prototype = {
       li.id = columns[i].name + "Header";
       li.textContent = columns[i].title;
       this._header.appendChild(li);
     }
   },
   dataIsOutdated: function TreeView_dataIsOutdated() {
     this._busyCover.classList.add("busy");
   },
-  display: function TreeView_display(data, filterByName) {
+  display: function TreeView_display(data, resources, filterByName) {
     this._busyCover.classList.remove("busy");
     this._filterByName = filterByName;
+    this._resources = resources;
+    this._addResourceIconStyles();
     this._filterByNameReg = null; // lazy init
     if (this._filterByName === "")
       this._filterByName = null;
     this._horizontalScrollbox.innerHTML = "";
     this._horizontalScrollbox.data = data[0].getData();
     if (this._pendingActionsProcessingCallback) {
       cancelAnimationFrame(this._pendingActionsProcessingCallback);
       this._pendingActionsProcessingCallback = 0;
@@ -121,34 +129,37 @@ TreeView.prototype = {
     this._pendingActions = [];
 
     this._pendingActions.push({
       parentElement: this._horizontalScrollbox,
       parentNode: null,
       data: data[0].getData()
     });
     this._processPendingActionsChunk();
-    this._select(this._horizontalScrollbox.firstChild);
-    this._toggle(this._horizontalScrollbox.firstChild);
-    this._container.focus();
+    if (this._initSelection === true) {
+      this._initSelection = false;
+      this._select(this._horizontalScrollbox.firstChild);
+      this._toggle(this._horizontalScrollbox.firstChild);
+    }
+    changeFocus(this._container);
   },
   // Provide a snapshot of the reverse selection to restore with 'invert callback'
   getReverseSelectionSnapshot: function TreeView__getReverseSelectionSnapshot(isJavascriptOnly) {
     if (!this._selectedNode)
       return;
     var snapshot = [];
     var curr = this._selectedNode.data;
 
     while(curr) {
       if (isJavascriptOnly && curr.isJSFrame || !isJavascriptOnly) {
         snapshot.push(curr.name);
         //dump(JSON.stringify(curr.name) + "\n");
       }
-      if (curr.children && curr.children.length >= 1) {
-        curr = curr.children[0].getData();
+      if (curr.treeChildren && curr.treeChildren.length >= 1) {
+        curr = curr.treeChildren[0].getData();
       } else {
         break;
       }
     }
 
     return snapshot.reverse();
   },
   // Provide a snapshot of the current selection to restore
@@ -166,43 +177,44 @@ TreeView.prototype = {
 
     return snapshot.reverse();
   },
   setSelection: function TreeView_setSelection(frames) {
     this.restoreSelectionSnapshot(frames, false);
   },
   // Take a selection snapshot and restore the selection
   restoreSelectionSnapshot: function TreeView_restoreSelectionSnapshot(snapshot, allowNonContigious) {
+    //console.log("restore selection: " + JSON.stringify(snapshot));
     var currNode = this._horizontalScrollbox.firstChild;
     if (currNode.data.name == snapshot[0] || snapshot[0] == "(total)") {
       snapshot.shift();
     }
     //dump("len: " + snapshot.length + "\n");
     next_level: while (currNode && snapshot.length > 0) {
       this._toggle(currNode, false, true);
       this._syncProcessPendingActionProcessing();
       for (var i = 0; i < currNode.treeChildren.length; i++) {
         if (currNode.treeChildren[i].data.name == snapshot[0]) {
-          //dump("Found: " + currNode.treeChildren[i].data.name + "\n");
+          //console.log("Found: " + currNode.treeChildren[i].data.name + "\n");
           snapshot.shift();
           this._toggle(currNode, false, true);
           currNode = currNode.treeChildren[i];
           continue next_level;
         }
       }
       if (allowNonContigious === true) {
         // We need to do a Breadth-first search to find a match
         var pendingSearch = [currNode.data];
         while (pendingSearch.length > 0) {
           var node = pendingSearch.shift();
-          //dump("searching: " + node.name + " for: " + snapshot[0] + "\n");
-          if (!node.children)
+          //console.log("searching: " + node.name + " for: " + snapshot[0] + "\n");
+          if (!node.treeChildren)
             continue;
-          for (var i = 0; i < node.children.length; i++) {
-            var childNode = node.children[i].getData();
+          for (var i = 0; i < node.treeChildren.length; i++) {
+            var childNode = node.treeChildren[i].getData();
             if (childNode.name == snapshot[0]) {
               //dump("found: " + childNode.name + "\n");
               snapshot.shift();
               var nodesToToggle = [childNode];
               while (nodesToToggle[0].name != currNode.data.name) {
                 nodesToToggle.splice(0, 0, nodesToToggle[0].parent);
               }
               var lastToggle = currNode;
@@ -312,48 +324,76 @@ TreeView.prototype = {
       e.preventDefault();
     }
     this._verticalScrollbox.addEventListener("MozMousePixelScroll", scrollListener, false);
     this._verticalScrollbox.cleanUp = function () {
       self._verticalScrollbox.removeEventListener("MozMousePixelScroll", scrollListener, false);
     };
   },
   _scrollHeightChanged: function TreeView__scrollHeightChanged() {
-    this._leftColumnBackground.style.height = this._horizontalScrollbox.getBoundingClientRect().height + 'px';
+    if (!this._pendingScrollHeightChanged) {
+      var self = this;
+      this._pendingScrollHeightChanged = setTimeout(function() {
+        self._leftColumnBackground.style.height = self._horizontalScrollbox.getBoundingClientRect().height + 'px';
+        self._pendingScrollHeightChanged = null;
+      }, 0);
+    }
   },
   _createTree: function TreeView__createTree(parentElement, parentNode, data) {
     var div = document.createElement("div");
     div.className = "treeViewNode collapsed";
     var hasChildren = ("children" in data) && (data.children.length > 0);
     if (!hasChildren)
       div.classList.add("leaf");
     var treeLine = document.createElement("div");
     treeLine.className = "treeLine";
     treeLine.innerHTML = this._HTMLForFunction(data);
+    div.depth = parentNode ? parentNode.depth + 1 : 0;
+    div.style.marginLeft = div.depth + "em";
     // When this item is toggled we will expand its children
     div.pendingExpand = [];
     div.treeLine = treeLine;
     div.data = data;
+    // Useful for debugging
+    //this.uniqueID = this.uniqueID || 0;
+    //div.id = "Node" + this.uniqueID++;
     div.appendChild(treeLine);
     div.treeChildren = [];
     div.treeParent = parentNode;
     if (hasChildren) {
-      var parent = document.createElement("div");
-      parent.className = "treeViewNodeList";
       for (var i = 0; i < data.children.length; ++i) {
-        div.pendingExpand.push({parentElement: parent, parentNode: div, data: data.children[i].getData() });
+        div.pendingExpand.push({parentElement: this._horizontalScrollbox, parentNode: div, data: data.children[i].getData() });
       }
-      div.appendChild(parent);
     }
     if (parentNode) {
       parentNode.treeChildren.push(div);
     }
-    parentElement.appendChild(div);
+    if (parentNode != null) {
+      var nextTo;
+      if (parentNode.treeChildren.length > 1) {
+        nextTo = parentNode.treeChildren[parentNode.treeChildren.length-2].nextSibling;
+      } else {
+        nextTo = parentNode.nextSibling;
+      }
+      parentElement.insertBefore(div, nextTo);
+    } else {
+      parentElement.appendChild(div);
+    }
     return div;
   },
+  _addResourceIconStyles: function TreeView__addResourceIconStyles() {
+    var styles = [];
+    for (var resourceName in this._resources) {
+      var resource = this._resources[resourceName];
+      if (resource.icon) {
+        styles.push('.resourceIcon[data-resource="' + resourceName + '"] { background-image: url("' + resource.icon + '"); }');
+      }
+    }
+    this._styleElement.textContent = styles.join("\n");
+  },
   _populateContextMenu: function TreeView__populateContextMenu(event) {
     this._verticalScrollbox.setAttribute("contextmenu", "");
 
     var target = event.target;
     if (target.classList.contains("expandCollapseButton") ||
         target.classList.contains("focusCallstackButton"))
       return;
 
@@ -380,35 +420,36 @@ TreeView.prototype = {
     this._verticalScrollbox.setAttribute("contextmenu", this._contextMenu.id);
   },
   _contextMenuClick: function TreeView__contextMenuClick(node, menuItem) {
     this._fireEvent("contextMenuClick", { node: node, menuItem: menuItem });
   },
   _contextMenuForFunction: function TreeView__contextMenuForFunction(node) {
     // TODO move me outside tree.js
     var menu = [];
-    if (node.library != null && (
-      node.library.toLowerCase() == "xul" ||
-      node.library.toLowerCase() == "xul.dll"
+    if (node.library && (
+      node.library.toLowerCase() == "lib_xul" ||
+      node.library.toLowerCase() == "lib_xul.dll"
       )) {
       menu.push("View Source");
     }
     if (node.isJSFrame && node.scriptLocation) {
       menu.push("View JS Source");
     }
     menu.push("Focus Frame");
     menu.push("Focus Callstack");
     menu.push("Google Search");
     menu.push("Plugin View: Pie");
     menu.push("Plugin View: Tree");
     return menu;
   },
   _HTMLForFunction: function TreeView__HTMLForFunction(node) {
     var nodeName = escapeHTML(node.name);
-    var libName = node.library;
+    var resource = this._resources[node.library] || {};
+    var libName = escapeHTML(resource.name || "");
     if (this._filterByName) {
       if (!this._filterByNameReg) {
         this._filterByName = RegExp.escape(this._filterByName);
         this._filterByNameReg = new RegExp("(" + this._filterByName + ")","gi");
       }
       nodeName = nodeName.replace(this._filterByNameReg, "<a style='color:red;'>$1</a>");
       libName = libName.replace(this._filterByNameReg, "<a style='color:red;'>$1</a>");
     }
@@ -417,37 +458,49 @@ TreeView.prototype = {
       samplePercentage = "";
     } else {
       samplePercentage = (100 * node.ratio).toFixed(1) + "%";
     }
     return '<input type="button" value="Expand / Collapse" class="expandCollapseButton" tabindex="-1"> ' +
       '<span class="sampleCount">' + node.counter + '</span> ' +
       '<span class="samplePercentage">' + samplePercentage + '</span> ' +
       '<span class="selfSampleCount">' + node.selfCounter + '</span> ' +
+      '<span class="resourceIcon" data-resource="' + node.library + '"></span> ' +
       '<span class="functionName">' + nodeName + '</span>' +
       '<span class="libraryName">' + libName + '</span>' +
       '<input type="button" value="Focus Callstack" title="Focus Callstack" class="focusCallstackButton" tabindex="-1">';
   },
   _resolveChildren: function TreeView__resolveChildren(div, childrenCollapsedValue) {
     while (div.pendingExpand != null && div.pendingExpand.length > 0) {
       var pendingExpand = div.pendingExpand.shift();
       pendingExpand.allChildrenCollapsedValue = childrenCollapsedValue;
       this._pendingActions.push(pendingExpand);
       this._schedulePendingActionProcessing();
     }
   },
+  _showChild: function TreeView__showChild(div, isVisible) {
+    for (var i = 0; i < div.treeChildren.length; i++) {
+      div.treeChildren[i].style.display = isVisible?"":"none";
+      if (!isVisible) {
+        div.treeChildren[i].classList.add("collapsed");
+        this._showChild(div.treeChildren[i], isVisible);
+      }
+    }
+  },
   _toggle: function TreeView__toggle(div, /* optional */ newCollapsedValue, /* optional */ suppressScrollHeightNotification) {
     var currentCollapsedValue = this._isCollapsed(div);
     if (newCollapsedValue === undefined)
       newCollapsedValue = !currentCollapsedValue;
     if (newCollapsedValue) {
       div.classList.add("collapsed");
+      this._showChild(div, false);
     } else {
       this._resolveChildren(div, true);
       div.classList.remove("collapsed");
+      this._showChild(div, true);
     }
     if (!suppressScrollHeightNotification)
       this._scrollHeightChanged();
   },
   _toggleAll: function TreeView__toggleAll(subtreeRoot, /* optional */ newCollapsedValue, /* optional */ suppressScrollHeightNotification) {
 
     // Reset abort
     this._abortToggleAll = false;
@@ -495,16 +548,29 @@ TreeView.prototype = {
   _getNextSib: function TreeView__getNextSib(div) {
     if (div.treeParent == null)
       return null;
     var nodeIndex = div.treeParent.treeChildren.indexOf(div);
     if (nodeIndex == div.treeParent.treeChildren.length - 1)
       return this._getNextSib(div.treeParent);
     return div.treeParent.treeChildren[nodeIndex+1];
   },
+  _scheduleScrollIntoView: function TreeView__scheduleScrollIntoView(element, maxImportantWidth) {
+    // Schedule this on the animation frame otherwise we may run this more then once per frames
+    // causing more work then needed.
+    var self = this;
+    if (self._pendingAnimationFrame != null) {
+      return;
+    }
+    self._pendingAnimationFrame = requestAnimationFrame(function anim_frame() {
+      cancelAnimationFrame(self._pendingAnimationFrame);
+      self._pendingAnimationFrame = null;
+      self._scrollIntoView(element, maxImportantWidth);
+    });
+  },
   _scrollIntoView: function TreeView__scrollIntoView(element, maxImportantWidth) {
     // Make sure that element is inside the visible part of our scrollbox by
     // adjusting the scroll positions. If element is wider or
     // higher than the scroll port, the left and top edges are prioritized over
     // the right and bottom edges.
     // If maxImportantWidth is set, parts of the beyond this widths are
     // considered as not important; they'll not be moved into view.
 
@@ -536,19 +602,20 @@ TreeView.prototype = {
     if (this._selectedNode != null) {
       this._selectedNode.treeLine.classList.remove("selected");
       this._selectedNode = null;
     }
     if (li) {
       li.treeLine.classList.add("selected");
       this._selectedNode = li;
       var functionName = li.treeLine.querySelector(".functionName");
-      this._scrollIntoView(functionName, 400);
+      this._scheduleScrollIntoView(functionName, 400);
       this._fireEvent("select", li.data);
     }
+    updateDocumentURL();
   },
   _isCollapsed: function TreeView__isCollapsed(div) {
     return div.classList.contains("collapsed");
   },
   _getParentTreeViewNode: function TreeView__getParentTreeViewNode(node) {
     while (node) {
       if (node.nodeType != node.ELEMENT_NODE)
         break;
@@ -610,16 +677,17 @@ TreeView.prototype = {
         this._select(this._getLastChild(prevSib));
       } else if (parent != null) {
         this._select(parent);
       }
     } else if (event.keyCode == 39) { // KEY_RIGHT
       var isCollapsed = this._isCollapsed(selected);
       if (isCollapsed) {
         this._toggle(selected);
+        this._syncProcessPendingActionProcessing();
       } else {
         // Do KEY_DOWN
         var nextSib = this._getNextSib(selected);
         var child = this._getFirstChild(selected); 
         if (child != null) {
           this._select(child);
         } else if (nextSib) {
           this._select(nextSib);
--- a/browser/devtools/profiler/cleopatra/js/ui.js
+++ b/browser/devtools/profiler/cleopatra/js/ui.js
@@ -54,48 +54,68 @@ function FileList() {
   this._fileItemList = [];
 }
 
 FileList.prototype = {
   getContainer: function FileList_getContainer() {
     return this._container;
   },
 
+  clearFiles: function FileList_clearFiles() {
+    this.fileItemList = [];
+    this._selectedFileItem = null;
+    this._container.innerHTML = "";
+  },
+
   loadProfileListFromLocalStorage: function FileList_loadProfileListFromLocalStorage() {
     var self = this;
     gLocalStorage.getProfileList(function(profileList) {
-      for (var i = 0; i < profileList.length; i++) {
+      for (var i = profileList.length - 1; i >= 0; i--) {
         (function closure() {
           // This only carries info about the profile and the access key to retrieve it.
           var profileInfo = profileList[i];
           //PROFILERTRACE("Profile list from local storage: " + JSON.stringify(profileInfo));
-          var fileEntry = self.addFile(profileInfo.profileKey, "local storage", function fileEntryClick() {
+          var dateObj = new Date(profileInfo.date);
+          var fileEntry = self.addFile(profileInfo, dateObj.toLocaleString(), function fileEntryClick() {
+            PROFILERLOG("open: " + profileInfo.profileKey + "\n");
             loadLocalStorageProfile(profileInfo.profileKey);
           });
         })();
       }
     });
+    gLocalStorage.onProfileListChange(function(profileList) {
+      self.clearFiles();
+      self.loadProfileListFromLocalStorage();
+    });
   },
 
-  addFile: function FileList_addFile(fileName, description, onselect) {
+  addFile: function FileList_addFile(profileInfo, description, onselect) {
     var li = document.createElement("li");
 
-    li.fileName = fileName || "New Profile";
+    var fileName;
+    if (profileInfo.profileKey && profileInfo.profileKey.indexOf("http://profile-store.commondatastorage.googleapis.com/") >= 0) {
+      fileName = profileInfo.profileKey.substring(54);
+      fileName = fileName.substring(0, 8) + "..." + fileName.substring(28);
+    } else {
+      fileName = profileInfo.name;
+    }
+    li.fileName = fileName || "(New Profile)";
     li.description = description || "(empty)";
 
     li.className = "fileListItem";
     if (!this._selectedFileItem) {
       li.classList.add("selected");
       this._selectedFileItem = li;
     }
 
-    li.onselect = onselect;
     var self = this;
     li.onclick = function() {
       self.setSelection(li);
+      if (onselect)
+        onselect();
     }
 
     var fileListItemTitleSpan = document.createElement("span");
     fileListItemTitleSpan.className = "fileListItemTitle";
     fileListItemTitleSpan.textContent = li.fileName;
     li.appendChild(fileListItemTitleSpan);
 
     var fileListItemDescriptionSpan = document.createElement("span");
@@ -116,35 +136,39 @@ FileList.prototype = {
     }
     this._selectedFileItem = fileEntry;
     fileEntry.classList.add("selected");
     if (this._selectedFileItem.onselect)
       this._selectedFileItem.onselect();
   },
 
   profileParsingFinished: function FileList_profileParsingFinished() {
-    this._container.querySelector(".fileListItemTitle").textContent = "Current Profile";
-    this._container.querySelector(".fileListItemDescription").textContent = gNumSamples + " Samples";
+    //this._container.querySelector(".fileListItemTitle").textContent = "Current Profile";
+    //this._container.querySelector(".fileListItemDescription").textContent = gNumSamples + " Samples";
   }
 }
 
 function treeObjSort(a, b) {
   return b.counter - a.counter;
 }
 
 function ProfileTreeManager() {
   this.treeView = new TreeView();
   this.treeView.setColumns([
     { name: "sampleCount", title: "Running time" },
     { name: "selfSampleCount", title: "Self" },
-    { name: "symbolName", title: "Symbol Name"},
+    { name: "resource", title: "" },
+    { name: "symbolName", title: "Symbol Name"}
   ]);
   var self = this;
   this.treeView.addEventListener("select", function (frameData) {
     self.highlightFrame(frameData);
+    if (window.comparator_setSelection) {
+      window.comparator_setSelection(gTreeManager.serializeCurrentSelectionSnapshot(), frameData);
+    }
   });
   this.treeView.addEventListener("contextMenuClick", function (e) {
     self._onContextMenuClick(e);
   });
   this.treeView.addEventListener("focusCallstackButtonClicked", function (frameData) {
     var focusedCallstack = self._getCallstackUpTo(frameData);
     focusOnCallstack(focusedCallstack, frameData.name);
   });
@@ -160,22 +184,31 @@ ProfileTreeManager.prototype = {
     return this._container;
   },
   highlightFrame: function Treedisplay_highlightFrame(frameData) {
     setHighlightedCallstack(this._getCallstackUpTo(frameData), this._getHeaviestCallstack(frameData));
   },
   dataIsOutdated: function ProfileTreeManager_dataIsOutdated() {
     this.treeView.dataIsOutdated();
   },
-  saveSelectionSnapshot: function ProfileTreeManager_getSelectionSnapshot(isJavascriptOnly) {
+  saveSelectionSnapshot: function ProfileTreeManager_saveSelectionSnapshot(isJavascriptOnly) {
     this._savedSnapshot = this.treeView.getSelectionSnapshot(isJavascriptOnly);
   },
-  saveReverseSelectionSnapshot: function ProfileTreeManager_getReverseSelectionSnapshot(isJavascriptOnly) {
+  saveReverseSelectionSnapshot: function ProfileTreeManager_saveReverseSelectionSnapshot(isJavascriptOnly) {
     this._savedSnapshot = this.treeView.getReverseSelectionSnapshot(isJavascriptOnly);
   },
+  hasNonTrivialSelection: function ProfileTreeManager_hasNonTrivialSelection() {
+    return this.treeView.getSelectionSnapshot().length > 1;
+  },
+  serializeCurrentSelectionSnapshot: function ProfileTreeManager_serializeCurrentSelectionSnapshot() {
+    return JSON.stringify(this.treeView.getSelectionSnapshot());
+  },
+  restoreSerializedSelectionSnapshot: function ProfileTreeManager_restoreSerializedSelectionSnapshot(selection) {
+    this._savedSnapshot = JSON.parse(selection);
+  },
   _restoreSelectionSnapshot: function ProfileTreeManager__restoreSelectionSnapshot(snapshot, allowNonContigous) {
     return this.treeView.restoreSelectionSnapshot(snapshot, allowNonContigous);
   },
   setSelection: function ProfileTreeManager_setSelection(frames) {
     return this.treeView.setSelection(frames);
   },
   _getCallstackUpTo: function ProfileTreeManager__getCallstackUpTo(frame) {
     var callstack = [];
@@ -226,47 +259,48 @@ ProfileTreeManager.prototype = {
     } else if (menuItem == "Focus Callstack") {
       var focusedCallstack = this._getCallstackUpTo(node);
       focusOnCallstack(focusedCallstack, node.name);
     }
   },
   setAllowNonContigous: function ProfileTreeManager_setAllowNonContigous() {
     this._allowNonContigous = true;
   },
-  display: function ProfileTreeManager_display(tree, symbols, functions, useFunctions, filterByName) {
-    this.treeView.display(this.convertToJSTreeData(tree, symbols, functions, useFunctions), filterByName);
+  display: function ProfileTreeManager_display(tree, symbols, functions, resources, useFunctions, filterByName) {
+    this.treeView.display(this.convertToJSTreeData(tree, symbols, functions, useFunctions), resources, filterByName);
     if (this._savedSnapshot) {
+      var old = this._savedSnapshot.clone();
       this._restoreSelectionSnapshot(this._savedSnapshot, this._allowNonContigous);
-      this._savedSnapshot = null;
+      this._savedSnapshot = old;
       this._allowNonContigous = false;
     }
   },
   convertToJSTreeData: function ProfileTreeManager__convertToJSTreeData(rootNode, symbols, functions, useFunctions) {
     var totalSamples = rootNode.counter;
     function createTreeViewNode(node, parent) {
       var curObj = {};
       curObj.parent = parent;
       curObj.counter = node.counter;
       var selfCounter = node.counter;
       for (var i = 0; i < node.children.length; ++i) {
         selfCounter -= node.children[i].counter;
       }
       curObj.selfCounter = selfCounter;
       curObj.ratio = node.counter / totalSamples;
       curObj.fullFrameNamesAsInSample = node.mergedNames ? node.mergedNames : [node.name];
-      if (useFunctions ? !(node.name in functions) : !(node.name in symbols)) {
+      if (!(node.name in (useFunctions ? functions : symbols))) {
         curObj.name = node.name;
         curObj.library = "";
       } else {
         var functionObj = useFunctions ? functions[node.name] : functions[symbols[node.name].functionIndex];
         var info = {
           functionName: functionObj.functionName,
           libraryName: functionObj.libraryName,
           lineInformation: useFunctions ? "" : symbols[node.name].lineInformation
-        };
+        };  
         curObj.name = (info.functionName + " " + info.lineInformation).trim();
         curObj.library = info.libraryName;
         curObj.isJSFrame = functionObj.isJSFrame;
         if (functionObj.scriptLocation) {
           curObj.scriptLocation = functionObj.scriptLocation;
         }
       }
       if (node.children.length) {
@@ -276,17 +310,17 @@ ProfileTreeManager.prototype = {
     }
     function getChildrenObjects(children, parent) {
       var sortedChildren = children.slice(0).sort(treeObjSort);
       return sortedChildren.map(function (child) {
         var createdNode = null;
         return {
           getData: function () {
             if (!createdNode) {
-              createdNode = createTreeViewNode(child, parent);
+              createdNode = createTreeViewNode(child, parent); 
             }
             return createdNode;
           }
         };
       });
     }
     return getChildrenObjects([rootNode], null);
   },
@@ -297,17 +331,17 @@ function SampleBar() {
   this._container.id = "sampleBar";
   this._container.className = "sideBar";
 
   this._header = document.createElement("h2");
   this._header.innerHTML = "Selection - Most time spent in:";
   this._header.alt = "This shows the heaviest leaf of the selected sample. Use this to get a quick glimpse of where the selection is spending most of its time.";
   this._container.appendChild(this._header);
 
-  this._text = document.createElement("span");
+  this._text = document.createElement("ul");
   this._text.style.whiteSpace = "pre";
   this._text.innerHTML = "Sample text";
   this._container.appendChild(this._text);
 }
 
 SampleBar.prototype = {
   getContainer: function SampleBar_getContainer() {
     return this._container;
@@ -317,21 +351,22 @@ SampleBar.prototype = {
     var list = [];
 
     this._text.innerHTML = "";
 
     for (var i = 0; i < sample.length; i++) {
       var functionObj = gMergeFunctions ? gFunctions[sample[i]] : gFunctions[symbols[sample[i]].functionIndex];
       if (!functionObj)
         continue;
+      var functionItem = document.createElement("li");
       var functionLink = document.createElement("a");
-      functionLink.textContent = "- " + functionObj.functionName;
+      functionLink.textContent = functionLink.title = functionObj.functionName;
       functionLink.href = "#";
-      this._text.appendChild(functionLink);
-      this._text.appendChild(document.createElement("br"));
+      functionItem.appendChild(functionLink);
+      this._text.appendChild(functionItem);
       list.push(functionObj.functionName);
       functionLink.selectIndex = i;
       functionLink.onclick = function() {
         var selectedFrames = [];
         if (gInvertCallstack) {
           for (var i = 0; i <= this.selectIndex; i++) {
             var functionObj = gMergeFunctions ? gFunctions[sample[i]] : gFunctions[symbols[sample[i]].functionIndex];
             selectedFrames.push(functionObj.functionName);
@@ -373,21 +408,19 @@ PluginView.prototype = {
     // This creates extra scrollbar so only do it when needed
     this._container.style.top = "0px";
     this._container.style.visibility = '';
   },
   display: function(pluginName, param, data) {
     this._iframe.src = "js/plugins/" + pluginName + "/index.html";
     var self = this;
     this._iframe.onload = function() {
-      console.log("Pluginview '" + pluginName + " iframe onload");
       self._iframe.contentWindow.initCleopatraPlugin(data, param, gSymbols);
     }
     this.show();
-    //console.log(gSymbols);
   },
 }
 
 function HistogramView() {
   this._container = document.createElement("div");
   this._container.className = "histogram";
 
   this._canvas = this._createCanvas();
@@ -412,16 +445,19 @@ HistogramView.prototype = {
     canvas.height = 60;
     canvas.style.width = "100%";
     canvas.style.height = "100%";
     return canvas;
   },
   getContainer: function HistogramView_getContainer() {
     return this._container;
   },
+  selectRange: function HistogramView_selectRange(start, end) {
+    this._rangeSelector._finishSelection(start, end);
+  },
   showVideoFramePosition: function HistogramView_showVideoFramePosition(frame) {
     if (!this._frameStart || !this._frameStart[frame])
       return;
     var frameStart = this._frameStart[frame];
     // Now we look for the frame end. Because we can swap frame we don't present we have to look ahead
     // in the stream if frame+1 doesn't exist.
     var frameEnd = this._frameStart[frame+1];
     for (var i = 0; i < 10 && !frameEnd; i++) {
@@ -446,90 +482,114 @@ HistogramView.prototype = {
     }
     return markers;
   },
   _calculateWidthMultiplier: function () {
     var minWidth = 2000;
     return Math.ceil(minWidth / this._widthSum);
   },
   histogramClick: function HistogramView_histogramClick(index) {
-    var sample = this._histogramData[index];
+    var sample = this._histogramData[index]; 
     var frames = sample.frames;
-    if (gSampleBar) {
-      var list = gSampleBar.setSample(frames[0]);
-      gTreeManager.setSelection(list);
-      setHighlightedCallstack(frames[0], frames[0]);
-    }
+    var list = gSampleBar.setSample(frames[0]);
+    gTreeManager.setSelection(list);
+    setHighlightedCallstack(frames[0], frames[0]);
   },
   display: function HistogramView_display(histogramData, frameStart, widthSum, highlightedCallstack) {
     this._histogramData = histogramData;
-    PROFILERTRACE("FRAME START: " + frameStart + "\n");
     this._frameStart = frameStart;
     this._widthSum = widthSum;
     this._widthMultiplier = this._calculateWidthMultiplier();
     this._canvas.width = this._widthMultiplier * this._widthSum;
     this._render(highlightedCallstack);
     this._busyCover.classList.remove("busy");
   },
+  _scheduleRender: function HistogramView__scheduleRender(highlightedCallstack) {
+    var self = this;
+    if (self._pendingAnimationFrame != null) {
+      return;
+    }
+    self._pendingAnimationFrame = requestAnimationFrame(function anim_frame() {
+      cancelAnimationFrame(self._pendingAnimationFrame);
+      self._pendingAnimationFrame = null;
+      self._render(highlightedCallstack);
+    });
+  },
   _render: function HistogramView__render(highlightedCallstack) {
     var ctx = this._canvas.getContext("2d");
     var height = this._canvas.height;
     ctx.setTransform(this._widthMultiplier, 0, 0, 1, 0, 0);
+    ctx.font = "20px Georgia";
     ctx.clearRect(0, 0, this._widthSum, height);
 
     var self = this;
+    var markerCount = 0;
     for (var i = 0; i < this._histogramData.length; i++) {
       var step = this._histogramData[i];
       var isSelected = self._isStepSelected(step, highlightedCallstack);
       var isInRangeSelector = self._isInRangeSelector(i);
       if (isSelected) {
         ctx.fillStyle = "green";
       } else if (isInRangeSelector) {
         ctx.fillStyle = "blue";
       } else {
         ctx.fillStyle = step.color;
       }
       var roundedHeight = Math.round(step.value * height);
       ctx.fillRect(step.x, height - roundedHeight, step.width, roundedHeight);
+      if (step.marker) {
+        var x = step.x + step.width + 2;
+        var endPoint = x + ctx.measureText(step.marker).width;
+        var lastDataPoint = this._histogramData[this._histogramData.length-1];
+        if (endPoint >= lastDataPoint.x + lastDataPoint.width) {
+          x -= endPoint - (lastDataPoint.x + lastDataPoint.width) - 1;
+        }
+        ctx.fillText(step.marker, x, 15 + ((markerCount % 2) == 0 ? 0 : 20));
+        markerCount++;
+      }
     }
 
     this._finishedRendering = true;
   },
   highlightedCallstackChanged: function HistogramView_highlightedCallstackChanged(highlightedCallstack) {
-    this._render(highlightedCallstack);
+    this._scheduleRender(highlightedCallstack);
   },
   _isInRangeSelector: function HistogramView_isInRangeSelector(index) {
     return false;
   },
   _isStepSelected: function HistogramView__isStepSelected(step, highlightedCallstack) {
     if ("marker" in step)
       return false;
-    return step.frames.some(function isCallstackSelected(frames) {
+
+    search_frames: for (var i = 0; i < step.frames.length; i++) {
+      var frames = step.frames[i];
+
       if (frames.length < highlightedCallstack.length ||
           highlightedCallstack.length <= (gInvertCallstack ? 0 : 1))
-        return false;
+        continue;
 
       var compareFrames = frames;
       if (gInvertCallstack) {
         for (var j = 0; j < highlightedCallstack.length; j++) {
           var compareFrameIndex = compareFrames.length - 1 - j;
           if (highlightedCallstack[j] != compareFrames[compareFrameIndex]) {
-            return false;
+            continue search_frames;
           }
         }
       } else {
         for (var j = 0; j < highlightedCallstack.length; j++) {
           var compareFrameIndex = j;
           if (highlightedCallstack[j] != compareFrames[compareFrameIndex]) {
-            return false;
+            continue search_frames;
           }
         }
       }
       return true;
-    });
+    };
+    return false;
   },
   getHistogramData: function HistogramView__getHistogramData() {
     return this._histogramData;
   },
   _getStepColor: function HistogramView__getStepColor(step) {
       if ("responsiveness" in step.extraInfo) {
         var res = step.extraInfo.responsiveness;
         var redComponent = Math.round(255 * Math.min(1, res / kDelayUntilWorstResponsiveness));
@@ -601,25 +661,28 @@ RangeSelector.prototype = {
     this.drawHiliteRectangle(this._selectedRange.startX, 0, width, height);
     //this._finishSelection(startIndex, endIndex);
   },
   enableRangeSelectionOnHistogram: function RangeSelector_enableRangeSelectionOnHistogram() {
     var graph = this._graph;
     var isDrawingRectangle = false;
     var origX, origY;
     var self = this;
+    // Compute this on the mouse down rather then forcing a sync reflow
+    // every frame.
+    var boundingRect = null;
     function histogramClick(clickX, clickY) {
       clickX = Math.min(clickX, graph.parentNode.getBoundingClientRect().right);
       clickX = clickX - graph.parentNode.getBoundingClientRect().left;
       var index = self._histogramIndexFromPoint(clickX);
       self._histogram.histogramClick(index);
     }
     function updateHiliteRectangle(newX, newY) {
-      newX = Math.min(newX, graph.parentNode.getBoundingClientRect().right);
-      var startX = Math.min(newX, origX) - graph.parentNode.getBoundingClientRect().left;
+      newX = Math.min(newX, boundingRect.right);
+      var startX = Math.min(newX, origX) - boundingRect.left;
       var startY = 0;
       var width = Math.abs(newX - origX);
       var height = graph.parentNode.clientHeight;
       if (startX < 0) {
         width += startX;
         startX = 0;
       }
       self._selectedRange.startX = startX;
@@ -632,16 +695,17 @@ RangeSelector.prototype = {
     graph.addEventListener("mousedown", function(e) {
       if (e.button != 0)
         return;
       graph.style.cursor = "col-resize";
       isDrawingRectangle = true;
       self.beginHistogramSelection();
       origX = e.pageX;
       origY = e.pageY;
+      boundingRect = graph.parentNode.getBoundingClientRect();
       if (this.setCapture)
         this.setCapture();
       // Reset the highlight rectangle
       updateHiliteRectangle(e.pageX, e.pageY);
       e.preventDefault();
       this._movedDuringClick = false;
     }, false);
     graph.addEventListener("mouseup", function(e) {
@@ -654,24 +718,22 @@ RangeSelector.prototype = {
         isDrawingRectangle = false;
         updateHiliteRectangle(e.pageX, e.pageY);
         self.finishHistogramSelection(e.pageX != origX);
         if (e.pageX == origX) {
           // Simple click in the histogram
           var index = self._sampleIndexFromPoint(e.pageX - graph.parentNode.getBoundingClientRect().left);
           // TODO Select this sample in the tree view
           var sample = gCurrentlyShownSampleData[index];
-          console.log("Should select: " + sample);
         }
       }
     }, false);
     graph.addEventListener("mousemove", function(e) {
       this._movedDuringClick = true;
       if (isDrawingRectangle) {
-        console.log(e.pageX);
         updateMouseMarker(-1); // Clear
         updateHiliteRectangle(e.pageX, e.pageY);
       } else {
         updateMouseMarker(e.pageX);
       }
     }, false);
     graph.addEventListener("mouseout", function(e) {
       updateMouseMarker(-1); // Clear
@@ -734,21 +796,22 @@ RangeSelector.prototype = {
 function videoPaneTimeChange(video) {
   if (!gMeta || !gMeta.frameStart)
     return;
 
   var frame = gVideoPane.getCurrentFrameNumber();
   //var frameStart = gMeta.frameStart[frame];
   //var frameEnd = gMeta.frameStart[frame+1]; // If we don't have a frameEnd assume the end of the profile
 
-  gHistogramView.showVideoFramePosition(frame);
+  gHistogramView.showVideoFramePosition(frame); 
 }
 
 
 window.onpopstate = function(ev) {
+  return; // Conflicts with document url
   if (!gBreadcrumbTrail)
     return;
   console.log("pop: " + JSON.stringify(ev.state));
   gBreadcrumbTrail.pop();
   if (ev.state) {
     console.log("state");
     if (ev.state.action === "popbreadcrumb") {
       console.log("bread");
@@ -815,20 +878,27 @@ BreadcrumbTrail.prototype = {
   addAndEnter: function BreadcrumbTrail_addAndEnter(breadcrumb) {
     var removalHandle = this.add(breadcrumb);
     this._enter(this._breadcrumbs.length - 1);
   },
   pop : function BreadcrumbTrail_pop() {
     if (this._breadcrumbs.length-2 >= 0)
       this._enter(this._breadcrumbs.length-2);
   },
-  _enter: function BreadcrumbTrail__select(index) {
+  enterLastItem: function BreadcrumbTrail_enterLastItem(forceSelection) {
+    this._enter(this._breadcrumbs.length-1, forceSelection);
+  },
+  _enter: function BreadcrumbTrail__select(index, forceSelection) {
     if (index == this._selectedBreadcrumbIndex)
       return;
-    gTreeManager.saveSelectionSnapshot();
+    if (forceSelection) {
+      gTreeManager.restoreSerializedSelectionSnapshot(forceSelection);
+    } else {
+      gTreeManager.saveSelectionSnapshot();
+    }
     var prevSelected = this._breadcrumbs[this._selectedBreadcrumbIndex];
     if (prevSelected)
       prevSelected.classList.remove("selected");
     var li = this._breadcrumbs[index];
     if (this === gBreadcrumbTrail && index != 0) {
       // Support for back button, disabled until the forward button is implemented.
       //var state = {action: "popbreadcrumb",};
       //window.history.pushState(state, "Cleopatra");
@@ -930,101 +1000,179 @@ function avgResponsiveness() {
 }
 
 function copyProfile() {
   window.prompt ("Copy to clipboard: Ctrl+C, Enter", document.getElementById("data").value);
 }
 
 function saveProfileToLocalStorage() {
   Parser.getSerializedProfile(true, function (serializedProfile) {
-    gLocalStorage.storeLocalProfile(serializedProfile, function profileSaved() {
+    gLocalStorage.storeLocalProfile(serializedProfile, gMeta.profileId, function profileSaved() {
 
     });
   });
 }
 function downloadProfile() {
   Parser.getSerializedProfile(true, function (serializedProfile) {
     var blob = new Blob([serializedProfile], { "type": "application/octet-stream" });
     location.href = window.URL.createObjectURL(blob);
   });
 }
 
+function promptUploadProfile(selected) {
+  var overlay = document.createElement("div");
+  overlay.style.position = "absolute";
+  overlay.style.top = 0;
+  overlay.style.left = 0;
+  overlay.style.width = "100%";
+  overlay.style.height = "100%";
+  overlay.style.backgroundColor = "transparent";
+
+  var bg = document.createElement("div");
+  bg.style.position = "absolute";
+  bg.style.top = 0;
+  bg.style.left = 0;
+  bg.style.width = "100%";
+  bg.style.height = "100%";
+  bg.style.opacity = "0.6";
+  bg.style.backgroundColor = "#aaaaaa";
+  overlay.appendChild(bg);
+
+  var contentDiv = document.createElement("div");
+  contentDiv.className = "sideBar";
+  contentDiv.style.position = "absolute";
+  contentDiv.style.top = "50%";
+  contentDiv.style.left = "50%";
+  contentDiv.style.width = "40em";
+  contentDiv.style.height = "20em";
+  contentDiv.style.marginLeft = "-20em";
+  contentDiv.style.marginTop = "-10em";
+  contentDiv.style.padding = "10px";
+  contentDiv.style.border = "2px solid black";
+  contentDiv.style.backgroundColor = "rgb(219, 223, 231)";
+  overlay.appendChild(contentDiv);
+
+  var noticeHTML = "";
+  noticeHTML += "<center><h2 style='font-size: 2em'>Upload Profile - Privacy Notice</h2></center>";
+  noticeHTML += "You're about to upload your profile publicly where anyone will be able to access it. ";
+  noticeHTML += "To better diagnose performance problems profiles include the following information:";
+  noticeHTML += "<ul>";
+  noticeHTML += " <li>The <b>URLs</b> and scripts of the tabs that were executing.</li>";
+  noticeHTML += " <li>The <b>metadata of all your Add-ons</b> to identify slow Add-ons.</li>";
+  noticeHTML += " <li>Firefox build and runtime configuration.</li>";
+  noticeHTML += "</ul><br>";
+  noticeHTML += "To view all the information you can download the full profile to a file and open the json structure with a text editor.<br><br>";
+  contentDiv.innerHTML = noticeHTML;
+
+  var cancelButton = document.createElement("input");
+  cancelButton.style.position = "absolute";
+  cancelButton.style.bottom = "10px";
+  cancelButton.type = "button";
+  cancelButton.value = "Cancel";
+  cancelButton.onclick = function() {
+    document.body.removeChild(overlay);
+  }
+  contentDiv.appendChild(cancelButton);
+
+  var uploadButton = document.createElement("input");
+  uploadButton.style.position = "absolute";
+  uploadButton.style.right = "10px";
+  uploadButton.style.bottom = "10px";
+  uploadButton.type = "button";
+  uploadButton.value = "Upload";
+  uploadButton.onclick = function() {
+    document.body.removeChild(overlay);
+    uploadProfile(selected);
+  }
+  contentDiv.appendChild(uploadButton);
+
+  document.body.appendChild(overlay);
+}
+
 function uploadProfile(selected) {
   Parser.getSerializedProfile(!selected, function (dataToUpload) {
     var oXHR = new XMLHttpRequest();
-    oXHR.open("POST", "http://profile-store.appspot.com/store", true);
     oXHR.onload = function (oEvent) {
-      if (oXHR.status == 200) {
-        document.getElementById("upload_status").innerHTML = "Success! Use this <a href='" + document.URL.split('?')[0] + "?report=" + oXHR.responseText + "'>link</a>";
-      } else {
+      if (oXHR.status == 200) {  
+        gReportID = oXHR.responseText;
+        updateDocumentURL();
+        document.getElementById("upload_status").innerHTML = "Success! Use this <a id='linkElem'>link</a>";
+        document.getElementById("linkElem").href = document.URL;
+      } else {  
         document.getElementById("upload_status").innerHTML = "Error " + oXHR.status + " occurred uploading your file.";
-      }
+      }  
     };
     oXHR.onerror = function (oEvent) {
       document.getElementById("upload_status").innerHTML = "Error " + oXHR.status + " occurred uploading your file.";
     }
-    oXHR.onprogress = function (oEvent) {
+    oXHR.upload.onprogress = function(oEvent) {
       if (oEvent.lengthComputable) {
-        document.getElementById("upload_status").innerHTML = "Uploading: " + ((oEvent.loaded / oEvent.total)*100) + "%";
+        var progress = Math.round((oEvent.loaded / oEvent.total)*100);
+        if (progress == 100) {
+          document.getElementById("upload_status").innerHTML = "Uploading: Waiting for server side compression";
+        } else {
+          document.getElementById("upload_status").innerHTML = "Uploading: " + Math.round((oEvent.loaded / oEvent.total)*100) + "%";
+        }
       }
-    }
+    };
 
     var dataSize;
     if (dataToUpload.length > 1024*1024) {
-      dataSize = (dataToUpload.length/1024/1024) + " MB(s)";
+      dataSize = (dataToUpload.length/1024/1024).toFixed(1) + " MB(s)";
     } else {
-      dataSize = (dataToUpload.length/1024) + " KB(s)";
+      dataSize = (dataToUpload.length/1024).toFixed(1) + " KB(s)";
     }
 
     var formData = new FormData();
     formData.append("file", dataToUpload);
     document.getElementById("upload_status").innerHTML = "Uploading Profile (" + dataSize + ")";
+    oXHR.open("POST", "http://profile-store.appspot.com/store", true);
     oXHR.send(formData);
   });
 }
 
 function populate_skip_symbol() {
   var skipSymbolCtrl = document.getElementById('skipsymbol')
   //skipSymbolCtrl.options = gSkipSymbols;
   for (var i = 0; i < gSkipSymbols.length; i++) {
     var elOptNew = document.createElement('option');
     elOptNew.text = gSkipSymbols[i];
     elOptNew.value = gSkipSymbols[i];
     elSel.add(elOptNew);
   }
-
+    
 }
 
 function delete_skip_symbol() {
   var skipSymbol = document.getElementById('skipsymbol').value
 }
 
 function add_skip_symbol() {
-
+  
 }
 
 var gFilterChangeCallback = null;
 var gFilterChangeDelay = 1200;
 function filterOnChange() {
   if (gFilterChangeCallback != null) {
     clearTimeout(gFilterChangeCallback);
     gFilterChangeCallback = null;
   }
 
   gFilterChangeCallback = setTimeout(filterUpdate, gFilterChangeDelay);
 }
 function filterUpdate() {
   gFilterChangeCallback = null;
 
-  filtersChanged();
+  filtersChanged(); 
 
   var filterNameInput = document.getElementById("filterName");
   if (filterNameInput != null) {
-    filterNameInput.focus();
-  }
+    changeFocus(filterNameInput);
+  } 
 }
 
 // Maps document id to a tooltip description
 var tooltip = {
   "mergeFunctions" : "Ignore line information and merge samples based on function names.",
   "showJank" : "Show only samples with >50ms responsiveness.",
   "showJS" : "Show only samples which involve running chrome or content Javascript code.",
   "mergeUnbranched" : "Collapse unbranched call paths in the call tree into a single node.",
@@ -1032,17 +1180,17 @@ var tooltip = {
   "invertCallstack" : "Invert the callstack (Heavy view) to find the most expensive leaf functions.",
   "upload" : "Upload the full profile to public cloud storage to share with others.",
   "upload_select" : "Upload only the selected view.",
   "download" : "Initiate a download of the full profile.",
 }
 
 function addTooltips() {
   for (var elemId in tooltip) {
-    var elem = document.getElementById(elemId);
+    var elem = document.getElementById(elemId); 
     if (!elem)
       continue;
     if (elem.parentNode.nodeName.toLowerCase() == "label")
       elem = elem.parentNode;
     elem.title = tooltip[elemId];
   }
 }
 
@@ -1078,82 +1226,94 @@ InfoBar.prototype = {
     infoText += "<h2>Selection Info</h2>\n<dl>\n";
     infoText += "  <dt>Avg. Responsiveness:</dt><dd>" + avgResponsiveness().toFixed(2) + " ms</dd>\n";
     infoText += "  <dt>Max Responsiveness:</dt><dd>" + maxResponsiveness().toFixed(2) + " ms</dd>\n";
     infoText += "  <dt>Real Interval:</dt><dd>" + effectiveInterval() + "</dd>";
     infoText += "</dl>\n";
     infoText += "<h2>Pre Filtering</h2>\n";
     // Disable for now since it's buggy and not useful
     //infoText += "<label><input type='checkbox' id='mergeFunctions' " + (gMergeFunctions ?" checked='true' ":" ") + " onchange='toggleMergeFunctions()'/>Functions, not lines</label><br>\n";
-    infoText += "<label><input type='checkbox' id='showJS' " + (gJavascriptOnly ?" checked='true' ":" ") + " onchange='toggleJavascriptOnly()'/>Javascript only</label><br>\n";
 
     var filterNameInputOld = document.getElementById("filterName");
-    infoText += "<label>Filter:\n";
-    infoText += "<input type='search' id='filterName' oninput='filterOnChange()'/></label>\n";
+    infoText += "<a>Filter:\n";
+    infoText += "<input type='search' id='filterName' oninput='filterOnChange()'/></a>\n";
 
     infoText += "<h2>Post Filtering</h2>\n";
     infoText += "<label><input type='checkbox' id='showJank' " + (gJankOnly ?" checked='true' ":" ") + " onchange='toggleJank()'/>Show Jank only</label>\n";
     infoText += "<h2>View Options</h2>\n";
+    infoText += "<label><input type='checkbox' id='showJS' " + (gJavascriptOnly ?" checked='true' ":" ") + " onchange='toggleJavascriptOnly()'/>Javascript only</label><br>\n";
     infoText += "<label><input type='checkbox' id='mergeUnbranched' " + (gMergeUnbranched ?" checked='true' ":" ") + " onchange='toggleMergeUnbranched()'/>Merge unbranched call paths</label><br>\n";
     infoText += "<label><input type='checkbox' id='invertCallstack' " + (gInvertCallstack ?" checked='true' ":" ") + " onchange='toggleInvertCallStack()'/>Invert callstack</label><br>\n";
 
     infoText += "<h2>Share</h2>\n";
     infoText += "<div id='upload_status' aria-live='polite'>No upload in progress</div><br>\n";
     infoText += "<input type='button' id='upload' value='Upload full profile'>\n";
     infoText += "<input type='button' id='upload_select' value='Upload view'><br>\n";
     infoText += "<input type='button' id='download' value='Download full profile'>\n";
 
+    infoText += "<h2>Compare</h2>\n";
+    infoText += "<input type='button' id='compare' value='Compare'>\n";
+
     //infoText += "<br>\n";
     //infoText += "Skip functions:<br>\n";
     //infoText += "<select size=8 id='skipsymbol'></select><br />"
     //infoText += "<input type='button' id='delete_skipsymbol' value='Delete'/><br />\n";
     //infoText += "<input type='button' id='add_skipsymbol' value='Add'/><br />\n";
-
+    
     infobar.innerHTML = infoText;
     addTooltips();
 
     var filterNameInputNew = document.getElementById("filterName");
     if (filterNameInputOld != null && filterNameInputNew != null) {
       filterNameInputNew.parentNode.replaceChild(filterNameInputOld, filterNameInputNew);
       //filterNameInputNew.value = filterNameInputOld.value;
+    } else if (gQueryParamFilterName != null) {
+      filterNameInputNew.value = gQueryParamFilterName;
+      gQueryParamFilterName = null;
+    }
+    document.getElementById('compare').onclick = function() {
+      openProfileCompare();
     }
     document.getElementById('upload').onclick = function() {
-      uploadProfile(false);
+      promptUploadProfile(false);
     };
     document.getElementById('download').onclick = downloadProfile;
     document.getElementById('upload_select').onclick = function() {
-      uploadProfile(true);
+      promptUploadProfile(true);
     };
     //document.getElementById('delete_skipsymbol').onclick = delete_skip_symbol;
     //document.getElementById('add_skipsymbol').onclick = add_skip_symbol;
 
     //populate_skip_symbol();
   }
 }
 
-// in light mode we simplify the UI by default
-var gLightMode = false;
 var gNumSamples = 0;
 var gMeta = null;
 var gSymbols = {};
 var gFunctions = {};
+var gResources = {};
 var gHighlightedCallstack = [];
+var gFrameView = null;
 var gTreeManager = null;
 var gSampleBar = null;
 var gBreadcrumbTrail = null;
 var gHistogramView = null;
 var gDiagnosticBar = null;
 var gVideoPane = null;
 var gPluginView = null;
 var gFileList = null;
 var gInfoBar = null;
 var gMainArea = null;
 var gCurrentlyShownSampleData = null;
 var gSkipSymbols = ["test2", "test1"];
 var gAppendVideoCapture = null;
+var gQueryParamFilterName = null;
+var gRestoreSelection = null;
+var gReportID = null;
 
 function getTextData() {
   var data = [];
   var samples = gCurrentlyShownSampleData;
   for (var i = 0; i < samples.length; i++) {
     data.push(samples[i].lines.join("\n"));
   }
   return data.join("\n");
@@ -1185,17 +1345,17 @@ function loadLocalStorageProfile(profile
   var reporter = enterProgressUI();
   var subreporters = reporter.addSubreporters({
     fileLoading: 1000,
     parsing: 1000
   });
 
   gLocalStorage.getProfile(profileKey, function(profile) {
     subreporters.fileLoading.finish();
-    loadRawProfile(subreporters.parsing, JSON.stringify(profile));
+    loadRawProfile(subreporters.parsing, profile, profileKey);
   });
   subreporters.fileLoading.begin("Reading local storage...");
 }
 
 function appendVideoCapture(videoCapture) {
   if (videoCapture.indexOf("://") == -1) {
     videoCapture = EIDETICKER_BASE_URL + videoCapture;
   }
@@ -1249,24 +1409,28 @@ function loadProfileURL(url) {
     fileLoading: 1000,
     parsing: 1000
   });
 
   var xhr = new XMLHttpRequest();
   xhr.open("GET", url, true);
   xhr.responseType = "text";
   xhr.onreadystatechange = function (e) {
-    if (xhr.readyState === 4 && xhr.status === 200) {
+    if (xhr.readyState === 4 && (xhr.status === 200 || xhr.status === 0)) {
       subreporters.fileLoading.finish();
       PROFILERLOG("Got profile from '" + url + "'.");
-      loadRawProfile(subreporters.parsing, xhr.responseText);
+      if (xhr.responseText == null || xhr.responseText === "") {
+        subreporters.fileLoading.begin("Profile '" + url + "' is empty. Did you set the CORS headers?");
+        return;
+      }
+      loadRawProfile(subreporters.parsing, xhr.responseText, url);
     }
   };
-  xhr.onerror = function (e) {
-    subreporters.fileLoading.begin("Error fetching profile :(. URL:  " + url);
+  xhr.onerror = function (e) { 
+    subreporters.fileLoading.begin("Error fetching profile :(. URL: '" + url + "'. Did you set the CORS headers?");
   }
   xhr.onprogress = function (e) {
     if (e.lengthComputable && (e.loaded <= e.total)) {
       subreporters.fileLoading.setProgress(e.loaded / e.total);
     } else {
       subreporters.fileLoading.setProgress(NaN);
     }
   };
@@ -1276,39 +1440,44 @@ function loadProfileURL(url) {
 
 function loadProfile(rawProfile) {
   if (!rawProfile)
     return;
   var reporter = enterProgressUI();
   loadRawProfile(reporter, rawProfile);
 }
 
-function loadRawProfile(reporter, rawProfile) {
+function loadRawProfile(reporter, rawProfile, profileId) {
   PROFILERLOG("Parse raw profile: ~" + rawProfile.length + " bytes");
   reporter.begin("Parsing...");
+  if (rawProfile == null || rawProfile.length === 0) {
+    reporter.begin("Profile is null or empty");
+    return;
+  }
   var startTime = Date.now();
   var parseRequest = Parser.parse(rawProfile, {
-    appendVideoCapture : gAppendVideoCapture,
+    appendVideoCapture : gAppendVideoCapture,  
+    profileId: profileId,
   });
   gVideoCapture = null;
   parseRequest.addEventListener("progress", function (progress, action) {
     if (action)
       reporter.setAction(action);
     reporter.setProgress(progress);
   });
   parseRequest.addEventListener("finished", function (result) {
     console.log("parsing (in worker): " + (Date.now() - startTime) + "ms");
     reporter.finish();
     gMeta = result.meta;
     gNumSamples = result.numSamples;
     gSymbols = result.symbols;
     gFunctions = result.functions;
+    gResources = result.resources;
     enterFinishedProfileUI();
-    if (gFileList)
-      gFileList.profileParsingFinished();
+    gFileList.profileParsingFinished();
   });
 }
 
 var gImportFromAddonSubreporters = null;
 
 window.addEventListener("message", function messageFromAddon(msg) {
   // This is triggered by the profiler add-on.
   var o = JSON.parse(msg.data);
@@ -1325,26 +1494,19 @@ window.addEventListener("message", funct
       gImportFromAddonSubreporters.import.setProgress(o.progress);
       if (o.action != null) {
           gImportFromAddonSubreporters.import.setAction(o.action);
       }
       break;
     case "importFromAddonFinish":
       importFromAddonFinish(o.rawProfile);
       break;
-    case "receiveProfileData":
-      receiveProfileData(o.rawProfile);
-      break;
   }
 });
 
-function receiveProfileData(data) {
- loadProfile(JSON.stringify(data));
-}
-
 function importFromAddonFinish(rawProfile) {
   gImportFromAddonSubreporters.import.finish();
   loadRawProfile(gImportFromAddonSubreporters.parsing, rawProfile);
 }
 
 var gInvertCallstack = false;
 function toggleInvertCallStack() {
   gTreeManager.saveReverseSelectionSnapshot(gJavascriptOnly);
@@ -1352,23 +1514,23 @@ function toggleInvertCallStack() {
   var startTime = Date.now();
   viewOptionsChanged();
   console.log("invert time: " + (Date.now() - startTime) + "ms");
 }
 
 var gMergeUnbranched = false;
 function toggleMergeUnbranched() {
   gMergeUnbranched = !gMergeUnbranched;
-  viewOptionsChanged();
+  viewOptionsChanged(); 
 }
 
 var gMergeFunctions = true;
 function toggleMergeFunctions() {
   gMergeFunctions = !gMergeFunctions;
-  filtersChanged();
+  filtersChanged(); 
 }
 
 var gJankOnly = false;
 var gJankThreshold = 50 /* ms */;
 function toggleJank(/* optional */ threshold) {
   // Currently we have no way to change the threshold in the UI
   // once we add this we will need to change the tooltip.
   gJankOnly = !gJankOnly;
@@ -1388,30 +1550,36 @@ function toggleJavascriptOnly() {
   }
   gJavascriptOnly = !gJavascriptOnly;
   gTreeManager.saveSelectionSnapshot(gJavascriptOnly);
   filtersChanged();
 }
 
 var gSampleFilters = [];
 function focusOnSymbol(focusSymbol, name) {
-  var newFilterChain = gSampleFilters.concat([{type: "FocusedFrameSampleFilter", focusedSymbol: focusSymbol}]);
+  var newFilterChain = gSampleFilters.concat([{type: "FocusedFrameSampleFilter", name: name, focusedSymbol: focusSymbol}]);
   gBreadcrumbTrail.addAndEnter({
     title: name,
     enterCallback: function () {
       gSampleFilters = newFilterChain;
       filtersChanged();
     }
   });
 }
 
-function focusOnCallstack(focusedCallstack, name) {
+function focusOnCallstack(focusedCallstack, name, overwriteCallstack) {
+  var invertCallback =  gInvertCallstack;
+  if (overwriteCallstack != null) {
+    invertCallstack = overwriteCallstack;
+  }
   var filter = {
-    type: gInvertCallstack ? "FocusedCallstackPostfixSampleFilter" : "FocusedCallstackPrefixSampleFilter",
-    focusedCallstack: focusedCallstack
+    type: !invertCallstack ? "FocusedCallstackPostfixSampleFilter" : "FocusedCallstackPrefixSampleFilter",
+    name: name,
+    focusedCallstack: focusedCallstack,
+    appliesToJS: gJavascriptOnly
   };
   var newFilterChain = gSampleFilters.concat([filter]);
   gBreadcrumbTrail.addAndEnter({
     title: name,
     enterCallback: function () {
       gSampleFilters = newFilterChain;
       filtersChanged();
     }
@@ -1445,40 +1613,34 @@ function viewJSSource(sample) {
 function setHighlightedCallstack(samples, heaviestSample) {
   PROFILERTRACE("highlight: " + samples);
   gHighlightedCallstack = samples;
   gHistogramView.highlightedCallstackChanged(gHighlightedCallstack);
   if (!gInvertCallstack) {
     // Always show heavy
     heaviestSample = heaviestSample.clone().reverse();
   }
-  if (gSampleBar)
+  
+  if (gSampleBar) {
     gSampleBar.setSample(heaviestSample);
+  }
 }
 
-function enterMainUI(isLightMode) {
-  if (isLightMode !== undefined) {
-    gLightMode = isLightMode;
-    if (gLightMode) {
-      gJavascriptOnly = true;
-    }
-  }
-
+function enterMainUI() {
   var uiContainer = document.createElement("div");
   uiContainer.id = "ui";
 
-  //gFileList.loadProfileListFromLocalStorage();
-  if (!gLightMode) {
-    gFileList = new FileList();
-    uiContainer.appendChild(gFileList.getContainer());
+  gFileList = new FileList();
+  uiContainer.appendChild(gFileList.getContainer());
 
-    gFileList.addFile();
-    gInfoBar = new InfoBar();
-    uiContainer.appendChild(gInfoBar.getContainer());
-  }
+  //gFileList.addFile();
+  gFileList.loadProfileListFromLocalStorage();
+
+  gInfoBar = new InfoBar();
+  uiContainer.appendChild(gInfoBar.getContainer());
 
   gMainArea = document.createElement("div");
   gMainArea.id = "mainarea";
   uiContainer.appendChild(gMainArea);
   document.body.appendChild(uiContainer);
 
   var profileEntryPane = document.createElement("div");
   profileEntryPane.className = "profileEntryPane";
@@ -1517,19 +1679,17 @@ function enterProgressUI() {
   gMainArea.appendChild(profileProgressPane);
 
   Parser.updateLogSetting();
 
   return totalProgressReporter;
 }
 
 function enterFinishedProfileUI() {
-  //dump("prepare to save\n");
-  //saveProfileToLocalStorage();
-  //dump("prepare to saved\n");
+  saveProfileToLocalStorage();
 
   var finishedProfilePaneBackgroundCover = document.createElement("div");
   finishedProfilePaneBackgroundCover.className = "finishedProfilePaneBackgroundCover";
 
   var finishedProfilePane = document.createElement("table");
   var rowIndex = 0;
   var currRow;
   finishedProfilePane.style.width = "100%";
@@ -1539,49 +1699,54 @@ function enterFinishedProfileUI() {
   finishedProfilePane.cellSpacing = "0";
   finishedProfilePane.borderCollapse = "collapse";
   finishedProfilePane.className = "finishedProfilePane";
   setTimeout(function() {
     // Work around a webkit bug. For some reason the table doesn't show up
     // until some actions happen such as focusing this box
     var filterNameInput = document.getElementById("filterName");
     if (filterNameInput != null) {
-      filterNameInput.focus();
+      changeFocus(filterNameInput);
      }
   }, 100);
 
   gBreadcrumbTrail = new BreadcrumbTrail();
   currRow = finishedProfilePane.insertRow(rowIndex++);
   currRow.insertCell(0).appendChild(gBreadcrumbTrail.getContainer());
 
   gHistogramView = new HistogramView();
   currRow = finishedProfilePane.insertRow(rowIndex++);
   currRow.insertCell(0).appendChild(gHistogramView.getContainer());
 
-  if (typeof DiagnosticBar !== "undefined") {
-    gDiagnosticBar = new DiagnosticBar();
-    gDiagnosticBar.setDetailsListener(function(details) {
-      if (details.indexOf("bug ") == 0) {
-        window.open('https://bugzilla.mozilla.org/show_bug.cgi?id=' + details.substring(4));
-      } else {
-        var sourceView = new SourceView();
-        sourceView.setText("Diagnostic", js_beautify(details));
-        gMainArea.appendChild(sourceView.getContainer());
-      }
-    });
+  if (false && gLocation.indexOf("file:") == 0) {
+    // Local testing for frameView
+    gFrameView = new FrameView();
     currRow = finishedProfilePane.insertRow(rowIndex++);
-    currRow.insertCell(0).appendChild(gDiagnosticBar.getContainer());
+    currRow.insertCell(0).appendChild(gFrameView.getContainer());
   }
 
+  gDiagnosticBar = new DiagnosticBar();
+  gDiagnosticBar.setDetailsListener(function(details) {
+    if (details.indexOf("bug ") == 0) {
+      window.open('https://bugzilla.mozilla.org/show_bug.cgi?id=' + details.substring(4));
+    } else {
+      var sourceView = new SourceView();
+      sourceView.setText("Diagnostic", js_beautify(details));
+      gMainArea.appendChild(sourceView.getContainer());
+    }
+  });
+  currRow = finishedProfilePane.insertRow(rowIndex++);
+  currRow.insertCell(0).appendChild(gDiagnosticBar.getContainer());
+
   // For testing:
   //gMeta.videoCapture = {
   //  src: "http://videos-cdn.mozilla.net/brand/Mozilla_Firefox_Manifesto_v0.2_640.webm",
   //};
 
-  if (!gLightMode && gMeta && gMeta.videoCapture) {
+  if (gMeta && gMeta.videoCapture) {
     gVideoPane = new VideoPane(gMeta.videoCapture);
     gVideoPane.onTimeChange(videoPaneTimeChange);
     currRow = finishedProfilePane.insertRow(rowIndex++);
     currRow.insertCell(0).appendChild(gVideoPane.getContainer());
   }
 
   var treeContainerDiv = document.createElement("div");
   treeContainerDiv.className = "treeContainer";
@@ -1590,74 +1755,122 @@ function enterFinishedProfileUI() {
 
   gTreeManager = new ProfileTreeManager();
   currRow = finishedProfilePane.insertRow(rowIndex++);
   currRow.style.height = "100%";
   var cell = currRow.insertCell(0);
   cell.appendChild(treeContainerDiv);
   treeContainerDiv.appendChild(gTreeManager.getContainer());
 
-  if (!gLightMode) {
-    gSampleBar = new SampleBar();
-    treeContainerDiv.appendChild(gSampleBar.getContainer());
-  }
+  gSampleBar = new SampleBar();
+  treeContainerDiv.appendChild(gSampleBar.getContainer());
 
   // sampleBar
 
-  if (!gLightMode) {
-    gPluginView = new PluginView();
-    //currRow = finishedProfilePane.insertRow(4);
-    treeContainerDiv.appendChild(gPluginView.getContainer());
-  }
+  gPluginView = new PluginView();
+  //currRow = finishedProfilePane.insertRow(4);
+  treeContainerDiv.appendChild(gPluginView.getContainer());
 
   gMainArea.appendChild(finishedProfilePaneBackgroundCover);
   gMainArea.appendChild(finishedProfilePane);
 
+  var currentBreadcrumb = gSampleFilters;
   gBreadcrumbTrail.add({
     title: "Complete Profile",
     enterCallback: function () {
       gSampleFilters = [];
       filtersChanged();
     }
   });
+  if (currentBreadcrumb == null || currentBreadcrumb.length == 0) {
+    gTreeManager.restoreSerializedSelectionSnapshot(gRestoreSelection);
+    viewOptionsChanged();
+  }
+  for (var i = 0; i < currentBreadcrumb.length; i++) {
+    var filter = currentBreadcrumb[i];
+    var forceSelection = null;
+    if (gRestoreSelection != null && i == currentBreadcrumb.length - 1) {
+      forceSelection = gRestoreSelection;
+    }
+    switch (filter.type) {
+      case "FocusedFrameSampleFilter":
+        focusOnSymbol(filter.name, filter.symbolName);
+        gBreadcrumbTrail.enterLastItem(forceSelection);
+      case "FocusedCallstackPrefixSampleFilter":
+        focusOnCallstack(filter.focusedCallstack, filter.name, false);
+        gBreadcrumbTrail.enterLastItem(forceSelection);
+      case "FocusedCallstackPostfixSampleFilter":
+        focusOnCallstack(filter.focusedCallstack, filter.name, true);
+        gBreadcrumbTrail.enterLastItem(forceSelection);
+      case "RangeSampleFilter":
+        gHistogramView.selectRange(filter.start, filter.end);
+        gBreadcrumbTrail.enterLastItem(forceSelection);
+    }
+  }
+}
+
+// Make all focus change events go through this function.
+// This function will mediate the focus changes in case
+// that we're in a compare view. In a compare view an inactive
+// instance of cleopatra should not steal focus from the active
+// cleopatra instance.
+function changeFocus(elem) {
+  if (window.comparator_changeFocus) {
+    window.comparator_changeFocus(elem);
+  } else {
+    PROFILERLOG("FOCUS\n\n\n\n\n\n\n\n\n");
+    elem.focus();
+  }
+}
+
+function comparator_receiveSelection(snapshot, frameData) {
+  gTreeManager.restoreSerializedSelectionSnapshot(snapshot); 
+  if (frameData)
+    gTreeManager.highlightFrame(frameData);
+  viewOptionsChanged();
 }
 
 function filtersChanged() {
+  if (window.comparator_setSelection) {
+  //  window.comparator_setSelection(gTreeManager.serializeCurrentSelectionSnapshot(), null);
+  }
+  updateDocumentURL();
   var data = { symbols: {}, functions: {}, samples: [] };
 
   gHistogramView.dataIsOutdated();
   var filterNameInput = document.getElementById("filterName");
   var updateRequest = Parser.updateFilters({
     mergeFunctions: gMergeFunctions,
-    nameFilter: (filterNameInput && filterNameInput.value) || "",
+    nameFilter: (filterNameInput && filterNameInput.value) || gQueryParamFilterName || "",
     sampleFilters: gSampleFilters,
     jankOnly: gJankOnly,
     javascriptOnly: gJavascriptOnly
   });
   var start = Date.now();
   updateRequest.addEventListener("finished", function (filteredSamples) {
     console.log("profile filtering (in worker): " + (Date.now() - start) + "ms.");
     gCurrentlyShownSampleData = filteredSamples;
-    if (gInfoBar)
-      gInfoBar.display();
+    gInfoBar.display();
 
-    if (gPluginView && gSampleFilters.length > 0 && gSampleFilters[gSampleFilters.length-1].type === "PluginView") {
+    if (gSampleFilters.length > 0 && gSampleFilters[gSampleFilters.length-1].type === "PluginView") {
       start = Date.now();
       gPluginView.display(gSampleFilters[gSampleFilters.length-1].pluginName, gSampleFilters[gSampleFilters.length-1].param,
                           gCurrentlyShownSampleData, gHighlightedCallstack);
       console.log("plugin displaying: " + (Date.now() - start) + "ms.");
-    } else if (gPluginView) {
+    } else {
       gPluginView.hide();
     }
   });
 
   var histogramRequest = Parser.calculateHistogramData();
   histogramRequest.addEventListener("finished", function (data) {
     start = Date.now();
     gHistogramView.display(data.histogramData, data.frameStart, data.widthSum, gHighlightedCallstack);
+    if (gFrameView)
+      gFrameView.display(data.histogramData, data.frameStart, data.widthSum, gHighlightedCallstack);
     console.log("histogram displaying: " + (Date.now() - start) + "ms.");
   });
 
   if (gDiagnosticBar) {
     var diagnosticsRequest = Parser.calculateDiagnosticItems(gMeta);
     diagnosticsRequest.addEventListener("finished", function (diagnosticItems) {
       start = Date.now();
       gDiagnosticBar.display(diagnosticItems);
@@ -1672,12 +1885,123 @@ function viewOptionsChanged() {
   gTreeManager.dataIsOutdated();
   var filterNameInput = document.getElementById("filterName");
   var updateViewOptionsRequest = Parser.updateViewOptions({
     invertCallstack: gInvertCallstack,
     mergeUnbranched: gMergeUnbranched
   });
   updateViewOptionsRequest.addEventListener("finished", function (calltree) {
     var start = Date.now();
-    gTreeManager.display(calltree, gSymbols, gFunctions, gMergeFunctions, filterNameInput && filterNameInput.value);
+    gTreeManager.display(calltree, gSymbols, gFunctions, gResources, gMergeFunctions, filterNameInput && filterNameInput.value);
     console.log("tree displaying: " + (Date.now() - start) + "ms.");
   });
 }
+
+function loadQueryData(queryData) {
+  var isFiltersChanged = false;
+  var queryDataOriginal = queryData;
+  var queryData = {};
+  for (var i in queryDataOriginal) {
+    queryData[i] = unQueryEscape(queryDataOriginal[i]);
+  }
+  if (queryData.search) {
+    gQueryParamFilterName = queryData.search;
+    isFiltersChanged = true;
+  }
+  if (queryData.jankOnly) {
+    gJankOnly = queryData.jankOnly;
+    isFiltersChanged = true;
+  }
+  if (queryData.javascriptOnly) {
+    gJavascriptOnly = queryData.javascriptOnly;
+    isFiltersChanged = true;
+  }
+  if (queryData.mergeUnbranched) {
+    gMergeUnbranched = queryData.mergeUnbranched;
+    isFiltersChanged = true;
+  }
+  if (queryData.invertCallback) {
+    gInvertCallstack = queryData.invertCallback;
+    isFiltersChanged = true;
+  }
+  if (queryData.report) {
+    gReportID = queryData.report;
+  }
+  if (queryData.filter) {
+    var filterChain = JSON.parse(queryData.filter);
+    gSampleFilters = filterChain;
+  }
+  if (queryData.selection) {
+    var selection = queryData.selection;
+    gRestoreSelection = selection;
+  }
+
+  if (isFiltersChanged) {
+    //filtersChanged();
+  }
+}
+
+function unQueryEscape(str) {
+  return decodeURIComponent(str);
+}
+
+function queryEscape(str) {
+  return encodeURIComponent(encodeURIComponent(str));
+}
+
+function updateDocumentURL() {
+  location.hash = getDocumentHashString();
+  return document.location;
+}
+
+function getDocumentHashString() {
+  var query = "";
+  if (gReportID) {
+    if (query != "")
+      query += "&";
+    query += "report=" + queryEscape(gReportID);
+  }
+  if (document.getElementById("filterName") != null &&
+      document.getElementById("filterName").value != null &&
+      document.getElementById("filterName").value != "") {
+    if (query != "")
+      query += "&";
+    query += "search=" + queryEscape(document.getElementById("filterName").value);
+  }
+  // For now don't restore the view rest
+  return query;
+  if (gJankOnly) {
+    if (query != "")
+      query += "&";
+    query += "jankOnly=" + queryEscape(gJankOnly);
+  }
+  if (gJavascriptOnly) {
+    if (query != "")
+      query += "&";
+    query += "javascriptOnly=" + queryEscape(gJavascriptOnly);
+  }
+  if (gMergeUnbranched) {
+    if (query != "")
+      query += "&";
+    query += "mergeUnbranched=" + queryEscape(gMergeUnbranched);
+  }
+  if (gInvertCallstack) {
+    if (query != "")
+      query += "&";
+    query += "invertCallback=" + queryEscape(gInvertCallstack);
+  }
+  if (gSampleFilters && gSampleFilters.length != 0) {
+    if (query != "")
+      query += "&";
+    query += "filter=" + queryEscape(JSON.stringify(gSampleFilters));
+  }
+  if (gTreeManager.hasNonTrivialSelection()) {
+    if (query != "")
+      query += "&";
+    query += "selection=" + queryEscape(gTreeManager.serializeCurrentSelectionSnapshot());
+  }
+  if (!gReportID) {
+    query = "uploadProfileFirst!";
+  }
+
+  return query;
+}
+
--- a/python/mozbuild/mozbuild/mozconfig.py
+++ b/python/mozbuild/mozbuild/mozconfig.py
@@ -3,19 +3,16 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from __future__ import unicode_literals
 
 import os
 import re
 import subprocess
 
-import pymake.parser
-from pymake.data import Makefile
-
 from mach.mixin.process import ProcessExecutionMixin
 
 
 MOZ_MYCONFIG_ERROR = '''
 The MOZ_MYCONFIG environment variable to define the location of mozconfigs
 is deprecated. If you wish to define the mozconfig path via an environment
 variable, use MOZCONFIG instead.
 '''.strip()
--- a/toolkit/components/satchel/nsFormAutoComplete.js
+++ b/toolkit/components/satchel/nsFormAutoComplete.js
@@ -237,17 +237,18 @@ FormAutoComplete.prototype = {
          * 2) how recently it was last used - items used recently are ranked higher
          * 3) additional weight for aged entries surviving expiry - these entries are relevant
          *    since they have been used multiple times over a large time span so rank them higher
          * The score is then divided by the bucket size and we round the result so that entries
          * with a very similar frecency are bucketed together with an alphabetical sort. This is
          * to reduce the amount of moving around by entries while typing.
          */
 
-        let query = "SELECT value, " +
+        let query = "/* do not warn (bug 496471): can't use an index */ " +
+                    "SELECT value, " +
                     "ROUND( " +
                         "timesUsed / MAX(1.0, (lastUsed - firstUsed) / :timeGroupingSize) * " +
                         "MAX(1.0, :maxTimeGroupings - (:now - lastUsed) / :timeGroupingSize) * "+
                         "MAX(1.0, :agedWeight * (firstUsed < :expiryDate)) / " +
                         ":bucketSize "+
                     ", 3) AS frecency, " +
                     boundaryCalc + " AS boundaryBonuses " +
                     "FROM moz_formhistory " +