Merge mozilla-central to b2g-inbound

This commit is contained in:
Carsten "Tomcat" Book 2015-01-14 15:00:18 +01:00
Родитель adf90cc975 fedd16f693
Коммит aa779853d3
234 изменённых файлов: 3540 добавлений и 5570 удалений

Просмотреть файл

@ -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);
} }

9
intl/locale/Makefile.in Normal file
Просмотреть файл

@ -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. */

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше