about:startup - merge with trunk

This commit is contained in:
Daniel Brooks 2010-09-27 05:38:17 -05:00
commit 7961ced955
68 changed files with 8846 additions and 25 deletions

View File

@ -493,7 +493,7 @@ const gPopupBlockerObserver = {
if (pageReport) {
for (var i = 0; i < pageReport.length; ++i) {
// popupWindowURI will be null if the file picker popup is blocked.
// xxxdz this should make the option say "Show file picker" and do it (Bug 590306)
// xxxdz this should make the option say "Show file picker" and do it (Bug 590306)
if (!pageReport[i].popupWindowURI)
continue;
var popupURIspec = pageReport[i].popupWindowURI.spec;
@ -4254,7 +4254,7 @@ var XULBrowserWindow = {
// Of course, this is especially wrong with bfcache on...
// fix bug 253793 - turn off highlight when page changes
gFindBar.getElement("highlight").checked = false;
gFindBar.getElement("highlight").checked = false;
}
// See bug 358202, when tabs are switched during a drag operation,

0
browser/base/jar.mn Normal file → Executable file
View File

0
browser/components/about/AboutRedirector.cpp Normal file → Executable file
View File

0
browser/components/sessionstore/nsISessionStartup.idl Normal file → Executable file
View File

36
browser/components/sessionstore/src/nsSessionStartup.js Normal file → Executable file
View File

@ -1,4 +1,4 @@
/*
/*
# ***** BEGIN LICENSE BLOCK *****
# * Version: MPL 1.1/GPL 2.0/LGPL 2.1
# *
@ -34,33 +34,33 @@
# * the provisions above, a recipient may use your version of this file under
# * the terms of any one of the MPL, the GPL or the LGPL.
# *
# * ***** END LICENSE BLOCK *****
# * ***** END LICENSE BLOCK *****
*/
/**
# * Session Storage and Restoration
# *
# *
# * Overview
# * This service reads user's session file at startup, and makes a determination
# * as to whether the session should be restored. It will restore the session
# * This service reads user's session file at startup, and makes a determination
# * as to whether the session should be restored. It will restore the session
# * under the circumstances described below. If the auto-start Private Browsing
# * mode is active, however, the session is never restored.
# *
# *
# * Crash Detection
# * The session file stores a session.state property, that
# * indicates whether the browser is currently running. When the browser shuts
# * The session file stores a session.state property, that
# * indicates whether the browser is currently running. When the browser shuts
# * down, the field is changed to "stopped". At startup, this field is read, and
# * if its value is "running", then it's assumed that the browser had previously
# * crashed, or at the very least that something bad happened, and that we should
# * restore the session.
# *
# *
# * Forced Restarts
# * In the event that a restart is required due to application update or extension
# * installation, set the browser.sessionstore.resume_session_once pref to true,
# * and the session will be restored the next time the browser starts.
# *
# *
# * Always Resume
# * This service will always resume the session if the integer pref
# * This service will always resume the session if the integer pref
# * browser.startup.page is set to 3.
*/
@ -112,15 +112,15 @@ SessionStartup.prototype = {
getService(Ci.nsIProperties);
let sessionFile = dirService.get("ProfD", Ci.nsILocalFile);
sessionFile.append("sessionstore.js");
let doResumeSession = prefBranch.getBoolPref("sessionstore.resume_session_once") ||
prefBranch.getIntPref("startup.page") == 3;
// only read the session file if config allows possibility of restoring
var resumeFromCrash = prefBranch.getBoolPref("sessionstore.resume_from_crash");
if (!resumeFromCrash && !doResumeSession || !sessionFile.exists())
return;
// get string containing session state
this._iniString = this._readStateFile(sessionFile);
if (!this._iniString)
@ -146,7 +146,7 @@ SessionStartup.prototype = {
let lastSessionCrashed =
initialState && initialState.session && initialState.session.state &&
initialState.session.state == STATE_RUNNING_STR;
// set the startup type
if (lastSessionCrashed && resumeFromCrash)
this._sessionType = Ci.nsISessionStartup.RECOVER_SESSION;
@ -176,11 +176,11 @@ SessionStartup.prototype = {
*/
observe: function sss_observe(aSubject, aTopic, aData) {
switch (aTopic) {
case "app-startup":
case "app-startup":
Services.obs.addObserver(this, "final-ui-startup", true);
Services.obs.addObserver(this, "quit-application", true);
break;
case "final-ui-startup":
case "final-ui-startup":
Services.obs.removeObserver(this, "final-ui-startup");
Services.obs.removeObserver(this, "quit-application");
this.init();
@ -216,7 +216,7 @@ SessionStartup.prototype = {
var wType = aWindow.document.documentElement.getAttribute("windowtype");
if (wType != "navigator:browser")
return;
/**
* Note: this relies on the fact that nsBrowserContentHandler will return
* a different value the first time its getter is called after an update,

0
browser/themes/gnomestripe/browser/Toolbar-small.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

0
browser/themes/gnomestripe/browser/Toolbar.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

@ -91,6 +91,8 @@ static RedirEntry kRedirMap[] = {
{ "addons", "chrome://mozapps/content/extensions/extensions.xul",
nsIAboutModule::ALLOW_SCRIPT },
{ "support", "chrome://global/content/aboutSupport.xhtml",
nsIAboutModule::ALLOW_SCRIPT },
{ "startup", "chrome://global/content/aboutStartup.xhtml",
nsIAboutModule::ALLOW_SCRIPT }
};
static const int kRedirTotal = NS_ARRAY_LENGTH(kRedirMap);

View File

@ -186,6 +186,7 @@ const mozilla::Module::ContractIDEntry kDocShellContracts[] = {
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "memory", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "addons", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "support", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "startup", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
{ NS_URI_LOADER_CONTRACTID, &kNS_URI_LOADER_CID },
{ NS_DOCUMENTLOADER_SERVICE_CONTRACTID, &kNS_DOCUMENTLOADER_SERVICE_CID },
{ NS_EXTERNALHELPERAPPSERVICE_CONTRACTID, &kNS_EXTERNALHELPERAPPSERVICE_CID },

0
js/src/shell/njs Executable file → Normal file
View File

0
js/src/tests/js1_8_1/trace/regress-489682.js Normal file → Executable file
View File

0
layout/style/crashtests/592698-1.html Executable file → Normal file
View File

View File

View File

Before

Width:  |  Height:  |  Size: 180 B

After

Width:  |  Height:  |  Size: 180 B

View File

Before

Width:  |  Height:  |  Size: 178 B

After

Width:  |  Height:  |  Size: 178 B

View File

Before

Width:  |  Height:  |  Size: 120 B

After

Width:  |  Height:  |  Size: 120 B

View File

Before

Width:  |  Height:  |  Size: 105 B

After

Width:  |  Height:  |  Size: 105 B

View File

Before

Width:  |  Height:  |  Size: 111 B

After

Width:  |  Height:  |  Size: 111 B

View File

Before

Width:  |  Height:  |  Size: 110 B

After

Width:  |  Height:  |  Size: 110 B

View File

Before

Width:  |  Height:  |  Size: 119 B

After

Width:  |  Height:  |  Size: 119 B

View File

Before

Width:  |  Height:  |  Size: 101 B

After

Width:  |  Height:  |  Size: 101 B

View File

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

View File

View File

View File

View File

View File

View File

View File

@ -121,6 +121,8 @@ interface nsIAppStartup2 : nsIAppStartup
* True if the application is in the process of shutting down.
*/
readonly attribute boolean shuttingDown;
readonly attribute PRUint64 restoredTimestamp;
};
%{C++

View File

@ -24,6 +24,7 @@
* Pierre Phaneuf <pp@ludusdesign.com>
* Robert O'Callahan <roc+moz@cs.cmu.edu>
* Benjamin Smedberg <bsmedberg@covad.net>
* Daniel Brooks <db48x@db48x.net>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
@ -69,8 +70,13 @@
#include "nsWidgetsCID.h"
#include "nsAppShellCID.h"
#include "mozilla/Services.h"
#include "mozilla/storage.h"
#include "mozilla/FunctionTimer.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsIXULRuntime.h"
#include "nsIXULAppInfo.h"
#include "nsXPCOMCIDInternal.h"
#include "nsIPropertyBag2.h"
static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
@ -92,6 +98,10 @@ public:
}
};
nsresult OpenStartupDatabase(mozIStorageConnection **db);
nsresult EnsureTable(mozIStorageConnection *db, const nsACString &table,
const nsACString &schema);
//
// nsAppStartup
//
@ -125,6 +135,9 @@ nsAppStartup::Init()
NS_TIME_FUNCTION_MARK("Got Observer service");
os->AddObserver(this, "quit-application-forced", PR_TRUE);
os->AddObserver(this, "sessionstore-browser-state-restored", PR_TRUE);
os->AddObserver(this, "sessionstore-windows-restored", PR_TRUE);
os->AddObserver(this, "AddonManager-event", PR_TRUE);
os->AddObserver(this, "profile-change-teardown", PR_TRUE);
os->AddObserver(this, "xul-window-registered", PR_TRUE);
os->AddObserver(this, "xul-window-destroyed", PR_TRUE);
@ -467,7 +480,7 @@ nsAppStartup::CreateChromeWindow2(nsIWebBrowserChrome *aParent,
nsCOMPtr<nsIAppShellService> appShell(do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
if (!appShell)
return NS_ERROR_FAILURE;
appShell->CreateTopLevelWindow(0, 0, aChromeFlags,
nsIAppShellService::SIZE_TO_CONTENT,
nsIAppShellService::SIZE_TO_CONTENT,
@ -508,9 +521,156 @@ nsAppStartup::Observe(nsISupports *aSubject,
EnterLastWindowClosingSurvivalArea();
} else if (!strcmp(aTopic, "xul-window-destroyed")) {
ExitLastWindowClosingSurvivalArea();
} else if ((!strcmp(aTopic, "sessionstore-browser-state-restored")) ||
(!strcmp(aTopic, "sessionstore-windows-restored"))) {
RecordStartupDuration();
} else if (!strcmp(aTopic, "AddonManager-event")) {
RecordAddonEvent(aData, aSubject);
} else {
NS_ERROR("Unexpected observer topic.");
}
return NS_OK;
}
nsresult nsAppStartup::RecordStartupDuration()
{
nsresult rv;
PRTime launched = 0, started = 0;
mRestoredTimestamp = PR_Now();
nsCOMPtr<mozIStorageConnection> db;
rv = OpenStartupDatabase(getter_AddRefs(db));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<mozIStorageStatement> statement;
rv = db->CreateStatement(NS_LITERAL_CSTRING("INSERT INTO duration VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)"),
getter_AddRefs(statement));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIXULRuntime> runtime = do_GetService(XULRUNTIME_SERVICE_CONTRACTID);
nsCOMPtr<nsIXULAppInfo> appinfo = do_QueryInterface(runtime);
runtime->GetLaunchTimestamp((PRUint64*)&launched);
runtime->GetStartupTimestamp((PRUint64*)&started);
if (!launched)
launched = started;
nsCAutoString appVersion, appBuild, platformVersion, platformBuild;
appinfo->GetVersion(appVersion);
appinfo->GetAppBuildID(appBuild);
appinfo->GetPlatformVersion(platformVersion);
appinfo->GetPlatformBuildID(platformBuild);
rv = statement->BindInt64Parameter(0, launched);
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->BindInt64Parameter(1, started - launched);
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->BindInt64Parameter(2, mRestoredTimestamp - started);
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->BindStringParameter(3, NS_ConvertUTF8toUTF16(appVersion));
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->BindStringParameter(4, NS_ConvertUTF8toUTF16(appBuild));
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->BindStringParameter(5, NS_ConvertUTF8toUTF16(platformVersion));
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->BindStringParameter(6, NS_ConvertUTF8toUTF16(platformBuild));
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->Execute();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult nsAppStartup::RecordAddonEvent(const PRUnichar *event, nsISupports *details)
{
PRTime now = PR_Now();
nsresult rv;
nsCOMPtr<mozIStorageConnection> db;
rv = OpenStartupDatabase(getter_AddRefs(db));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<mozIStorageStatement> statement;
rv = db->CreateStatement(NS_LITERAL_CSTRING("INSERT INTO events VALUES (?1, ?2, ?3, ?4, ?5)"),
getter_AddRefs(statement));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIPropertyBag2> bag = do_QueryInterface(details);
nsAutoString id, name, version;
bag->GetPropertyAsAString(NS_LITERAL_STRING("id"), id);
bag->GetPropertyAsAString(NS_LITERAL_STRING("name"), name);
bag->GetPropertyAsAString(NS_LITERAL_STRING("version"), version);
rv = statement->BindInt64Parameter(0, now);
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->BindStringParameter(1, id);
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->BindStringParameter(2, name);
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->BindStringParameter(3, version);
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->BindStringParameter(4, nsDependentString(event));
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->Execute();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult OpenStartupDatabase(mozIStorageConnection **db)
{
nsresult rv;
nsCOMPtr<nsIFile> file;
rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(file));
NS_ENSURE_SUCCESS(rv, rv);
rv = file->Append(NS_LITERAL_STRING("startup.sqlite"));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<mozIStorageService> svc = do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = svc->OpenDatabase(file, db);
if (NS_ERROR_FILE_CORRUPTED == rv)
{
svc->BackupDatabaseFile(file, NS_LITERAL_STRING("startup.sqlite.backup"),
nsnull, nsnull);
rv = svc->OpenDatabase(file, db);
NS_ENSURE_SUCCESS(rv, rv);
}
NS_ENSURE_SUCCESS(rv, rv);
rv = EnsureTable(*db,
NS_LITERAL_CSTRING("duration"),
NS_LITERAL_CSTRING("timestamp INTEGER, launch INTEGER, startup INTEGER, appVersion TEXT, appBuild TEXT, platformVersion TEXT, platformBuild TEXT"));
NS_ENSURE_SUCCESS(rv, rv);
rv = EnsureTable(*db,
NS_LITERAL_CSTRING("events"),
NS_LITERAL_CSTRING("timestamp INTEGER, id TEXT, name TEXT, version TEXT, action TEXT"));
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult EnsureTable(mozIStorageConnection *db, const nsACString &table,
const nsACString &schema)
{
nsresult rv;
PRBool exists = false;
rv = db->TableExists(table, &exists);
NS_ENSURE_SUCCESS(rv, rv);
if (!exists)
rv = db->CreateTable(PromiseFlatCString(table).get(),
PromiseFlatCString(schema).get());
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
NS_IMETHODIMP
nsAppStartup::GetRestoredTimestamp(PRUint64 *aResult)
{
*aResult = (PRTime)mRestoredTimestamp;
return NS_OK;
}

View File

@ -85,6 +85,11 @@ private:
PRPackedBool mShuttingDown; // Quit method reentrancy check
PRPackedBool mAttemptingQuit; // Quit(eAttemptQuit) still trying
PRPackedBool mRestart; // Quit(eRestart)
PRTime mRestoredTimestamp;
nsresult RecordStartupDuration();
nsresult RecordAddonEvent(const PRUnichar *event, nsISupports *details);
};
#endif // nsAppStartup_h__

326
toolkit/content/aboutStartup.js Executable file
View File

@ -0,0 +1,326 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the about:startup page.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Daniel Brooks <db48x@db48x.net>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
var Cc = Components.classes;
var Ci = Components.interfaces;
let stringsvc = Components.classes["@mozilla.org/intl/stringbundle;1"]
.getService(Components.interfaces.nsIStringBundleService);
let strings = stringsvc.createBundle("chrome://global/locale/aboutStartup.properties");
let branding = stringsvc.createBundle("chrome://branding/locale/brand.properties");
let brandShortName = branding.GetStringFromName("brandShortName");
function displayTimestamp(id, µs) document.getElementById(id).textContent = formatstamp(µs);
function displayDuration(id, µs) document.getElementById(id).nextSibling.textContent = formatms(msFromµs(µs));
function formatStr(str, args) strings.formatStringFromName("about.startup."+ str, args, args.length);
function appVersion(version, build) formatStr("appVersion", [brandShortName, version, build]);
function formatExtension(str, name, version) formatStr("extension"+str, [name, version]);
function msFromµs(µs) µs / 1000;
function formatstamp(µs) new Date(msFromµs(µs));
function formatµs(µs) µs + " µs";
function formatms(ms) formatStr("milliseconds", [ms]);
function point(stamp, µs, v, b) [msFromµs(stamp), msFromµs(µs), { appVersion: v, appBuild: b }];
function range(a, b) ({ from: msFromµs(a), to: msFromµs(b || a) });
function mark(x, y) { var r = {}; x && (r.xaxis = x); y && (r.yaxis = y); return r };
function label(r, l) $.extend(r, { label: l });
function color(r, c) $.extend(r, { color: c });
function major(r) color(r, "#444");
function minor(r) color(r, "#AAA");
function green(r) color(r, "#00F");
function majorMark(x, l) label(major(mark(range(x))), l);
function minorMark(x, l) label(minor(mark(range(x))), l);
function extensionMark(x, l) label(green(mark(range(x))), l);
///// First, display the timings from the current startup
let launched, startup, restored;
let runtime = Cc["@mozilla.org/xre/runtime;1"].getService(Ci.nsIXULRuntime);
try {
displayTimestamp("launched", launched = runtime.launchTimestamp);
} catch(x) { }
displayTimestamp("started", startup = runtime.startupTimestamp);
if (launched)
displayDuration("started", startup - launched);
let app = Cc["@mozilla.org/toolkit/app-startup;1"]
.getService(Ci.nsIAppStartup2);
displayTimestamp("restored", restored = app.restoredTimestamp);
displayDuration("restored", restored - startup);
///// Next, load the database
var file = Components.classes["@mozilla.org/file/directory_service;1"]
.getService(Components.interfaces.nsIProperties)
.get("ProfD", Components.interfaces.nsIFile);
file.append("startup.sqlite");
var svc = Components.classes["@mozilla.org/storage/service;1"]
.getService(Components.interfaces.mozIStorageService);
var db = svc.openDatabase(file);
///// set up the graph options
var graph, overview;
var options = { legend: { show: true, position: "ne", margin: 10,
labelBoxBorderColor: "transparent" },
xaxis: { mode: "time" },
yaxis: { min: 0, tickFormatter: formatms },
selection: { mode: "xy", color: "#00A" },
grid: { show: true, borderWidth: 0, markings: [],
aboveData: true, tickColor: "white" },
series: { lines: { show: true, fill: true },
points: { show: true, fill: true },
},
colors: ["#67A9D8", "#FFC170"],
};
var overviewOpts = $.extend(true, {}, options,
{ legend: { show: false },
xaxis: { ticks: [], mode: "time" },
yaxis: { ticks: [], min: 0,
autoscaleMargin: 0.1 },
grid: { show: false },
series: { lines: { show: true, fill: true,
lineWidth: 1 },
points: { show: false },
shadowSize: 0
},
});
var series = [{ label: "Launch Time",
data: []
},
{ label: "Startup Time",
data: []
}
];
$("#graph").bind("plotselected", function (event, ranges)
{
// do the zooming
graph = $.plot($("#graph"),
series,
$.extend(true, {}, options,
{ xaxis: { min: ranges.xaxis.from,
max: ranges.xaxis.to
},
yaxis: { min: ranges.yaxis.from,
max: ranges.yaxis.to
},
}));
// don't fire event on the overview to prevent eternal loop
overview.setSelection(ranges, true);
});
$("#overview").bind("plotselected", function (event, ranges) {
graph.setSelection(ranges);
});
///// read everything from the database and graph it
let work = (function()
{
populateMeasurements();
yield;
populateEvents();
yield;
})();
function go()
{
try {
work.next();
} catch (x if x instanceof StopIteration) {
// harmless
}
}
go();
function populateMeasurements()
{
let s = "SELECT timestamp, launch, startup, appVersion, appBuild FROM duration";
var query = db.createStatement(s);
var lastver, lastbuild;
query.executeAsync({
handleResult: function(results)
{
let hasresults = false;
let table = document.getElementById("duration-table");
for (let row = results.getNextRow(); row; row = results.getNextRow())
{
hasresults = true;
let stamp = row.getResultByName("timestamp");
let version = row.getResultByName("appVersion");
let build = row.getResultByName("appBuild");
if (lastver != version)
{
options.grid.markings.push(majorMark(stamp, appVersion(version,
build)));
}
else
if (lastbuild != build)
options.grid.markings.push(minorMark(stamp, appVersion(version,
build)));
lastver = version;
lastbuild = build;
let l = row.getResultByName("launch"),
s = row.getResultByName("startup");
series[1].data.push(point(stamp, l, version, build));
series[0].data.push(point(stamp, l + s, version, build));
table.appendChild(tr(td(formatstamp(stamp)),
td(formatms(msFromµs(l))),
td(formatms(msFromµs(s))),
td(formatms(msFromµs((l + s)))),
td(version),
td(build)));
}
if (hasresults)
$("#duration-table > .empty").hide();
},
handleError: function(error)
{
$("#duration-table").appendChild(tr(td("Error: "+ error.message +" ("+
error.result +")")));
},
handleCompletion: function()
{
var table = $("table");
var height = $(window).height() - (table.offset().top +
table.outerHeight(true))
- 110;
$("#graph").height(Math.max(350, height));
options.xaxis.min = Date.now() - 604800000; // 7 days in milliseconds
var max = 0;
for each (let [stamp, d] in series[0].data)
if (stamp >= options.xaxis.min && d > max)
max = d;
options.yaxis.max = max;
graph = $.plot($("#graph"), series, options);
var offset = graph.getPlotOffset().left;
$("#overview").width($("#overview").width() - offset);
$("#overview").css("margin-left", offset);
overview = $.plot($("#overview"), series, overviewOpts);
go();
},
});
}
function populateEvents()
{
let s = "SELECT timestamp, id, name, version, action FROM events";
var query = db.createStatement(s);
let lastver, lastbuild;
let hasresults;
query.executeAsync({
handleResult: function(results)
{
Application.getExtensions(function (extensions)
{
let table = document.getElementById("events-table");
for (let row = results.getNextRow(); row; row = results.getNextRow())
{
hasresults = true;
let stamp = row.getResultByName("timestamp"),
id = row.getResultByName("id"),
extension = extensions.get(id),
name = extension ? extension.name : row.getResultByName("name"),
version = row.getResultByName("version"),
action = row.getResultByName("action");
options.grid.markings.push(extensionMark(stamp,
formatExtension(action,
name,
version)));
table.appendChild(tr(td(formatstamp(stamp)),
td(action),
td(name),
td(id),
td(version)));
}
if (hasresults)
$("#events-table > .empty").hide();
});
},
handleError: function(error)
{
$("#events-table").appendChild(tr(td("Error: "+ error.message +" ("+
error.result +")")));
},
handleCompletion: function()
{
graph = $.plot($("#graph"), series, options);
go();
},
});
}
function td(str)
{
let cell = document.createElement("td");
cell.innerHTML = str;
return cell;
}
function tr()
{
let row = document.createElement("tr");
Array.forEach(arguments, function(cell) { row.appendChild(cell); });
return row;
}
function showTable()
{
$("#showtable").hide();
$("#showgraph").show();
$("#graphs").hide();
$("#tables").show();
}
function showGraph()
{
$("#showgraph").hide();
$("#showtable").show();
$("#tables").hide();
$("#graphs").show();
}

View File

@ -0,0 +1,122 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- ***** BEGIN LICENSE BLOCK *****
- Version: MPL 1.1/GPL 2.0/LGPL 2.1
-
- The contents of this file are subject to the Mozilla Public License Version
- 1.1 (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
- http://www.mozilla.org/MPL/
-
- Software distributed under the License is distributed on an "AS IS" basis,
- WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- for the specific language governing rights and limitations under the
- License.
-
- The Original Code is the about:startup page.
-
- The Initial Developer of the Original Code is
- the Mozilla Corporation.
- Portions created by the Initial Developer are Copyright (C) 2010
- the Initial Developer. All Rights Reserved.
-
- Contributor(s):
- Daniel Brooks <db48x@db48x.net>
-
- Alternatively, the contents of this file may be used under the terms of
- either the GNU General Public License Version 2 or later (the "GPL"), or
- the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- in which case the provisions of the GPL or the LGPL are applicable instead
- of those above. If you wish to allow use of your version of this file only
- under the terms of either the GPL or the LGPL, and not to allow others to
- use your version of this file under the terms of the MPL, indicate your
- decision by deleting the provisions above and replace them with the notice
- and other provisions required by the LGPL or the GPL. If you do not delete
- the provisions above, a recipient may use your version of this file under
- the terms of any one of the MPL, the GPL or the LGPL.
-
- ***** END LICENSE BLOCK ***** -->
<!DOCTYPE html [
<!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
%htmlDTD;
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
%globalDTD;
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
%brandDTD;
<!ENTITY % aboutStartupDTD SYSTEM "chrome://global/locale/aboutStartup.dtd">
%aboutStartupDTD;
]>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>&about.startup.title;</title>
<link rel="stylesheet" href="chrome://global/skin/aboutSupport.css" type="text/css"/>
<link rel="stylesheet" href="chrome://global/skin/aboutStartup.css" type="text/css"/>
<script type="application/javascript" src="chrome://jquery/content/jquery.js"/>
<script type="application/javascript" src="chrome://jquery/content/jquery.flot.js"/>
<script type="application/javascript" src="chrome://jquery/content/jquery.flot.selection.js"/>
</head>
<body>
<h1>&about.startup.title;</h1>
<table>
<tr>
<th></th>
<th>&about.startup.timestamp;</th>
<th>&about.startup.elapsed;</th>
</tr>
<tr>
<td>&about.startup.app.launched;</td>
<td id="launched"></td><td></td>
</tr>
<tr>
<td>&about.startup.app.started;</td>
<td id="started"></td><td></td>
</tr>
<tr>
<td>&about.startup.app.ready;</td>
<td id="restored"></td><td></td>
</tr>
</table>
<div id="toggle-container">
<a class="toggle" id="showtable"
href="#" onclick="showTable()">&about.startup.table;</a>
<a class="toggle" id="showgraph"
href="#" onclick="showGraph()">&about.startup.graph;</a>
</div>
<div id="graphs">
<div id="graph"/>
<div id="overview"/>
</div>
<div id="tables">
<table id="duration-table">
<tr>
<th>&about.startup.timestamp;</th>
<th>&about.startup.duration.launch;</th>
<th>&about.startup.duration.startup;</th>
<th>&about.startup.duration.ready;</th>
<th colspan="2">&about.startup.version;</th>
</tr>
<tr class="empty">
<td colspan="6"><i>&about.startup.noevents;</i></td>
</tr>
</table>
<table id="events-table">
<tr>
<th>&about.startup.timestamp;</th>
<th>&about.startup.action;</th>
<th>&about.startup.extension;</th>
<th>&about.startup.extensionID;</th>
<th>&about.startup.version;</th>
</tr>
<tr class="empty">
<td colspan="5"><i>&about.startup.noevents;</i></td>
</tr>
</table>
</div>
<script type="application/javascript;version=1.8"
src="chrome://global/content/aboutStartup.js"/>
</body>
</html>

View File

@ -2,6 +2,7 @@ toolkit.jar:
% content global %content/global/ contentaccessible=yes
% content global-platform %content/global-platform/ platform
% content global-region %content/global-region/
% content jquery %content/jquery/
# provide the nsTransferable in nsDragAndDrop.js to extensions that have to
# work with Geckos from before 1.9, when there was a separate file
% override chrome://global/content/nsTransferable.js chrome://global/content/nsDragAndDrop.js
@ -16,6 +17,8 @@ toolkit.jar:
* content/global/aboutRights-unbranded.xhtml (aboutRights-unbranded.xhtml)
* content/global/aboutSupport.js
* content/global/aboutSupport.xhtml
content/global/aboutStartup.xhtml (aboutStartup.xhtml)
content/global/aboutStartup.js (aboutStartup.js)
content/global/directionDetector.html
content/global/plugins.html
content/global/plugins.css
@ -83,3 +86,12 @@ toolkit.jar:
#ifdef MOZ_SVG
content/global/svg/svgBindings.xml (/layout/svg/base/src/resources/content/svgBindings.xml)
#endif
content\jquery\jquery.js (jquery/jquery.js)
content\jquery\jquery.colorhelpers.js (jquery/jquery.colorhelpers.js)
content\jquery\jquery.flot.crosshair.js (jquery/jquery.flot.crosshair.js)
content\jquery\jquery.flot.image.js (jquery/jquery.flot.image.js)
content\jquery\jquery.flot.js (jquery/jquery.flot.js)
content\jquery\jquery.flot.navigate.js (jquery/jquery.flot.navigate.js)
content\jquery\jquery.flot.selection.js (jquery/jquery.flot.selection.js)
content\jquery\jquery.flot.stack.js (jquery/jquery.flot.stack.js)
content\jquery\jquery.flot.threshold.js (jquery/jquery.flot.threshold.js)

View File

@ -0,0 +1,22 @@
Copyright (c) 2007-2009 IOLA and Ole Laursen
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,174 @@
/* Plugin for jQuery for working with colors.
*
* Version 1.0.
*
* Inspiration from jQuery color animation plugin by John Resig.
*
* Released under the MIT license by Ole Laursen, October 2009.
*
* Examples:
*
* $.color.parse("#fff").scale('rgb', 0.25).add('a', -0.5).toString()
* var c = $.color.extract($("#mydiv"), 'background-color');
* console.log(c.r, c.g, c.b, c.a);
* $.color.make(100, 50, 25, 0.4).toString() // returns "rgba(100,50,25,0.4)"
*
* Note that .scale() and .add() work in-place instead of returning
* new objects.
*/
(function() {
jQuery.color = {};
// construct color object with some convenient chainable helpers
jQuery.color.make = function (r, g, b, a) {
var o = {};
o.r = r || 0;
o.g = g || 0;
o.b = b || 0;
o.a = a != null ? a : 1;
o.add = function (c, d) {
for (var i = 0; i < c.length; ++i)
o[c.charAt(i)] += d;
return o.normalize();
};
o.scale = function (c, f) {
for (var i = 0; i < c.length; ++i)
o[c.charAt(i)] *= f;
return o.normalize();
};
o.toString = function () {
if (o.a >= 1.0) {
return "rgb("+[o.r, o.g, o.b].join(",")+")";
} else {
return "rgba("+[o.r, o.g, o.b, o.a].join(",")+")";
}
};
o.normalize = function () {
function clamp(min, value, max) {
return value < min ? min: (value > max ? max: value);
}
o.r = clamp(0, parseInt(o.r), 255);
o.g = clamp(0, parseInt(o.g), 255);
o.b = clamp(0, parseInt(o.b), 255);
o.a = clamp(0, o.a, 1);
return o;
};
o.clone = function () {
return jQuery.color.make(o.r, o.b, o.g, o.a);
};
return o.normalize();
}
// extract CSS color property from element, going up in the DOM
// if it's "transparent"
jQuery.color.extract = function (elem, css) {
var c;
do {
c = elem.css(css).toLowerCase();
// keep going until we find an element that has color, or
// we hit the body
if (c != '' && c != 'transparent')
break;
elem = elem.parent();
} while (!jQuery.nodeName(elem.get(0), "body"));
// catch Safari's way of signalling transparent
if (c == "rgba(0, 0, 0, 0)")
c = "transparent";
return jQuery.color.parse(c);
}
// parse CSS color string (like "rgb(10, 32, 43)" or "#fff"),
// returns color object
jQuery.color.parse = function (str) {
var res, m = jQuery.color.make;
// Look for rgb(num,num,num)
if (res = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(str))
return m(parseInt(res[1], 10), parseInt(res[2], 10), parseInt(res[3], 10));
// Look for rgba(num,num,num,num)
if (res = /rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str))
return m(parseInt(res[1], 10), parseInt(res[2], 10), parseInt(res[3], 10), parseFloat(res[4]));
// Look for rgb(num%,num%,num%)
if (res = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(str))
return m(parseFloat(res[1])*2.55, parseFloat(res[2])*2.55, parseFloat(res[3])*2.55);
// Look for rgba(num%,num%,num%,num)
if (res = /rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str))
return m(parseFloat(res[1])*2.55, parseFloat(res[2])*2.55, parseFloat(res[3])*2.55, parseFloat(res[4]));
// Look for #a0b1c2
if (res = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(str))
return m(parseInt(res[1], 16), parseInt(res[2], 16), parseInt(res[3], 16));
// Look for #fff
if (res = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(str))
return m(parseInt(res[1]+res[1], 16), parseInt(res[2]+res[2], 16), parseInt(res[3]+res[3], 16));
// Otherwise, we're most likely dealing with a named color
var name = jQuery.trim(str).toLowerCase();
if (name == "transparent")
return m(255, 255, 255, 0);
else {
res = lookupColors[name];
return m(res[0], res[1], res[2]);
}
}
var lookupColors = {
aqua:[0,255,255],
azure:[240,255,255],
beige:[245,245,220],
black:[0,0,0],
blue:[0,0,255],
brown:[165,42,42],
cyan:[0,255,255],
darkblue:[0,0,139],
darkcyan:[0,139,139],
darkgrey:[169,169,169],
darkgreen:[0,100,0],
darkkhaki:[189,183,107],
darkmagenta:[139,0,139],
darkolivegreen:[85,107,47],
darkorange:[255,140,0],
darkorchid:[153,50,204],
darkred:[139,0,0],
darksalmon:[233,150,122],
darkviolet:[148,0,211],
fuchsia:[255,0,255],
gold:[255,215,0],
green:[0,128,0],
indigo:[75,0,130],
khaki:[240,230,140],
lightblue:[173,216,230],
lightcyan:[224,255,255],
lightgreen:[144,238,144],
lightgrey:[211,211,211],
lightpink:[255,182,193],
lightyellow:[255,255,224],
lime:[0,255,0],
magenta:[255,0,255],
maroon:[128,0,0],
navy:[0,0,128],
olive:[128,128,0],
orange:[255,165,0],
pink:[255,192,203],
purple:[128,0,128],
violet:[128,0,128],
red:[255,0,0],
silver:[192,192,192],
white:[255,255,255],
yellow:[255,255,0]
};
})();

View File

@ -0,0 +1,156 @@
/*
Flot plugin for showing a crosshair, thin lines, when the mouse hovers
over the plot.
crosshair: {
mode: null or "x" or "y" or "xy"
color: color
lineWidth: number
}
Set the mode to one of "x", "y" or "xy". The "x" mode enables a
vertical crosshair that lets you trace the values on the x axis, "y"
enables a horizontal crosshair and "xy" enables them both. "color" is
the color of the crosshair (default is "rgba(170, 0, 0, 0.80)"),
"lineWidth" is the width of the drawn lines (default is 1).
The plugin also adds four public methods:
- setCrosshair(pos)
Set the position of the crosshair. Note that this is cleared if
the user moves the mouse. "pos" should be on the form { x: xpos,
y: ypos } (or x2 and y2 if you're using the secondary axes), which
is coincidentally the same format as what you get from a "plothover"
event. If "pos" is null, the crosshair is cleared.
- clearCrosshair()
Clear the crosshair.
- lockCrosshair(pos)
Cause the crosshair to lock to the current location, no longer
updating if the user moves the mouse. Optionally supply a position
(passed on to setCrosshair()) to move it to.
Example usage:
var myFlot = $.plot( $("#graph"), ..., { crosshair: { mode: "x" } } };
$("#graph").bind("plothover", function (evt, position, item) {
if (item) {
// Lock the crosshair to the data point being hovered
myFlot.lockCrosshair({ x: item.datapoint[0], y: item.datapoint[1] });
}
else {
// Return normal crosshair operation
myFlot.unlockCrosshair();
}
});
- unlockCrosshair()
Free the crosshair to move again after locking it.
*/
(function ($) {
var options = {
crosshair: {
mode: null, // one of null, "x", "y" or "xy",
color: "rgba(170, 0, 0, 0.80)",
lineWidth: 1
}
};
function init(plot) {
// position of crosshair in pixels
var crosshair = { x: -1, y: -1, locked: false };
plot.setCrosshair = function setCrosshair(pos) {
if (!pos)
crosshair.x = -1;
else {
var axes = plot.getAxes();
crosshair.x = Math.max(0, Math.min(pos.x != null ? axes.xaxis.p2c(pos.x) : axes.x2axis.p2c(pos.x2), plot.width()));
crosshair.y = Math.max(0, Math.min(pos.y != null ? axes.yaxis.p2c(pos.y) : axes.y2axis.p2c(pos.y2), plot.height()));
}
plot.triggerRedrawOverlay();
};
plot.clearCrosshair = plot.setCrosshair; // passes null for pos
plot.lockCrosshair = function lockCrosshair(pos) {
if (pos)
plot.setCrosshair(pos);
crosshair.locked = true;
}
plot.unlockCrosshair = function unlockCrosshair() {
crosshair.locked = false;
}
plot.hooks.bindEvents.push(function (plot, eventHolder) {
if (!plot.getOptions().crosshair.mode)
return;
eventHolder.mouseout(function () {
if (crosshair.x != -1) {
crosshair.x = -1;
plot.triggerRedrawOverlay();
}
});
eventHolder.mousemove(function (e) {
if (plot.getSelection && plot.getSelection()) {
crosshair.x = -1; // hide the crosshair while selecting
return;
}
if (crosshair.locked)
return;
var offset = plot.offset();
crosshair.x = Math.max(0, Math.min(e.pageX - offset.left, plot.width()));
crosshair.y = Math.max(0, Math.min(e.pageY - offset.top, plot.height()));
plot.triggerRedrawOverlay();
});
});
plot.hooks.drawOverlay.push(function (plot, ctx) {
var c = plot.getOptions().crosshair;
if (!c.mode)
return;
var plotOffset = plot.getPlotOffset();
ctx.save();
ctx.translate(plotOffset.left, plotOffset.top);
if (crosshair.x != -1) {
ctx.strokeStyle = c.color;
ctx.lineWidth = c.lineWidth;
ctx.lineJoin = "round";
ctx.beginPath();
if (c.mode.indexOf("x") != -1) {
ctx.moveTo(crosshair.x, 0);
ctx.lineTo(crosshair.x, plot.height());
}
if (c.mode.indexOf("y") != -1) {
ctx.moveTo(0, crosshair.y);
ctx.lineTo(plot.width(), crosshair.y);
}
ctx.stroke();
}
ctx.restore();
});
}
$.plot.plugins.push({
init: init,
options: options,
name: 'crosshair',
version: '1.0'
});
})(jQuery);

View File

@ -0,0 +1,237 @@
/*
Flot plugin for plotting images, e.g. useful for putting ticks on a
prerendered complex visualization.
The data syntax is [[image, x1, y1, x2, y2], ...] where (x1, y1) and
(x2, y2) are where you intend the two opposite corners of the image to
end up in the plot. Image must be a fully loaded Javascript image (you
can make one with new Image()). If the image is not complete, it's
skipped when plotting.
There are two helpers included for retrieving images. The easiest work
the way that you put in URLs instead of images in the data (like
["myimage.png", 0, 0, 10, 10]), then call $.plot.image.loadData(data,
options, callback) where data and options are the same as you pass in
to $.plot. This loads the images, replaces the URLs in the data with
the corresponding images and calls "callback" when all images are
loaded (or failed loading). In the callback, you can then call $.plot
with the data set. See the included example.
A more low-level helper, $.plot.image.load(urls, callback) is also
included. Given a list of URLs, it calls callback with an object
mapping from URL to Image object when all images are loaded or have
failed loading.
Options for the plugin are
series: {
images: {
show: boolean
anchor: "corner" or "center"
alpha: [0,1]
}
}
which can be specified for a specific series
$.plot($("#placeholder"), [{ data: [ ... ], images: { ... } ])
Note that because the data format is different from usual data points,
you can't use images with anything else in a specific data series.
Setting "anchor" to "center" causes the pixels in the image to be
anchored at the corner pixel centers inside of at the pixel corners,
effectively letting half a pixel stick out to each side in the plot.
A possible future direction could be support for tiling for large
images (like Google Maps).
*/
(function ($) {
var options = {
series: {
images: {
show: false,
alpha: 1,
anchor: "corner" // or "center"
}
}
};
$.plot.image = {};
$.plot.image.loadDataImages = function (series, options, callback) {
var urls = [], points = [];
var defaultShow = options.series.images.show;
$.each(series, function (i, s) {
if (!(defaultShow || s.images.show))
return;
if (s.data)
s = s.data;
$.each(s, function (i, p) {
if (typeof p[0] == "string") {
urls.push(p[0]);
points.push(p);
}
});
});
$.plot.image.load(urls, function (loadedImages) {
$.each(points, function (i, p) {
var url = p[0];
if (loadedImages[url])
p[0] = loadedImages[url];
});
callback();
});
}
$.plot.image.load = function (urls, callback) {
var missing = urls.length, loaded = {};
if (missing == 0)
callback({});
$.each(urls, function (i, url) {
var handler = function () {
--missing;
loaded[url] = this;
if (missing == 0)
callback(loaded);
};
$('<img />').load(handler).error(handler).attr('src', url);
});
}
function draw(plot, ctx) {
var plotOffset = plot.getPlotOffset();
$.each(plot.getData(), function (i, series) {
var points = series.datapoints.points,
ps = series.datapoints.pointsize;
for (var i = 0; i < points.length; i += ps) {
var img = points[i],
x1 = points[i + 1], y1 = points[i + 2],
x2 = points[i + 3], y2 = points[i + 4],
xaxis = series.xaxis, yaxis = series.yaxis,
tmp;
// actually we should check img.complete, but it
// appears to be a somewhat unreliable indicator in
// IE6 (false even after load event)
if (!img || img.width <= 0 || img.height <= 0)
continue;
if (x1 > x2) {
tmp = x2;
x2 = x1;
x1 = tmp;
}
if (y1 > y2) {
tmp = y2;
y2 = y1;
y1 = tmp;
}
// if the anchor is at the center of the pixel, expand the
// image by 1/2 pixel in each direction
if (series.images.anchor == "center") {
tmp = 0.5 * (x2-x1) / (img.width - 1);
x1 -= tmp;
x2 += tmp;
tmp = 0.5 * (y2-y1) / (img.height - 1);
y1 -= tmp;
y2 += tmp;
}
// clip
if (x1 == x2 || y1 == y2 ||
x1 >= xaxis.max || x2 <= xaxis.min ||
y1 >= yaxis.max || y2 <= yaxis.min)
continue;
var sx1 = 0, sy1 = 0, sx2 = img.width, sy2 = img.height;
if (x1 < xaxis.min) {
sx1 += (sx2 - sx1) * (xaxis.min - x1) / (x2 - x1);
x1 = xaxis.min;
}
if (x2 > xaxis.max) {
sx2 += (sx2 - sx1) * (xaxis.max - x2) / (x2 - x1);
x2 = xaxis.max;
}
if (y1 < yaxis.min) {
sy2 += (sy1 - sy2) * (yaxis.min - y1) / (y2 - y1);
y1 = yaxis.min;
}
if (y2 > yaxis.max) {
sy1 += (sy1 - sy2) * (yaxis.max - y2) / (y2 - y1);
y2 = yaxis.max;
}
x1 = xaxis.p2c(x1);
x2 = xaxis.p2c(x2);
y1 = yaxis.p2c(y1);
y2 = yaxis.p2c(y2);
// the transformation may have swapped us
if (x1 > x2) {
tmp = x2;
x2 = x1;
x1 = tmp;
}
if (y1 > y2) {
tmp = y2;
y2 = y1;
y1 = tmp;
}
tmp = ctx.globalAlpha;
ctx.globalAlpha *= series.images.alpha;
ctx.drawImage(img,
sx1, sy1, sx2 - sx1, sy2 - sy1,
x1 + plotOffset.left, y1 + plotOffset.top,
x2 - x1, y2 - y1);
ctx.globalAlpha = tmp;
}
});
}
function processRawData(plot, series, data, datapoints) {
if (!series.images.show)
return;
// format is Image, x1, y1, x2, y2 (opposite corners)
datapoints.format = [
{ required: true },
{ x: true, number: true, required: true },
{ y: true, number: true, required: true },
{ x: true, number: true, required: true },
{ y: true, number: true, required: true }
];
}
function init(plot) {
plot.hooks.processRawData.push(processRawData);
plot.hooks.draw.push(draw);
}
$.plot.plugins.push({
init: init,
options: options,
name: 'image',
version: '1.1'
});
})(jQuery);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,272 @@
/*
Flot plugin for adding panning and zooming capabilities to a plot.
The default behaviour is double click and scrollwheel up/down to zoom
in, drag to pan. The plugin defines plot.zoom({ center }),
plot.zoomOut() and plot.pan(offset) so you easily can add custom
controls. It also fires a "plotpan" and "plotzoom" event when
something happens, useful for synchronizing plots.
Example usage:
plot = $.plot(...);
// zoom default amount in on the pixel (100, 200)
plot.zoom({ center: { left: 10, top: 20 } });
// zoom out again
plot.zoomOut({ center: { left: 10, top: 20 } });
// pan 100 pixels to the left and 20 down
plot.pan({ left: -100, top: 20 })
Options:
zoom: {
interactive: false
trigger: "dblclick" // or "click" for single click
amount: 1.5 // 2 = 200% (zoom in), 0.5 = 50% (zoom out)
}
pan: {
interactive: false
}
xaxis, yaxis, x2axis, y2axis: {
zoomRange: null // or [number, number] (min range, max range)
panRange: null // or [number, number] (min, max)
}
"interactive" enables the built-in drag/click behaviour. "amount" is
the amount to zoom the viewport relative to the current range, so 1 is
100% (i.e. no change), 1.5 is 150% (zoom in), 0.7 is 70% (zoom out).
"zoomRange" is the interval in which zooming can happen, e.g. with
zoomRange: [1, 100] the zoom will never scale the axis so that the
difference between min and max is smaller than 1 or larger than 100.
You can set either of them to null to ignore.
"panRange" confines the panning to stay within a range, e.g. with
panRange: [-10, 20] panning stops at -10 in one end and at 20 in the
other. Either can be null.
*/
// First two dependencies, jquery.event.drag.js and
// jquery.mousewheel.js, we put them inline here to save people the
// effort of downloading them.
/*
jquery.event.drag.js ~ v1.5 ~ Copyright (c) 2008, Three Dub Media (http://threedubmedia.com)
Licensed under the MIT License ~ http://threedubmedia.googlecode.com/files/MIT-LICENSE.txt
*/
(function(E){E.fn.drag=function(L,K,J){if(K){this.bind("dragstart",L)}if(J){this.bind("dragend",J)}return !L?this.trigger("drag"):this.bind("drag",K?K:L)};var A=E.event,B=A.special,F=B.drag={not:":input",distance:0,which:1,dragging:false,setup:function(J){J=E.extend({distance:F.distance,which:F.which,not:F.not},J||{});J.distance=I(J.distance);A.add(this,"mousedown",H,J);if(this.attachEvent){this.attachEvent("ondragstart",D)}},teardown:function(){A.remove(this,"mousedown",H);if(this===F.dragging){F.dragging=F.proxy=false}G(this,true);if(this.detachEvent){this.detachEvent("ondragstart",D)}}};B.dragstart=B.dragend={setup:function(){},teardown:function(){}};function H(L){var K=this,J,M=L.data||{};if(M.elem){K=L.dragTarget=M.elem;L.dragProxy=F.proxy||K;L.cursorOffsetX=M.pageX-M.left;L.cursorOffsetY=M.pageY-M.top;L.offsetX=L.pageX-L.cursorOffsetX;L.offsetY=L.pageY-L.cursorOffsetY}else{if(F.dragging||(M.which>0&&L.which!=M.which)||E(L.target).is(M.not)){return }}switch(L.type){case"mousedown":E.extend(M,E(K).offset(),{elem:K,target:L.target,pageX:L.pageX,pageY:L.pageY});A.add(document,"mousemove mouseup",H,M);G(K,false);F.dragging=null;return false;case !F.dragging&&"mousemove":if(I(L.pageX-M.pageX)+I(L.pageY-M.pageY)<M.distance){break}L.target=M.target;J=C(L,"dragstart",K);if(J!==false){F.dragging=K;F.proxy=L.dragProxy=E(J||K)[0]}case"mousemove":if(F.dragging){J=C(L,"drag",K);if(B.drop){B.drop.allowed=(J!==false);B.drop.handler(L)}if(J!==false){break}L.type="mouseup"}case"mouseup":A.remove(document,"mousemove mouseup",H);if(F.dragging){if(B.drop){B.drop.handler(L)}C(L,"dragend",K)}G(K,true);F.dragging=F.proxy=M.elem=false;break}return true}function C(M,K,L){M.type=K;var J=E.event.handle.call(L,M);return J===false?false:J||M.result}function I(J){return Math.pow(J,2)}function D(){return(F.dragging===false)}function G(K,J){if(!K){return }K.unselectable=J?"off":"on";K.onselectstart=function(){return J};if(K.style){K.style.MozUserSelect=J?"":"none"}}})(jQuery);
/* jquery.mousewheel.min.js
* Copyright (c) 2009 Brandon Aaron (http://brandonaaron.net)
* Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
* and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
* Thanks to: http://adomas.org/javascript-mouse-wheel/ for some pointers.
* Thanks to: Mathias Bank(http://www.mathias-bank.de) for a scope bug fix.
*
* Version: 3.0.2
*
* Requires: 1.2.2+
*/
(function(c){var a=["DOMMouseScroll","mousewheel"];c.event.special.mousewheel={setup:function(){if(this.addEventListener){for(var d=a.length;d;){this.addEventListener(a[--d],b,false)}}else{this.onmousewheel=b}},teardown:function(){if(this.removeEventListener){for(var d=a.length;d;){this.removeEventListener(a[--d],b,false)}}else{this.onmousewheel=null}}};c.fn.extend({mousewheel:function(d){return d?this.bind("mousewheel",d):this.trigger("mousewheel")},unmousewheel:function(d){return this.unbind("mousewheel",d)}});function b(f){var d=[].slice.call(arguments,1),g=0,e=true;f=c.event.fix(f||window.event);f.type="mousewheel";if(f.wheelDelta){g=f.wheelDelta/120}if(f.detail){g=-f.detail/3}d.unshift(f,g);return c.event.handle.apply(this,d)}})(jQuery);
(function ($) {
var options = {
xaxis: {
zoomRange: null, // or [number, number] (min range, max range)
panRange: null // or [number, number] (min, max)
},
zoom: {
interactive: false,
trigger: "dblclick", // or "click" for single click
amount: 1.5 // how much to zoom relative to current position, 2 = 200% (zoom in), 0.5 = 50% (zoom out)
},
pan: {
interactive: false
}
};
function init(plot) {
function bindEvents(plot, eventHolder) {
var o = plot.getOptions();
if (o.zoom.interactive) {
function clickHandler(e, zoomOut) {
var c = plot.offset();
c.left = e.pageX - c.left;
c.top = e.pageY - c.top;
if (zoomOut)
plot.zoomOut({ center: c });
else
plot.zoom({ center: c });
}
eventHolder[o.zoom.trigger](clickHandler);
eventHolder.mousewheel(function (e, delta) {
clickHandler(e, delta < 0);
return false;
});
}
if (o.pan.interactive) {
var prevCursor = 'default', pageX = 0, pageY = 0;
eventHolder.bind("dragstart", { distance: 10 }, function (e) {
if (e.which != 1) // only accept left-click
return false;
eventHolderCursor = eventHolder.css('cursor');
eventHolder.css('cursor', 'move');
pageX = e.pageX;
pageY = e.pageY;
});
eventHolder.bind("drag", function (e) {
// unused at the moment, but we need it here to
// trigger the dragstart/dragend events
});
eventHolder.bind("dragend", function (e) {
eventHolder.css('cursor', prevCursor);
plot.pan({ left: pageX - e.pageX,
top: pageY - e.pageY });
});
}
}
plot.zoomOut = function (args) {
if (!args)
args = {};
if (!args.amount)
args.amount = plot.getOptions().zoom.amount
args.amount = 1 / args.amount;
plot.zoom(args);
}
plot.zoom = function (args) {
if (!args)
args = {};
var axes = plot.getAxes(),
options = plot.getOptions(),
c = args.center,
amount = args.amount ? args.amount : options.zoom.amount,
w = plot.width(), h = plot.height();
if (!c)
c = { left: w / 2, top: h / 2 };
var xf = c.left / w,
x1 = c.left - xf * w / amount,
x2 = c.left + (1 - xf) * w / amount,
yf = c.top / h,
y1 = c.top - yf * h / amount,
y2 = c.top + (1 - yf) * h / amount;
function scaleAxis(min, max, name) {
var axis = axes[name],
axisOptions = options[name];
if (!axis.used)
return;
min = axis.c2p(min);
max = axis.c2p(max);
if (max < min) { // make sure min < max
var tmp = min
min = max;
max = tmp;
}
var range = max - min, zr = axisOptions.zoomRange;
if (zr &&
((zr[0] != null && range < zr[0]) ||
(zr[1] != null && range > zr[1])))
return;
axisOptions.min = min;
axisOptions.max = max;
}
scaleAxis(x1, x2, 'xaxis');
scaleAxis(x1, x2, 'x2axis');
scaleAxis(y1, y2, 'yaxis');
scaleAxis(y1, y2, 'y2axis');
plot.setupGrid();
plot.draw();
if (!args.preventEvent)
plot.getPlaceholder().trigger("plotzoom", [ plot ]);
}
plot.pan = function (args) {
var l = +args.left, t = +args.top,
axes = plot.getAxes(), options = plot.getOptions();
if (isNaN(l))
l = 0;
if (isNaN(t))
t = 0;
function panAxis(delta, name) {
var axis = axes[name],
axisOptions = options[name],
min, max;
if (!axis.used)
return;
min = axis.c2p(axis.p2c(axis.min) + delta),
max = axis.c2p(axis.p2c(axis.max) + delta);
var pr = axisOptions.panRange;
if (pr) {
// check whether we hit the wall
if (pr[0] != null && pr[0] > min) {
delta = pr[0] - min;
min += delta;
max += delta;
}
if (pr[1] != null && pr[1] < max) {
delta = pr[1] - max;
min += delta;
max += delta;
}
}
axisOptions.min = min;
axisOptions.max = max;
}
panAxis(l, 'xaxis');
panAxis(l, 'x2axis');
panAxis(t, 'yaxis');
panAxis(t, 'y2axis');
plot.setupGrid();
plot.draw();
if (!args.preventEvent)
plot.getPlaceholder().trigger("plotpan", [ plot ]);
}
plot.hooks.bindEvents.push(bindEvents);
}
$.plot.plugins.push({
init: init,
options: options,
name: 'navigate',
version: '1.1'
});
})(jQuery);

View File

@ -0,0 +1,299 @@
/*
Flot plugin for selecting regions.
The plugin defines the following options:
selection: {
mode: null or "x" or "y" or "xy",
color: color
}
You enable selection support by setting the mode to one of "x", "y" or
"xy". In "x" mode, the user will only be able to specify the x range,
similarly for "y" mode. For "xy", the selection becomes a rectangle
where both ranges can be specified. "color" is color of the selection.
When selection support is enabled, a "plotselected" event will be emitted
on the DOM element you passed into the plot function. The event
handler gets one extra parameter with the ranges selected on the axes,
like this:
placeholder.bind("plotselected", function(event, ranges) {
alert("You selected " + ranges.xaxis.from + " to " + ranges.xaxis.to)
// similar for yaxis, secondary axes are in x2axis
// and y2axis if present
});
The "plotselected" event is only fired when the user has finished
making the selection. A "plotselecting" event is fired during the
process with the same parameters as the "plotselected" event, in case
you want to know what's happening while it's happening,
A "plotunselected" event with no arguments is emitted when the user
clicks the mouse to remove the selection.
The plugin allso adds the following methods to the plot object:
- setSelection(ranges, preventEvent)
Set the selection rectangle. The passed in ranges is on the same
form as returned in the "plotselected" event. If the selection
mode is "x", you should put in either an xaxis (or x2axis) object,
if the mode is "y" you need to put in an yaxis (or y2axis) object
and both xaxis/x2axis and yaxis/y2axis if the selection mode is
"xy", like this:
setSelection({ xaxis: { from: 0, to: 10 }, yaxis: { from: 40, to: 60 } });
setSelection will trigger the "plotselected" event when called. If
you don't want that to happen, e.g. if you're inside a
"plotselected" handler, pass true as the second parameter.
- clearSelection(preventEvent)
Clear the selection rectangle. Pass in true to avoid getting a
"plotunselected" event.
- getSelection()
Returns the current selection in the same format as the
"plotselected" event. If there's currently no selection, the
function returns null.
*/
(function ($) {
function init(plot) {
var selection = {
first: { x: -1, y: -1}, second: { x: -1, y: -1},
show: false,
active: false
};
// FIXME: The drag handling implemented here should be
// abstracted out, there's some similar code from a library in
// the navigation plugin, this should be massaged a bit to fit
// the Flot cases here better and reused. Doing this would
// make this plugin much slimmer.
var savedhandlers = {};
function onMouseMove(e) {
if (selection.active) {
plot.getPlaceholder().trigger("plotselecting", [ getSelection() ]);
updateSelection(e);
}
}
function onMouseDown(e) {
if (e.which != 1) // only accept left-click
return;
// cancel out any text selections
document.body.focus();
// prevent text selection and drag in old-school browsers
if (document.onselectstart !== undefined && savedhandlers.onselectstart == null) {
savedhandlers.onselectstart = document.onselectstart;
document.onselectstart = function () { return false; };
}
if (document.ondrag !== undefined && savedhandlers.ondrag == null) {
savedhandlers.ondrag = document.ondrag;
document.ondrag = function () { return false; };
}
setSelectionPos(selection.first, e);
selection.active = true;
$(document).one("mouseup", onMouseUp);
}
function onMouseUp(e) {
// revert drag stuff for old-school browsers
if (document.onselectstart !== undefined)
document.onselectstart = savedhandlers.onselectstart;
if (document.ondrag !== undefined)
document.ondrag = savedhandlers.ondrag;
// no more draggy-dee-drag
selection.active = false;
updateSelection(e);
if (selectionIsSane())
triggerSelectedEvent();
else {
// this counts as a clear
plot.getPlaceholder().trigger("plotunselected", [ ]);
plot.getPlaceholder().trigger("plotselecting", [ null ]);
}
return false;
}
function getSelection() {
if (!selectionIsSane())
return null;
var x1 = Math.min(selection.first.x, selection.second.x),
x2 = Math.max(selection.first.x, selection.second.x),
y1 = Math.max(selection.first.y, selection.second.y),
y2 = Math.min(selection.first.y, selection.second.y);
var r = {};
var axes = plot.getAxes();
if (axes.xaxis.used)
r.xaxis = { from: axes.xaxis.c2p(x1), to: axes.xaxis.c2p(x2) };
if (axes.x2axis.used)
r.x2axis = { from: axes.x2axis.c2p(x1), to: axes.x2axis.c2p(x2) };
if (axes.yaxis.used)
r.yaxis = { from: axes.yaxis.c2p(y1), to: axes.yaxis.c2p(y2) };
if (axes.y2axis.used)
r.y2axis = { from: axes.y2axis.c2p(y1), to: axes.y2axis.c2p(y2) };
return r;
}
function triggerSelectedEvent() {
var r = getSelection();
plot.getPlaceholder().trigger("plotselected", [ r ]);
// backwards-compat stuff, to be removed in future
var axes = plot.getAxes();
if (axes.xaxis.used && axes.yaxis.used)
plot.getPlaceholder().trigger("selected", [ { x1: r.xaxis.from, y1: r.yaxis.from, x2: r.xaxis.to, y2: r.yaxis.to } ]);
}
function clamp(min, value, max) {
return value < min? min: (value > max? max: value);
}
function setSelectionPos(pos, e) {
var o = plot.getOptions();
var offset = plot.getPlaceholder().offset();
var plotOffset = plot.getPlotOffset();
pos.x = clamp(0, e.pageX - offset.left - plotOffset.left, plot.width());
pos.y = clamp(0, e.pageY - offset.top - plotOffset.top, plot.height());
if (o.selection.mode == "y")
pos.x = pos == selection.first? 0: plot.width();
if (o.selection.mode == "x")
pos.y = pos == selection.first? 0: plot.height();
}
function updateSelection(pos) {
if (pos.pageX == null)
return;
setSelectionPos(selection.second, pos);
if (selectionIsSane()) {
selection.show = true;
plot.triggerRedrawOverlay();
}
else
clearSelection(true);
}
function clearSelection(preventEvent) {
if (selection.show) {
selection.show = false;
plot.triggerRedrawOverlay();
if (!preventEvent)
plot.getPlaceholder().trigger("plotunselected", [ ]);
}
}
function setSelection(ranges, preventEvent) {
var axis, range, axes = plot.getAxes();
var o = plot.getOptions();
if (o.selection.mode == "y") {
selection.first.x = 0;
selection.second.x = plot.width();
}
else {
axis = ranges["xaxis"]? axes["xaxis"]: (ranges["x2axis"]? axes["x2axis"]: axes["xaxis"]);
range = ranges["xaxis"] || ranges["x2axis"] || { from:ranges["x1"], to:ranges["x2"] }
selection.first.x = axis.p2c(Math.min(range.from, range.to));
selection.second.x = axis.p2c(Math.max(range.from, range.to));
}
if (o.selection.mode == "x") {
selection.first.y = 0;
selection.second.y = plot.height();
}
else {
axis = ranges["yaxis"]? axes["yaxis"]: (ranges["y2axis"]? axes["y2axis"]: axes["yaxis"]);
range = ranges["yaxis"] || ranges["y2axis"] || { from:ranges["y1"], to:ranges["y2"] }
selection.first.y = axis.p2c(Math.min(range.from, range.to));
selection.second.y = axis.p2c(Math.max(range.from, range.to));
}
selection.show = true;
plot.triggerRedrawOverlay();
if (!preventEvent)
triggerSelectedEvent();
}
function selectionIsSane() {
var minSize = 5;
return Math.abs(selection.second.x - selection.first.x) >= minSize &&
Math.abs(selection.second.y - selection.first.y) >= minSize;
}
plot.clearSelection = clearSelection;
plot.setSelection = setSelection;
plot.getSelection = getSelection;
plot.hooks.bindEvents.push(function(plot, eventHolder) {
var o = plot.getOptions();
if (o.selection.mode != null)
eventHolder.mousemove(onMouseMove);
if (o.selection.mode != null)
eventHolder.mousedown(onMouseDown);
});
plot.hooks.drawOverlay.push(function (plot, ctx) {
// draw selection
if (selection.show && selectionIsSane()) {
var plotOffset = plot.getPlotOffset();
var o = plot.getOptions();
ctx.save();
ctx.translate(plotOffset.left, plotOffset.top);
var c = $.color.parse(o.selection.color);
ctx.strokeStyle = c.scale('a', 0.8).toString();
ctx.lineWidth = 1;
ctx.lineJoin = "round";
ctx.fillStyle = c.scale('a', 0.4).toString();
var x = Math.min(selection.first.x, selection.second.x),
y = Math.min(selection.first.y, selection.second.y),
w = Math.abs(selection.second.x - selection.first.x),
h = Math.abs(selection.second.y - selection.first.y);
ctx.fillRect(x, y, w, h);
ctx.strokeRect(x, y, w, h);
ctx.restore();
}
});
}
$.plot.plugins.push({
init: init,
options: {
selection: {
mode: null, // one of null, "x", "y" or "xy"
color: "#e8cfac"
}
},
name: 'selection',
version: '1.0'
});
})(jQuery);

View File

@ -0,0 +1,152 @@
/*
Flot plugin for stacking data sets, i.e. putting them on top of each
other, for accumulative graphs. Note that the plugin assumes the data
is sorted on x. Also note that stacking a mix of positive and negative
values in most instances doesn't make sense (so it looks weird).
Two or more series are stacked when their "stack" attribute is set to
the same key (which can be any number or string or just "true"). To
specify the default stack, you can set
series: {
stack: null or true or key (number/string)
}
or specify it for a specific series
$.plot($("#placeholder"), [{ data: [ ... ], stack: true ])
The stacking order is determined by the order of the data series in
the array (later series end up on top of the previous).
Internally, the plugin modifies the datapoints in each series, adding
an offset to the y value. For line series, extra data points are
inserted through interpolation. For bar charts, the second y value is
also adjusted.
*/
(function ($) {
var options = {
series: { stack: null } // or number/string
};
function init(plot) {
function findMatchingSeries(s, allseries) {
var res = null
for (var i = 0; i < allseries.length; ++i) {
if (s == allseries[i])
break;
if (allseries[i].stack == s.stack)
res = allseries[i];
}
return res;
}
function stackData(plot, s, datapoints) {
if (s.stack == null)
return;
var other = findMatchingSeries(s, plot.getData());
if (!other)
return;
var ps = datapoints.pointsize,
points = datapoints.points,
otherps = other.datapoints.pointsize,
otherpoints = other.datapoints.points,
newpoints = [],
px, py, intery, qx, qy, bottom,
withlines = s.lines.show, withbars = s.bars.show,
withsteps = withlines && s.lines.steps,
i = 0, j = 0, l;
while (true) {
if (i >= points.length)
break;
l = newpoints.length;
if (j >= otherpoints.length
|| otherpoints[j] == null
|| points[i] == null) {
// degenerate cases
for (m = 0; m < ps; ++m)
newpoints.push(points[i + m]);
i += ps;
}
else {
// cases where we actually got two points
px = points[i];
py = points[i + 1];
qx = otherpoints[j];
qy = otherpoints[j + 1];
bottom = 0;
if (px == qx) {
for (m = 0; m < ps; ++m)
newpoints.push(points[i + m]);
newpoints[l + 1] += qy;
bottom = qy;
i += ps;
j += otherps;
}
else if (px > qx) {
// we got past point below, might need to
// insert interpolated extra point
if (withlines && i > 0 && points[i - ps] != null) {
intery = py + (points[i - ps + 1] - py) * (qx - px) / (points[i - ps] - px);
newpoints.push(qx);
newpoints.push(intery + qy)
for (m = 2; m < ps; ++m)
newpoints.push(points[i + m]);
bottom = qy;
}
j += otherps;
}
else {
for (m = 0; m < ps; ++m)
newpoints.push(points[i + m]);
// we might be able to interpolate a point below,
// this can give us a better y
if (withlines && j > 0 && otherpoints[j - ps] != null)
bottom = qy + (otherpoints[j - ps + 1] - qy) * (px - qx) / (otherpoints[j - ps] - qx);
newpoints[l + 1] += bottom;
i += ps;
}
if (l != newpoints.length && withbars)
newpoints[l + 2] += bottom;
}
// maintain the line steps invariant
if (withsteps && l != newpoints.length && l > 0
&& newpoints[l] != null
&& newpoints[l] != newpoints[l - ps]
&& newpoints[l + 1] != newpoints[l - ps + 1]) {
for (m = 0; m < ps; ++m)
newpoints[l + ps + m] = newpoints[l + m];
newpoints[l + 1] = newpoints[l - ps + 1];
}
}
datapoints.points = newpoints;
}
plot.hooks.processDatapoints.push(stackData);
}
$.plot.plugins.push({
init: init,
options: options,
name: 'stack',
version: '1.0'
});
})(jQuery);

View File

@ -0,0 +1,103 @@
/*
Flot plugin for thresholding data. Controlled through the option
"threshold" in either the global series options
series: {
threshold: {
below: number
color: colorspec
}
}
or in a specific series
$.plot($("#placeholder"), [{ data: [ ... ], threshold: { ... }}])
The data points below "below" are drawn with the specified color. This
makes it easy to mark points below 0, e.g. for budget data.
Internally, the plugin works by splitting the data into two series,
above and below the threshold. The extra series below the threshold
will have its label cleared and the special "originSeries" attribute
set to the original series. You may need to check for this in hover
events.
*/
(function ($) {
var options = {
series: { threshold: null } // or { below: number, color: color spec}
};
function init(plot) {
function thresholdData(plot, s, datapoints) {
if (!s.threshold)
return;
var ps = datapoints.pointsize, i, x, y, p, prevp,
thresholded = $.extend({}, s); // note: shallow copy
thresholded.datapoints = { points: [], pointsize: ps };
thresholded.label = null;
thresholded.color = s.threshold.color;
thresholded.threshold = null;
thresholded.originSeries = s;
thresholded.data = [];
var below = s.threshold.below,
origpoints = datapoints.points,
addCrossingPoints = s.lines.show;
threspoints = [];
newpoints = [];
for (i = 0; i < origpoints.length; i += ps) {
x = origpoints[i]
y = origpoints[i + 1];
prevp = p;
if (y < below)
p = threspoints;
else
p = newpoints;
if (addCrossingPoints && prevp != p && x != null
&& i > 0 && origpoints[i - ps] != null) {
var interx = (x - origpoints[i - ps]) / (y - origpoints[i - ps + 1]) * (below - y) + x;
prevp.push(interx);
prevp.push(below);
for (m = 2; m < ps; ++m)
prevp.push(origpoints[i + m]);
p.push(null); // start new segment
p.push(null);
for (m = 2; m < ps; ++m)
p.push(origpoints[i + m]);
p.push(interx);
p.push(below);
for (m = 2; m < ps; ++m)
p.push(origpoints[i + m]);
}
p.push(x);
p.push(y);
}
datapoints.points = newpoints;
thresholded.datapoints.points = threspoints;
if (thresholded.datapoints.points.length > 0)
plot.getData().push(thresholded);
// FIXME: there are probably some edge cases left in bars
}
plot.hooks.processDatapoints.push(thresholdData);
}
$.plot.plugins.push({
init: init,
options: options,
name: 'threshold',
version: '1.0'
});
})(jQuery);

4376
toolkit/content/jquery/jquery.js vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,44 @@
<!-- LOCALIZATION NOTE: The terminology here relies on some subtle
distinctions between closely-related concepts, so I will try to
explain them in the localization notes. These strings are all used on
the about:startup page. -->
<!ENTITY about.startup.title "&brandShortName; Startup History">
<!ENTITY about.startup.timestamp "Timestamp">
<!ENTITY about.startup.elapsed "Elapsed Time">
<!-- LOCALIZATION NOTE (about.startup.app.launched): The timestamp for
when the user told his operating system to begin running Firefox. -->
<!ENTITY about.startup.app.launched "&brandShortName; Launched">
<!-- LOCALIZATION NOTE (about.startup.app.started): The timestamp for
when the operating system handed control over to Firefox itself. -->
<!ENTITY about.startup.app.started "&brandShortName; Started">
<!-- LOCALIZATION NOTE (about.startup.app.ready): The timestamp for
when the Firefox startup process finished, and when Firefox was first
ready for the user to use. -->
<!ENTITY about.startup.app.ready "&brandShortName; Ready">
<!-- LOCALIZATION NOTE (about.startup.duration.launch): The amount of
time between the launch and startup timestamps; the duration of the
launch process. -->
<!ENTITY about.startup.duration.launch "Launch Time">
<!-- LOCALIZATION NOTE (about.startup.duration.startup): The amount of
time between the startup and ready timestamps; the duration of the
startup process. -->
<!ENTITY about.startup.duration.startup "Startup Time">
<!-- LOCALIZATION NOTE (about.startup.duration.ready): The total time
that the user had to wait between when they lauched firefox and it was
ready to use. -->
<!ENTITY about.startup.duration.ready "Elapsed Time">
<!ENTITY about.startup.version "Version">
<!ENTITY about.startup.table "Show Table">
<!ENTITY about.startup.graph "Show Graph">
<!ENTITY about.startup.extension "Extension">
<!ENTITY about.startup.extensionID "ID">
<!ENTITY about.startup.action "Action">
<!ENTITY about.startup.noevents "No Events Recorded">

View File

@ -0,0 +1,22 @@
# LOCALIZATION NOTE (about.startup.appVersion): %1$S will be
# &brandShortName;, %2$S will be the version number, and %$S3 will be
# the Gecko build id. ex: "Firefox 4.0b6pre (20100909051952)"
about.startup.appVersion=%1$S %2$S (%3$S)
# LOCALIZATION NOTE (about.startup.extensionInstalled): %1$S will be
# the name of an extension, and %2$S will be its version number
about.startup.extensionInstalled=%1$S %2$S installed
# LOCALIZATION NOTE (about.startup.extensionUninstalled): %1$S will be
# the name of an extension, and %2$S will be its version number
about.startup.extensionUninstalled=%1$S %2$S uninstalled
# LOCALIZATION NOTE (about.startup.extensionUpgraded): %1$S will be
# the name of an extension, and %2$S will be its version number
about.startup.extensionUpgraded=%1$S upgraded to %2$S
# LOCALIZATION NOTE (about.startup.extensionEnabled): %1$S will be the
# name of an extension, and %2$S will be its version number
about.startup.extensionEnabled=%1$S %2$S enabled
# LOCALIZATION NOTE (about.startup.extensionDisabled): %1$S will be
# the name of an extension, and %2$S will be its version number
about.startup.extensionDisabled=%1$S %2$S disabled
# LOCALIZATION NOTE (about.startup.milliseconds): formats a number
# (%1$S) as milliseconds
about.startup.milliseconds=%1$S ms

View File

@ -8,6 +8,8 @@
locale/@AB_CD@/global/aboutRights.properties (%chrome/global/aboutRights.properties)
locale/@AB_CD@/global/aboutSupport.dtd (%chrome/global/aboutSupport.dtd)
locale/@AB_CD@/global/aboutSupport.properties (%chrome/global/aboutSupport.properties)
locale/@AB_CD@/global/aboutStartup.dtd (%chrome/global/aboutStartup.dtd)
locale/@AB_CD@/global/aboutStartup.properties (%chrome/global/aboutStartup.properties)
locale/@AB_CD@/global/actions.dtd (%chrome/global/actions.dtd)
locale/@AB_CD@/global/appPicker.dtd (%chrome/global/appPicker.dtd)
locale/@AB_CD@/global/brand.dtd (generic/chrome/global/brand.dtd)

View File

@ -226,10 +226,11 @@ var AddonManagerInternal = {
startup: function AMI_startup() {
if (this.started)
return;
this.installListeners = [];
this.addonListeners = [];
this._addNotificationListeners();
let appChanged = undefined;
try {
@ -844,13 +845,41 @@ var AddonManagerInternal = {
return i != aListener;
});
},
get autoUpdateDefault() {
try {
return Services.prefs.getBoolPref(PREF_EM_AUTOUPDATE_DEFAULT);
} catch(e) { }
return true;
}
},
/**
* Adds an AddonListener that uses the observer service to notify
* native code of the extension events.
*
* Currently only handles that subset of the events and data that
* the about:startup page requires.
*
*/
_addNotificationListeners: function()
{
const svc = Cc["@mozilla.org/observer-service;1"]
.getService(Ci.nsIObserverService);
function notify(msg, extension)
{
let bag = Cc["@mozilla.org/hash-property-bag;1"]
.createInstance(Ci.nsIWritablePropertyBag2);
bag.setPropertyAsAString("id", extension.id);
bag.setPropertyAsAString("name", extension.name);
bag.setPropertyAsAString("version", extension.version);
svc.notifyObservers(bag, "AddonManager-event", msg);
}
this.addAddonListener({ onEnabling: function(extension) { notify("Enabled", extension) },
onDisabling: function(extension) { notify("Disabled", extension) },
onInstalling: function(extension) { notify("Installed", extension) },
onUninstalling: function(extension) { notify("Uninstalled", extension) },
});
},
};
/**

View File

@ -0,0 +1,34 @@
h1 {
font-size: 140%;
margin: .5em 0;
}
#graph, #overview {
margin: 0;
width: 100%;
height: 350px;
}
#overview {
height: 100px;
}
.toggle {
font-size: smaller;
padding: 1pt;
position: absolute;
right: 0;
text-align: right;
}
#showgraph {
display: none;
}
#tables {
display: none;
}
.legend table {
width: auto;
}

View File

@ -47,3 +47,4 @@ toolkit.jar:
+ skin/classic/global/icons/sslWarning.png (icons/sslWarning.png)
+ skin/classic/global/icons/wrap.png (icons/wrap.png)
+ skin/classic/global/toolbar/spring.png (toolbar/spring.png)
+ skin/classic/global/aboutStartup.css

View File

@ -0,0 +1,34 @@
h1 {
font-size: 140%;
margin: .5em 0;
}
#graph, #overview {
margin: 0;
width: 100%;
height: 350px;
}
#overview {
height: 100px;
}
.toggle {
font-size: smaller;
padding: 1pt;
position: absolute;
right: 0;
text-align: right;
}
#showgraph {
display: none;
}
#tables {
display: none;
}
.legend table {
width: auto;
}

View File

@ -156,3 +156,4 @@ toolkit.jar:
skin/classic/global/tree/item.png (tree/item.png)
skin/classic/global/tree/sort-asc.gif (tree/sort-asc.gif)
skin/classic/global/tree/sort-dsc.gif (tree/sort-dsc.gif)
skin/classic/global/aboutStartup.css

View File

@ -0,0 +1,34 @@
h1 {
font-size: 140%;
margin: .5em 0;
}
#graph, #overview {
margin: 0;
width: 100%;
height: 350px;
}
#overview {
height: 100px;
}
.toggle {
font-size: smaller;
padding: 1pt;
position: absolute;
right: 0;
text-align: right;
}
#showgraph {
display: none;
}
#tables {
display: none;
}
.legend table {
width: auto;
}

View File

@ -168,6 +168,7 @@ toolkit.jar:
skin/classic/global/tree/sort-dsc-classic.png (tree/sort-dsc-classic.png)
skin/classic/global/tree/twisty-clsd.png (tree/twisty-clsd.png)
skin/classic/global/tree/twisty-open.png (tree/twisty-open.png)
skin/classic/global/aboutStartup.css
#ifdef XP_WIN
toolkit.jar:
@ -343,4 +344,5 @@ toolkit.jar:
skin/classic/aero/global/tree/twisty-open-rtl.png (tree/twisty-open-rtl-aero.png)
skin/classic/aero/global/tree/twisty-open-hover.png (tree/twisty-open-hover-aero.png)
skin/classic/aero/global/tree/twisty-open-hover-rtl.png (tree/twisty-open-hover-rtl-aero.png)
skin/classic/aero/global/aboutStartup.css
#endif

4
toolkit/xre/nsAppData.cpp Normal file → Executable file
View File

@ -72,6 +72,10 @@ ScopedAppData::ScopedAppData(const nsXREAppData* aAppData)
this->size = aAppData->size;
if (aAppData->size > offsetof(nsXREAppData, startupTimestamp)) {
this->startupTimestamp = PR_Now();
}
SetAllocatedString(this->vendor, aAppData->vendor);
SetAllocatedString(this->name, aAppData->name);
SetAllocatedString(this->version, aAppData->version);

66
toolkit/xre/nsAppRunner.cpp Normal file → Executable file
View File

@ -26,6 +26,7 @@
* Fredrik Holmqvist <thesuckiestemail@yahoo.se>
* Ben Turner <mozilla@songbirdnest.com>
* Sergei Dolgov <sergei_d@fi.tartu.ee>
* Daniel Brooks <db48x@db48x.net>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -835,6 +836,71 @@ nsXULAppInfo::InvalidateCachesOnRestart()
return NS_OK;
}
/* readonly attribute unsigned long launchTimestamp; */
NS_IMETHODIMP nsXULAppInfo::GetLaunchTimestamp(PRUint64 *aTimestamp)
{
#ifdef XP_UNIX
FILE *uptime;
long tickspersecond = sysconf(_SC_CLK_TCK);
unsigned long long sec, ssec;
uptime = fopen("/proc/uptime", "r");
fscanf(uptime, "%lld.%lld", &sec, &ssec);
PRTime boottime = PR_Now() - (((sec * PR_MSEC_PER_SEC) + (ssec * 10)) * PR_USEC_PER_MSEC);
fclose(uptime);
FILE *pidstat;
pid_t pid = getpid();
char *statpath = PR_smprintf("/proc/%d/stat", pid);
pidstat = fopen(statpath, "r");
if (!pidstat)
return NS_ERROR_FAILURE;
char stat[512];
memset(&stat, 0, 512);
int n = fread(&stat, 1, 511, pidstat);
if (n <= 0)
return NS_ERROR_FAILURE;
fclose(pidstat);
PR_smprintf_free(statpath);
PRTime starttime = 0;
sscanf(strrchr(stat, ')') + 2,
"%*c %*d %*d %*d %*d %*d %*u %*u %*u %*u "
"%*u %*u %*u %*u %*u %*d %*d %*d %*d %llu",
&starttime);
*aTimestamp = boottime + ((starttime / tickspersecond) * PR_USEC_PER_SEC);
return NS_OK;
#elif XP_WIN
FILETIME start, foo, bar, baz;
bool success = GetProcessTimes(GetCurrentProcess(), &start, &foo, &bar, &baz);
if (success)
{
// copied from NSPR _PR_FileTimeToPRTime
CopyMemory(aTimestamp, &start, sizeof(PRTime));
#ifdef __GNUC__
*aTimestamp = (*aTimestamp - 116444736000000000LL) / 10LL;
#else
*aTimestamp = (*aTimestamp - 116444736000000000i64) / 10i64;
#endif
return NS_OK;
}
return NS_ERROR_FAILURE;
#else
return NS_ERROR_NOT_IMPLEMENTED;
#endif
}
/* readonly attribute unsigned long startupTimestamp; */
NS_IMETHODIMP nsXULAppInfo::GetStartupTimestamp(PRUint64 *aTimestamp)
{
*aTimestamp = gAppData->startupTimestamp;
return NS_OK;
}
#ifdef XP_WIN
// Matches the enum in WinNT.h for the Vista SDK but renamed so that we can
// safely build with the Vista SDK and without it.

5
xpcom/build/nsXULAppAPI.h Normal file → Executable file
View File

@ -151,6 +151,11 @@ struct nsXREAppData
* UAppData = $HOME/$profile
*/
const char *profile;
/**
*
*/
PRTime startupTimestamp;
};
/**

0
xpcom/system/nsIXULAppInfo.idl Normal file → Executable file
View File

3
xpcom/system/nsIXULRuntime.idl Normal file → Executable file
View File

@ -108,6 +108,9 @@ interface nsIXULRuntime : nsISupports
*/
void invalidateCachesOnRestart();
readonly attribute unsigned long long launchTimestamp;
readonly attribute unsigned long long startupTimestamp;
/**
* Starts a child process. This method is intented to pre-start a
* content child process so that when it is actually needed, it is