зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to b2g-inbound
This commit is contained in:
Коммит
aa779853d3
1
AUTHORS
1
AUTHORS
|
@ -14,6 +14,7 @@ Aaron Nowack <anowack@mimiru.net>
|
||||||
Aaron Reed <aaronr@us.ibm.com>
|
Aaron Reed <aaronr@us.ibm.com>
|
||||||
Aaron Spangler <aaron@spangler.ods.org>
|
Aaron Spangler <aaron@spangler.ods.org>
|
||||||
Aaron Train <aaron.train@gmail.com>
|
Aaron Train <aaron.train@gmail.com>
|
||||||
|
Abdelrhman Ahmed <a.ahmed1026@gmail.com>
|
||||||
Achim Hasenmueller <achimha@innotek.de>
|
Achim Hasenmueller <achimha@innotek.de>
|
||||||
ActiveState Tool Corp.
|
ActiveState Tool Corp.
|
||||||
Adam Barth <hk9565@gmail.com>
|
Adam Barth <hk9565@gmail.com>
|
||||||
|
|
|
@ -1,2 +1,9 @@
|
||||||
[
|
[
|
||||||
|
{
|
||||||
|
"size": 80458572,
|
||||||
|
"digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
|
||||||
|
"algorithm": "sha512",
|
||||||
|
"filename": "gcc.tar.xz",
|
||||||
|
"unpack": "True"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,2 +1,9 @@
|
||||||
[
|
[
|
||||||
|
{
|
||||||
|
"size": 80458572,
|
||||||
|
"digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
|
||||||
|
"algorithm": "sha512",
|
||||||
|
"filename": "gcc.tar.xz",
|
||||||
|
"unpack": "True"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,2 +1,9 @@
|
||||||
[
|
[
|
||||||
|
{
|
||||||
|
"size": 80458572,
|
||||||
|
"digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
|
||||||
|
"algorithm": "sha512",
|
||||||
|
"filename": "gcc.tar.xz",
|
||||||
|
"unpack": "True"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,2 +1,9 @@
|
||||||
[
|
[
|
||||||
|
{
|
||||||
|
"size": 80458572,
|
||||||
|
"digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
|
||||||
|
"algorithm": "sha512",
|
||||||
|
"filename": "gcc.tar.xz",
|
||||||
|
"unpack": "True"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -5,5 +5,12 @@
|
||||||
"algorithm": "sha512",
|
"algorithm": "sha512",
|
||||||
"filename": "backup-flame.tar.xz",
|
"filename": "backup-flame.tar.xz",
|
||||||
"comment": "v18D"
|
"comment": "v18D"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size": 80458572,
|
||||||
|
"digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
|
||||||
|
"algorithm": "sha512",
|
||||||
|
"filename": "gcc.tar.xz",
|
||||||
|
"unpack": "True"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -3,5 +3,12 @@
|
||||||
"digest": "8d1a71552ffee561e93b5b3f1bb47866592ab958f908007c75561156430eb1b85a265bfc4dc2038e58dda0264daa9854877a84ef3b591c9ac2f1ab97c098e61e",
|
"digest": "8d1a71552ffee561e93b5b3f1bb47866592ab958f908007c75561156430eb1b85a265bfc4dc2038e58dda0264daa9854877a84ef3b591c9ac2f1ab97c098e61e",
|
||||||
"filename": "backup-flame.tar.xz",
|
"filename": "backup-flame.tar.xz",
|
||||||
"algorithm": "sha512"
|
"algorithm": "sha512"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size": 80458572,
|
||||||
|
"digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
|
||||||
|
"algorithm": "sha512",
|
||||||
|
"filename": "gcc.tar.xz",
|
||||||
|
"unpack": "True"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -10,5 +10,12 @@
|
||||||
"digest": "ea03de74df73b05e939c314cd15c54aac7b5488a407b7cc4f5f263f3049a1f69642c567dd35c43d0bc3f0d599d0385a26ab2dd947a6b18f9044e4918b382eea7",
|
"digest": "ea03de74df73b05e939c314cd15c54aac7b5488a407b7cc4f5f263f3049a1f69642c567dd35c43d0bc3f0d599d0385a26ab2dd947a6b18f9044e4918b382eea7",
|
||||||
"algorithm": "sha512",
|
"algorithm": "sha512",
|
||||||
"filename": "Adreno200-AU_LINUX_ANDROID_ICS_CHOCO_CS.04.00.03.06.001.zip"
|
"filename": "Adreno200-AU_LINUX_ANDROID_ICS_CHOCO_CS.04.00.03.06.001.zip"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size": 80458572,
|
||||||
|
"digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
|
||||||
|
"algorithm": "sha512",
|
||||||
|
"filename": "gcc.tar.xz",
|
||||||
|
"unpack": "True"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -28,5 +28,12 @@
|
||||||
"digest": "d6a969fad4e53b617a21026062767f4b89d301464c38e9c9d04ba7dc7dcad72f1b337c284243a81e74de713171af5dd9a13aaaa7efd10109a0847ed23bf6e539",
|
"digest": "d6a969fad4e53b617a21026062767f4b89d301464c38e9c9d04ba7dc7dcad72f1b337c284243a81e74de713171af5dd9a13aaaa7efd10109a0847ed23bf6e539",
|
||||||
"algorithm": "sha512",
|
"algorithm": "sha512",
|
||||||
"filename": "tt_setup.sh"
|
"filename": "tt_setup.sh"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size": 80458572,
|
||||||
|
"digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
|
||||||
|
"algorithm": "sha512",
|
||||||
|
"filename": "gcc.tar.xz",
|
||||||
|
"unpack": "True"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -10,5 +10,12 @@
|
||||||
"digest": "ea03de74df73b05e939c314cd15c54aac7b5488a407b7cc4f5f263f3049a1f69642c567dd35c43d0bc3f0d599d0385a26ab2dd947a6b18f9044e4918b382eea7",
|
"digest": "ea03de74df73b05e939c314cd15c54aac7b5488a407b7cc4f5f263f3049a1f69642c567dd35c43d0bc3f0d599d0385a26ab2dd947a6b18f9044e4918b382eea7",
|
||||||
"algorithm": "sha512",
|
"algorithm": "sha512",
|
||||||
"filename": "Adreno200-AU_LINUX_ANDROID_ICS_CHOCO_CS.04.00.03.06.001.zip"
|
"filename": "Adreno200-AU_LINUX_ANDROID_ICS_CHOCO_CS.04.00.03.06.001.zip"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size": 80458572,
|
||||||
|
"digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
|
||||||
|
"algorithm": "sha512",
|
||||||
|
"filename": "gcc.tar.xz",
|
||||||
|
"unpack": "True"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -5,3 +5,12 @@
|
||||||
# This file is included at the top of all b2g mozconfigs
|
# This file is included at the top of all b2g mozconfigs
|
||||||
|
|
||||||
. "$topsrcdir/build/mozconfig.common"
|
. "$topsrcdir/build/mozconfig.common"
|
||||||
|
|
||||||
|
# Normally, we'd set this unconditionally, but this file is also used
|
||||||
|
# for local builds and there is no other mozconfig in this tree that
|
||||||
|
# is included on device builds.
|
||||||
|
if test -d $topsrcdir/../gcc/bin; then
|
||||||
|
HOST_CC="$topsrcdir/../gcc/bin/gcc"
|
||||||
|
HOST_CXX="$topsrcdir/../gcc/bin/g++"
|
||||||
|
ac_add_options --enable-stdcxx-compat
|
||||||
|
fi
|
||||||
|
|
|
@ -16,6 +16,13 @@
|
||||||
"digest": "27aced8feb0e757d61df37839e62410ff30a059cfa8f04897d29ab74b787c765313acf904b1f9cf311c3e682883514df7da54197665251ef9b8bdad6bd0f62c5",
|
"digest": "27aced8feb0e757d61df37839e62410ff30a059cfa8f04897d29ab74b787c765313acf904b1f9cf311c3e682883514df7da54197665251ef9b8bdad6bd0f62c5",
|
||||||
"algorithm": "sha512",
|
"algorithm": "sha512",
|
||||||
"filename": "lge-mako-jwr66v-985845e4.tgz"
|
"filename": "lge-mako-jwr66v-985845e4.tgz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size": 80458572,
|
||||||
|
"digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
|
||||||
|
"algorithm": "sha512",
|
||||||
|
"filename": "gcc.tar.xz",
|
||||||
|
"unpack": "True"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -16,5 +16,12 @@
|
||||||
"digest": "709a281438607f10c9f55683303d5cc1d8720520e26a8ae45f48b7ccb9265ba8939c4a077ae3368afc891bbd7426013edfbbbb3dbdeab5b1f06e60901b141de6",
|
"digest": "709a281438607f10c9f55683303d5cc1d8720520e26a8ae45f48b7ccb9265ba8939c4a077ae3368afc891bbd7426013edfbbbb3dbdeab5b1f06e60901b141de6",
|
||||||
"algorithm": "sha512",
|
"algorithm": "sha512",
|
||||||
"filename": "boot.img"
|
"filename": "boot.img"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size": 80458572,
|
||||||
|
"digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
|
||||||
|
"algorithm": "sha512",
|
||||||
|
"filename": "gcc.tar.xz",
|
||||||
|
"unpack": "True"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -732,7 +732,6 @@
|
||||||
@BINPATH@/res/fonts/*
|
@BINPATH@/res/fonts/*
|
||||||
@BINPATH@/res/dtd/*
|
@BINPATH@/res/dtd/*
|
||||||
@BINPATH@/res/html/*
|
@BINPATH@/res/html/*
|
||||||
@BINPATH@/res/langGroups.properties
|
|
||||||
@BINPATH@/res/language.properties
|
@BINPATH@/res/language.properties
|
||||||
@BINPATH@/res/entityTables/*
|
@BINPATH@/res/entityTables/*
|
||||||
#ifdef XP_MACOSX
|
#ifdef XP_MACOSX
|
||||||
|
|
|
@ -26,13 +26,6 @@ pref("app.update.url.details", "https://www.mozilla.org/firefox/aurora/");
|
||||||
// app.update.checkInstallTime is true.
|
// app.update.checkInstallTime is true.
|
||||||
pref("app.update.checkInstallTime.days", 2);
|
pref("app.update.checkInstallTime.days", 2);
|
||||||
|
|
||||||
// code usage depends on contracts, please contact the Firefox module owner if you have questions
|
|
||||||
pref("browser.search.param.yahoo-fr", "moz35");
|
|
||||||
pref("browser.search.param.yahoo-fr-ja", "mozff");
|
|
||||||
#ifdef MOZ_METRO
|
|
||||||
pref("browser.search.param.yahoo-fr-metro", "");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Number of usages of the web console or scratchpad.
|
// Number of usages of the web console or scratchpad.
|
||||||
// If this is less than 5, then pasting code into the web console or scratchpad is disabled
|
// If this is less than 5, then pasting code into the web console or scratchpad is disabled
|
||||||
pref("devtools.selfxss.count", 5);
|
pref("devtools.selfxss.count", 5);
|
|
@ -24,13 +24,6 @@ pref("app.update.url.details", "https://nightly.mozilla.org");
|
||||||
// app.update.checkInstallTime is true.
|
// app.update.checkInstallTime is true.
|
||||||
pref("app.update.checkInstallTime.days", 2);
|
pref("app.update.checkInstallTime.days", 2);
|
||||||
|
|
||||||
// code usage depends on contracts, please contact the Firefox module owner if you have questions
|
|
||||||
pref("browser.search.param.yahoo-fr", "moz35");
|
|
||||||
pref("browser.search.param.yahoo-fr-ja", "mozff");
|
|
||||||
#ifdef MOZ_METRO
|
|
||||||
pref("browser.search.param.yahoo-fr-metro", "");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Number of usages of the web console or scratchpad.
|
// Number of usages of the web console or scratchpad.
|
||||||
// If this is less than 5, then pasting code into the web console or scratchpad is disabled
|
// If this is less than 5, then pasting code into the web console or scratchpad is disabled
|
||||||
pref("devtools.selfxss.count", 5);
|
pref("devtools.selfxss.count", 5);
|
|
@ -23,14 +23,6 @@ pref("app.update.url.details", "https://www.mozilla.org/%LOCALE%/firefox/notes")
|
||||||
// app.update.checkInstallTime is true.
|
// app.update.checkInstallTime is true.
|
||||||
pref("app.update.checkInstallTime.days", 63);
|
pref("app.update.checkInstallTime.days", 63);
|
||||||
|
|
||||||
// code usage depends on contracts, please contact the Firefox module owner if you have questions
|
|
||||||
pref("browser.search.param.yahoo-fr", "moz35");
|
|
||||||
pref("browser.search.param.yahoo-fr-ja", "mozff");
|
|
||||||
#ifdef MOZ_METRO
|
|
||||||
pref("browser.search.param.ms-pc-metro", "MOZW");
|
|
||||||
pref("browser.search.param.yahoo-fr-metro", "mozilla_metro_search");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Number of usages of the web console or scratchpad.
|
// Number of usages of the web console or scratchpad.
|
||||||
// If this is less than 5, then pasting code into the web console or scratchpad is disabled
|
// If this is less than 5, then pasting code into the web console or scratchpad is disabled
|
||||||
pref("devtools.selfxss.count", 0);
|
pref("devtools.selfxss.count", 0);
|
|
@ -23,13 +23,6 @@ pref("app.update.url.details", "https://nightly.mozilla.org");
|
||||||
// app.update.checkInstallTime is true.
|
// app.update.checkInstallTime is true.
|
||||||
pref("app.update.checkInstallTime.days", 2);
|
pref("app.update.checkInstallTime.days", 2);
|
||||||
|
|
||||||
// code usage depends on contracts, please contact the Firefox module owner if you have questions
|
|
||||||
pref("browser.search.param.yahoo-fr", "moz35");
|
|
||||||
pref("browser.search.param.yahoo-fr-ja", "mozff");
|
|
||||||
#ifdef MOZ_METRO
|
|
||||||
pref("browser.search.param.yahoo-fr-metro", "");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Number of usages of the web console or scratchpad.
|
// Number of usages of the web console or scratchpad.
|
||||||
// If this is less than 5, then pasting code into the web console or scratchpad is disabled
|
// If this is less than 5, then pasting code into the web console or scratchpad is disabled
|
||||||
pref("devtools.selfxss.count", 0);
|
pref("devtools.selfxss.count", 0);
|
|
@ -3969,8 +3969,12 @@ OverflowableToolbar.prototype = {
|
||||||
},
|
},
|
||||||
|
|
||||||
onOverflow: function(aEvent) {
|
onOverflow: function(aEvent) {
|
||||||
|
// The rangeParent check is here because of bug 1111986 and ensuring that
|
||||||
|
// overflow events from the bookmarks toolbar items or similar things that
|
||||||
|
// manage their own overflow don't trigger an overflow on the entire toolbar
|
||||||
if (!this._enabled ||
|
if (!this._enabled ||
|
||||||
(aEvent && aEvent.target != this._toolbar.customizationTarget))
|
(aEvent && aEvent.target != this._toolbar.customizationTarget) ||
|
||||||
|
(aEvent && aEvent.rangeParent))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let child = this._target.lastChild;
|
let child = this._target.lastChild;
|
||||||
|
|
|
@ -85,6 +85,8 @@ browser.jar:
|
||||||
content/browser/devtools/webaudioeditor/views/utils.js (webaudioeditor/views/utils.js)
|
content/browser/devtools/webaudioeditor/views/utils.js (webaudioeditor/views/utils.js)
|
||||||
content/browser/devtools/webaudioeditor/views/context.js (webaudioeditor/views/context.js)
|
content/browser/devtools/webaudioeditor/views/context.js (webaudioeditor/views/context.js)
|
||||||
content/browser/devtools/webaudioeditor/views/inspector.js (webaudioeditor/views/inspector.js)
|
content/browser/devtools/webaudioeditor/views/inspector.js (webaudioeditor/views/inspector.js)
|
||||||
|
content/browser/devtools/webaudioeditor/views/properties.js (webaudioeditor/views/properties.js)
|
||||||
|
content/browser/devtools/webaudioeditor/views/automation.js (webaudioeditor/views/automation.js)
|
||||||
content/browser/devtools/profiler.xul (profiler/profiler.xul)
|
content/browser/devtools/profiler.xul (profiler/profiler.xul)
|
||||||
content/browser/devtools/profiler.js (profiler/profiler.js)
|
content/browser/devtools/profiler.js (profiler/profiler.js)
|
||||||
content/browser/devtools/ui-recordings.js (profiler/ui-recordings.js)
|
content/browser/devtools/ui-recordings.js (profiler/ui-recordings.js)
|
||||||
|
@ -98,6 +100,7 @@ browser.jar:
|
||||||
content/browser/devtools/performance/views/details-call-tree.js (performance/views/details-call-tree.js)
|
content/browser/devtools/performance/views/details-call-tree.js (performance/views/details-call-tree.js)
|
||||||
content/browser/devtools/performance/views/details-waterfall.js (performance/views/details-waterfall.js)
|
content/browser/devtools/performance/views/details-waterfall.js (performance/views/details-waterfall.js)
|
||||||
content/browser/devtools/performance/views/details-flamegraph.js (performance/views/details-flamegraph.js)
|
content/browser/devtools/performance/views/details-flamegraph.js (performance/views/details-flamegraph.js)
|
||||||
|
content/browser/devtools/performance/views/recordings.js (performance/views/recordings.js)
|
||||||
#endif
|
#endif
|
||||||
content/browser/devtools/responsivedesign/resize-commands.js (responsivedesign/resize-commands.js)
|
content/browser/devtools/responsivedesign/resize-commands.js (responsivedesign/resize-commands.js)
|
||||||
content/browser/devtools/commandline.css (commandline/commandline.css)
|
content/browser/devtools/commandline.css (commandline/commandline.css)
|
||||||
|
|
|
@ -0,0 +1,248 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const { PerformanceIO } = require("devtools/performance/io");
|
||||||
|
|
||||||
|
const RECORDING_IN_PROGRESS = exports.RECORDING_IN_PROGRESS = -1;
|
||||||
|
const RECORDING_UNAVAILABLE = exports.RECORDING_UNAVAILABLE = null;
|
||||||
|
/**
|
||||||
|
* Model for a wholistic profile, containing start/stop times, profiling data, frames data,
|
||||||
|
* timeline (marker, tick, memory) data, and methods to start/stop recording.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const RecordingModel = function (options={}) {
|
||||||
|
this._front = options.front;
|
||||||
|
this._performance = options.performance;
|
||||||
|
this._label = options.label || "";
|
||||||
|
};
|
||||||
|
|
||||||
|
RecordingModel.prototype = {
|
||||||
|
_localStartTime: RECORDING_UNAVAILABLE,
|
||||||
|
_startTime: RECORDING_UNAVAILABLE,
|
||||||
|
_endTime: RECORDING_UNAVAILABLE,
|
||||||
|
_markers: [],
|
||||||
|
_frames: [],
|
||||||
|
_ticks: [],
|
||||||
|
_memory: [],
|
||||||
|
_profilerData: {},
|
||||||
|
_label: "",
|
||||||
|
_imported: false,
|
||||||
|
_isRecording: false,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads a recording from a file.
|
||||||
|
*
|
||||||
|
* @param nsILocalFile file
|
||||||
|
* The file to import the data form.
|
||||||
|
*/
|
||||||
|
importRecording: Task.async(function *(file) {
|
||||||
|
let recordingData = yield PerformanceIO.loadRecordingFromFile(file);
|
||||||
|
|
||||||
|
this._imported = true;
|
||||||
|
this._label = recordingData.profilerData.profilerLabel || "";
|
||||||
|
this._startTime = recordingData.interval.startTime;
|
||||||
|
this._endTime = recordingData.interval.endTime;
|
||||||
|
this._markers = recordingData.markers;
|
||||||
|
this._frames = recordingData.frames;
|
||||||
|
this._memory = recordingData.memory;
|
||||||
|
this._ticks = recordingData.ticks;
|
||||||
|
this._profilerData = recordingData.profilerData;
|
||||||
|
|
||||||
|
return recordingData;
|
||||||
|
}),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the current recording to a file.
|
||||||
|
*
|
||||||
|
* @param nsILocalFile file
|
||||||
|
* The file to stream the data into.
|
||||||
|
*/
|
||||||
|
exportRecording: Task.async(function *(file) {
|
||||||
|
let recordingData = this.getAllData();
|
||||||
|
yield PerformanceIO.saveRecordingToFile(recordingData, file);
|
||||||
|
}),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts recording with the PerformanceFront, storing the start times
|
||||||
|
* on the model.
|
||||||
|
*/
|
||||||
|
startRecording: Task.async(function *() {
|
||||||
|
// Times must come from the actor in order to be self-consistent.
|
||||||
|
// However, we also want to update the view with the elapsed time
|
||||||
|
// even when the actor is not generating data. To do this we get
|
||||||
|
// the local time and use it to compute a reasonable elapsed time.
|
||||||
|
this._localStartTime = this._performance.now();
|
||||||
|
|
||||||
|
let { startTime } = yield this._front.startRecording({
|
||||||
|
withTicks: true,
|
||||||
|
withMemory: true
|
||||||
|
});
|
||||||
|
this._isRecording = true;
|
||||||
|
|
||||||
|
this._startTime = startTime;
|
||||||
|
this._endTime = RECORDING_IN_PROGRESS;
|
||||||
|
this._markers = [];
|
||||||
|
this._frames = [];
|
||||||
|
this._memory = [];
|
||||||
|
this._ticks = [];
|
||||||
|
}),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops recording with the PerformanceFront, storing the end times
|
||||||
|
* on the model.
|
||||||
|
*/
|
||||||
|
stopRecording: Task.async(function *() {
|
||||||
|
let results = yield this._front.stopRecording();
|
||||||
|
this._isRecording = false;
|
||||||
|
|
||||||
|
// If `endTime` is not yielded from timeline actor (< Fx36), fake it.
|
||||||
|
if (!results.endTime) {
|
||||||
|
results.endTime = this._startTime + this.getLocalElapsedTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
this._endTime = results.endTime;
|
||||||
|
this._profilerData = results.profilerData;
|
||||||
|
this._markers = this._markers.sort((a,b) => (a.start > b.start));
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the profile's label, from `console.profile(LABEL)`.
|
||||||
|
*/
|
||||||
|
getLabel: function () {
|
||||||
|
return this._label;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the amount of time elapsed locally after starting a recording.
|
||||||
|
*/
|
||||||
|
getLocalElapsedTime: function () {
|
||||||
|
return this._performance.now() - this._localStartTime;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns duration of this recording, in milliseconds.
|
||||||
|
*/
|
||||||
|
getDuration: function () {
|
||||||
|
let { startTime, endTime } = this.getInterval();
|
||||||
|
return endTime - startTime;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the time interval for the current recording.
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
getInterval: function() {
|
||||||
|
let startTime = this._startTime;
|
||||||
|
let endTime = this._endTime;
|
||||||
|
|
||||||
|
// Compute an approximate ending time for the current recording. This is
|
||||||
|
// needed to ensure that the view updates even when new data is
|
||||||
|
// not being generated.
|
||||||
|
if (endTime == RECORDING_IN_PROGRESS) {
|
||||||
|
endTime = startTime + this.getLocalElapsedTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
return { startTime, endTime };
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the accumulated markers in the current recording.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
getMarkers: function() {
|
||||||
|
return this._markers;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the accumulated stack frames in the current recording.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
getFrames: function() {
|
||||||
|
return this._frames;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the accumulated memory measurements in this recording.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
getMemory: function() {
|
||||||
|
return this._memory;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the accumulated refresh driver ticks in this recording.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
getTicks: function() {
|
||||||
|
return this._ticks;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the profiler data in this recording.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
getProfilerData: function() {
|
||||||
|
return this._profilerData;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets all the data in this recording.
|
||||||
|
*/
|
||||||
|
getAllData: function() {
|
||||||
|
let interval = this.getInterval();
|
||||||
|
let markers = this.getMarkers();
|
||||||
|
let frames = this.getFrames();
|
||||||
|
let memory = this.getMemory();
|
||||||
|
let ticks = this.getTicks();
|
||||||
|
let profilerData = this.getProfilerData();
|
||||||
|
return { interval, markers, frames, memory, ticks, profilerData };
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a boolean indicating whether or not this recording model
|
||||||
|
* is recording.
|
||||||
|
*/
|
||||||
|
isRecording: function () {
|
||||||
|
return this._isRecording;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fired whenever the PerformanceFront emits markers, memory or ticks.
|
||||||
|
*/
|
||||||
|
addTimelineData: function (eventName, ...data) {
|
||||||
|
// If this model isn't currently recording,
|
||||||
|
// ignore the timeline data.
|
||||||
|
if (!this.isRecording()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (eventName) {
|
||||||
|
// Accumulate markers into an array.
|
||||||
|
case "markers":
|
||||||
|
let [markers] = data;
|
||||||
|
Array.prototype.push.apply(this._markers, markers);
|
||||||
|
break;
|
||||||
|
// Accumulate stack frames into an array.
|
||||||
|
case "frames":
|
||||||
|
let [, frames] = data;
|
||||||
|
Array.prototype.push.apply(this._frames, frames);
|
||||||
|
break;
|
||||||
|
// Accumulate memory measurements into an array.
|
||||||
|
case "memory":
|
||||||
|
let [delta, measurement] = data;
|
||||||
|
this._memory.push({ delta, value: measurement.total / 1024 / 1024 });
|
||||||
|
break;
|
||||||
|
// Save the accumulated refresh driver ticks.
|
||||||
|
case "ticks":
|
||||||
|
let [, timestamps] = data;
|
||||||
|
this._ticks = timestamps;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.RecordingModel = RecordingModel;
|
|
@ -6,6 +6,7 @@
|
||||||
EXTRA_JS_MODULES.devtools.performance += [
|
EXTRA_JS_MODULES.devtools.performance += [
|
||||||
'modules/front.js',
|
'modules/front.js',
|
||||||
'modules/io.js',
|
'modules/io.js',
|
||||||
|
'modules/recording-model.js',
|
||||||
'panel.js'
|
'panel.js'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,11 @@ devtools.lazyImporter(this, "CanvasGraphUtils",
|
||||||
"resource:///modules/devtools/Graphs.jsm");
|
"resource:///modules/devtools/Graphs.jsm");
|
||||||
devtools.lazyImporter(this, "LineGraphWidget",
|
devtools.lazyImporter(this, "LineGraphWidget",
|
||||||
"resource:///modules/devtools/Graphs.jsm");
|
"resource:///modules/devtools/Graphs.jsm");
|
||||||
|
devtools.lazyImporter(this, "SideMenuWidget",
|
||||||
|
"resource:///modules/devtools/SideMenuWidget.jsm");
|
||||||
|
|
||||||
|
const { RecordingModel, RECORDING_IN_PROGRESS, RECORDING_UNAVAILABLE } =
|
||||||
|
devtools.require("devtools/performance/recording-model");
|
||||||
|
|
||||||
devtools.lazyImporter(this, "FlameGraphUtils",
|
devtools.lazyImporter(this, "FlameGraphUtils",
|
||||||
"resource:///modules/devtools/FlameGraph.jsm");
|
"resource:///modules/devtools/FlameGraph.jsm");
|
||||||
|
@ -49,12 +54,17 @@ devtools.lazyImporter(this, "FlameGraph",
|
||||||
|
|
||||||
// Events emitted by various objects in the panel.
|
// Events emitted by various objects in the panel.
|
||||||
const EVENTS = {
|
const EVENTS = {
|
||||||
|
// Emitted by the PerformanceController or RecordingView
|
||||||
|
// when a recording model is selected
|
||||||
|
RECORDING_SELECTED: "Performance:RecordingSelected",
|
||||||
|
|
||||||
// Emitted by the PerformanceView on record button click
|
// Emitted by the PerformanceView on record button click
|
||||||
UI_START_RECORDING: "Performance:UI:StartRecording",
|
UI_START_RECORDING: "Performance:UI:StartRecording",
|
||||||
UI_STOP_RECORDING: "Performance:UI:StopRecording",
|
UI_STOP_RECORDING: "Performance:UI:StopRecording",
|
||||||
|
|
||||||
// Emitted by the PerformanceView on import or export button click
|
// Emitted by the PerformanceView on import button click
|
||||||
UI_IMPORT_RECORDING: "Performance:UI:ImportRecording",
|
UI_IMPORT_RECORDING: "Performance:UI:ImportRecording",
|
||||||
|
// Emitted by the RecordingsView on export button click
|
||||||
UI_EXPORT_RECORDING: "Performance:UI:ExportRecording",
|
UI_EXPORT_RECORDING: "Performance:UI:ExportRecording",
|
||||||
|
|
||||||
// When a recording is started or stopped via the PerformanceController
|
// When a recording is started or stopped via the PerformanceController
|
||||||
|
@ -96,11 +106,6 @@ const EVENTS = {
|
||||||
FLAMEGRAPH_RENDERED: "Performance:UI:FlameGraphRendered"
|
FLAMEGRAPH_RENDERED: "Performance:UI:FlameGraphRendered"
|
||||||
};
|
};
|
||||||
|
|
||||||
// Constant defining the end time for a recording that hasn't finished
|
|
||||||
// or is not yet available.
|
|
||||||
const RECORDING_IN_PROGRESS = -1;
|
|
||||||
const RECORDING_UNAVAILABLE = null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current target and the profiler connection, set by this tool's host.
|
* The current target and the profiler connection, set by this tool's host.
|
||||||
*/
|
*/
|
||||||
|
@ -150,18 +155,8 @@ let PrefObserver = {
|
||||||
* UI interaction.
|
* UI interaction.
|
||||||
*/
|
*/
|
||||||
let PerformanceController = {
|
let PerformanceController = {
|
||||||
/**
|
_recordings: [],
|
||||||
* Permanent storage for the markers and the memory measurements streamed by
|
_currentRecording: null,
|
||||||
* the backend, along with the start and end timestamps.
|
|
||||||
*/
|
|
||||||
_localStartTime: RECORDING_UNAVAILABLE,
|
|
||||||
_startTime: RECORDING_UNAVAILABLE,
|
|
||||||
_endTime: RECORDING_UNAVAILABLE,
|
|
||||||
_markers: [],
|
|
||||||
_frames: [],
|
|
||||||
_memory: [],
|
|
||||||
_ticks: [],
|
|
||||||
_profilerData: {},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listen for events emitted by the current tab target and
|
* Listen for events emitted by the current tab target and
|
||||||
|
@ -173,11 +168,13 @@ let PerformanceController = {
|
||||||
this.importRecording = this.importRecording.bind(this);
|
this.importRecording = this.importRecording.bind(this);
|
||||||
this.exportRecording = this.exportRecording.bind(this);
|
this.exportRecording = this.exportRecording.bind(this);
|
||||||
this._onTimelineData = this._onTimelineData.bind(this);
|
this._onTimelineData = this._onTimelineData.bind(this);
|
||||||
|
this._onRecordingSelectFromView = this._onRecordingSelectFromView.bind(this);
|
||||||
|
|
||||||
PerformanceView.on(EVENTS.UI_START_RECORDING, this.startRecording);
|
PerformanceView.on(EVENTS.UI_START_RECORDING, this.startRecording);
|
||||||
PerformanceView.on(EVENTS.UI_STOP_RECORDING, this.stopRecording);
|
PerformanceView.on(EVENTS.UI_STOP_RECORDING, this.stopRecording);
|
||||||
PerformanceView.on(EVENTS.UI_EXPORT_RECORDING, this.exportRecording);
|
|
||||||
PerformanceView.on(EVENTS.UI_IMPORT_RECORDING, this.importRecording);
|
PerformanceView.on(EVENTS.UI_IMPORT_RECORDING, this.importRecording);
|
||||||
|
RecordingsView.on(EVENTS.UI_EXPORT_RECORDING, this.exportRecording);
|
||||||
|
RecordingsView.on(EVENTS.RECORDING_SELECTED, this._onRecordingSelectFromView);
|
||||||
|
|
||||||
gFront.on("ticks", this._onTimelineData); // framerate
|
gFront.on("ticks", this._onTimelineData); // framerate
|
||||||
gFront.on("markers", this._onTimelineData); // timeline markers
|
gFront.on("markers", this._onTimelineData); // timeline markers
|
||||||
|
@ -191,8 +188,9 @@ let PerformanceController = {
|
||||||
destroy: function() {
|
destroy: function() {
|
||||||
PerformanceView.off(EVENTS.UI_START_RECORDING, this.startRecording);
|
PerformanceView.off(EVENTS.UI_START_RECORDING, this.startRecording);
|
||||||
PerformanceView.off(EVENTS.UI_STOP_RECORDING, this.stopRecording);
|
PerformanceView.off(EVENTS.UI_STOP_RECORDING, this.stopRecording);
|
||||||
PerformanceView.off(EVENTS.UI_EXPORT_RECORDING, this.exportRecording);
|
|
||||||
PerformanceView.off(EVENTS.UI_IMPORT_RECORDING, this.importRecording);
|
PerformanceView.off(EVENTS.UI_IMPORT_RECORDING, this.importRecording);
|
||||||
|
RecordingsView.off(EVENTS.UI_EXPORT_RECORDING, this.exportRecording);
|
||||||
|
RecordingsView.off(EVENTS.RECORDING_SELECTED, this._onRecordingSelectFromView);
|
||||||
|
|
||||||
gFront.off("ticks", this._onTimelineData);
|
gFront.off("ticks", this._onTimelineData);
|
||||||
gFront.off("markers", this._onTimelineData);
|
gFront.off("markers", this._onTimelineData);
|
||||||
|
@ -205,25 +203,11 @@ let PerformanceController = {
|
||||||
* when the front has started to record.
|
* when the front has started to record.
|
||||||
*/
|
*/
|
||||||
startRecording: Task.async(function *() {
|
startRecording: Task.async(function *() {
|
||||||
// Times must come from the actor in order to be self-consistent.
|
let model = this.createNewRecording();
|
||||||
// However, we also want to update the view with the elapsed time
|
this.setCurrentRecording(model);
|
||||||
// even when the actor is not generating data. To do this we get
|
yield model.startRecording();
|
||||||
// the local time and use it to compute a reasonable elapsed time.
|
|
||||||
this._localStartTime = performance.now();
|
|
||||||
|
|
||||||
let { startTime } = yield gFront.startRecording({
|
this.emit(EVENTS.RECORDING_STARTED, model);
|
||||||
withTicks: true,
|
|
||||||
withMemory: true
|
|
||||||
});
|
|
||||||
|
|
||||||
this._startTime = startTime;
|
|
||||||
this._endTime = RECORDING_IN_PROGRESS;
|
|
||||||
this._markers = [];
|
|
||||||
this._frames = [];
|
|
||||||
this._memory = [];
|
|
||||||
this._ticks = [];
|
|
||||||
|
|
||||||
this.emit(EVENTS.RECORDING_STARTED);
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -231,63 +215,76 @@ let PerformanceController = {
|
||||||
* when the front has stopped recording.
|
* when the front has stopped recording.
|
||||||
*/
|
*/
|
||||||
stopRecording: Task.async(function *() {
|
stopRecording: Task.async(function *() {
|
||||||
let results = yield gFront.stopRecording();
|
let recording = this._getLatest();
|
||||||
|
yield recording.stopRecording();
|
||||||
|
|
||||||
// If `endTime` is not yielded from timeline actor (< Fx36), fake it.
|
this.emit(EVENTS.RECORDING_STOPPED, recording);
|
||||||
if (!results.endTime) {
|
|
||||||
results.endTime = this._startTime + this.getLocalElapsedTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
this._endTime = results.endTime;
|
|
||||||
this._profilerData = results.profilerData;
|
|
||||||
this._markers = this._markers.sort((a,b) => (a.start > b.start));
|
|
||||||
|
|
||||||
this.emit(EVENTS.RECORDING_STOPPED);
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves the current recording to a file.
|
* Saves the current recording to a file.
|
||||||
*
|
*
|
||||||
|
* @param RecordingModel recording
|
||||||
|
* The model that holds the recording data.
|
||||||
* @param nsILocalFile file
|
* @param nsILocalFile file
|
||||||
* The file to stream the data into.
|
* The file to stream the data into.
|
||||||
*/
|
*/
|
||||||
exportRecording: Task.async(function*(_, file) {
|
exportRecording: Task.async(function*(_, recording, file) {
|
||||||
let recordingData = this.getAllData();
|
let recordingData = recording.getAllData();
|
||||||
yield PerformanceIO.saveRecordingToFile(recordingData, file);
|
yield PerformanceIO.saveRecordingToFile(recordingData, file);
|
||||||
|
|
||||||
this.emit(EVENTS.RECORDING_EXPORTED, recordingData);
|
this.emit(EVENTS.RECORDING_EXPORTED, recordingData);
|
||||||
}),
|
}),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads a recording from a file, replacing the current one.
|
* Loads a recording from a file, adding it to the recordings list.
|
||||||
* XXX: Handle multiple recordings, bug 1111004.
|
|
||||||
*
|
*
|
||||||
* @param nsILocalFile file
|
* @param nsILocalFile file
|
||||||
* The file to import the data from.
|
* The file to import the data from.
|
||||||
*/
|
*/
|
||||||
importRecording: Task.async(function*(_, file) {
|
importRecording: Task.async(function*(_, file) {
|
||||||
let recordingData = yield PerformanceIO.loadRecordingFromFile(file);
|
let model = this.createNewRecording();
|
||||||
|
yield model.importRecording(file);
|
||||||
|
|
||||||
this._startTime = recordingData.interval.startTime;
|
this.emit(EVENTS.RECORDING_IMPORTED, model.getAllData(), model);
|
||||||
this._endTime = recordingData.interval.endTime;
|
|
||||||
this._markers = recordingData.markers;
|
|
||||||
this._frames = recordingData.frames;
|
|
||||||
this._memory = recordingData.memory;
|
|
||||||
this._ticks = recordingData.ticks;
|
|
||||||
this._profilerData = recordingData.profilerData;
|
|
||||||
|
|
||||||
this.emit(EVENTS.RECORDING_IMPORTED, recordingData);
|
|
||||||
|
|
||||||
// Flush the current recording.
|
|
||||||
this.emit(EVENTS.RECORDING_STARTED);
|
|
||||||
this.emit(EVENTS.RECORDING_STOPPED);
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new RecordingModel, fires events and stores it
|
||||||
|
* internally in the controller.
|
||||||
|
*/
|
||||||
|
createNewRecording: function () {
|
||||||
|
let model = new RecordingModel({
|
||||||
|
front: gFront,
|
||||||
|
performance: performance
|
||||||
|
});
|
||||||
|
this._recordings.push(model);
|
||||||
|
this.emit(EVENTS.RECORDING_CREATED, model);
|
||||||
|
return model;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the active RecordingModel to `recording`.
|
||||||
|
*/
|
||||||
|
setCurrentRecording: function (recording) {
|
||||||
|
if (this._currentRecording !== recording) {
|
||||||
|
this._currentRecording = recording;
|
||||||
|
this.emit(EVENTS.RECORDING_SELECTED, recording);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the current active RecordingModel.
|
||||||
|
*/
|
||||||
|
getCurrentRecording: function () {
|
||||||
|
return this._currentRecording;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the amount of time elapsed locally after starting a recording.
|
* Gets the amount of time elapsed locally after starting a recording.
|
||||||
*/
|
*/
|
||||||
getLocalElapsedTime: function () {
|
getLocalElapsedTime: function () {
|
||||||
return performance.now() - this._localStartTime;
|
return this.getCurrentRecording().getLocalElapsedTime;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -295,17 +292,7 @@ let PerformanceController = {
|
||||||
* @return object
|
* @return object
|
||||||
*/
|
*/
|
||||||
getInterval: function() {
|
getInterval: function() {
|
||||||
let startTime = this._startTime;
|
return this.getCurrentRecording().getInterval();
|
||||||
let endTime = this._endTime;
|
|
||||||
|
|
||||||
// Compute an approximate ending time for the current recording. This is
|
|
||||||
// needed to ensure that the view updates even when new data is
|
|
||||||
// not being generated.
|
|
||||||
if (endTime == RECORDING_IN_PROGRESS) {
|
|
||||||
endTime = startTime + this.getLocalElapsedTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
return { startTime, endTime };
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -313,7 +300,7 @@ let PerformanceController = {
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
getMarkers: function() {
|
getMarkers: function() {
|
||||||
return this._markers;
|
return this.getCurrentRecording().getMarkers();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -321,7 +308,7 @@ let PerformanceController = {
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
getFrames: function() {
|
getFrames: function() {
|
||||||
return this._frames;
|
return this.getCurrentRecording().getFrames();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -329,7 +316,7 @@ let PerformanceController = {
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
getMemory: function() {
|
getMemory: function() {
|
||||||
return this._memory;
|
return this.getCurrentRecording().getMemory();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -337,7 +324,7 @@ let PerformanceController = {
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
getTicks: function() {
|
getTicks: function() {
|
||||||
return this._ticks;
|
return this.getCurrentRecording().getTicks();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -345,48 +332,41 @@ let PerformanceController = {
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
getProfilerData: function() {
|
getProfilerData: function() {
|
||||||
return this._profilerData;
|
return this.getCurrentRecording().getProfilerData();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets all the data in this recording.
|
* Gets all the data in this recording.
|
||||||
*/
|
*/
|
||||||
getAllData: function() {
|
getAllData: function() {
|
||||||
let interval = this.getInterval();
|
return this.getCurrentRecording().getAllData();
|
||||||
let markers = this.getMarkers();
|
},
|
||||||
let frames = this.getFrames();
|
|
||||||
let memory = this.getMemory();
|
/**
|
||||||
let ticks = this.getTicks();
|
/**
|
||||||
let profilerData = this.getProfilerData();
|
* Get most recently added profile that was triggered manually (via UI)
|
||||||
return { interval, markers, frames, memory, ticks, profilerData };
|
*/
|
||||||
|
_getLatest: function () {
|
||||||
|
for (let i = this._recordings.length - 1; i >= 0; i--) {
|
||||||
|
return this._recordings[i];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fired whenever the PerformanceFront emits markers, memory or ticks.
|
* Fired whenever the PerformanceFront emits markers, memory or ticks.
|
||||||
*/
|
*/
|
||||||
_onTimelineData: function (eventName, ...data) {
|
_onTimelineData: function (...data) {
|
||||||
// Accumulate markers into an array.
|
this._recordings.forEach(profile => profile.addTimelineData.apply(profile, data));
|
||||||
if (eventName == "markers") {
|
this.emit(EVENTS.TIMELINE_DATA, ...data);
|
||||||
let [markers] = data;
|
},
|
||||||
Array.prototype.push.apply(this._markers, markers);
|
|
||||||
}
|
|
||||||
// Accumulate stack frames into an array.
|
|
||||||
else if (eventName == "frames") {
|
|
||||||
let [delta, frames] = data;
|
|
||||||
Array.prototype.push.apply(this._frames, frames);
|
|
||||||
}
|
|
||||||
// Accumulate memory measurements into an array.
|
|
||||||
else if (eventName == "memory") {
|
|
||||||
let [delta, measurement] = data;
|
|
||||||
this._memory.push({ delta, value: measurement.total / 1024 / 1024 });
|
|
||||||
}
|
|
||||||
// Save the accumulated refresh driver ticks.
|
|
||||||
else if (eventName == "ticks") {
|
|
||||||
let [delta, timestamps] = data;
|
|
||||||
this._ticks = timestamps;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.emit(EVENTS.TIMELINE_DATA, eventName, ...data);
|
/**
|
||||||
|
* Fired from RecordingsView, we listen on the PerformanceController
|
||||||
|
* so we can set it here and re-emit on the controller, where all views can listen.
|
||||||
|
*/
|
||||||
|
_onRecordingSelectFromView: function (_, recording) {
|
||||||
|
this.setCurrentRecording(recording);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -13,23 +13,21 @@ let PerformanceView = {
|
||||||
initialize: function () {
|
initialize: function () {
|
||||||
this._recordButton = $("#record-button");
|
this._recordButton = $("#record-button");
|
||||||
this._importButton = $("#import-button");
|
this._importButton = $("#import-button");
|
||||||
this._exportButton = $("#export-button");
|
|
||||||
|
|
||||||
this._onRecordButtonClick = this._onRecordButtonClick.bind(this);
|
this._onRecordButtonClick = this._onRecordButtonClick.bind(this);
|
||||||
this._onImportButtonClick = this._onImportButtonClick.bind(this);
|
this._onImportButtonClick = this._onImportButtonClick.bind(this);
|
||||||
this._onExportButtonClick = this._onExportButtonClick.bind(this);
|
|
||||||
this._lockRecordButton = this._lockRecordButton.bind(this);
|
this._lockRecordButton = this._lockRecordButton.bind(this);
|
||||||
this._unlockRecordButton = this._unlockRecordButton.bind(this);
|
this._unlockRecordButton = this._unlockRecordButton.bind(this);
|
||||||
|
|
||||||
this._recordButton.addEventListener("click", this._onRecordButtonClick);
|
this._recordButton.addEventListener("click", this._onRecordButtonClick);
|
||||||
this._importButton.addEventListener("click", this._onImportButtonClick);
|
this._importButton.addEventListener("click", this._onImportButtonClick);
|
||||||
this._exportButton.addEventListener("click", this._onExportButtonClick);
|
|
||||||
|
|
||||||
// Bind to controller events to unlock the record button
|
// Bind to controller events to unlock the record button
|
||||||
PerformanceController.on(EVENTS.RECORDING_STARTED, this._unlockRecordButton);
|
PerformanceController.on(EVENTS.RECORDING_STARTED, this._unlockRecordButton);
|
||||||
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._unlockRecordButton);
|
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._unlockRecordButton);
|
||||||
|
|
||||||
return promise.all([
|
return promise.all([
|
||||||
|
RecordingsView.initialize(),
|
||||||
OverviewView.initialize(),
|
OverviewView.initialize(),
|
||||||
DetailsView.initialize()
|
DetailsView.initialize()
|
||||||
]);
|
]);
|
||||||
|
@ -41,12 +39,12 @@ let PerformanceView = {
|
||||||
destroy: function () {
|
destroy: function () {
|
||||||
this._recordButton.removeEventListener("click", this._onRecordButtonClick);
|
this._recordButton.removeEventListener("click", this._onRecordButtonClick);
|
||||||
this._importButton.removeEventListener("click", this._onImportButtonClick);
|
this._importButton.removeEventListener("click", this._onImportButtonClick);
|
||||||
this._exportButton.removeEventListener("click", this._onExportButtonClick);
|
|
||||||
|
|
||||||
PerformanceController.off(EVENTS.RECORDING_STARTED, this._unlockRecordButton);
|
PerformanceController.off(EVENTS.RECORDING_STARTED, this._unlockRecordButton);
|
||||||
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._unlockRecordButton);
|
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._unlockRecordButton);
|
||||||
|
|
||||||
return promise.all([
|
return promise.all([
|
||||||
|
RecordingsView.destroy(),
|
||||||
OverviewView.destroy(),
|
OverviewView.destroy(),
|
||||||
DetailsView.destroy()
|
DetailsView.destroy()
|
||||||
]);
|
]);
|
||||||
|
@ -94,23 +92,6 @@ let PerformanceView = {
|
||||||
if (fp.show() == Ci.nsIFilePicker.returnOK) {
|
if (fp.show() == Ci.nsIFilePicker.returnOK) {
|
||||||
this.emit(EVENTS.UI_IMPORT_RECORDING, fp.file);
|
this.emit(EVENTS.UI_IMPORT_RECORDING, fp.file);
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handler for clicking the export button.
|
|
||||||
*/
|
|
||||||
_onExportButtonClick: function(e) {
|
|
||||||
let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
|
|
||||||
fp.init(window, L10N.getStr("recordingsList.saveDialogTitle"), Ci.nsIFilePicker.modeSave);
|
|
||||||
fp.appendFilter(L10N.getStr("recordingsList.saveDialogJSONFilter"), "*.json");
|
|
||||||
fp.appendFilter(L10N.getStr("recordingsList.saveDialogAllFilter"), "*.*");
|
|
||||||
fp.defaultString = "profile.json";
|
|
||||||
|
|
||||||
fp.open({ done: result => {
|
|
||||||
if (result != Ci.nsIFilePicker.returnCancel) {
|
|
||||||
this.emit(EVENTS.UI_EXPORT_RECORDING, fp.file);
|
|
||||||
}
|
|
||||||
}});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -16,19 +16,35 @@
|
||||||
<script src="chrome://browser/content/devtools/theme-switching.js"/>
|
<script src="chrome://browser/content/devtools/theme-switching.js"/>
|
||||||
<script type="application/javascript" src="performance/performance-controller.js"/>
|
<script type="application/javascript" src="performance/performance-controller.js"/>
|
||||||
<script type="application/javascript" src="performance/performance-view.js"/>
|
<script type="application/javascript" src="performance/performance-view.js"/>
|
||||||
|
<script type="application/javascript" src="performance/recording-model.js"/>
|
||||||
<script type="application/javascript" src="performance/views/overview.js"/>
|
<script type="application/javascript" src="performance/views/overview.js"/>
|
||||||
<script type="application/javascript" src="performance/views/details.js"/>
|
<script type="application/javascript" src="performance/views/details.js"/>
|
||||||
<script type="application/javascript" src="performance/views/details-call-tree.js"/>
|
<script type="application/javascript" src="performance/views/details-call-tree.js"/>
|
||||||
<script type="application/javascript" src="performance/views/details-waterfall.js"/>
|
<script type="application/javascript" src="performance/views/details-waterfall.js"/>
|
||||||
<script type="application/javascript" src="performance/views/details-flamegraph.js"/>
|
<script type="application/javascript" src="performance/views/details-flamegraph.js"/>
|
||||||
|
<script type="application/javascript" src="performance/views/recordings.js"/>
|
||||||
|
|
||||||
<vbox class="theme-body" flex="1">
|
<hbox class="theme-body" flex="1">
|
||||||
<toolbar id="performance-toolbar" class="devtools-toolbar">
|
<vbox id="recordings-pane">
|
||||||
<hbox id="performance-toolbar-controls-recordings" class="devtools-toolbarbutton-group">
|
<toolbar id="recordings-toolbar"
|
||||||
|
class="devtools-toolbar">
|
||||||
|
<hbox id="recordings-controls"
|
||||||
|
class="devtools-toolbarbutton-group">
|
||||||
<toolbarbutton id="record-button"
|
<toolbarbutton id="record-button"
|
||||||
class="devtools-toolbarbutton"
|
class="devtools-toolbarbutton"
|
||||||
tooltiptext="&profilerUI.recordButton.tooltip;"/>
|
tooltiptext="&profilerUI.recordButton.tooltip;"/>
|
||||||
|
<toolbarbutton id="import-button"
|
||||||
|
class="devtools-toolbarbutton"
|
||||||
|
label="&profilerUI.importButton;"/>
|
||||||
|
<toolbarbutton id="clear-button"
|
||||||
|
class="devtools-toolbarbutton"
|
||||||
|
label="&profilerUI.clearButton;"/>
|
||||||
</hbox>
|
</hbox>
|
||||||
|
</toolbar>
|
||||||
|
<vbox id="recordings-list" flex="1"/>
|
||||||
|
</vbox>
|
||||||
|
<vbox flex="1">
|
||||||
|
<toolbar id="performance-toolbar" class="devtools-toolbar">
|
||||||
<hbox id="performance-toolbar-controls-detail-views" class="devtools-toolbarbutton-group">
|
<hbox id="performance-toolbar-controls-detail-views" class="devtools-toolbarbutton-group">
|
||||||
<toolbarbutton id="select-waterfall-view"
|
<toolbarbutton id="select-waterfall-view"
|
||||||
class="devtools-toolbarbutton"
|
class="devtools-toolbarbutton"
|
||||||
|
@ -41,17 +57,6 @@
|
||||||
data-view="flamegraph" />
|
data-view="flamegraph" />
|
||||||
</hbox>
|
</hbox>
|
||||||
<spacer flex="1"></spacer>
|
<spacer flex="1"></spacer>
|
||||||
<hbox id="performance-toolbar-controls-storage" class="devtools-toolbarbutton-group">
|
|
||||||
<toolbarbutton id="import-button"
|
|
||||||
class="devtools-toolbarbutton"
|
|
||||||
label="&profilerUI.importButton;"/>
|
|
||||||
<toolbarbutton id="export-button"
|
|
||||||
class="devtools-toolbarbutton"
|
|
||||||
label="&profilerUI.exportButton;"/>
|
|
||||||
<toolbarbutton id="clear-button"
|
|
||||||
class="devtools-toolbarbutton"
|
|
||||||
label="&profilerUI.clearButton;"/>
|
|
||||||
</hbox>
|
|
||||||
</toolbar>
|
</toolbar>
|
||||||
|
|
||||||
<vbox id="overview-pane">
|
<vbox id="overview-pane">
|
||||||
|
@ -59,9 +64,7 @@
|
||||||
<hbox id="memory-overview"/>
|
<hbox id="memory-overview"/>
|
||||||
<hbox id="time-framerate"/>
|
<hbox id="time-framerate"/>
|
||||||
</vbox>
|
</vbox>
|
||||||
|
|
||||||
<deck id="details-pane" flex="1">
|
<deck id="details-pane" flex="1">
|
||||||
|
|
||||||
<hbox id="waterfall-view" flex="1">
|
<hbox id="waterfall-view" flex="1">
|
||||||
<vbox id="waterfall-breakdown" flex="1" />
|
<vbox id="waterfall-breakdown" flex="1" />
|
||||||
<splitter class="devtools-side-splitter"/>
|
<splitter class="devtools-side-splitter"/>
|
||||||
|
@ -100,9 +103,9 @@
|
||||||
</hbox>
|
</hbox>
|
||||||
<vbox class="call-tree-cells-container" flex="1"/>
|
<vbox class="call-tree-cells-container" flex="1"/>
|
||||||
</vbox>
|
</vbox>
|
||||||
|
|
||||||
<hbox id="flamegraph-view" flex="1">
|
<hbox id="flamegraph-view" flex="1">
|
||||||
</hbox>
|
</hbox>
|
||||||
</deck>
|
</deck>
|
||||||
</vbox>
|
</vbox>
|
||||||
|
</hbox>
|
||||||
</window>
|
</window>
|
||||||
|
|
|
@ -39,4 +39,7 @@ support-files =
|
||||||
[browser_perf_recordings-io-01.js]
|
[browser_perf_recordings-io-01.js]
|
||||||
[browser_perf_recordings-io-02.js]
|
[browser_perf_recordings-io-02.js]
|
||||||
[browser_perf_recordings-io-03.js]
|
[browser_perf_recordings-io-03.js]
|
||||||
|
[browser_perf-recording-selected-01.js]
|
||||||
|
[browser_perf-recording-selected-02.js]
|
||||||
|
[browser_perf-recording-selected-03.js]
|
||||||
[browser_perf_recordings-io-04.js]
|
[browser_perf_recordings-io-04.js]
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests if the profiler correctly handles multiple recordings and can
|
||||||
|
* successfully switch between them.
|
||||||
|
*/
|
||||||
|
|
||||||
|
let test = Task.async(function*() {
|
||||||
|
let { target, panel, toolbox } = yield initPerformance(SIMPLE_URL);
|
||||||
|
let { EVENTS, PerformanceController, RecordingsView } = panel.panelWin;
|
||||||
|
|
||||||
|
yield startRecording(panel);
|
||||||
|
yield stopRecording(panel);
|
||||||
|
|
||||||
|
yield startRecording(panel);
|
||||||
|
yield stopRecording(panel);
|
||||||
|
|
||||||
|
is(RecordingsView.itemCount, 2,
|
||||||
|
"There should be two recordings visible.");
|
||||||
|
is(RecordingsView.selectedIndex, 1,
|
||||||
|
"The second recording item should be selected.");
|
||||||
|
|
||||||
|
let select = once(PerformanceController, EVENTS.RECORDING_SELECTED);
|
||||||
|
RecordingsView.selectedIndex = 0;
|
||||||
|
yield select;
|
||||||
|
|
||||||
|
is(RecordingsView.itemCount, 2,
|
||||||
|
"There should still be two recordings visible.");
|
||||||
|
is(RecordingsView.selectedIndex, 0,
|
||||||
|
"The first recording item should be selected.");
|
||||||
|
|
||||||
|
yield teardown(panel);
|
||||||
|
finish();
|
||||||
|
});
|
|
@ -0,0 +1,45 @@
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests if the profiler correctly handles multiple recordings and can
|
||||||
|
* successfully switch between them, even when one of them is in progress.
|
||||||
|
*/
|
||||||
|
|
||||||
|
let test = Task.async(function*() {
|
||||||
|
let { target, panel, toolbox } = yield initPerformance(SIMPLE_URL);
|
||||||
|
let { EVENTS, PerformanceController, RecordingsView } = panel.panelWin;
|
||||||
|
|
||||||
|
yield startRecording(panel);
|
||||||
|
yield stopRecording(panel);
|
||||||
|
|
||||||
|
yield startRecording(panel);
|
||||||
|
|
||||||
|
is(RecordingsView.itemCount, 2,
|
||||||
|
"There should be two recordings visible.");
|
||||||
|
is(RecordingsView.selectedIndex, 1,
|
||||||
|
"The new recording item should be selected.");
|
||||||
|
|
||||||
|
let select = once(PerformanceController, EVENTS.RECORDING_SELECTED);
|
||||||
|
RecordingsView.selectedIndex = 0;
|
||||||
|
yield select;
|
||||||
|
|
||||||
|
is(RecordingsView.itemCount, 2,
|
||||||
|
"There should still be two recordings visible.");
|
||||||
|
is(RecordingsView.selectedIndex, 0,
|
||||||
|
"The first recording item should be selected now.");
|
||||||
|
|
||||||
|
select = once(PerformanceController, EVENTS.RECORDING_SELECTED);
|
||||||
|
RecordingsView.selectedIndex = 1;
|
||||||
|
yield select;
|
||||||
|
|
||||||
|
is(RecordingsView.itemCount, 2,
|
||||||
|
"There should still be two recordings visible.");
|
||||||
|
is(RecordingsView.selectedIndex, 1,
|
||||||
|
"The second recording item should be selected again.");
|
||||||
|
|
||||||
|
yield stopRecording(panel);
|
||||||
|
|
||||||
|
yield teardown(panel);
|
||||||
|
finish();
|
||||||
|
});
|
|
@ -0,0 +1,33 @@
|
||||||
|
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests if the profiler UI does not forget that recording is active when
|
||||||
|
* selected recording changes. Bug 1060885.
|
||||||
|
*/
|
||||||
|
|
||||||
|
let test = Task.async(function*() {
|
||||||
|
let { target, panel, toolbox } = yield initPerformance(SIMPLE_URL);
|
||||||
|
let { $, EVENTS, PerformanceController, RecordingsView } = panel.panelWin;
|
||||||
|
|
||||||
|
yield startRecording(panel);
|
||||||
|
yield stopRecording(panel);
|
||||||
|
|
||||||
|
yield startRecording(panel);
|
||||||
|
|
||||||
|
info("Selecting recording #0 and waiting for it to be displayed.");
|
||||||
|
let select = once(PerformanceController, EVENTS.RECORDING_SELECTED);
|
||||||
|
RecordingsView.selectedIndex = 0;
|
||||||
|
yield select;
|
||||||
|
|
||||||
|
ok($("#record-button").hasAttribute("checked"),
|
||||||
|
"Button is still checked after selecting another item.");
|
||||||
|
|
||||||
|
ok(!$("#record-button").hasAttribute("locked"),
|
||||||
|
"Button is not locked after selecting another item.");
|
||||||
|
|
||||||
|
yield stopRecording(panel);
|
||||||
|
yield teardown(panel);
|
||||||
|
finish();
|
||||||
|
});
|
|
@ -23,7 +23,7 @@ let test = Task.async(function*() {
|
||||||
file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("666", 8));
|
file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("666", 8));
|
||||||
|
|
||||||
let exported = once(PerformanceController, EVENTS.RECORDING_EXPORTED);
|
let exported = once(PerformanceController, EVENTS.RECORDING_EXPORTED);
|
||||||
yield PerformanceController.exportRecording("", file);
|
yield PerformanceController.exportRecording("", PerformanceController.getCurrentRecording(), file);
|
||||||
|
|
||||||
yield exported;
|
yield exported;
|
||||||
ok(true, "The recording data appears to have been successfully saved.");
|
ok(true, "The recording data appears to have been successfully saved.");
|
||||||
|
|
|
@ -13,10 +13,12 @@ let CallTreeView = {
|
||||||
initialize: function () {
|
initialize: function () {
|
||||||
this._callTree = $(".call-tree-cells-container");
|
this._callTree = $(".call-tree-cells-container");
|
||||||
this._onRecordingStopped = this._onRecordingStopped.bind(this);
|
this._onRecordingStopped = this._onRecordingStopped.bind(this);
|
||||||
|
this._onRecordingSelected = this._onRecordingSelected.bind(this);
|
||||||
this._onRangeChange = this._onRangeChange.bind(this);
|
this._onRangeChange = this._onRangeChange.bind(this);
|
||||||
this._onLink = this._onLink.bind(this);
|
this._onLink = this._onLink.bind(this);
|
||||||
|
|
||||||
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
|
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
|
||||||
|
PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
|
||||||
OverviewView.on(EVENTS.OVERVIEW_RANGE_SELECTED, this._onRangeChange);
|
OverviewView.on(EVENTS.OVERVIEW_RANGE_SELECTED, this._onRangeChange);
|
||||||
OverviewView.on(EVENTS.OVERVIEW_RANGE_CLEARED, this._onRangeChange);
|
OverviewView.on(EVENTS.OVERVIEW_RANGE_CLEARED, this._onRangeChange);
|
||||||
},
|
},
|
||||||
|
@ -26,6 +28,7 @@ let CallTreeView = {
|
||||||
*/
|
*/
|
||||||
destroy: function () {
|
destroy: function () {
|
||||||
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
|
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
|
||||||
|
PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
|
||||||
OverviewView.off(EVENTS.OVERVIEW_RANGE_SELECTED, this._onRangeChange);
|
OverviewView.off(EVENTS.OVERVIEW_RANGE_SELECTED, this._onRangeChange);
|
||||||
OverviewView.off(EVENTS.OVERVIEW_RANGE_CLEARED, this._onRangeChange);
|
OverviewView.off(EVENTS.OVERVIEW_RANGE_CLEARED, this._onRangeChange);
|
||||||
},
|
},
|
||||||
|
@ -51,6 +54,19 @@ let CallTreeView = {
|
||||||
this.render(profilerData);
|
this.render(profilerData);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a recording has been selected.
|
||||||
|
*/
|
||||||
|
_onRecordingSelected: function (_, recording) {
|
||||||
|
// If not recording, then this recording is done and we can render all of it
|
||||||
|
// Otherwise, TODO in bug 1120699 will hide the details view altogether if
|
||||||
|
// this is still recording.
|
||||||
|
if (!recording.isRecording()) {
|
||||||
|
let profilerData = recording.getProfilerData();
|
||||||
|
this.render(profilerData);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fired when a range is selected or cleared in the OverviewView.
|
* Fired when a range is selected or cleared in the OverviewView.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -13,6 +13,7 @@ let WaterfallView = {
|
||||||
initialize: Task.async(function *() {
|
initialize: Task.async(function *() {
|
||||||
this._onRecordingStarted = this._onRecordingStarted.bind(this);
|
this._onRecordingStarted = this._onRecordingStarted.bind(this);
|
||||||
this._onRecordingStopped = this._onRecordingStopped.bind(this);
|
this._onRecordingStopped = this._onRecordingStopped.bind(this);
|
||||||
|
this._onRecordingSelected = this._onRecordingSelected.bind(this);
|
||||||
this._onMarkerSelected = this._onMarkerSelected.bind(this);
|
this._onMarkerSelected = this._onMarkerSelected.bind(this);
|
||||||
this._onResize = this._onResize.bind(this);
|
this._onResize = this._onResize.bind(this);
|
||||||
|
|
||||||
|
@ -25,6 +26,7 @@ let WaterfallView = {
|
||||||
|
|
||||||
PerformanceController.on(EVENTS.RECORDING_STARTED, this._onRecordingStarted);
|
PerformanceController.on(EVENTS.RECORDING_STARTED, this._onRecordingStarted);
|
||||||
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
|
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
|
||||||
|
PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
|
||||||
|
|
||||||
this.waterfall.recalculateBounds();
|
this.waterfall.recalculateBounds();
|
||||||
}),
|
}),
|
||||||
|
@ -39,6 +41,7 @@ let WaterfallView = {
|
||||||
|
|
||||||
PerformanceController.off(EVENTS.RECORDING_STARTED, this._onRecordingStarted);
|
PerformanceController.off(EVENTS.RECORDING_STARTED, this._onRecordingStarted);
|
||||||
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
|
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
|
||||||
|
PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -67,6 +70,15 @@ let WaterfallView = {
|
||||||
this.render();
|
this.render();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a recording is selected.
|
||||||
|
*/
|
||||||
|
_onRecordingSelected: function (_, recording) {
|
||||||
|
if (!recording.isRecording()) {
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a marker is selected in the waterfall view,
|
* Called when a marker is selected in the waterfall view,
|
||||||
* updating the markers detail view.
|
* updating the markers detail view.
|
||||||
|
|
|
@ -30,6 +30,7 @@ let OverviewView = {
|
||||||
initialize: Task.async(function *() {
|
initialize: Task.async(function *() {
|
||||||
this._onRecordingStarted = this._onRecordingStarted.bind(this);
|
this._onRecordingStarted = this._onRecordingStarted.bind(this);
|
||||||
this._onRecordingStopped = this._onRecordingStopped.bind(this);
|
this._onRecordingStopped = this._onRecordingStopped.bind(this);
|
||||||
|
this._onRecordingSelected = this._onRecordingSelected.bind(this);
|
||||||
this._onRecordingTick = this._onRecordingTick.bind(this);
|
this._onRecordingTick = this._onRecordingTick.bind(this);
|
||||||
this._onGraphMouseUp = this._onGraphMouseUp.bind(this);
|
this._onGraphMouseUp = this._onGraphMouseUp.bind(this);
|
||||||
this._onGraphScroll = this._onGraphScroll.bind(this);
|
this._onGraphScroll = this._onGraphScroll.bind(this);
|
||||||
|
@ -47,6 +48,7 @@ let OverviewView = {
|
||||||
|
|
||||||
PerformanceController.on(EVENTS.RECORDING_STARTED, this._onRecordingStarted);
|
PerformanceController.on(EVENTS.RECORDING_STARTED, this._onRecordingStarted);
|
||||||
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
|
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
|
||||||
|
PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
|
||||||
}),
|
}),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -63,6 +65,7 @@ let OverviewView = {
|
||||||
clearNamedTimeout("graph-scroll");
|
clearNamedTimeout("graph-scroll");
|
||||||
PerformanceController.off(EVENTS.RECORDING_STARTED, this._onRecordingStarted);
|
PerformanceController.off(EVENTS.RECORDING_STARTED, this._onRecordingStarted);
|
||||||
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
|
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
|
||||||
|
PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -185,27 +188,42 @@ let OverviewView = {
|
||||||
/**
|
/**
|
||||||
* Called when recording starts.
|
* Called when recording starts.
|
||||||
*/
|
*/
|
||||||
_onRecordingStarted: function () {
|
_onRecordingStarted: function (_, recording) {
|
||||||
|
this._checkSelection(recording);
|
||||||
this._timeoutId = setTimeout(this._onRecordingTick, OVERVIEW_UPDATE_INTERVAL);
|
this._timeoutId = setTimeout(this._onRecordingTick, OVERVIEW_UPDATE_INTERVAL);
|
||||||
|
|
||||||
this.framerateGraph.dropSelection();
|
this.framerateGraph.dropSelection();
|
||||||
this.framerateGraph.selectionEnabled = false;
|
|
||||||
this.markersOverview.selectionEnabled = false;
|
|
||||||
this.memoryOverview.selectionEnabled = false;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when recording stops.
|
* Called when recording stops.
|
||||||
*/
|
*/
|
||||||
_onRecordingStopped: function () {
|
_onRecordingStopped: function (_, recording) {
|
||||||
|
this._checkSelection(recording);
|
||||||
clearTimeout(this._timeoutId);
|
clearTimeout(this._timeoutId);
|
||||||
this._timeoutId = null;
|
this._timeoutId = null;
|
||||||
|
|
||||||
this.render(FRAMERATE_GRAPH_HIGH_RES_INTERVAL);
|
this.render(FRAMERATE_GRAPH_HIGH_RES_INTERVAL);
|
||||||
|
},
|
||||||
|
|
||||||
this.framerateGraph.selectionEnabled = true;
|
/**
|
||||||
this.markersOverview.selectionEnabled = true;
|
* Called when a new recording is selected.
|
||||||
this.memoryOverview.selectionEnabled = true;
|
*/
|
||||||
|
_onRecordingSelected: function (_, recording) {
|
||||||
|
this.framerateGraph.dropSelection();
|
||||||
|
this._checkSelection(recording);
|
||||||
|
|
||||||
|
// If timeout exists, we have something recording, so
|
||||||
|
// this will still tick away at rendering. Otherwise, force a render.
|
||||||
|
if (!this._timeoutId) {
|
||||||
|
this.render(FRAMERATE_GRAPH_HIGH_RES_INTERVAL);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_checkSelection: function (recording) {
|
||||||
|
let selectionEnabled = !recording.isRecording();
|
||||||
|
this.framerateGraph.selectionEnabled = selectionEnabled;
|
||||||
|
this.markersOverview.selectionEnabled = selectionEnabled;
|
||||||
|
this.memoryOverview.selectionEnabled = selectionEnabled;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,237 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Functions handling the recordings UI.
|
||||||
|
*/
|
||||||
|
let RecordingsView = Heritage.extend(WidgetMethods, {
|
||||||
|
/**
|
||||||
|
* Initialization function, called when the tool is started.
|
||||||
|
*/
|
||||||
|
initialize: function() {
|
||||||
|
this.widget = new SideMenuWidget($("#recordings-list"));
|
||||||
|
|
||||||
|
this._onSelect = this._onSelect.bind(this);
|
||||||
|
this._onRecordingStarted = this._onRecordingStarted.bind(this);
|
||||||
|
this._onRecordingStopped = this._onRecordingStopped.bind(this);
|
||||||
|
this._onRecordingImported = this._onRecordingImported.bind(this);
|
||||||
|
this._onSaveButtonClick = this._onSaveButtonClick.bind(this);
|
||||||
|
|
||||||
|
this.emptyText = L10N.getStr("noRecordingsText");
|
||||||
|
|
||||||
|
PerformanceController.on(EVENTS.RECORDING_STARTED, this._onRecordingStarted);
|
||||||
|
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
|
||||||
|
PerformanceController.on(EVENTS.RECORDING_IMPORTED, this._onRecordingImported);
|
||||||
|
this.widget.addEventListener("select", this._onSelect, false);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destruction function, called when the tool is closed.
|
||||||
|
*/
|
||||||
|
destroy: function() {
|
||||||
|
PerformanceController.off(EVENTS.RECORDING_STARTED, this._onRecordingStarted);
|
||||||
|
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
|
||||||
|
PerformanceController.off(EVENTS.RECORDING_IMPORTED, this._onRecordingImported);
|
||||||
|
this.widget.removeEventListener("select", this._onSelect, false);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an empty recording to this container.
|
||||||
|
*
|
||||||
|
* @param RecordingModel recording
|
||||||
|
* A model for the new recording item created.
|
||||||
|
*/
|
||||||
|
addEmptyRecording: function (recording) {
|
||||||
|
let titleNode = document.createElement("label");
|
||||||
|
titleNode.className = "plain recording-item-title";
|
||||||
|
titleNode.setAttribute("value", recording.getLabel() ||
|
||||||
|
L10N.getFormatStr("recordingsList.itemLabel", this.itemCount + 1));
|
||||||
|
|
||||||
|
let durationNode = document.createElement("label");
|
||||||
|
durationNode.className = "plain recording-item-duration";
|
||||||
|
durationNode.setAttribute("value",
|
||||||
|
L10N.getStr("recordingsList.recordingLabel"));
|
||||||
|
|
||||||
|
let saveNode = document.createElement("label");
|
||||||
|
saveNode.className = "plain recording-item-save";
|
||||||
|
saveNode.addEventListener("click", this._onSaveButtonClick);
|
||||||
|
|
||||||
|
let hspacer = document.createElement("spacer");
|
||||||
|
hspacer.setAttribute("flex", "1");
|
||||||
|
|
||||||
|
let footerNode = document.createElement("hbox");
|
||||||
|
footerNode.className = "recording-item-footer";
|
||||||
|
footerNode.appendChild(durationNode);
|
||||||
|
footerNode.appendChild(hspacer);
|
||||||
|
footerNode.appendChild(saveNode);
|
||||||
|
|
||||||
|
let vspacer = document.createElement("spacer");
|
||||||
|
vspacer.setAttribute("flex", "1");
|
||||||
|
|
||||||
|
let contentsNode = document.createElement("vbox");
|
||||||
|
contentsNode.className = "recording-item";
|
||||||
|
contentsNode.setAttribute("flex", "1");
|
||||||
|
contentsNode.appendChild(titleNode);
|
||||||
|
contentsNode.appendChild(vspacer);
|
||||||
|
contentsNode.appendChild(footerNode);
|
||||||
|
|
||||||
|
// Append a recording item to this container.
|
||||||
|
return this.push([contentsNode], {
|
||||||
|
// Store the recording model that contains all the data to be
|
||||||
|
// rendered in the item.
|
||||||
|
attachment: recording
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signals that a recording session has started.
|
||||||
|
*
|
||||||
|
* @param RecordingModel recording
|
||||||
|
* Model of the recording that was started.
|
||||||
|
*/
|
||||||
|
_onRecordingStarted: function (_, recording) {
|
||||||
|
// Insert a "dummy" recording item, to hint that recording has now started.
|
||||||
|
let recordingItem;
|
||||||
|
|
||||||
|
// If a label is specified (e.g due to a call to `console.profile`),
|
||||||
|
// then try reusing a pre-existing recording item, if there is one.
|
||||||
|
// This is symmetrical to how `this.handleRecordingEnded` works.
|
||||||
|
if (recording.getLabel()) {
|
||||||
|
recordingItem = this.getItemForAttachment(e =>
|
||||||
|
e.getLabel() === recording.getLabel());
|
||||||
|
}
|
||||||
|
// Otherwise, create a new empty recording item.
|
||||||
|
if (!recordingItem) {
|
||||||
|
recordingItem = this.addEmptyRecording(recording);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark the corresponding item as being a "record in progress".
|
||||||
|
recordingItem.isRecording = true;
|
||||||
|
|
||||||
|
// If this is a manual recording, immediately select it.
|
||||||
|
if (!recording.getLabel()) {
|
||||||
|
this.selectedItem = recordingItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.emit(EVENTS.RECORDING_SELECTED, recording);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signals that a recording session has ended.
|
||||||
|
*
|
||||||
|
* @param RecordingModel recording
|
||||||
|
* The model of the recording that just stopped.
|
||||||
|
*/
|
||||||
|
_onRecordingStopped: function (_, recording) {
|
||||||
|
let profileLabel = recording.getLabel();
|
||||||
|
let recordingItem;
|
||||||
|
|
||||||
|
// If a label is specified (e.g due to a call to `console.profileEnd`),
|
||||||
|
// then try reusing a pre-existing recording item, if there is one.
|
||||||
|
// This is symmetrical to how `this.handleRecordingStarted` works.
|
||||||
|
if (profileLabel) {
|
||||||
|
recordingItem = this.getItemForAttachment(e =>
|
||||||
|
e.profilerData.profileLabel == profileLabel);
|
||||||
|
}
|
||||||
|
// Otherwise, just use the first available recording item.
|
||||||
|
if (!recordingItem) {
|
||||||
|
recordingItem = this.getItemForPredicate(e => e.isRecording);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark the corresponding item as being a "finished recording".
|
||||||
|
recordingItem.isRecording = false;
|
||||||
|
|
||||||
|
// Render the recording item with finalized information (timing, etc)
|
||||||
|
this.finalizeRecording(recordingItem);
|
||||||
|
this.forceSelect(recordingItem);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signals that a recording has been imported.
|
||||||
|
*
|
||||||
|
* @param object recordingData
|
||||||
|
* The profiler and refresh driver ticks data received from the front.
|
||||||
|
* @param RecordingModel model
|
||||||
|
* The recording model containing data on the recording session.
|
||||||
|
*/
|
||||||
|
_onRecordingImported: function (_, recordingData, model) {
|
||||||
|
let recordingItem = this.addEmptyRecording(model);
|
||||||
|
recordingItem.isRecording = false;
|
||||||
|
|
||||||
|
// Immediately select the imported recording
|
||||||
|
this.selectedItem = recordingItem;
|
||||||
|
|
||||||
|
// Render the recording item with finalized information (timing, etc)
|
||||||
|
this.finalizeRecording(recordingItem);
|
||||||
|
|
||||||
|
// Fire the selection and allow to propogate.
|
||||||
|
this.emit(EVENTS.RECORDING_SELECTED, model);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds recording data to a recording item in this container.
|
||||||
|
*
|
||||||
|
* @param Item recordingItem
|
||||||
|
* An item inserted via `RecordingsView.addEmptyRecording`.
|
||||||
|
*/
|
||||||
|
finalizeRecording: function (recordingItem) {
|
||||||
|
let model = recordingItem.attachment;
|
||||||
|
|
||||||
|
let saveNode = $(".recording-item-save", recordingItem.target);
|
||||||
|
saveNode.setAttribute("value",
|
||||||
|
L10N.getStr("recordingsList.saveLabel"));
|
||||||
|
|
||||||
|
let durationMillis = model.getDuration().toFixed(0);
|
||||||
|
let durationNode = $(".recording-item-duration", recordingItem.target);
|
||||||
|
durationNode.setAttribute("value",
|
||||||
|
L10N.getFormatStr("recordingsList.durationLabel", durationMillis));
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The select listener for this container.
|
||||||
|
*/
|
||||||
|
_onSelect: Task.async(function*({ detail: recordingItem }) {
|
||||||
|
// TODO 1120699
|
||||||
|
// show appropriate empty/recording panels for several scenarios below
|
||||||
|
if (!recordingItem) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let model = recordingItem.attachment;
|
||||||
|
|
||||||
|
// If recording, don't abort completely, as we still want to fire an event
|
||||||
|
// for selection so we can continue repainting the overview graphs.
|
||||||
|
if (recordingItem.isRecording) {
|
||||||
|
this.emit(EVENTS.RECORDING_SELECTED, model);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.emit(EVENTS.RECORDING_SELECTED, model);
|
||||||
|
}),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The click listener for the "save" button of each item in this container.
|
||||||
|
*/
|
||||||
|
_onSaveButtonClick: function (e) {
|
||||||
|
let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
|
||||||
|
fp.init(window, L10N.getStr("recordingsList.saveDialogTitle"), Ci.nsIFilePicker.modeSave);
|
||||||
|
fp.appendFilter(L10N.getStr("recordingsList.saveDialogJSONFilter"), "*.json");
|
||||||
|
fp.appendFilter(L10N.getStr("recordingsList.saveDialogAllFilter"), "*.*");
|
||||||
|
fp.defaultString = "profile.json";
|
||||||
|
|
||||||
|
fp.open({ done: result => {
|
||||||
|
if (result == Ci.nsIFilePicker.returnCancel) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let recordingItem = this.getItemForElement(e.target);
|
||||||
|
this.emit(EVENTS.UI_EXPORT_RECORDING, recordingItem.attachment, fp.file);
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenient way of emitting events from the RecordingsView.
|
||||||
|
*/
|
||||||
|
EventEmitter.decorate(RecordingsView);
|
|
@ -755,7 +755,7 @@ var ProjectEditor = Class({
|
||||||
if (this.hasUnsavedResources) {
|
if (this.hasUnsavedResources) {
|
||||||
return confirm(
|
return confirm(
|
||||||
getLocalizedString("projecteditor.confirmUnsavedTitle"),
|
getLocalizedString("projecteditor.confirmUnsavedTitle"),
|
||||||
getLocalizedString("projecteditor.confirmUnsavedLabel")
|
getLocalizedString("projecteditor.confirmUnsavedLabel2")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,9 @@ function startupWebAudioEditor() {
|
||||||
return all([
|
return all([
|
||||||
WebAudioEditorController.initialize(),
|
WebAudioEditorController.initialize(),
|
||||||
ContextView.initialize(),
|
ContextView.initialize(),
|
||||||
InspectorView.initialize()
|
InspectorView.initialize(),
|
||||||
|
PropertiesView.initialize(),
|
||||||
|
AutomationView.initialize()
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,6 +29,8 @@ function shutdownWebAudioEditor() {
|
||||||
WebAudioEditorController.destroy(),
|
WebAudioEditorController.destroy(),
|
||||||
ContextView.destroy(),
|
ContextView.destroy(),
|
||||||
InspectorView.destroy(),
|
InspectorView.destroy(),
|
||||||
|
PropertiesView.destroy(),
|
||||||
|
AutomationView.destroy()
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,6 +87,7 @@ let WebAudioEditorController = {
|
||||||
$("#content").hidden = true;
|
$("#content").hidden = true;
|
||||||
ContextView.resetUI();
|
ContextView.resetUI();
|
||||||
InspectorView.resetUI();
|
InspectorView.resetUI();
|
||||||
|
PropertiesView.resetUI();
|
||||||
},
|
},
|
||||||
|
|
||||||
// Since node create and connect are probably executed back to back,
|
// Since node create and connect are probably executed back to back,
|
||||||
|
|
|
@ -10,7 +10,8 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||||
Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
|
Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
|
||||||
Cu.import("resource:///modules/devtools/gDevTools.jsm");
|
Cu.import("resource:///modules/devtools/gDevTools.jsm");
|
||||||
|
|
||||||
const require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
|
const devtools = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools;
|
||||||
|
const { require } = devtools;
|
||||||
|
|
||||||
let { console } = Cu.import("resource://gre/modules/devtools/Console.jsm", {});
|
let { console } = Cu.import("resource://gre/modules/devtools/Console.jsm", {});
|
||||||
let { EventTarget } = require("sdk/event/target");
|
let { EventTarget } = require("sdk/event/target");
|
||||||
|
@ -21,6 +22,8 @@ const STRINGS_URI = "chrome://browser/locale/devtools/webaudioeditor.properties"
|
||||||
const L10N = new ViewHelpers.L10N(STRINGS_URI);
|
const L10N = new ViewHelpers.L10N(STRINGS_URI);
|
||||||
const Telemetry = require("devtools/shared/telemetry");
|
const Telemetry = require("devtools/shared/telemetry");
|
||||||
const telemetry = new Telemetry();
|
const telemetry = new Telemetry();
|
||||||
|
devtools.lazyImporter(this, "LineGraphWidget",
|
||||||
|
"resource:///modules/devtools/Graphs.jsm");
|
||||||
|
|
||||||
// Override DOM promises with Promise.jsm helpers
|
// Override DOM promises with Promise.jsm helpers
|
||||||
const { defer, all } = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise;
|
const { defer, all } = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise;
|
||||||
|
@ -53,11 +56,17 @@ const EVENTS = {
|
||||||
// When an audio node is finished loading in the Properties tab.
|
// When an audio node is finished loading in the Properties tab.
|
||||||
UI_PROPERTIES_TAB_RENDERED: "WebAudioEditor:UIPropertiesTabRendered",
|
UI_PROPERTIES_TAB_RENDERED: "WebAudioEditor:UIPropertiesTabRendered",
|
||||||
|
|
||||||
|
// When an audio node is finished loading in the Automation tab.
|
||||||
|
UI_AUTOMATION_TAB_RENDERED: "WebAudioEditor:UIAutomationTabRendered",
|
||||||
|
|
||||||
// When the Audio Context graph finishes rendering.
|
// When the Audio Context graph finishes rendering.
|
||||||
// Is called with two arguments, first representing number of nodes
|
// Is called with two arguments, first representing number of nodes
|
||||||
// rendered, second being the number of edge connections rendering (not counting
|
// rendered, second being the number of edge connections rendering (not counting
|
||||||
// param edges), followed by the count of the param edges rendered.
|
// param edges), followed by the count of the param edges rendered.
|
||||||
UI_GRAPH_RENDERED: "WebAudioEditor:UIGraphRendered"
|
UI_GRAPH_RENDERED: "WebAudioEditor:UIGraphRendered",
|
||||||
|
|
||||||
|
// Called when the inspector splitter is moved and resized.
|
||||||
|
UI_INSPECTOR_RESIZE: "WebAudioEditor:UIInspectorResize"
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -85,6 +85,17 @@ const AudioNodeModel = Class({
|
||||||
return this.actor.getParams();
|
return this.actor.getParams();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a promise that resolves to an object containing an
|
||||||
|
* array of event information and an array of automation data.
|
||||||
|
*
|
||||||
|
* @param String paramName
|
||||||
|
* @return Promise->Array
|
||||||
|
*/
|
||||||
|
getAutomationData: function (paramName) {
|
||||||
|
return this.actor.getAutomationData(paramName);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Takes a `dagreD3.Digraph` object and adds this node to
|
* Takes a `dagreD3.Digraph` object and adds this node to
|
||||||
* the graph to be rendered.
|
* the graph to be rendered.
|
||||||
|
|
|
@ -61,3 +61,6 @@ skip-if = true # bug 1010423
|
||||||
[browser_wa_properties-view-media-nodes.js]
|
[browser_wa_properties-view-media-nodes.js]
|
||||||
[browser_wa_properties-view-params.js]
|
[browser_wa_properties-view-params.js]
|
||||||
[browser_wa_properties-view-params-objects.js]
|
[browser_wa_properties-view-params-objects.js]
|
||||||
|
|
||||||
|
[browser_wa_automation-view-01.js]
|
||||||
|
[browser_wa_automation-view-02.js]
|
||||||
|
|
|
@ -39,8 +39,6 @@ add_task(function*() {
|
||||||
}
|
}
|
||||||
else if (param === "curve") {
|
else if (param === "curve") {
|
||||||
is(flags["Float32Array"], true, "`curve` param has Float32Array flag");
|
is(flags["Float32Array"], true, "`curve` param has Float32Array flag");
|
||||||
} else {
|
|
||||||
is(Object.keys(flags), 0, type + "-" + param + " has no flags set")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,8 +36,6 @@ add_task(function*() {
|
||||||
}
|
}
|
||||||
else if (param === "curve") {
|
else if (param === "curve") {
|
||||||
is(flags["Float32Array"], true, "`curve` param has Float32Array flag");
|
is(flags["Float32Array"], true, "`curve` param has Float32Array flag");
|
||||||
} else {
|
|
||||||
is(Object.keys(flags), 0, type + "-" + param + " has no flags set")
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that automation view shows the correct view depending on if events
|
||||||
|
* or params exist.
|
||||||
|
*/
|
||||||
|
|
||||||
|
add_task(function*() {
|
||||||
|
let { target, panel } = yield initWebAudioEditor(AUTOMATION_URL);
|
||||||
|
let { panelWin } = panel;
|
||||||
|
let { gFront, $, $$, EVENTS } = panelWin;
|
||||||
|
|
||||||
|
let started = once(gFront, "start-context");
|
||||||
|
|
||||||
|
reload(target);
|
||||||
|
|
||||||
|
let [actors] = yield Promise.all([
|
||||||
|
get3(gFront, "create-node"),
|
||||||
|
waitForGraphRendered(panelWin, 3, 2)
|
||||||
|
]);
|
||||||
|
let nodeIds = actors.map(actor => actor.actorID);
|
||||||
|
|
||||||
|
// Oscillator node
|
||||||
|
click(panelWin, findGraphNode(panelWin, nodeIds[1]));
|
||||||
|
yield waitForInspectorRender(panelWin, EVENTS);
|
||||||
|
click(panelWin, $("#automation-tab"));
|
||||||
|
|
||||||
|
ok(isVisible($("#automation-graph-container")), "graph container should be visible");
|
||||||
|
ok(isVisible($("#automation-content")), "automation content should be visible");
|
||||||
|
ok(!isVisible($("#automation-no-events")), "no-events panel should not be visible");
|
||||||
|
ok(!isVisible($("#automation-empty")), "empty panel should not be visible");
|
||||||
|
|
||||||
|
// Gain node
|
||||||
|
click(panelWin, findGraphNode(panelWin, nodeIds[2]));
|
||||||
|
yield waitForInspectorRender(panelWin, EVENTS);
|
||||||
|
click(panelWin, $("#automation-tab"));
|
||||||
|
|
||||||
|
ok(!isVisible($("#automation-graph-container")), "graph container should be visible");
|
||||||
|
ok(isVisible($("#automation-content")), "automation content should not be visible");
|
||||||
|
ok(isVisible($("#automation-no-events")), "no-events panel should be visible");
|
||||||
|
ok(!isVisible($("#automation-empty")), "empty panel should not be visible");
|
||||||
|
|
||||||
|
// destination node
|
||||||
|
click(panelWin, findGraphNode(panelWin, nodeIds[0]));
|
||||||
|
yield waitForInspectorRender(panelWin, EVENTS);
|
||||||
|
click(panelWin, $("#automation-tab"));
|
||||||
|
|
||||||
|
ok(!isVisible($("#automation-graph-container")), "graph container should not be visible");
|
||||||
|
ok(!isVisible($("#automation-content")), "automation content should not be visible");
|
||||||
|
ok(!isVisible($("#automation-no-events")), "no-events panel should not be visible");
|
||||||
|
ok(isVisible($("#automation-empty")), "empty panel should be visible");
|
||||||
|
|
||||||
|
yield teardown(target);
|
||||||
|
});
|
|
@ -0,0 +1,56 @@
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that automation view selects the first parameter by default and
|
||||||
|
* switching between AudioParam rerenders the graph.
|
||||||
|
*/
|
||||||
|
|
||||||
|
add_task(function*() {
|
||||||
|
let { target, panel } = yield initWebAudioEditor(AUTOMATION_URL);
|
||||||
|
let { panelWin } = panel;
|
||||||
|
let { gFront, $, $$, EVENTS, AutomationView } = panelWin;
|
||||||
|
|
||||||
|
let started = once(gFront, "start-context");
|
||||||
|
|
||||||
|
reload(target);
|
||||||
|
|
||||||
|
let [actors] = yield Promise.all([
|
||||||
|
get3(gFront, "create-node"),
|
||||||
|
waitForGraphRendered(panelWin, 3, 2)
|
||||||
|
]);
|
||||||
|
let nodeIds = actors.map(actor => actor.actorID);
|
||||||
|
|
||||||
|
|
||||||
|
// Oscillator node
|
||||||
|
click(panelWin, findGraphNode(panelWin, nodeIds[1]));
|
||||||
|
yield waitForInspectorRender(panelWin, EVENTS);
|
||||||
|
click(panelWin, $("#automation-tab"));
|
||||||
|
|
||||||
|
ok(AutomationView._selectedParamName, "frequency",
|
||||||
|
"AutomatioView is set on 'frequency'");
|
||||||
|
ok($(".automation-param-button[data-param='frequency']").getAttribute("selected"),
|
||||||
|
"frequency param should be selected on load");
|
||||||
|
ok(!$(".automation-param-button[data-param='detune']").getAttribute("selected"),
|
||||||
|
"detune param should not be selected on load");
|
||||||
|
ok(isVisible($("#automation-content")), "automation content should be visible");
|
||||||
|
ok(isVisible($("#automation-graph-container")), "graph container should be visible");
|
||||||
|
ok(!isVisible($("#automation-no-events")), "no-events panel should not be visible");
|
||||||
|
|
||||||
|
click(panelWin, $(".automation-param-button[data-param='detune']"));
|
||||||
|
yield once(panelWin, EVENTS.UI_AUTOMATION_TAB_RENDERED);
|
||||||
|
|
||||||
|
ok(true, "automation tab rerendered");
|
||||||
|
|
||||||
|
ok(AutomationView._selectedParamName, "detune",
|
||||||
|
"AutomatioView is set on 'detune'");
|
||||||
|
ok(!$(".automation-param-button[data-param='frequency']").getAttribute("selected"),
|
||||||
|
"frequency param should not be selected after clicking detune");
|
||||||
|
ok($(".automation-param-button[data-param='detune']").getAttribute("selected"),
|
||||||
|
"detune param should be selected after clicking detune");
|
||||||
|
ok(isVisible($("#automation-content")), "automation content should be visible");
|
||||||
|
ok(!isVisible($("#automation-graph-container")), "graph container should not be visible");
|
||||||
|
ok(isVisible($("#automation-no-events")), "no-events panel should be visible");
|
||||||
|
|
||||||
|
yield teardown(target);
|
||||||
|
});
|
|
@ -51,8 +51,9 @@ add_task(function*() {
|
||||||
ok(!isVisible($("#web-audio-editor-tabs")),
|
ok(!isVisible($("#web-audio-editor-tabs")),
|
||||||
"InspectorView tabs are still hidden.");
|
"InspectorView tabs are still hidden.");
|
||||||
|
|
||||||
|
let nodeSet = once(panelWin, EVENTS.UI_INSPECTOR_NODE_SET);
|
||||||
click(panelWin, findGraphNode(panelWin, nodeIds[1]));
|
click(panelWin, findGraphNode(panelWin, nodeIds[1]));
|
||||||
yield once(panelWin, EVENTS.UI_INSPECTOR_NODE_SET);
|
yield nodeSet;
|
||||||
|
|
||||||
ok(!isVisible($("#web-audio-editor-details-pane-empty")),
|
ok(!isVisible($("#web-audio-editor-details-pane-empty")),
|
||||||
"Empty message hides even when loading node while open.");
|
"Empty message hides even when loading node while open.");
|
||||||
|
|
|
@ -30,12 +30,13 @@ add_task(function*() {
|
||||||
is($("#web-audio-inspector-title").value, "AudioNode Inspector",
|
is($("#web-audio-inspector-title").value, "AudioNode Inspector",
|
||||||
"Inspector should have default title when empty.");
|
"Inspector should have default title when empty.");
|
||||||
|
|
||||||
click(panelWin, findGraphNode(panelWin, nodeIds[1]));
|
|
||||||
// Wait for the node to be set as well as the inspector to come fully into the view
|
// Wait for the node to be set as well as the inspector to come fully into the view
|
||||||
yield Promise.all([
|
let nodeSet = Promise.all([
|
||||||
once(panelWin, EVENTS.UI_INSPECTOR_NODE_SET),
|
once(panelWin, EVENTS.UI_INSPECTOR_NODE_SET),
|
||||||
once(panelWin, EVENTS.UI_INSPECTOR_TOGGLED)
|
once(panelWin, EVENTS.UI_INSPECTOR_TOGGLED)
|
||||||
]);
|
]);
|
||||||
|
click(panelWin, findGraphNode(panelWin, nodeIds[1]));
|
||||||
|
yield nodeSet;
|
||||||
|
|
||||||
ok(InspectorView.isVisible(), "InspectorView shown once node selected.");
|
ok(InspectorView.isVisible(), "InspectorView shown once node selected.");
|
||||||
ok(!isVisible($("#web-audio-editor-details-pane-empty")),
|
ok(!isVisible($("#web-audio-editor-details-pane-empty")),
|
||||||
|
@ -49,8 +50,9 @@ add_task(function*() {
|
||||||
is($("#web-audio-editor-tabs").selectedIndex, 0,
|
is($("#web-audio-editor-tabs").selectedIndex, 0,
|
||||||
"default tab selected should be the parameters tab.");
|
"default tab selected should be the parameters tab.");
|
||||||
|
|
||||||
|
nodeSet = once(panelWin, EVENTS.UI_INSPECTOR_NODE_SET);
|
||||||
click(panelWin, findGraphNode(panelWin, nodeIds[2]));
|
click(panelWin, findGraphNode(panelWin, nodeIds[2]));
|
||||||
yield once(panelWin, EVENTS.UI_INSPECTOR_NODE_SET);
|
yield nodeSet;
|
||||||
|
|
||||||
is($("#web-audio-inspector-title").value, "Gain",
|
is($("#web-audio-inspector-title").value, "Gain",
|
||||||
"Inspector title updates when a new node is selected.");
|
"Inspector title updates when a new node is selected.");
|
||||||
|
|
|
@ -8,8 +8,8 @@
|
||||||
add_task(function*() {
|
add_task(function*() {
|
||||||
let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
|
let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
|
||||||
let { panelWin } = panel;
|
let { panelWin } = panel;
|
||||||
let { gFront, $, $$, EVENTS, InspectorView } = panelWin;
|
let { gFront, $, $$, EVENTS, PropertiesView } = panelWin;
|
||||||
let gVars = InspectorView._propsView;
|
let gVars = PropertiesView._propsView;
|
||||||
|
|
||||||
let started = once(gFront, "start-context");
|
let started = once(gFront, "start-context");
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ add_task(function*() {
|
||||||
click(panelWin, findGraphNode(panelWin, nodeIds[1]));
|
click(panelWin, findGraphNode(panelWin, nodeIds[1]));
|
||||||
// Wait for the node to be set as well as the inspector to come fully into the view
|
// Wait for the node to be set as well as the inspector to come fully into the view
|
||||||
yield Promise.all([
|
yield Promise.all([
|
||||||
once(panelWin, EVENTS.UI_INSPECTOR_NODE_SET),
|
waitForInspectorRender(panelWin, EVENTS),
|
||||||
once(panelWin, EVENTS.UI_INSPECTOR_TOGGLED)
|
once(panelWin, EVENTS.UI_INSPECTOR_TOGGLED)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -37,17 +37,17 @@ add_task(function*() {
|
||||||
}, "default loaded string");
|
}, "default loaded string");
|
||||||
|
|
||||||
click(panelWin, findGraphNode(panelWin, nodeIds[2]));
|
click(panelWin, findGraphNode(panelWin, nodeIds[2]));
|
||||||
yield once(panelWin, EVENTS.UI_INSPECTOR_NODE_SET);
|
yield waitForInspectorRender(panelWin, EVENTS),
|
||||||
checkVariableView(gVars, 0, {
|
checkVariableView(gVars, 0, {
|
||||||
"gain": 0
|
"gain": 0
|
||||||
}, "default loaded number");
|
}, "default loaded number");
|
||||||
|
|
||||||
click(panelWin, findGraphNode(panelWin, nodeIds[1]));
|
click(panelWin, findGraphNode(panelWin, nodeIds[1]));
|
||||||
yield once(panelWin, EVENTS.UI_INSPECTOR_NODE_SET);
|
yield waitForInspectorRender(panelWin, EVENTS),
|
||||||
yield setAndCheck(0, "type", "square", "square", "sets string as string");
|
yield setAndCheck(0, "type", "square", "square", "sets string as string");
|
||||||
|
|
||||||
click(panelWin, findGraphNode(panelWin, nodeIds[2]));
|
click(panelWin, findGraphNode(panelWin, nodeIds[2]));
|
||||||
yield once(panelWin, EVENTS.UI_INSPECTOR_NODE_SET);
|
yield waitForInspectorRender(panelWin, EVENTS),
|
||||||
yield setAndCheck(0, "gain", "0.005", 0.005, "sets number as number");
|
yield setAndCheck(0, "gain", "0.005", 0.005, "sets number as number");
|
||||||
yield setAndCheck(0, "gain", "0.1", 0.1, "sets float as float");
|
yield setAndCheck(0, "gain", "0.1", 0.1, "sets float as float");
|
||||||
yield setAndCheck(0, "gain", ".2", 0.2, "sets float without leading zero as float");
|
yield setAndCheck(0, "gain", ".2", 0.2, "sets float without leading zero as float");
|
||||||
|
|
|
@ -8,8 +8,8 @@
|
||||||
add_task(function*() {
|
add_task(function*() {
|
||||||
let { target, panel } = yield initWebAudioEditor(COMPLEX_CONTEXT_URL);
|
let { target, panel } = yield initWebAudioEditor(COMPLEX_CONTEXT_URL);
|
||||||
let { panelWin } = panel;
|
let { panelWin } = panel;
|
||||||
let { gFront, $, $$, EVENTS, InspectorView } = panelWin;
|
let { gFront, $, $$, EVENTS, PropertiesView } = panelWin;
|
||||||
let gVars = InspectorView._propsView;
|
let gVars = PropertiesView._propsView;
|
||||||
|
|
||||||
let started = once(gFront, "start-context");
|
let started = once(gFront, "start-context");
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ add_task(function*() {
|
||||||
click(panelWin, findGraphNode(panelWin, nodeIds[3]));
|
click(panelWin, findGraphNode(panelWin, nodeIds[3]));
|
||||||
// Wait for the node to be set as well as the inspector to come fully into the view
|
// Wait for the node to be set as well as the inspector to come fully into the view
|
||||||
yield Promise.all([
|
yield Promise.all([
|
||||||
once(panelWin, EVENTS.UI_INSPECTOR_NODE_SET),
|
waitForInspectorRender(panelWin, EVENTS),
|
||||||
once(panelWin, EVENTS.UI_INSPECTOR_TOGGLED),
|
once(panelWin, EVENTS.UI_INSPECTOR_TOGGLED),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
|
@ -37,8 +37,8 @@ function waitForDeviceClosed() {
|
||||||
add_task(function*() {
|
add_task(function*() {
|
||||||
let { target, panel } = yield initWebAudioEditor(MEDIA_NODES_URL);
|
let { target, panel } = yield initWebAudioEditor(MEDIA_NODES_URL);
|
||||||
let { panelWin } = panel;
|
let { panelWin } = panel;
|
||||||
let { gFront, $, $$, EVENTS, InspectorView } = panelWin;
|
let { gFront, $, $$, EVENTS, PropertiesView } = panelWin;
|
||||||
let gVars = InspectorView._propsView;
|
let gVars = PropertiesView._propsView;
|
||||||
|
|
||||||
// Auto enable getUserMedia
|
// Auto enable getUserMedia
|
||||||
let mediaPermissionPref = Services.prefs.getBoolPref(MEDIA_PERMISSION);
|
let mediaPermissionPref = Services.prefs.getBoolPref(MEDIA_PERMISSION);
|
||||||
|
@ -59,7 +59,7 @@ add_task(function*() {
|
||||||
|
|
||||||
for (let i = 0; i < types.length; i++) {
|
for (let i = 0; i < types.length; i++) {
|
||||||
click(panelWin, findGraphNode(panelWin, nodeIds[i]));
|
click(panelWin, findGraphNode(panelWin, nodeIds[i]));
|
||||||
yield once(panelWin, EVENTS.UI_INSPECTOR_NODE_SET);
|
yield waitForInspectorRender(panelWin, EVENTS);
|
||||||
checkVariableView(gVars, 0, NODE_DEFAULT_VALUES[types[i]], types[i]);
|
checkVariableView(gVars, 0, NODE_DEFAULT_VALUES[types[i]], types[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
add_task(function*() {
|
add_task(function*() {
|
||||||
let { target, panel } = yield initWebAudioEditor(BUFFER_AND_ARRAY_URL);
|
let { target, panel } = yield initWebAudioEditor(BUFFER_AND_ARRAY_URL);
|
||||||
let { panelWin } = panel;
|
let { panelWin } = panel;
|
||||||
let { gFront, $, $$, EVENTS, InspectorView } = panelWin;
|
let { gFront, $, $$, EVENTS, PropertiesView } = panelWin;
|
||||||
let gVars = InspectorView._propsView;
|
let gVars = PropertiesView._propsView;
|
||||||
|
|
||||||
let started = once(gFront, "start-context");
|
let started = once(gFront, "start-context");
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ add_task(function*() {
|
||||||
let nodeIds = actors.map(actor => actor.actorID);
|
let nodeIds = actors.map(actor => actor.actorID);
|
||||||
|
|
||||||
click(panelWin, findGraphNode(panelWin, nodeIds[2]));
|
click(panelWin, findGraphNode(panelWin, nodeIds[2]));
|
||||||
yield once(panelWin, EVENTS.UI_INSPECTOR_NODE_SET);
|
yield waitForInspectorRender(panelWin, EVENTS);
|
||||||
checkVariableView(gVars, 0, {
|
checkVariableView(gVars, 0, {
|
||||||
"curve": "Float32Array"
|
"curve": "Float32Array"
|
||||||
}, "WaveShaper's `curve` is listed as an `Float32Array`.");
|
}, "WaveShaper's `curve` is listed as an `Float32Array`.");
|
||||||
|
@ -33,7 +33,7 @@ add_task(function*() {
|
||||||
ok(state, "Float32Array property should not have a dropdown.");
|
ok(state, "Float32Array property should not have a dropdown.");
|
||||||
|
|
||||||
click(panelWin, findGraphNode(panelWin, nodeIds[1]));
|
click(panelWin, findGraphNode(panelWin, nodeIds[1]));
|
||||||
yield once(panelWin, EVENTS.UI_INSPECTOR_NODE_SET);
|
yield waitForInspectorRender(panelWin, EVENTS);
|
||||||
checkVariableView(gVars, 0, {
|
checkVariableView(gVars, 0, {
|
||||||
"buffer": "AudioBuffer"
|
"buffer": "AudioBuffer"
|
||||||
}, "AudioBufferSourceNode's `buffer` is listed as an `AudioBuffer`.");
|
}, "AudioBufferSourceNode's `buffer` is listed as an `AudioBuffer`.");
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
add_task(function*() {
|
add_task(function*() {
|
||||||
let { target, panel } = yield initWebAudioEditor(SIMPLE_NODES_URL);
|
let { target, panel } = yield initWebAudioEditor(SIMPLE_NODES_URL);
|
||||||
let { panelWin } = panel;
|
let { panelWin } = panel;
|
||||||
let { gFront, $, $$, EVENTS, InspectorView } = panelWin;
|
let { gFront, $, $$, EVENTS, PropertiesView } = panelWin;
|
||||||
let gVars = InspectorView._propsView;
|
let gVars = PropertiesView._propsView;
|
||||||
|
|
||||||
let started = once(gFront, "start-context");
|
let started = once(gFront, "start-context");
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ add_task(function*() {
|
||||||
|
|
||||||
for (let i = 0; i < types.length; i++) {
|
for (let i = 0; i < types.length; i++) {
|
||||||
click(panelWin, findGraphNode(panelWin, nodeIds[i]));
|
click(panelWin, findGraphNode(panelWin, nodeIds[i]));
|
||||||
yield once(panelWin, EVENTS.UI_INSPECTOR_NODE_SET);
|
yield waitForInspectorRender(panelWin, EVENTS);
|
||||||
checkVariableView(gVars, 0, NODE_DEFAULT_VALUES[types[i]], types[i]);
|
checkVariableView(gVars, 0, NODE_DEFAULT_VALUES[types[i]], types[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,8 @@
|
||||||
add_task(function*() {
|
add_task(function*() {
|
||||||
let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
|
let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
|
||||||
let { panelWin } = panel;
|
let { panelWin } = panel;
|
||||||
let { gFront, $, $$, EVENTS, InspectorView } = panelWin;
|
let { gFront, $, $$, EVENTS, PropertiesView } = panelWin;
|
||||||
let gVars = InspectorView._propsView;
|
let gVars = PropertiesView._propsView;
|
||||||
|
|
||||||
let started = once(gFront, "start-context");
|
let started = once(gFront, "start-context");
|
||||||
|
|
||||||
|
@ -23,19 +23,19 @@ add_task(function*() {
|
||||||
|
|
||||||
// Gain node
|
// Gain node
|
||||||
click(panelWin, findGraphNode(panelWin, nodeIds[2]));
|
click(panelWin, findGraphNode(panelWin, nodeIds[2]));
|
||||||
yield once(panelWin, EVENTS.UI_INSPECTOR_NODE_SET);
|
yield waitForInspectorRender(panelWin, EVENTS);
|
||||||
|
|
||||||
ok(isVisible($("#properties-tabpanel-content")), "Parameters shown when they exist.");
|
ok(isVisible($("#properties-content")), "Parameters shown when they exist.");
|
||||||
ok(!isVisible($("#properties-tabpanel-content-empty")),
|
ok(!isVisible($("#properties-empty")),
|
||||||
"Empty message hidden when AudioParams exist.");
|
"Empty message hidden when AudioParams exist.");
|
||||||
|
|
||||||
// Destination node
|
// Destination node
|
||||||
click(panelWin, findGraphNode(panelWin, nodeIds[0]));
|
click(panelWin, findGraphNode(panelWin, nodeIds[0]));
|
||||||
yield once(panelWin, EVENTS.UI_INSPECTOR_NODE_SET);
|
yield waitForInspectorRender(panelWin, EVENTS);
|
||||||
|
|
||||||
ok(!isVisible($("#properties-tabpanel-content")),
|
ok(!isVisible($("#properties-content")),
|
||||||
"Parameters hidden when they don't exist.");
|
"Parameters hidden when they don't exist.");
|
||||||
ok(isVisible($("#properties-tabpanel-content-empty")),
|
ok(isVisible($("#properties-empty")),
|
||||||
"Empty message shown when no AudioParams exist.");
|
"Empty message shown when no AudioParams exist.");
|
||||||
|
|
||||||
yield teardown(target);
|
yield teardown(target);
|
||||||
|
|
|
@ -35,12 +35,13 @@ add_task(function*() {
|
||||||
is(eventName, exp[0], "correct eventName in event");
|
is(eventName, exp[0], "correct eventName in event");
|
||||||
is(paramName, "frequency", "correct paramName in event");
|
is(paramName, "frequency", "correct paramName in event");
|
||||||
is(args.length, exp.length - 1, "correct length in args");
|
is(args.length, exp.length - 1, "correct length in args");
|
||||||
|
|
||||||
args.forEach((a, i) => {
|
args.forEach((a, i) => {
|
||||||
// In the case of an array
|
// In the case of an array
|
||||||
if (typeof a === "object") {
|
if (typeof a === "object") {
|
||||||
a.forEach((f, j) => is(f, exp[i + 1][j], "correct argument in args"));
|
a.forEach((f, j) => is(f, exp[i + 1][j], `correct argument in Float32Array: ${f}`));
|
||||||
} else {
|
} else {
|
||||||
is(a, exp[i + 1], "correct argument in args");
|
is(a, exp[i + 1], `correct ${i+1}th argument in args: ${a}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
events.push([eventName].concat(args));
|
events.push([eventName].concat(args));
|
||||||
|
|
|
@ -231,7 +231,7 @@ function checkVariableView (view, index, hash, description = "") {
|
||||||
// If node shouldn't display any properties, ensure that the 'empty' message is
|
// If node shouldn't display any properties, ensure that the 'empty' message is
|
||||||
// visible
|
// visible
|
||||||
if (!variables.length) {
|
if (!variables.length) {
|
||||||
ok(isVisible(scope.window.$("#properties-tabpanel-content-empty")),
|
ok(isVisible(scope.window.$("#properties-empty")),
|
||||||
description + " should show the empty properties tab.");
|
description + " should show the empty properties tab.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -413,10 +413,10 @@ function checkAutomationValue (values, time, expected) {
|
||||||
*/
|
*/
|
||||||
function getValueAt (values, time) {
|
function getValueAt (values, time) {
|
||||||
for (let i = 0; i < values.length; i++) {
|
for (let i = 0; i < values.length; i++) {
|
||||||
if (values[i].t === time) {
|
if (values[i].delta === time) {
|
||||||
return values[i].value;
|
return values[i].value;
|
||||||
}
|
}
|
||||||
if (values[i].t > time) {
|
if (values[i].delta > time) {
|
||||||
return (values[i - 1].value + values[i].value) / 2;
|
return (values[i - 1].value + values[i].value) / 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -424,6 +424,16 @@ function checkAutomationValue (values, time, expected) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for all inspector tabs to complete rendering.
|
||||||
|
*/
|
||||||
|
function waitForInspectorRender (panelWin, EVENTS) {
|
||||||
|
return Promise.all([
|
||||||
|
once(panelWin, EVENTS.UI_PROPERTIES_TAB_RENDERED),
|
||||||
|
once(panelWin, EVENTS.UI_AUTOMATION_TAB_RENDERED)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of audio node properties to test against expectations of the AudioNode actor
|
* List of audio node properties to test against expectations of the AudioNode actor
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,159 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Functions handling the audio node inspector UI.
|
||||||
|
*/
|
||||||
|
|
||||||
|
let AutomationView = {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialization function called when the tool starts up.
|
||||||
|
*/
|
||||||
|
initialize: function () {
|
||||||
|
this._buttons = $("#automation-param-toolbar-buttons");
|
||||||
|
this.graph = new LineGraphWidget($("#automation-graph"), { avg: false });
|
||||||
|
this.graph.selectionEnabled = false;
|
||||||
|
|
||||||
|
this._onButtonClick = this._onButtonClick.bind(this);
|
||||||
|
this._onNodeSet = this._onNodeSet.bind(this);
|
||||||
|
this._onResize = this._onResize.bind(this);
|
||||||
|
|
||||||
|
this._buttons.addEventListener("click", this._onButtonClick);
|
||||||
|
window.on(EVENTS.UI_INSPECTOR_RESIZE, this._onResize);
|
||||||
|
window.on(EVENTS.UI_INSPECTOR_NODE_SET, this._onNodeSet);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destruction function called when the tool cleans up.
|
||||||
|
*/
|
||||||
|
destroy: function () {
|
||||||
|
this._buttons.removeEventListener("click", this._onButtonClick);
|
||||||
|
window.off(EVENTS.UI_INSPECTOR_RESIZE, this._onResize);
|
||||||
|
window.off(EVENTS.UI_INSPECTOR_NODE_SET, this._onNodeSet);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Empties out the props view.
|
||||||
|
*/
|
||||||
|
resetUI: function () {
|
||||||
|
this._currentNode = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On a new node selection, create the Automation panel for
|
||||||
|
* that specific node.
|
||||||
|
*/
|
||||||
|
build: Task.async(function* () {
|
||||||
|
let node = this._currentNode;
|
||||||
|
|
||||||
|
let props = yield node.getParams();
|
||||||
|
let params = props.filter(({ flags }) => flags && flags.param);
|
||||||
|
|
||||||
|
this._createParamButtons(params);
|
||||||
|
|
||||||
|
this._selectedParamName = params[0] ? params[0].param : null;
|
||||||
|
this.render();
|
||||||
|
}),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the graph for specified `paramName`. Called when
|
||||||
|
* the parameter view is changed, or when new param data events
|
||||||
|
* are fired for the currently specified param.
|
||||||
|
*/
|
||||||
|
render: Task.async(function *() {
|
||||||
|
let node = this._currentNode;
|
||||||
|
let paramName = this._selectedParamName;
|
||||||
|
// Escape if either node or parameter name does not exist.
|
||||||
|
if (!node || !paramName) {
|
||||||
|
this._setState("no-params");
|
||||||
|
window.emit(EVENTS.UI_AUTOMATION_TAB_RENDERED, null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let { values, events } = yield node.getAutomationData(paramName);
|
||||||
|
this._setState(events.length ? "show" : "no-events");
|
||||||
|
yield this.graph.setDataWhenReady(values);
|
||||||
|
window.emit(EVENTS.UI_AUTOMATION_TAB_RENDERED, node.id);
|
||||||
|
}),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the buttons for each AudioParam, that when clicked,
|
||||||
|
* render the graph for that AudioParam.
|
||||||
|
*/
|
||||||
|
_createParamButtons: function (params) {
|
||||||
|
this._buttons.innerHTML = "";
|
||||||
|
params.forEach((param, i) => {
|
||||||
|
let button = document.createElement("toolbarbutton");
|
||||||
|
button.setAttribute("class", "devtools-toolbarbutton automation-param-button");
|
||||||
|
button.setAttribute("data-param", param.param);
|
||||||
|
// Set label to the parameter name, should not be L10N'd
|
||||||
|
button.setAttribute("label", param.param);
|
||||||
|
|
||||||
|
// If first button, set to 'selected' for styling
|
||||||
|
if (i === 0) {
|
||||||
|
button.setAttribute("selected", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._buttons.appendChild(button);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internally sets the current audio node and rebuilds appropriate
|
||||||
|
* views.
|
||||||
|
*/
|
||||||
|
_setAudioNode: function (node) {
|
||||||
|
this._currentNode = node;
|
||||||
|
if (this._currentNode) {
|
||||||
|
this.build();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggles the subviews to display messages whether or not
|
||||||
|
* the audio node has no AudioParams, no automation events, or
|
||||||
|
* shows the graph.
|
||||||
|
*/
|
||||||
|
_setState: function (state) {
|
||||||
|
let contentView = $("#automation-content");
|
||||||
|
let emptyView = $("#automation-empty");
|
||||||
|
|
||||||
|
let graphView = $("#automation-graph-container");
|
||||||
|
let noEventsView = $("#automation-no-events");
|
||||||
|
|
||||||
|
contentView.hidden = state === "no-params";
|
||||||
|
emptyView.hidden = state !== "no-params";
|
||||||
|
|
||||||
|
graphView.hidden = state !== "show";
|
||||||
|
noEventsView.hidden = state !== "no-events";
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event handlers
|
||||||
|
*/
|
||||||
|
|
||||||
|
_onButtonClick: function (e) {
|
||||||
|
Array.forEach($$(".automation-param-button"), $btn => $btn.removeAttribute("selected"));
|
||||||
|
let paramName = e.target.getAttribute("data-param");
|
||||||
|
e.target.setAttribute("selected", true);
|
||||||
|
this._selectedParamName = paramName;
|
||||||
|
this.render();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the inspector is resized.
|
||||||
|
*/
|
||||||
|
_onResize: function () {
|
||||||
|
this.graph.refresh();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the inspector view determines a node is selected.
|
||||||
|
*/
|
||||||
|
_onNodeSet: function (_, id) {
|
||||||
|
this._setAudioNode(id != null ? gAudioNodes.get(id) : null);
|
||||||
|
}
|
||||||
|
};
|
|
@ -38,7 +38,6 @@ let ContextView = {
|
||||||
initialize: function() {
|
initialize: function() {
|
||||||
this._onGraphNodeClick = this._onGraphNodeClick.bind(this);
|
this._onGraphNodeClick = this._onGraphNodeClick.bind(this);
|
||||||
this._onThemeChange = this._onThemeChange.bind(this);
|
this._onThemeChange = this._onThemeChange.bind(this);
|
||||||
this._onNodeSelect = this._onNodeSelect.bind(this);
|
|
||||||
this._onStartContext = this._onStartContext.bind(this);
|
this._onStartContext = this._onStartContext.bind(this);
|
||||||
this._onEvent = this._onEvent.bind(this);
|
this._onEvent = this._onEvent.bind(this);
|
||||||
|
|
||||||
|
@ -46,7 +45,6 @@ let ContextView = {
|
||||||
$('#graph-target').addEventListener('click', this._onGraphNodeClick, false);
|
$('#graph-target').addEventListener('click', this._onGraphNodeClick, false);
|
||||||
|
|
||||||
window.on(EVENTS.THEME_CHANGE, this._onThemeChange);
|
window.on(EVENTS.THEME_CHANGE, this._onThemeChange);
|
||||||
window.on(EVENTS.UI_INSPECTOR_NODE_SET, this._onNodeSelect);
|
|
||||||
window.on(EVENTS.START_CONTEXT, this._onStartContext);
|
window.on(EVENTS.START_CONTEXT, this._onStartContext);
|
||||||
gAudioNodes.on("*", this._onEvent);
|
gAudioNodes.on("*", this._onEvent);
|
||||||
},
|
},
|
||||||
|
@ -62,7 +60,6 @@ let ContextView = {
|
||||||
}
|
}
|
||||||
$('#graph-target').removeEventListener('click', this._onGraphNodeClick, false);
|
$('#graph-target').removeEventListener('click', this._onGraphNodeClick, false);
|
||||||
window.off(EVENTS.THEME_CHANGE, this._onThemeChange);
|
window.off(EVENTS.THEME_CHANGE, this._onThemeChange);
|
||||||
window.off(EVENTS.UI_INSPECTOR_NODE_SET, this._onNodeSelect);
|
|
||||||
window.off(EVENTS.START_CONTEXT, this._onStartContext);
|
window.off(EVENTS.START_CONTEXT, this._onStartContext);
|
||||||
gAudioNodes.off("*", this._onEvent);
|
gAudioNodes.off("*", this._onEvent);
|
||||||
},
|
},
|
||||||
|
@ -112,7 +109,6 @@ let ContextView = {
|
||||||
* Makes the corresponding graph node appear "focused", removing
|
* Makes the corresponding graph node appear "focused", removing
|
||||||
* focused styles from all other nodes. If no `actorID` specified,
|
* focused styles from all other nodes. If no `actorID` specified,
|
||||||
* make all nodes appear unselected.
|
* make all nodes appear unselected.
|
||||||
* Called from UI_INSPECTOR_NODE_SELECT.
|
|
||||||
*/
|
*/
|
||||||
focusNode: function (actorID) {
|
focusNode: function (actorID) {
|
||||||
// Remove class "selected" from all nodes
|
// Remove class "selected" from all nodes
|
||||||
|
@ -272,10 +268,6 @@ let ContextView = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_onNodeSelect: function (eventName, id) {
|
|
||||||
this.focusNode(id);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fired when the devtools theme changes.
|
* Fired when the devtools theme changes.
|
||||||
*/
|
*/
|
||||||
|
@ -300,6 +292,9 @@ let ContextView = {
|
||||||
if (!node)
|
if (!node)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
window.emit(EVENTS.UI_SELECT_NODE, node.getAttribute("data-id"));
|
let id = node.getAttribute("data-id");
|
||||||
|
|
||||||
|
this.focusNode(id);
|
||||||
|
window.emit(EVENTS.UI_SELECT_NODE, id);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,25 +3,13 @@
|
||||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
Cu.import("resource:///modules/devtools/VariablesView.jsm");
|
|
||||||
Cu.import("resource:///modules/devtools/VariablesViewController.jsm");
|
|
||||||
|
|
||||||
// Strings for rendering
|
|
||||||
const EXPAND_INSPECTOR_STRING = L10N.getStr("expandInspector");
|
|
||||||
const COLLAPSE_INSPECTOR_STRING = L10N.getStr("collapseInspector");
|
|
||||||
|
|
||||||
// Store width as a preference rather than hardcode
|
// Store width as a preference rather than hardcode
|
||||||
// TODO bug 1009056
|
// TODO bug 1009056
|
||||||
const INSPECTOR_WIDTH = 300;
|
const INSPECTOR_WIDTH = 300;
|
||||||
|
|
||||||
const GENERIC_VARIABLES_VIEW_SETTINGS = {
|
// Strings for rendering
|
||||||
searchEnabled: false,
|
const EXPAND_INSPECTOR_STRING = L10N.getStr("expandInspector");
|
||||||
editableValueTooltip: "",
|
const COLLAPSE_INSPECTOR_STRING = L10N.getStr("collapseInspector");
|
||||||
editableNameTooltip: "",
|
|
||||||
preventDisableOnChange: true,
|
|
||||||
preventDescriptorModifiers: false,
|
|
||||||
eval: () => {}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Functions handling the audio node inspector UI.
|
* Functions handling the audio node inspector UI.
|
||||||
|
@ -41,10 +29,9 @@ let InspectorView = {
|
||||||
* Initialization function called when the tool starts up.
|
* Initialization function called when the tool starts up.
|
||||||
*/
|
*/
|
||||||
initialize: function () {
|
initialize: function () {
|
||||||
this._tabsPane = $("#web-audio-editor-tabs");
|
|
||||||
|
|
||||||
// Set up view controller
|
// Set up view controller
|
||||||
this.el = $("#web-audio-inspector");
|
this.el = $("#web-audio-inspector");
|
||||||
|
this.splitter = $("#inspector-splitter");
|
||||||
this.el.setAttribute("width", INSPECTOR_WIDTH);
|
this.el.setAttribute("width", INSPECTOR_WIDTH);
|
||||||
this.button = $("#inspector-pane-toggle");
|
this.button = $("#inspector-pane-toggle");
|
||||||
mixin(this, ToggleMixin);
|
mixin(this, ToggleMixin);
|
||||||
|
@ -53,13 +40,11 @@ let InspectorView = {
|
||||||
// Hide inspector view on startup
|
// Hide inspector view on startup
|
||||||
this.hideImmediately();
|
this.hideImmediately();
|
||||||
|
|
||||||
this._onEval = this._onEval.bind(this);
|
|
||||||
this._onNodeSelect = this._onNodeSelect.bind(this);
|
this._onNodeSelect = this._onNodeSelect.bind(this);
|
||||||
this._onDestroyNode = this._onDestroyNode.bind(this);
|
this._onDestroyNode = this._onDestroyNode.bind(this);
|
||||||
|
this._onResize = this._onResize.bind(this);
|
||||||
|
|
||||||
this._propsView = new VariablesView($("#properties-tabpanel-content"), GENERIC_VARIABLES_VIEW_SETTINGS);
|
this.splitter.addEventListener("mouseup", this._onResize);
|
||||||
this._propsView.eval = this._onEval;
|
|
||||||
|
|
||||||
window.on(EVENTS.UI_SELECT_NODE, this._onNodeSelect);
|
window.on(EVENTS.UI_SELECT_NODE, this._onNodeSelect);
|
||||||
gAudioNodes.on("remove", this._onDestroyNode);
|
gAudioNodes.on("remove", this._onDestroyNode);
|
||||||
},
|
},
|
||||||
|
@ -69,12 +54,13 @@ let InspectorView = {
|
||||||
*/
|
*/
|
||||||
destroy: function () {
|
destroy: function () {
|
||||||
this.unbindToggle();
|
this.unbindToggle();
|
||||||
|
this.splitter.removeEventListener("mouseup", this._onResize);
|
||||||
window.off(EVENTS.UI_SELECT_NODE, this._onNodeSelect);
|
window.off(EVENTS.UI_SELECT_NODE, this._onNodeSelect);
|
||||||
gAudioNodes.off("remove", this._onDestroyNode);
|
gAudioNodes.off("remove", this._onDestroyNode);
|
||||||
|
|
||||||
this.el = null;
|
this.el = null;
|
||||||
this.button = null;
|
this.button = null;
|
||||||
this._tabsPane = null;
|
this.splitter = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -96,8 +82,7 @@ let InspectorView = {
|
||||||
$("#web-audio-editor-details-pane-empty").setAttribute("hidden", "true");
|
$("#web-audio-editor-details-pane-empty").setAttribute("hidden", "true");
|
||||||
$("#web-audio-editor-tabs").removeAttribute("hidden");
|
$("#web-audio-editor-tabs").removeAttribute("hidden");
|
||||||
this._setTitle();
|
this._setTitle();
|
||||||
this._buildPropertiesView()
|
window.emit(EVENTS.UI_INSPECTOR_NODE_SET, this._currentNode.id);
|
||||||
.then(() => window.emit(EVENTS.UI_INSPECTOR_NODE_SET, this._currentNode.id));
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -112,7 +97,6 @@ let InspectorView = {
|
||||||
* Empties out the props view.
|
* Empties out the props view.
|
||||||
*/
|
*/
|
||||||
resetUI: function () {
|
resetUI: function () {
|
||||||
this._propsView.empty();
|
|
||||||
// Set current node to empty to load empty view
|
// Set current node to empty to load empty view
|
||||||
this.setCurrentAudioNode();
|
this.setCurrentAudioNode();
|
||||||
|
|
||||||
|
@ -129,97 +113,10 @@ let InspectorView = {
|
||||||
$("#web-audio-inspector-title").setAttribute("value", title);
|
$("#web-audio-inspector-title").setAttribute("value", title);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Reconstructs the `Properties` tab in the inspector
|
|
||||||
* with the `this._currentNode` as it's source.
|
|
||||||
*/
|
|
||||||
_buildPropertiesView: Task.async(function* () {
|
|
||||||
let propsView = this._propsView;
|
|
||||||
let node = this._currentNode;
|
|
||||||
propsView.empty();
|
|
||||||
|
|
||||||
let audioParamsScope = propsView.addScope("AudioParams");
|
|
||||||
let props = yield node.getParams();
|
|
||||||
|
|
||||||
// Disable AudioParams VariableView expansion
|
|
||||||
// when there are no props i.e. AudioDestinationNode
|
|
||||||
this._togglePropertiesView(!!props.length);
|
|
||||||
|
|
||||||
props.forEach(({ param, value, flags }) => {
|
|
||||||
let descriptor = {
|
|
||||||
value: value,
|
|
||||||
writable: !flags || !flags.readonly,
|
|
||||||
};
|
|
||||||
let item = audioParamsScope.addItem(param, descriptor);
|
|
||||||
|
|
||||||
// No items should currently display a dropdown
|
|
||||||
item.twisty = false;
|
|
||||||
});
|
|
||||||
|
|
||||||
audioParamsScope.expanded = true;
|
|
||||||
|
|
||||||
window.emit(EVENTS.UI_PROPERTIES_TAB_RENDERED, node.id);
|
|
||||||
}),
|
|
||||||
|
|
||||||
_togglePropertiesView: function (show) {
|
|
||||||
let propsView = $("#properties-tabpanel-content");
|
|
||||||
let emptyView = $("#properties-tabpanel-content-empty");
|
|
||||||
(show ? propsView : emptyView).removeAttribute("hidden");
|
|
||||||
(show ? emptyView : propsView).setAttribute("hidden", "true");
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the scope for AudioParams in the
|
|
||||||
* VariablesView.
|
|
||||||
*
|
|
||||||
* @return Scope
|
|
||||||
*/
|
|
||||||
_getAudioPropertiesScope: function () {
|
|
||||||
return this._propsView.getScopeAtIndex(0);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Event handlers
|
* Event handlers
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
|
||||||
* Executed when an audio prop is changed in the UI.
|
|
||||||
*/
|
|
||||||
_onEval: Task.async(function* (variable, value) {
|
|
||||||
let ownerScope = variable.ownerView;
|
|
||||||
let node = this._currentNode;
|
|
||||||
let propName = variable.name;
|
|
||||||
let error;
|
|
||||||
|
|
||||||
if (!variable._initialDescriptor.writable) {
|
|
||||||
error = new Error("Variable " + propName + " is not writable.");
|
|
||||||
} else {
|
|
||||||
// Cast value to proper type
|
|
||||||
try {
|
|
||||||
let number = parseFloat(value);
|
|
||||||
if (!isNaN(number)) {
|
|
||||||
value = number;
|
|
||||||
} else {
|
|
||||||
value = JSON.parse(value);
|
|
||||||
}
|
|
||||||
error = yield node.actor.setParam(propName, value);
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
error = e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO figure out how to handle and display set prop errors
|
|
||||||
// and enable `test/brorwser_wa_properties-view-edit.js`
|
|
||||||
// Bug 994258
|
|
||||||
if (!error) {
|
|
||||||
ownerScope.get(propName).setGrip(value);
|
|
||||||
window.emit(EVENTS.UI_SET_PARAM, node.id, propName, value);
|
|
||||||
} else {
|
|
||||||
window.emit(EVENTS.UI_SET_PARAM_ERROR, node.id, propName, value);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called on EVENTS.UI_SELECT_NODE, and takes an actorID `id`
|
* Called on EVENTS.UI_SELECT_NODE, and takes an actorID `id`
|
||||||
* and calls `setCurrentAudioNode` to scaffold the inspector view.
|
* and calls `setCurrentAudioNode` to scaffold the inspector view.
|
||||||
|
@ -231,6 +128,10 @@ let InspectorView = {
|
||||||
this.show();
|
this.show();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_onResize: function () {
|
||||||
|
window.emit(EVENTS.UI_INSPECTOR_RESIZE);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when `DESTROY_NODE` is fired to remove the node from props view if
|
* Called when `DESTROY_NODE` is fired to remove the node from props view if
|
||||||
* it's currently selected.
|
* it's currently selected.
|
||||||
|
|
|
@ -0,0 +1,164 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
Cu.import("resource:///modules/devtools/VariablesView.jsm");
|
||||||
|
Cu.import("resource:///modules/devtools/VariablesViewController.jsm");
|
||||||
|
|
||||||
|
const GENERIC_VARIABLES_VIEW_SETTINGS = {
|
||||||
|
searchEnabled: false,
|
||||||
|
editableValueTooltip: "",
|
||||||
|
editableNameTooltip: "",
|
||||||
|
preventDisableOnChange: true,
|
||||||
|
preventDescriptorModifiers: false,
|
||||||
|
eval: () => {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Functions handling the audio node inspector UI.
|
||||||
|
*/
|
||||||
|
|
||||||
|
let PropertiesView = {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialization function called when the tool starts up.
|
||||||
|
*/
|
||||||
|
initialize: function () {
|
||||||
|
this._onEval = this._onEval.bind(this);
|
||||||
|
this._onNodeSet = this._onNodeSet.bind(this);
|
||||||
|
|
||||||
|
window.on(EVENTS.UI_INSPECTOR_NODE_SET, this._onNodeSet);
|
||||||
|
this._propsView = new VariablesView($("#properties-content"), GENERIC_VARIABLES_VIEW_SETTINGS);
|
||||||
|
this._propsView.eval = this._onEval;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destruction function called when the tool cleans up.
|
||||||
|
*/
|
||||||
|
destroy: function () {
|
||||||
|
window.off(EVENTS.UI_INSPECTOR_NODE_SET, this._onNodeSet);
|
||||||
|
this._propsView = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Empties out the props view.
|
||||||
|
*/
|
||||||
|
resetUI: function () {
|
||||||
|
this._propsView.empty();
|
||||||
|
this._currentNode = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internally sets the current audio node and rebuilds appropriate
|
||||||
|
* views.
|
||||||
|
*/
|
||||||
|
_setAudioNode: function (node) {
|
||||||
|
this._currentNode = node;
|
||||||
|
if (this._currentNode) {
|
||||||
|
this._buildPropertiesView();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reconstructs the `Properties` tab in the inspector
|
||||||
|
* with the `this._currentNode` as it's source.
|
||||||
|
*/
|
||||||
|
_buildPropertiesView: Task.async(function* () {
|
||||||
|
let propsView = this._propsView;
|
||||||
|
let node = this._currentNode;
|
||||||
|
propsView.empty();
|
||||||
|
|
||||||
|
let audioParamsScope = propsView.addScope("AudioParams");
|
||||||
|
let props = yield node.getParams();
|
||||||
|
|
||||||
|
// Disable AudioParams VariableView expansion
|
||||||
|
// when there are no props i.e. AudioDestinationNode
|
||||||
|
this._togglePropertiesView(!!props.length);
|
||||||
|
|
||||||
|
props.forEach(({ param, value, flags }) => {
|
||||||
|
let descriptor = {
|
||||||
|
value: value,
|
||||||
|
writable: !flags || !flags.readonly,
|
||||||
|
};
|
||||||
|
let item = audioParamsScope.addItem(param, descriptor);
|
||||||
|
|
||||||
|
// No items should currently display a dropdown
|
||||||
|
item.twisty = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
audioParamsScope.expanded = true;
|
||||||
|
|
||||||
|
window.emit(EVENTS.UI_PROPERTIES_TAB_RENDERED, node.id);
|
||||||
|
}),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggles the display of the "empty" properties view when
|
||||||
|
* node has no properties to display.
|
||||||
|
*/
|
||||||
|
_togglePropertiesView: function (show) {
|
||||||
|
let propsView = $("#properties-content");
|
||||||
|
let emptyView = $("#properties-empty");
|
||||||
|
(show ? propsView : emptyView).removeAttribute("hidden");
|
||||||
|
(show ? emptyView : propsView).setAttribute("hidden", "true");
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the scope for AudioParams in the
|
||||||
|
* VariablesView.
|
||||||
|
*
|
||||||
|
* @return Scope
|
||||||
|
*/
|
||||||
|
_getAudioPropertiesScope: function () {
|
||||||
|
return this._propsView.getScopeAtIndex(0);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event handlers
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the inspector view determines a node is selected.
|
||||||
|
*/
|
||||||
|
_onNodeSet: function (_, id) {
|
||||||
|
this._setAudioNode(gAudioNodes.get(id));
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executed when an audio prop is changed in the UI.
|
||||||
|
*/
|
||||||
|
_onEval: Task.async(function* (variable, value) {
|
||||||
|
let ownerScope = variable.ownerView;
|
||||||
|
let node = this._currentNode;
|
||||||
|
let propName = variable.name;
|
||||||
|
let error;
|
||||||
|
|
||||||
|
if (!variable._initialDescriptor.writable) {
|
||||||
|
error = new Error("Variable " + propName + " is not writable.");
|
||||||
|
} else {
|
||||||
|
// Cast value to proper type
|
||||||
|
try {
|
||||||
|
let number = parseFloat(value);
|
||||||
|
if (!isNaN(number)) {
|
||||||
|
value = number;
|
||||||
|
} else {
|
||||||
|
value = JSON.parse(value);
|
||||||
|
}
|
||||||
|
error = yield node.actor.setParam(propName, value);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO figure out how to handle and display set prop errors
|
||||||
|
// and enable `test/brorwser_wa_properties-view-edit.js`
|
||||||
|
// Bug 994258
|
||||||
|
if (!error) {
|
||||||
|
ownerScope.get(propName).setGrip(value);
|
||||||
|
window.emit(EVENTS.UI_SET_PARAM, node.id, propName, value);
|
||||||
|
} else {
|
||||||
|
window.emit(EVENTS.UI_SET_PARAM_ERROR, node.id, propName, value);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
|
@ -25,6 +25,8 @@
|
||||||
<script type="application/javascript" src="webaudioeditor/views/utils.js"/>
|
<script type="application/javascript" src="webaudioeditor/views/utils.js"/>
|
||||||
<script type="application/javascript" src="webaudioeditor/views/context.js"/>
|
<script type="application/javascript" src="webaudioeditor/views/context.js"/>
|
||||||
<script type="application/javascript" src="webaudioeditor/views/inspector.js"/>
|
<script type="application/javascript" src="webaudioeditor/views/inspector.js"/>
|
||||||
|
<script type="application/javascript" src="webaudioeditor/views/properties.js"/>
|
||||||
|
<script type="application/javascript" src="webaudioeditor/views/automation.js"/>
|
||||||
|
|
||||||
<vbox class="theme-body" flex="1">
|
<vbox class="theme-body" flex="1">
|
||||||
<hbox id="reload-notice"
|
<hbox id="reload-notice"
|
||||||
|
@ -75,7 +77,7 @@
|
||||||
</vbox>
|
</vbox>
|
||||||
</box>
|
</box>
|
||||||
</hbox>
|
</hbox>
|
||||||
<splitter class="devtools-side-splitter"/>
|
<splitter id="inspector-splitter" class="devtools-side-splitter"/>
|
||||||
<vbox id="web-audio-inspector" hidden="true">
|
<vbox id="web-audio-inspector" hidden="true">
|
||||||
<hbox class="devtools-toolbar">
|
<hbox class="devtools-toolbar">
|
||||||
<label id="web-audio-inspector-title" value="&webAudioEditorUI.inspectorTitle;"></label>
|
<label id="web-audio-inspector-title" value="&webAudioEditorUI.inspectorTitle;"></label>
|
||||||
|
@ -90,16 +92,39 @@
|
||||||
<tabs>
|
<tabs>
|
||||||
<tab id="properties-tab"
|
<tab id="properties-tab"
|
||||||
label="&webAudioEditorUI.tab.properties;"/>
|
label="&webAudioEditorUI.tab.properties;"/>
|
||||||
|
<tab id="automation-tab"
|
||||||
|
label="&webAudioEditorUI.tab.automation;"/>
|
||||||
</tabs>
|
</tabs>
|
||||||
<tabpanels flex="1">
|
<tabpanels flex="1">
|
||||||
|
<!-- Properties Panel -->
|
||||||
<tabpanel id="properties-tabpanel"
|
<tabpanel id="properties-tabpanel"
|
||||||
class="tabpanel-content">
|
class="tabpanel-content">
|
||||||
<vbox id="properties-tabpanel-content" flex="1">
|
<vbox id="properties-content" flex="1" hidden="true">
|
||||||
</vbox>
|
</vbox>
|
||||||
<vbox id="properties-tabpanel-content-empty" flex="1" hidden="true">
|
<vbox id="properties-empty" flex="1" hidden="true">
|
||||||
<label value="&webAudioEditorUI.propertiesEmpty;"></label>
|
<label value="&webAudioEditorUI.propertiesEmpty;"></label>
|
||||||
</vbox>
|
</vbox>
|
||||||
</tabpanel>
|
</tabpanel>
|
||||||
|
|
||||||
|
<!-- Automation Panel -->
|
||||||
|
<tabpanel id="automation-tabpanel"
|
||||||
|
class="tabpanel-content">
|
||||||
|
<vbox id="automation-content" flex="1" hidden="true">
|
||||||
|
<toolbar id="automation-param-toolbar" class="devtools-toolbar">
|
||||||
|
<hbox id="automation-param-toolbar-buttons" class="devtools-toolbarbutton-group">
|
||||||
|
</hbox>
|
||||||
|
</toolbar>
|
||||||
|
<box id="automation-graph-container" flex="1">
|
||||||
|
<canvas id="automation-graph"></canvas>
|
||||||
|
</box>
|
||||||
|
<vbox id="automation-no-events" flex="1" hidden="true">
|
||||||
|
<label value="&webAudioEditorUI.automationNoEvents;"></label>
|
||||||
|
</vbox>
|
||||||
|
</vbox>
|
||||||
|
<vbox id="automation-empty" flex="1" hidden="true">
|
||||||
|
<label value="&webAudioEditorUI.automationEmpty;"></label>
|
||||||
|
</vbox>
|
||||||
|
</tabpanel>
|
||||||
</tabpanels>
|
</tabpanels>
|
||||||
</tabbox>
|
</tabbox>
|
||||||
</deck>
|
</deck>
|
||||||
|
|
|
@ -118,7 +118,7 @@ let UI = {
|
||||||
this._telemetry.toolClosed("webide");
|
this._telemetry.toolClosed("webide");
|
||||||
},
|
},
|
||||||
|
|
||||||
canWindowClose: function() {
|
canCloseProject: function() {
|
||||||
if (this.projecteditor) {
|
if (this.projecteditor) {
|
||||||
return this.projecteditor.confirmUnsaved();
|
return this.projecteditor.confirmUnsaved();
|
||||||
}
|
}
|
||||||
|
@ -155,6 +155,11 @@ let UI = {
|
||||||
this.updateCommands();
|
this.updateCommands();
|
||||||
this.updateConnectionTelemetry();
|
this.updateConnectionTelemetry();
|
||||||
break;
|
break;
|
||||||
|
case "before-project":
|
||||||
|
if (!this.canCloseProject()) {
|
||||||
|
details.cancel();
|
||||||
|
}
|
||||||
|
break;
|
||||||
case "project":
|
case "project":
|
||||||
this._updatePromise = Task.spawn(function() {
|
this._updatePromise = Task.spawn(function() {
|
||||||
UI.updateTitle();
|
UI.updateTitle();
|
||||||
|
@ -971,7 +976,7 @@ let UI = {
|
||||||
|
|
||||||
let Cmds = {
|
let Cmds = {
|
||||||
quit: function() {
|
quit: function() {
|
||||||
if (UI.canWindowClose()) {
|
if (UI.canCloseProject()) {
|
||||||
window.close();
|
window.close();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
<?xml-stylesheet href="chrome://global/skin/global.css"?>
|
<?xml-stylesheet href="chrome://global/skin/global.css"?>
|
||||||
<?xml-stylesheet href="chrome://webide/skin/webide.css"?>
|
<?xml-stylesheet href="chrome://webide/skin/webide.css"?>
|
||||||
|
|
||||||
<window id="webide" onclose="return UI.canWindowClose();"
|
<window id="webide" onclose="return UI.canCloseProject();"
|
||||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||||
title="&windowTitle;"
|
title="&windowTitle;"
|
||||||
|
|
|
@ -276,6 +276,13 @@ let AppManager = exports.AppManager = {
|
||||||
// A regular comparison still sees a difference when equal in some cases
|
// A regular comparison still sees a difference when equal in some cases
|
||||||
if (JSON.stringify(this._selectedProject) !==
|
if (JSON.stringify(this._selectedProject) !==
|
||||||
JSON.stringify(value)) {
|
JSON.stringify(value)) {
|
||||||
|
|
||||||
|
let cancelled = false;
|
||||||
|
this.update("before-project", { cancel: () => { cancelled = true; } });
|
||||||
|
if (cancelled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this._selectedProject = value;
|
this._selectedProject = value;
|
||||||
|
|
||||||
// Clear out tab store's selected state, if any
|
// Clear out tab store's selected state, if any
|
||||||
|
@ -303,6 +310,10 @@ let AppManager = exports.AppManager = {
|
||||||
removeSelectedProject: function() {
|
removeSelectedProject: function() {
|
||||||
let location = this.selectedProject.location;
|
let location = this.selectedProject.location;
|
||||||
AppManager.selectedProject = null;
|
AppManager.selectedProject = null;
|
||||||
|
// If the user cancels the removeProject operation, don't remove the project
|
||||||
|
if (AppManager.selectedProject != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
return AppProjects.remove(location);
|
return AppProjects.remove(location);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -745,7 +745,10 @@
|
||||||
@RESPATH@/res/fonts/*
|
@RESPATH@/res/fonts/*
|
||||||
@RESPATH@/res/dtd/*
|
@RESPATH@/res/dtd/*
|
||||||
@RESPATH@/res/html/*
|
@RESPATH@/res/html/*
|
||||||
|
#if defined(XP_MACOSX) || defined(XP_WIN)
|
||||||
|
; For SafariProfileMigrator.js.
|
||||||
@RESPATH@/res/langGroups.properties
|
@RESPATH@/res/langGroups.properties
|
||||||
|
#endif
|
||||||
@RESPATH@/res/language.properties
|
@RESPATH@/res/language.properties
|
||||||
@RESPATH@/res/entityTables/*
|
@RESPATH@/res/entityTables/*
|
||||||
#ifdef XP_MACOSX
|
#ifdef XP_MACOSX
|
||||||
|
|
|
@ -13,13 +13,13 @@
|
||||||
|
|
||||||
# LOCALIZATION NOTE (projecteditor.confirmUnsavedTitle):
|
# LOCALIZATION NOTE (projecteditor.confirmUnsavedTitle):
|
||||||
# This string is displayed as as the title of the confirm prompt that checks
|
# This string is displayed as as the title of the confirm prompt that checks
|
||||||
# to make sure if the project editor can be closed without saving changes
|
# to make sure if the project can be closed/switched without saving changes
|
||||||
projecteditor.confirmUnsavedTitle=Unsaved Changes
|
projecteditor.confirmUnsavedTitle=Unsaved Changes
|
||||||
|
|
||||||
# LOCALIZATION NOTE (projecteditor.confirmUnsavedLabel):
|
# LOCALIZATION NOTE (projecteditor.confirmUnsavedLabel2):
|
||||||
# This string is displayed as the message of the confirm prompt that checks
|
# This string is displayed as the message of the confirm prompt that checks
|
||||||
# to make sure if the project editor can be closed without saving changes
|
# to make sure if the project can be closed/switched without saving changes
|
||||||
projecteditor.confirmUnsavedLabel=You have unsaved changes that will be lost if you exit. Are you sure you want to continue?
|
projecteditor.confirmUnsavedLabel2=You have unsaved changes that will be lost. Are you sure you want to continue?
|
||||||
|
|
||||||
# LOCALIZATION NOTE (projecteditor.deleteLabel):
|
# LOCALIZATION NOTE (projecteditor.deleteLabel):
|
||||||
# This string is displayed as a context menu item for allowing the selected
|
# This string is displayed as a context menu item for allowing the selected
|
||||||
|
|
|
@ -27,6 +27,10 @@
|
||||||
- for the properties tab view. -->
|
- for the properties tab view. -->
|
||||||
<!ENTITY webAudioEditorUI.tab.properties "Parameters">
|
<!ENTITY webAudioEditorUI.tab.properties "Parameters">
|
||||||
|
|
||||||
|
<!-- LOCALIZATION NOTE (webAudioEditorUI.tab.automation): This is the label shown
|
||||||
|
- for the automation tab view. -->
|
||||||
|
<!ENTITY webAudioEditorUI.tab.automation "Automation">
|
||||||
|
|
||||||
<!-- LOCALIZATION NOTE (webAudioEditorUI.inspectorTitle): This is the title for the
|
<!-- LOCALIZATION NOTE (webAudioEditorUI.inspectorTitle): This is the title for the
|
||||||
- AudioNode inspector view. -->
|
- AudioNode inspector view. -->
|
||||||
<!ENTITY webAudioEditorUI.inspectorTitle "AudioNode Inspector">
|
<!ENTITY webAudioEditorUI.inspectorTitle "AudioNode Inspector">
|
||||||
|
@ -38,3 +42,12 @@
|
||||||
<!-- LOCALIZATION NOTE (webAudioEditorUI.propertiesEmpty): This is the title for the
|
<!-- LOCALIZATION NOTE (webAudioEditorUI.propertiesEmpty): This is the title for the
|
||||||
- AudioNode inspector view properties tab empty message. -->
|
- AudioNode inspector view properties tab empty message. -->
|
||||||
<!ENTITY webAudioEditorUI.propertiesEmpty "Node does not have any properties.">
|
<!ENTITY webAudioEditorUI.propertiesEmpty "Node does not have any properties.">
|
||||||
|
|
||||||
|
<!-- LOCALIZATION NOTE (webAudioEditorUI.automationEmpty): This is the title for the
|
||||||
|
- AudioNode inspector view automation tab empty message. -->
|
||||||
|
<!ENTITY webAudioEditorUI.automationEmpty "Node does not have any AudioParams.">
|
||||||
|
|
||||||
|
<!-- LOCALIZATION NOTE (webAudioEditorUI.automationNoEvents): This is the title for the
|
||||||
|
- AudioNode inspector view automation tab message when there are no automation
|
||||||
|
- events. -->
|
||||||
|
<!ENTITY webAudioEditorUI.automationNoEvents "AudioParam does not have any automation events.">
|
||||||
|
|
|
@ -362,3 +362,33 @@
|
||||||
.marker-details-duration {
|
.marker-details-duration {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Recording items */
|
||||||
|
|
||||||
|
.recording-item {
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recording-item-title {
|
||||||
|
font-size: 110%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recording-item-footer {
|
||||||
|
padding-top: 4px;
|
||||||
|
font-size: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recording-item-save {
|
||||||
|
text-decoration: underline;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recording-item-duration,
|
||||||
|
.recording-item-save {
|
||||||
|
color: var(--theme-body-color-alt);
|
||||||
|
}
|
||||||
|
|
||||||
|
#recordings-list .selected label {
|
||||||
|
/* Text inside a selected item should not be custom colored. */
|
||||||
|
color: inherit !important;
|
||||||
|
}
|
||||||
|
|
|
@ -145,6 +145,20 @@ text {
|
||||||
-moz-image-region: rect(0px,32px,16px,16px);
|
-moz-image-region: rect(0px,32px,16px,16px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Automation Styles
|
||||||
|
*/
|
||||||
|
|
||||||
|
#automation-param-toolbar .automation-param-button[selected] {
|
||||||
|
color: var(--theme-selection-color);
|
||||||
|
background-color: var(--theme-selection-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
#automation-graph {
|
||||||
|
overflow: hidden;
|
||||||
|
-moz-box-flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
@media (min-resolution: 2dppx) {
|
@media (min-resolution: 2dppx) {
|
||||||
#inspector-pane-toggle {
|
#inspector-pane-toggle {
|
||||||
list-style-image: url(debugger-collapse@2x.png);
|
list-style-image: url(debugger-collapse@2x.png);
|
||||||
|
|
|
@ -9083,6 +9083,8 @@ if test -z "$MOZ_NATIVE_JEMALLOC" -a "$MOZ_MEMORY" && test -n "$MOZ_JEMALLOC3" -
|
||||||
if test "$CROSS_COMPILE"; then
|
if test "$CROSS_COMPILE"; then
|
||||||
ac_configure_args="$ac_configure_args je_cv_static_page_shift=12"
|
ac_configure_args="$ac_configure_args je_cv_static_page_shift=12"
|
||||||
fi
|
fi
|
||||||
|
# Force disable DSS support in jemalloc.
|
||||||
|
ac_configure_args="$ac_configure_args ac_cv_func_sbrk=false"
|
||||||
|
|
||||||
if ! test -e memory/jemalloc; then
|
if ! test -e memory/jemalloc; then
|
||||||
mkdir -p memory/jemalloc
|
mkdir -p memory/jemalloc
|
||||||
|
|
|
@ -51,11 +51,10 @@ static RedirEntry kRedirMap[] = {
|
||||||
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
|
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
|
||||||
nsIAboutModule::ALLOW_SCRIPT |
|
nsIAboutModule::ALLOW_SCRIPT |
|
||||||
nsIAboutModule::HIDE_FROM_ABOUTABOUT },
|
nsIAboutModule::HIDE_FROM_ABOUTABOUT },
|
||||||
{ "compartments", "chrome://global/content/aboutCompartments.xhtml",
|
|
||||||
nsIAboutModule::ALLOW_SCRIPT |
|
|
||||||
nsIAboutModule::HIDE_FROM_ABOUTABOUT },
|
|
||||||
{ "memory", "chrome://global/content/aboutMemory.xhtml",
|
{ "memory", "chrome://global/content/aboutMemory.xhtml",
|
||||||
nsIAboutModule::ALLOW_SCRIPT },
|
nsIAboutModule::ALLOW_SCRIPT },
|
||||||
|
{ "compartments", "chrome://global/content/aboutCompartments.xhtml",
|
||||||
|
nsIAboutModule::ALLOW_SCRIPT },
|
||||||
{ "addons", "chrome://mozapps/content/extensions/extensions.xul",
|
{ "addons", "chrome://mozapps/content/extensions/extensions.xul",
|
||||||
nsIAboutModule::ALLOW_SCRIPT },
|
nsIAboutModule::ALLOW_SCRIPT },
|
||||||
{ "newaddon", "chrome://mozapps/content/extensions/newaddon.xul",
|
{ "newaddon", "chrome://mozapps/content/extensions/newaddon.xul",
|
||||||
|
|
|
@ -168,6 +168,7 @@ const mozilla::Module::ContractIDEntry kDocShellContracts[] = {
|
||||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "neterror", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
|
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "neterror", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
|
||||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "compartments", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
|
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "compartments", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
|
||||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "memory", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
|
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "memory", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
|
||||||
|
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "compartments", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
|
||||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "addons", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
|
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "addons", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
|
||||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "newaddon", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
|
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "newaddon", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
|
||||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "support", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
|
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "support", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
|
||||||
|
|
|
@ -33,7 +33,7 @@ DOMCursor::Reset()
|
||||||
MOZ_ASSERT(!mFinished);
|
MOZ_ASSERT(!mFinished);
|
||||||
|
|
||||||
// Reset the request state so we can FireSuccess() again.
|
// Reset the request state so we can FireSuccess() again.
|
||||||
mResult = JSVAL_VOID;
|
mResult.setUndefined();
|
||||||
mDone = false;
|
mDone = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ DOMCursor::Continue(ErrorResult& aRv)
|
||||||
MOZ_ASSERT(mCallback, "If you're creating your own cursor class with no callback, you should override Continue()");
|
MOZ_ASSERT(mCallback, "If you're creating your own cursor class with no callback, you should override Continue()");
|
||||||
|
|
||||||
// We need to have a result here because we must be in a 'success' state.
|
// We need to have a result here because we must be in a 'success' state.
|
||||||
if (mResult == JSVAL_VOID) {
|
if (mResult.isUndefined()) {
|
||||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ using mozilla::AutoSafeJSContext;
|
||||||
DOMRequest::DOMRequest(nsPIDOMWindow* aWindow)
|
DOMRequest::DOMRequest(nsPIDOMWindow* aWindow)
|
||||||
: DOMEventTargetHelper(aWindow->IsInnerWindow() ?
|
: DOMEventTargetHelper(aWindow->IsInnerWindow() ?
|
||||||
aWindow : aWindow->GetCurrentInnerWindow())
|
aWindow : aWindow->GetCurrentInnerWindow())
|
||||||
, mResult(JSVAL_VOID)
|
, mResult(JS::UndefinedValue())
|
||||||
, mDone(false)
|
, mDone(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DOMRequest,
|
||||||
DOMEventTargetHelper)
|
DOMEventTargetHelper)
|
||||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mError)
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mError)
|
||||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromise)
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromise)
|
||||||
tmp->mResult = JSVAL_VOID;
|
tmp->mResult.setUndefined();
|
||||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||||
|
|
||||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(DOMRequest,
|
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(DOMRequest,
|
||||||
|
@ -111,7 +111,7 @@ DOMRequest::FireSuccess(JS::Handle<JS::Value> aResult)
|
||||||
{
|
{
|
||||||
NS_ASSERTION(!mDone, "mDone shouldn't have been set to true already!");
|
NS_ASSERTION(!mDone, "mDone shouldn't have been set to true already!");
|
||||||
NS_ASSERTION(!mError, "mError shouldn't have been set!");
|
NS_ASSERTION(!mError, "mError shouldn't have been set!");
|
||||||
NS_ASSERTION(mResult == JSVAL_VOID, "mResult shouldn't have been set!");
|
NS_ASSERTION(mResult.isUndefined(), "mResult shouldn't have been set!");
|
||||||
|
|
||||||
mDone = true;
|
mDone = true;
|
||||||
if (aResult.isGCThing()) {
|
if (aResult.isGCThing()) {
|
||||||
|
@ -131,7 +131,7 @@ DOMRequest::FireError(const nsAString& aError)
|
||||||
{
|
{
|
||||||
NS_ASSERTION(!mDone, "mDone shouldn't have been set to true already!");
|
NS_ASSERTION(!mDone, "mDone shouldn't have been set to true already!");
|
||||||
NS_ASSERTION(!mError, "mError shouldn't have been set!");
|
NS_ASSERTION(!mError, "mError shouldn't have been set!");
|
||||||
NS_ASSERTION(mResult == JSVAL_VOID, "mResult shouldn't have been set!");
|
NS_ASSERTION(mResult.isUndefined(), "mResult shouldn't have been set!");
|
||||||
|
|
||||||
mDone = true;
|
mDone = true;
|
||||||
mError = new DOMError(GetOwner(), aError);
|
mError = new DOMError(GetOwner(), aError);
|
||||||
|
@ -148,7 +148,7 @@ DOMRequest::FireError(nsresult aError)
|
||||||
{
|
{
|
||||||
NS_ASSERTION(!mDone, "mDone shouldn't have been set to true already!");
|
NS_ASSERTION(!mDone, "mDone shouldn't have been set to true already!");
|
||||||
NS_ASSERTION(!mError, "mError shouldn't have been set!");
|
NS_ASSERTION(!mError, "mError shouldn't have been set!");
|
||||||
NS_ASSERTION(mResult == JSVAL_VOID, "mResult shouldn't have been set!");
|
NS_ASSERTION(mResult.isUndefined(), "mResult shouldn't have been set!");
|
||||||
|
|
||||||
mDone = true;
|
mDone = true;
|
||||||
mError = new DOMError(GetOwner(), aError);
|
mError = new DOMError(GetOwner(), aError);
|
||||||
|
@ -165,7 +165,7 @@ DOMRequest::FireDetailedError(DOMError* aError)
|
||||||
{
|
{
|
||||||
NS_ASSERTION(!mDone, "mDone shouldn't have been set to true already!");
|
NS_ASSERTION(!mDone, "mDone shouldn't have been set to true already!");
|
||||||
NS_ASSERTION(!mError, "mError shouldn't have been set!");
|
NS_ASSERTION(!mError, "mError shouldn't have been set!");
|
||||||
NS_ASSERTION(mResult == JSVAL_VOID, "mResult shouldn't have been set!");
|
NS_ASSERTION(mResult.isUndefined(), "mResult shouldn't have been set!");
|
||||||
NS_ASSERTION(aError, "No detailed error provided");
|
NS_ASSERTION(aError, "No detailed error provided");
|
||||||
|
|
||||||
mDone = true;
|
mDone = true;
|
||||||
|
|
|
@ -58,7 +58,7 @@ public:
|
||||||
|
|
||||||
void GetResult(JSContext*, JS::MutableHandle<JS::Value> aRetval) const
|
void GetResult(JSContext*, JS::MutableHandle<JS::Value> aRetval) const
|
||||||
{
|
{
|
||||||
NS_ASSERTION(mDone || mResult == JSVAL_VOID,
|
NS_ASSERTION(mDone || mResult.isUndefined(),
|
||||||
"Result should be undefined when pending");
|
"Result should be undefined when pending");
|
||||||
JS::ExposeValueToActiveJS(mResult);
|
JS::ExposeValueToActiveJS(mResult);
|
||||||
aRetval.set(mResult);
|
aRetval.set(mResult);
|
||||||
|
|
|
@ -1075,7 +1075,7 @@ nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
|
||||||
thisValue.setObject(*object);
|
thisValue.setObject(*object);
|
||||||
}
|
}
|
||||||
|
|
||||||
JS::Rooted<JS::Value> rval(cx, JSVAL_VOID);
|
JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue());
|
||||||
JS::Rooted<JS::Value> argv(cx, JS::ObjectValue(*param));
|
JS::Rooted<JS::Value> argv(cx, JS::ObjectValue(*param));
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
@ -302,7 +302,7 @@ nsXMLHttpRequest::nsXMLHttpRequest()
|
||||||
mIsAnon(false),
|
mIsAnon(false),
|
||||||
mFirstStartRequestSeen(false),
|
mFirstStartRequestSeen(false),
|
||||||
mInLoadProgressEvent(false),
|
mInLoadProgressEvent(false),
|
||||||
mResultJSON(JSVAL_VOID),
|
mResultJSON(JS::UndefinedValue()),
|
||||||
mResultArrayBuffer(nullptr),
|
mResultArrayBuffer(nullptr),
|
||||||
mIsMappedArrayBuffer(false),
|
mIsMappedArrayBuffer(false),
|
||||||
mXPCOMifier(nullptr)
|
mXPCOMifier(nullptr)
|
||||||
|
@ -324,7 +324,7 @@ nsXMLHttpRequest::~nsXMLHttpRequest()
|
||||||
NS_ABORT_IF_FALSE(!(mState & XML_HTTP_REQUEST_SYNCLOOPING), "we rather crash than hang");
|
NS_ABORT_IF_FALSE(!(mState & XML_HTTP_REQUEST_SYNCLOOPING), "we rather crash than hang");
|
||||||
mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
|
mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
|
||||||
|
|
||||||
mResultJSON = JSVAL_VOID;
|
mResultJSON.setUndefined();
|
||||||
mResultArrayBuffer = nullptr;
|
mResultArrayBuffer = nullptr;
|
||||||
mozilla::DropJSObjects(this);
|
mozilla::DropJSObjects(this);
|
||||||
}
|
}
|
||||||
|
@ -429,7 +429,7 @@ nsXMLHttpRequest::ResetResponse()
|
||||||
mBlobSet = nullptr;
|
mBlobSet = nullptr;
|
||||||
mResultArrayBuffer = nullptr;
|
mResultArrayBuffer = nullptr;
|
||||||
mArrayBufferBuilder.reset();
|
mArrayBufferBuilder.reset();
|
||||||
mResultJSON = JSVAL_VOID;
|
mResultJSON.setUndefined();
|
||||||
mDataAvailable = 0;
|
mDataAvailable = 0;
|
||||||
mLoadTransferred = 0;
|
mLoadTransferred = 0;
|
||||||
mResponseBodyDecodedPos = 0;
|
mResponseBodyDecodedPos = 0;
|
||||||
|
@ -489,7 +489,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsXMLHttpRequest,
|
||||||
nsXHREventTarget)
|
nsXHREventTarget)
|
||||||
tmp->mResultArrayBuffer = nullptr;
|
tmp->mResultArrayBuffer = nullptr;
|
||||||
tmp->mArrayBufferBuilder.reset();
|
tmp->mArrayBufferBuilder.reset();
|
||||||
tmp->mResultJSON = JSVAL_VOID;
|
tmp->mResultJSON.setUndefined();
|
||||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext)
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext)
|
||||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mChannel)
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mChannel)
|
||||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mResponseXML)
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mResponseXML)
|
||||||
|
|
|
@ -193,7 +193,7 @@ ErrorResult::ThrowJSException(JSContext* cx, JS::Handle<JS::Value> exn)
|
||||||
// Make sure mJSException is initialized _before_ we try to root it. But
|
// Make sure mJSException is initialized _before_ we try to root it. But
|
||||||
// don't set it to exn yet, because we don't want to do that until after we
|
// don't set it to exn yet, because we don't want to do that until after we
|
||||||
// root.
|
// root.
|
||||||
mJSException = JS::UndefinedValue();
|
mJSException.setUndefined();
|
||||||
if (!js::AddRawValueRoot(cx, &mJSException, "ErrorResult::mJSException")) {
|
if (!js::AddRawValueRoot(cx, &mJSException, "ErrorResult::mJSException")) {
|
||||||
// Don't use NS_ERROR_DOM_JS_EXCEPTION, because that indicates we have
|
// Don't use NS_ERROR_DOM_JS_EXCEPTION, because that indicates we have
|
||||||
// in fact rooted mJSException.
|
// in fact rooted mJSException.
|
||||||
|
|
|
@ -7877,7 +7877,7 @@ class CGGenericSetter(CGAbstractBindingMethod):
|
||||||
if (!setter(cx, obj, self, JSJitSetterCallArgs(args))) {
|
if (!setter(cx, obj, self, JSJitSetterCallArgs(args))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
args.rval().set(JSVAL_VOID);
|
args.rval().setUndefined();
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
AssertReturnTypeMatchesJitinfo(info, args.rval());
|
AssertReturnTypeMatchesJitinfo(info, args.rval());
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -64,7 +64,7 @@ BluetoothReplyRunnable::Run()
|
||||||
nsresult rv;
|
nsresult rv;
|
||||||
|
|
||||||
AutoSafeJSContext cx;
|
AutoSafeJSContext cx;
|
||||||
JS::Rooted<JS::Value> v(cx, JSVAL_VOID);
|
JS::Rooted<JS::Value> v(cx, JS::UndefinedValue());
|
||||||
|
|
||||||
if (mReply->type() != BluetoothReply::TBluetoothReplySuccess) {
|
if (mReply->type() != BluetoothReply::TBluetoothReplySuccess) {
|
||||||
rv = FireReply(v);
|
rv = FireReply(v);
|
||||||
|
|
|
@ -99,7 +99,7 @@ BluetoothReplyRunnable::Run()
|
||||||
MOZ_ASSERT(mReply);
|
MOZ_ASSERT(mReply);
|
||||||
|
|
||||||
AutoSafeJSContext cx;
|
AutoSafeJSContext cx;
|
||||||
JS::Rooted<JS::Value> v(cx, JSVAL_VOID);
|
JS::Rooted<JS::Value> v(cx, JS::UndefinedValue());
|
||||||
|
|
||||||
nsresult rv;
|
nsresult rv;
|
||||||
if (mReply->type() != BluetoothReply::TBluetoothReplySuccess) {
|
if (mReply->type() != BluetoothReply::TBluetoothReplySuccess) {
|
||||||
|
|
|
@ -2270,10 +2270,10 @@ nsDOMDeviceStorageCursor::Continue(ErrorResult& aRv)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mResult != JSVAL_VOID) {
|
if (!mResult.isUndefined()) {
|
||||||
// We call onsuccess multiple times. Clear the last
|
// We call onsuccess multiple times. Clear the last
|
||||||
// result.
|
// result.
|
||||||
mResult = JSVAL_VOID;
|
mResult.setUndefined();
|
||||||
mDone = false;
|
mDone = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ namespace dom {
|
||||||
NS_IMPL_CYCLE_COLLECTION_CLASS(MessageEvent)
|
NS_IMPL_CYCLE_COLLECTION_CLASS(MessageEvent)
|
||||||
|
|
||||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MessageEvent, Event)
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MessageEvent, Event)
|
||||||
tmp->mData = JSVAL_VOID;
|
tmp->mData.setUndefined();
|
||||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindowSource)
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindowSource)
|
||||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPortSource)
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPortSource)
|
||||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPorts)
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPorts)
|
||||||
|
@ -46,13 +46,13 @@ MessageEvent::MessageEvent(EventTarget* aOwner,
|
||||||
nsPresContext* aPresContext,
|
nsPresContext* aPresContext,
|
||||||
WidgetEvent* aEvent)
|
WidgetEvent* aEvent)
|
||||||
: Event(aOwner, aPresContext, aEvent)
|
: Event(aOwner, aPresContext, aEvent)
|
||||||
, mData(JSVAL_VOID)
|
, mData(JS::UndefinedValue())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageEvent::~MessageEvent()
|
MessageEvent::~MessageEvent()
|
||||||
{
|
{
|
||||||
mData = JSVAL_VOID;
|
mData.setUndefined();
|
||||||
DropJSObjects(this);
|
DropJSObjects(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,18 +2,18 @@
|
||||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||||
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
|
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
|
||||||
<!--
|
<!--
|
||||||
https://bugzilla.mozilla.org/show_bug.cgi?id=67949
|
https://bugzilla.mozilla.org/show_bug.cgi?id=679494
|
||||||
-->
|
-->
|
||||||
<window title="Mozilla Bug 67949" onload="doTest();"
|
<window title="Mozilla Bug 679494" onload="doTest();"
|
||||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||||
|
|
||||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
|
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
|
||||||
|
|
||||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=67949">Mozilla Bug 67949</a>
|
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=679494">Mozilla Bug 679494</a>
|
||||||
<p id="display"></p>
|
<p id="display"></p>
|
||||||
<div id="content" style="display: none">
|
<div id="content" style="display: none">
|
||||||
<iframe id="contentframe" src="http://mochi.test:8888/tests/content/event/test/file_679494.html"></iframe>
|
<iframe id="contentframe" src="http://mochi.test:8888/tests/dom/events/test/file_bug679494.html"></iframe>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|
|
@ -35,9 +35,9 @@ IDBCursor::IDBCursor(Type aType,
|
||||||
, mSourceIndex(aBackgroundActor->GetIndex())
|
, mSourceIndex(aBackgroundActor->GetIndex())
|
||||||
, mTransaction(mRequest->GetTransaction())
|
, mTransaction(mRequest->GetTransaction())
|
||||||
, mScriptOwner(mTransaction->Database()->GetScriptOwner())
|
, mScriptOwner(mTransaction->Database()->GetScriptOwner())
|
||||||
, mCachedKey(JSVAL_VOID)
|
, mCachedKey(JS::UndefinedValue())
|
||||||
, mCachedPrimaryKey(JSVAL_VOID)
|
, mCachedPrimaryKey(JS::UndefinedValue())
|
||||||
, mCachedValue(JSVAL_VOID)
|
, mCachedValue(JS::UndefinedValue())
|
||||||
, mKey(aKey)
|
, mKey(aKey)
|
||||||
, mType(aType)
|
, mType(aType)
|
||||||
, mDirection(aBackgroundActor->GetDirection())
|
, mDirection(aBackgroundActor->GetDirection())
|
||||||
|
|
|
@ -48,7 +48,7 @@ GenerateRequest(IDBIndex* aIndex)
|
||||||
|
|
||||||
IDBIndex::IDBIndex(IDBObjectStore* aObjectStore, const IndexMetadata* aMetadata)
|
IDBIndex::IDBIndex(IDBObjectStore* aObjectStore, const IndexMetadata* aMetadata)
|
||||||
: mObjectStore(aObjectStore)
|
: mObjectStore(aObjectStore)
|
||||||
, mCachedKeyPath(JSVAL_VOID)
|
, mCachedKeyPath(JS::UndefinedValue())
|
||||||
, mMetadata(aMetadata)
|
, mMetadata(aMetadata)
|
||||||
, mId(aMetadata->id())
|
, mId(aMetadata->id())
|
||||||
, mRooted(false)
|
, mRooted(false)
|
||||||
|
@ -63,7 +63,7 @@ IDBIndex::~IDBIndex()
|
||||||
AssertIsOnOwningThread();
|
AssertIsOnOwningThread();
|
||||||
|
|
||||||
if (mRooted) {
|
if (mRooted) {
|
||||||
mCachedKeyPath = JSVAL_VOID;
|
mCachedKeyPath.setUndefined();
|
||||||
mozilla::DropJSObjects(this);
|
mozilla::DropJSObjects(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -559,7 +559,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBIndex)
|
||||||
|
|
||||||
// Don't unlink mObjectStore!
|
// Don't unlink mObjectStore!
|
||||||
|
|
||||||
tmp->mCachedKeyPath = JSVAL_VOID;
|
tmp->mCachedKeyPath.setUndefined();
|
||||||
|
|
||||||
if (tmp->mRooted) {
|
if (tmp->mRooted) {
|
||||||
mozilla::DropJSObjects(tmp);
|
mozilla::DropJSObjects(tmp);
|
||||||
|
|
|
@ -44,8 +44,8 @@ IDBKeyRange::IDBKeyRange(nsISupports* aGlobal,
|
||||||
bool aUpperOpen,
|
bool aUpperOpen,
|
||||||
bool aIsOnly)
|
bool aIsOnly)
|
||||||
: mGlobal(aGlobal)
|
: mGlobal(aGlobal)
|
||||||
, mCachedLowerVal(JSVAL_VOID)
|
, mCachedLowerVal(JS::UndefinedValue())
|
||||||
, mCachedUpperVal(JSVAL_VOID)
|
, mCachedUpperVal(JS::UndefinedValue())
|
||||||
, mLowerOpen(aLowerOpen)
|
, mLowerOpen(aLowerOpen)
|
||||||
, mUpperOpen(aUpperOpen)
|
, mUpperOpen(aUpperOpen)
|
||||||
, mIsOnly(aIsOnly)
|
, mIsOnly(aIsOnly)
|
||||||
|
|
|
@ -856,7 +856,7 @@ const JSClass IDBObjectStore::sDummyPropJSClass = {
|
||||||
IDBObjectStore::IDBObjectStore(IDBTransaction* aTransaction,
|
IDBObjectStore::IDBObjectStore(IDBTransaction* aTransaction,
|
||||||
const ObjectStoreSpec* aSpec)
|
const ObjectStoreSpec* aSpec)
|
||||||
: mTransaction(aTransaction)
|
: mTransaction(aTransaction)
|
||||||
, mCachedKeyPath(JSVAL_VOID)
|
, mCachedKeyPath(JS::UndefinedValue())
|
||||||
, mSpec(aSpec)
|
, mSpec(aSpec)
|
||||||
, mId(aSpec->metadata().id())
|
, mId(aSpec->metadata().id())
|
||||||
, mRooted(false)
|
, mRooted(false)
|
||||||
|
@ -871,7 +871,7 @@ IDBObjectStore::~IDBObjectStore()
|
||||||
AssertIsOnOwningThread();
|
AssertIsOnOwningThread();
|
||||||
|
|
||||||
if (mRooted) {
|
if (mRooted) {
|
||||||
mCachedKeyPath = JSVAL_VOID;
|
mCachedKeyPath.setUndefined();
|
||||||
mozilla::DropJSObjects(this);
|
mozilla::DropJSObjects(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1487,7 +1487,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBObjectStore)
|
||||||
|
|
||||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mIndexes);
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mIndexes);
|
||||||
|
|
||||||
tmp->mCachedKeyPath = JSVAL_VOID;
|
tmp->mCachedKeyPath.setUndefined();
|
||||||
|
|
||||||
if (tmp->mRooted) {
|
if (tmp->mRooted) {
|
||||||
mozilla::DropJSObjects(tmp);
|
mozilla::DropJSObjects(tmp);
|
||||||
|
|
|
@ -118,7 +118,7 @@ GetJSValFromKeyPathString(JSContext* aCx,
|
||||||
IDB_ENSURE_TRUE(ok, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
IDB_ENSURE_TRUE(ok, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
|
||||||
// Treat explicitly undefined as an error.
|
// Treat explicitly undefined as an error.
|
||||||
if (intermediate == JSVAL_VOID) {
|
if (intermediate.isUndefined()) {
|
||||||
return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
|
return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
|
||||||
}
|
}
|
||||||
if (tokenizer.hasMoreTokens()) {
|
if (tokenizer.hasMoreTokens()) {
|
||||||
|
@ -149,7 +149,7 @@ GetJSValFromKeyPathString(JSContext* aCx,
|
||||||
// We have started inserting new objects or are about to just insert
|
// We have started inserting new objects or are about to just insert
|
||||||
// the first one.
|
// the first one.
|
||||||
|
|
||||||
*aKeyJSVal = JSVAL_VOID;
|
aKeyJSVal->setUndefined();
|
||||||
|
|
||||||
if (tokenizer.hasMoreTokens()) {
|
if (tokenizer.hasMoreTokens()) {
|
||||||
// If we're not at the end, we need to add a dummy object to the
|
// If we're not at the end, we need to add a dummy object to the
|
||||||
|
|
|
@ -119,7 +119,7 @@ MobileConnectionInfo::Update(nsIMobileConnectionInfo* aInfo)
|
||||||
|
|
||||||
// Update mSignalStrength
|
// Update mSignalStrength
|
||||||
AutoJSContext cx;
|
AutoJSContext cx;
|
||||||
JS::Rooted<JS::Value> signalStrength(cx, JSVAL_VOID);
|
JS::Rooted<JS::Value> signalStrength(cx, JS::UndefinedValue());
|
||||||
aInfo->GetSignalStrength(&signalStrength);
|
aInfo->GetSignalStrength(&signalStrength);
|
||||||
if (signalStrength.isNumber()) {
|
if (signalStrength.isNumber()) {
|
||||||
mSignalStrength.SetValue(signalStrength.toNumber());
|
mSignalStrength.SetValue(signalStrength.toNumber());
|
||||||
|
@ -128,7 +128,7 @@ MobileConnectionInfo::Update(nsIMobileConnectionInfo* aInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update mRelSignalStrength
|
// Update mRelSignalStrength
|
||||||
JS::Rooted<JS::Value> relSignalStrength(cx, JSVAL_VOID);
|
JS::Rooted<JS::Value> relSignalStrength(cx, JS::UndefinedValue());
|
||||||
aInfo->GetRelSignalStrength(&relSignalStrength);
|
aInfo->GetRelSignalStrength(&relSignalStrength);
|
||||||
if (relSignalStrength.isNumber()) {
|
if (relSignalStrength.isNumber()) {
|
||||||
mRelSignalStrength.SetValue(relSignalStrength.toNumber());
|
mRelSignalStrength.SetValue(relSignalStrength.toNumber());
|
||||||
|
|
|
@ -496,7 +496,7 @@ NPVariantToJSVal(NPP npp, JSContext *cx, const NPVariant *variant)
|
||||||
{
|
{
|
||||||
switch (variant->type) {
|
switch (variant->type) {
|
||||||
case NPVariantType_Void :
|
case NPVariantType_Void :
|
||||||
return JSVAL_VOID;
|
return JS::UndefinedValue();
|
||||||
case NPVariantType_Null :
|
case NPVariantType_Null :
|
||||||
return JSVAL_NULL;
|
return JSVAL_NULL;
|
||||||
case NPVariantType_Bool :
|
case NPVariantType_Bool :
|
||||||
|
@ -546,7 +546,7 @@ NPVariantToJSVal(NPP npp, JSContext *cx, const NPVariant *variant)
|
||||||
|
|
||||||
NS_ERROR("Unable to convert NPVariant to jsval!");
|
NS_ERROR("Unable to convert NPVariant to jsval!");
|
||||||
|
|
||||||
return JSVAL_VOID;
|
return JS::UndefinedValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -555,7 +555,7 @@ JSValToNPVariant(NPP npp, JSContext *cx, JS::Value val, NPVariant *variant)
|
||||||
NS_ASSERTION(npp, "Must have an NPP to wrap a jsval!");
|
NS_ASSERTION(npp, "Must have an NPP to wrap a jsval!");
|
||||||
|
|
||||||
if (val.isPrimitive()) {
|
if (val.isPrimitive()) {
|
||||||
if (val == JSVAL_VOID) {
|
if (val.isUndefined()) {
|
||||||
VOID_TO_NPVARIANT(*variant);
|
VOID_TO_NPVARIANT(*variant);
|
||||||
} else if (val.isNull()) {
|
} else if (val.isNull()) {
|
||||||
NULL_TO_NPVARIANT(*variant);
|
NULL_TO_NPVARIANT(*variant);
|
||||||
|
@ -1724,7 +1724,7 @@ NPObjWrapper_Convert(JSContext *cx, JS::Handle<JSObject*> obj, JSType hint, JS::
|
||||||
// called with no arguments. We work around this problem by giving plugins a
|
// called with no arguments. We work around this problem by giving plugins a
|
||||||
// [[DefaultValue]] which uses only toString and not valueOf.
|
// [[DefaultValue]] which uses only toString and not valueOf.
|
||||||
|
|
||||||
JS::Rooted<JS::Value> v(cx, JSVAL_VOID);
|
JS::Rooted<JS::Value> v(cx, JS::UndefinedValue());
|
||||||
if (!JS_GetProperty(cx, obj, "toString", &v))
|
if (!JS_GetProperty(cx, obj, "toString", &v))
|
||||||
return false;
|
return false;
|
||||||
if (!v.isPrimitive() && JS::IsCallable(v.toObjectOrNull())) {
|
if (!v.isPrimitive() && JS::IsCallable(v.toObjectOrNull())) {
|
||||||
|
|
|
@ -244,7 +244,7 @@ private:
|
||||||
void MaybeReportRejectedOnce() {
|
void MaybeReportRejectedOnce() {
|
||||||
MaybeReportRejected();
|
MaybeReportRejected();
|
||||||
RemoveFeature();
|
RemoveFeature();
|
||||||
mResult = JS::UndefinedValue();
|
mResult.setUndefined();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MaybeResolveInternal(JSContext* aCx,
|
void MaybeResolveInternal(JSContext* aCx,
|
||||||
|
|
|
@ -454,7 +454,7 @@ public:
|
||||||
EventRunnable(Proxy* aProxy, bool aUploadEvent, const nsString& aType,
|
EventRunnable(Proxy* aProxy, bool aUploadEvent, const nsString& aType,
|
||||||
bool aLengthComputable, uint64_t aLoaded, uint64_t aTotal)
|
bool aLengthComputable, uint64_t aLoaded, uint64_t aTotal)
|
||||||
: MainThreadProxyRunnable(aProxy->mWorkerPrivate, aProxy), mType(aType),
|
: MainThreadProxyRunnable(aProxy->mWorkerPrivate, aProxy), mType(aType),
|
||||||
mResponse(JSVAL_VOID), mLoaded(aLoaded), mTotal(aTotal),
|
mResponse(JS::UndefinedValue()), mLoaded(aLoaded), mTotal(aTotal),
|
||||||
mEventStreamId(aProxy->mInnerEventStreamId), mStatus(0), mReadyState(0),
|
mEventStreamId(aProxy->mInnerEventStreamId), mStatus(0), mReadyState(0),
|
||||||
mUploadEvent(aUploadEvent), mProgressEvent(true),
|
mUploadEvent(aUploadEvent), mProgressEvent(true),
|
||||||
mLengthComputable(aLengthComputable), mUseCachedArrayBufferResponse(false),
|
mLengthComputable(aLengthComputable), mUseCachedArrayBufferResponse(false),
|
||||||
|
@ -463,7 +463,7 @@ public:
|
||||||
|
|
||||||
EventRunnable(Proxy* aProxy, bool aUploadEvent, const nsString& aType)
|
EventRunnable(Proxy* aProxy, bool aUploadEvent, const nsString& aType)
|
||||||
: MainThreadProxyRunnable(aProxy->mWorkerPrivate, aProxy), mType(aType),
|
: MainThreadProxyRunnable(aProxy->mWorkerPrivate, aProxy), mType(aType),
|
||||||
mResponse(JSVAL_VOID), mLoaded(0), mTotal(0),
|
mResponse(JS::UndefinedValue()), mLoaded(0), mTotal(0),
|
||||||
mEventStreamId(aProxy->mInnerEventStreamId), mStatus(0), mReadyState(0),
|
mEventStreamId(aProxy->mInnerEventStreamId), mStatus(0), mReadyState(0),
|
||||||
mUploadEvent(aUploadEvent), mProgressEvent(false), mLengthComputable(0),
|
mUploadEvent(aUploadEvent), mProgressEvent(false), mLengthComputable(0),
|
||||||
mUseCachedArrayBufferResponse(false), mResponseTextResult(NS_OK),
|
mUseCachedArrayBufferResponse(false), mResponseTextResult(NS_OK),
|
||||||
|
|
|
@ -45,7 +45,7 @@ public:
|
||||||
nsresult mResponseResult;
|
nsresult mResponseResult;
|
||||||
|
|
||||||
StateData()
|
StateData()
|
||||||
: mStatus(0), mReadyState(0), mResponse(JSVAL_VOID),
|
: mStatus(0), mReadyState(0), mResponse(JS::UndefinedValue()),
|
||||||
mResponseTextResult(NS_OK), mStatusResult(NS_OK),
|
mResponseTextResult(NS_OK), mStatusResult(NS_OK),
|
||||||
mResponseResult(NS_OK)
|
mResponseResult(NS_OK)
|
||||||
{ }
|
{ }
|
||||||
|
|
|
@ -208,6 +208,7 @@ Layer::Layer(LayerManager* aManager, void* aImplData) :
|
||||||
mStickyPositionData(nullptr),
|
mStickyPositionData(nullptr),
|
||||||
mScrollbarTargetId(FrameMetrics::NULL_SCROLL_ID),
|
mScrollbarTargetId(FrameMetrics::NULL_SCROLL_ID),
|
||||||
mScrollbarDirection(ScrollDirection::NONE),
|
mScrollbarDirection(ScrollDirection::NONE),
|
||||||
|
mIsScrollbarContainer(false),
|
||||||
mDebugColorIndex(0),
|
mDebugColorIndex(0),
|
||||||
mAnimationGeneration(0)
|
mAnimationGeneration(0)
|
||||||
{}
|
{}
|
||||||
|
|
|
@ -1204,6 +1204,15 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set during construction for the container layer of scrollbar components.
|
||||||
|
void SetIsScrollbarContainer()
|
||||||
|
{
|
||||||
|
if (!mIsScrollbarContainer) {
|
||||||
|
mIsScrollbarContainer = true;
|
||||||
|
Mutated();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// These getters can be used anytime.
|
// These getters can be used anytime.
|
||||||
float GetOpacity() { return mOpacity; }
|
float GetOpacity() { return mOpacity; }
|
||||||
gfx::CompositionOp GetMixBlendMode() const { return mMixBlendMode; }
|
gfx::CompositionOp GetMixBlendMode() const { return mMixBlendMode; }
|
||||||
|
@ -1238,6 +1247,7 @@ public:
|
||||||
const LayerRect& GetStickyScrollRangeInner() { return mStickyPositionData->mInner; }
|
const LayerRect& GetStickyScrollRangeInner() { return mStickyPositionData->mInner; }
|
||||||
FrameMetrics::ViewID GetScrollbarTargetContainerId() { return mScrollbarTargetId; }
|
FrameMetrics::ViewID GetScrollbarTargetContainerId() { return mScrollbarTargetId; }
|
||||||
ScrollDirection GetScrollbarDirection() { return mScrollbarDirection; }
|
ScrollDirection GetScrollbarDirection() { return mScrollbarDirection; }
|
||||||
|
bool IsScrollbarContainer() { return mIsScrollbarContainer; }
|
||||||
Layer* GetMaskLayer() const { return mMaskLayer; }
|
Layer* GetMaskLayer() const { return mMaskLayer; }
|
||||||
|
|
||||||
|
|
||||||
|
@ -1676,6 +1686,7 @@ protected:
|
||||||
nsAutoPtr<StickyPositionData> mStickyPositionData;
|
nsAutoPtr<StickyPositionData> mStickyPositionData;
|
||||||
FrameMetrics::ViewID mScrollbarTargetId;
|
FrameMetrics::ViewID mScrollbarTargetId;
|
||||||
ScrollDirection mScrollbarDirection;
|
ScrollDirection mScrollbarDirection;
|
||||||
|
bool mIsScrollbarContainer;
|
||||||
DebugOnly<uint32_t> mDebugColorIndex;
|
DebugOnly<uint32_t> mDebugColorIndex;
|
||||||
// If this layer is used for OMTA, then this counter is used to ensure we
|
// If this layer is used for OMTA, then this counter is used to ensure we
|
||||||
// stay in sync with the animation manager
|
// stay in sync with the animation manager
|
||||||
|
|
|
@ -80,7 +80,21 @@ void
|
||||||
SoftwareDisplay::NotifyVsync(mozilla::TimeStamp aVsyncTimestamp)
|
SoftwareDisplay::NotifyVsync(mozilla::TimeStamp aVsyncTimestamp)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(IsInSoftwareVsyncThread());
|
MOZ_ASSERT(IsInSoftwareVsyncThread());
|
||||||
Display::NotifyVsync(aVsyncTimestamp);
|
|
||||||
|
mozilla::TimeStamp displayVsyncTime = aVsyncTimestamp;
|
||||||
|
mozilla::TimeStamp now = mozilla::TimeStamp::Now();
|
||||||
|
// Posted tasks can only have integer millisecond delays
|
||||||
|
// whereas TimeDurations can have floating point delays.
|
||||||
|
// Thus the vsync timestamp can be in the future, which large parts
|
||||||
|
// of the system can't handle, including animations. Force the timestamp to be now.
|
||||||
|
if (aVsyncTimestamp > now) {
|
||||||
|
displayVsyncTime = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
Display::NotifyVsync(displayVsyncTime);
|
||||||
|
|
||||||
|
// Prevent skew by still scheduling based on the original
|
||||||
|
// vsync timestamp
|
||||||
ScheduleNextVsync(aVsyncTimestamp);
|
ScheduleNextVsync(aVsyncTimestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
include $(topsrcdir)/config/rules.mk
|
||||||
|
|
||||||
|
PROPS2ARRAYS = $(topsrcdir)/intl/locale/props2arrays.py
|
||||||
|
langGroups.properties.h: $(PROPS2ARRAYS) langGroups.properties
|
||||||
|
$(PYTHON) $^ $@
|
|
@ -61,6 +61,10 @@ RESOURCE_FILES += [
|
||||||
'language.properties',
|
'language.properties',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
GENERATED_FILES += [
|
||||||
|
'langGroups.properties.h',
|
||||||
|
]
|
||||||
|
|
||||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'qt':
|
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'qt':
|
||||||
CXXFLAGS += CONFIG['MOZ_QT_CFLAGS']
|
CXXFLAGS += CONFIG['MOZ_QT_CFLAGS']
|
||||||
|
|
||||||
|
|
|
@ -5,33 +5,26 @@
|
||||||
|
|
||||||
#include "nsLanguageAtomService.h"
|
#include "nsLanguageAtomService.h"
|
||||||
#include "nsILocaleService.h"
|
#include "nsILocaleService.h"
|
||||||
|
#include "nsUConvPropertySearch.h"
|
||||||
#include "nsUnicharUtils.h"
|
#include "nsUnicharUtils.h"
|
||||||
#include "nsIAtom.h"
|
#include "nsIAtom.h"
|
||||||
|
#include "mozilla/ArrayUtils.h"
|
||||||
#include "mozilla/Services.h"
|
#include "mozilla/Services.h"
|
||||||
#include "nsServiceManagerUtils.h"
|
#include "nsServiceManagerUtils.h"
|
||||||
#include "mozilla/dom/EncodingUtils.h"
|
#include "mozilla/dom/EncodingUtils.h"
|
||||||
|
|
||||||
|
using namespace mozilla;
|
||||||
|
|
||||||
|
static const char* kLangGroups[][3] = {
|
||||||
|
#include "langGroups.properties.h"
|
||||||
|
};
|
||||||
|
|
||||||
NS_IMPL_ISUPPORTS(nsLanguageAtomService, nsILanguageAtomService)
|
NS_IMPL_ISUPPORTS(nsLanguageAtomService, nsILanguageAtomService)
|
||||||
|
|
||||||
nsLanguageAtomService::nsLanguageAtomService()
|
nsLanguageAtomService::nsLanguageAtomService()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
|
||||||
nsLanguageAtomService::InitLangGroupTable()
|
|
||||||
{
|
|
||||||
if (mLangGroups)
|
|
||||||
return NS_OK;
|
|
||||||
|
|
||||||
nsCOMPtr<nsIStringBundleService> bundleService =
|
|
||||||
mozilla::services::GetStringBundleService();
|
|
||||||
if (!bundleService)
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
|
|
||||||
return bundleService->CreateBundle("resource://gre/res/langGroups.properties",
|
|
||||||
getter_AddRefs(mLangGroups));
|
|
||||||
}
|
|
||||||
|
|
||||||
nsIAtom*
|
nsIAtom*
|
||||||
nsLanguageAtomService::LookupLanguage(const nsACString &aLanguage,
|
nsLanguageAtomService::LookupLanguage(const nsACString &aLanguage,
|
||||||
nsresult *aError)
|
nsresult *aError)
|
||||||
|
@ -96,21 +89,13 @@ nsLanguageAtomService::GetLanguageGroup(nsIAtom *aLanguage,
|
||||||
retVal = mLangToGroup.GetWeak(aLanguage);
|
retVal = mLangToGroup.GetWeak(aLanguage);
|
||||||
|
|
||||||
if (!retVal) {
|
if (!retVal) {
|
||||||
if (!mLangGroups) {
|
nsAutoCString langStr;
|
||||||
if (NS_FAILED(InitLangGroupTable())) {
|
aLanguage->ToUTF8String(langStr);
|
||||||
if (aError) {
|
|
||||||
*aError = NS_ERROR_FAILURE;
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nsAutoString langStr;
|
nsAutoCString langGroupStr;
|
||||||
aLanguage->ToString(langStr);
|
res = nsUConvPropertySearch::SearchPropertyValue(kLangGroups,
|
||||||
|
ArrayLength(kLangGroups),
|
||||||
nsXPIDLString langGroupStr;
|
langStr, langGroupStr);
|
||||||
res = mLangGroups->GetStringFromName(langStr.get(),
|
|
||||||
getter_Copies(langGroupStr));
|
|
||||||
while (NS_FAILED(res)) {
|
while (NS_FAILED(res)) {
|
||||||
int32_t hyphen = langStr.RFindChar('-');
|
int32_t hyphen = langStr.RFindChar('-');
|
||||||
if (hyphen <= 0) {
|
if (hyphen <= 0) {
|
||||||
|
@ -118,8 +103,9 @@ nsLanguageAtomService::GetLanguageGroup(nsIAtom *aLanguage,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
langStr.Truncate(hyphen);
|
langStr.Truncate(hyphen);
|
||||||
res = mLangGroups->GetStringFromName(langStr.get(),
|
res = nsUConvPropertySearch::SearchPropertyValue(kLangGroups,
|
||||||
getter_Copies(langGroupStr));
|
ArrayLength(kLangGroups),
|
||||||
|
langStr, langGroupStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsIAtom> langGroup = do_GetAtom(langGroupStr);
|
nsCOMPtr<nsIAtom> langGroup = do_GetAtom(langGroupStr);
|
||||||
|
|
|
@ -36,9 +36,6 @@ private:
|
||||||
~nsLanguageAtomService() { }
|
~nsLanguageAtomService() { }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
nsresult InitLangGroupTable();
|
|
||||||
|
|
||||||
nsInterfaceHashtable<nsISupportsHashKey, nsIAtom> mLangToGroup;
|
nsInterfaceHashtable<nsISupportsHashKey, nsIAtom> mLangToGroup;
|
||||||
nsCOMPtr<nsIStringBundle> mLangGroups;
|
|
||||||
nsCOMPtr<nsIAtom> mLocaleLanguage;
|
nsCOMPtr<nsIAtom> mLocaleLanguage;
|
||||||
};
|
};
|
||||||
|
|
|
@ -42,29 +42,6 @@
|
||||||
typedef bool
|
typedef bool
|
||||||
(* JSNative)(JSContext *cx, unsigned argc, JS::Value *vp);
|
(* JSNative)(JSContext *cx, unsigned argc, JS::Value *vp);
|
||||||
|
|
||||||
/* Typedef for native functions that may be called in parallel. */
|
|
||||||
typedef bool
|
|
||||||
(* JSParallelNative)(js::ForkJoinContext *cx, unsigned argc, JS::Value *vp);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Typedef for native functions that may be called either in parallel or
|
|
||||||
* sequential execution.
|
|
||||||
*/
|
|
||||||
typedef bool
|
|
||||||
(* JSThreadSafeNative)(js::ThreadSafeContext *cx, unsigned argc, JS::Value *vp);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Convenience wrappers for passing in ThreadSafeNative to places that expect
|
|
||||||
* a JSNative or a JSParallelNative.
|
|
||||||
*/
|
|
||||||
template <JSThreadSafeNative threadSafeNative>
|
|
||||||
inline bool
|
|
||||||
JSNativeThreadSafeWrapper(JSContext *cx, unsigned argc, JS::Value *vp);
|
|
||||||
|
|
||||||
template <JSThreadSafeNative threadSafeNative>
|
|
||||||
inline bool
|
|
||||||
JSParallelNativeThreadSafeWrapper(js::ForkJoinContext *cx, unsigned argc, JS::Value *vp);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Compute |this| for the |vp| inside a JSNative, either boxing primitives or
|
* Compute |this| for the |vp| inside a JSNative, either boxing primitives or
|
||||||
* replacing with the global object as necessary.
|
* replacing with the global object as necessary.
|
||||||
|
|
|
@ -75,12 +75,8 @@ static const uint32_t GRAY = 1;
|
||||||
*/
|
*/
|
||||||
const uintptr_t ChunkLocationBitNursery = 1; // Standard GGC nursery
|
const uintptr_t ChunkLocationBitNursery = 1; // Standard GGC nursery
|
||||||
const uintptr_t ChunkLocationBitTenuredHeap = 2; // Standard GGC tenured generation
|
const uintptr_t ChunkLocationBitTenuredHeap = 2; // Standard GGC tenured generation
|
||||||
const uintptr_t ChunkLocationBitPJSNewspace = 4; // The PJS generational GC's allocation space
|
|
||||||
const uintptr_t ChunkLocationBitPJSFromspace = 8; // The PJS generational GC's fromspace (during GC)
|
|
||||||
|
|
||||||
const uintptr_t ChunkLocationAnyNursery = ChunkLocationBitNursery |
|
const uintptr_t ChunkLocationAnyNursery = ChunkLocationBitNursery;
|
||||||
ChunkLocationBitPJSNewspace |
|
|
||||||
ChunkLocationBitPJSFromspace;
|
|
||||||
|
|
||||||
#ifdef JS_DEBUG
|
#ifdef JS_DEBUG
|
||||||
/* When downcasting, ensure we are actually the right type. */
|
/* When downcasting, ensure we are actually the right type. */
|
||||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче