Merge fx-team to m-c. a=merge

This commit is contained in:
Ryan VanderMeulen 2015-07-27 15:28:51 -04:00
commit 2023641b4b
23 changed files with 700 additions and 245 deletions

View File

@ -458,6 +458,8 @@ ContentSearchUIController.prototype = {
this._strings = strings;
this._updateDefaultEngineHeader();
this._updateSearchWithHeader();
document.getElementById("contentSearchSettingsButton").textContent =
this._strings.searchSettings;
},
_updateDefaultEngineHeader: function () {
@ -484,12 +486,10 @@ ContentSearchUIController.prototype = {
searchWithHeader.firstChild.remove();
}
if (this.input.value) {
searchWithHeader.appendChild(document.createTextNode(this._strings.searchFor));
let span = document.createElementNS(HTML_NS, "span");
span.setAttribute("class", "contentSearchSearchWithHeaderSearchText");
span.appendChild(document.createTextNode(" " + this.input.value + " "));
searchWithHeader.appendChild(span);
searchWithHeader.appendChild(document.createTextNode(this._strings.searchWith));
let html = "<span class='contentSearchSearchWithHeaderSearchText'>" +
this.input.value + "</span>";
html = this._strings.searchForKeywordsWith.replace("%S", html);
searchWithHeader.innerHTML = html;
return;
}
searchWithHeader.appendChild(document.createTextNode(this._strings.searchWithHeader));
@ -654,7 +654,6 @@ ContentSearchUIController.prototype = {
this._oneOffsTable.appendChild(headerRow);
let button = document.createElementNS(HTML_NS, "button");
button.appendChild(document.createTextNode("Change Search Settings"));
button.setAttribute("class", "contentSearchSettingsButton");
button.classList.add("contentSearchHeaderRow");
button.classList.add("contentSearchHeader");

View File

@ -5,6 +5,9 @@
Components.utils.import("resource://gre/modules/BrowserUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "LoginHelper",
"resource://gre/modules/LoginHelper.jsm");
var security = {
// Display the server certificate (static)
viewCert : function () {
@ -158,19 +161,8 @@ var security = {
/**
* Open the login manager window
*/
viewPasswords : function()
{
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator);
var win = wm.getMostRecentWindow("Toolkit:PasswordManager");
if (win) {
win.setFilter(this._getSecurityInfo().hostName);
win.focus();
}
else
window.openDialog("chrome://passwordmgr/content/passwordManager.xul",
"Toolkit:PasswordManager", "",
{filterString : this._getSecurityInfo().hostName});
viewPasswords : function() {
LoginHelper.openPasswordManager(window, this._getSecurityInfo().hostName);
},
_cert : null

View File

@ -368,7 +368,7 @@ var gMainPane = {
tabs = win.gBrowser.visibleTabs.slice(win.gBrowser._numPinnedTabs);
tabs = tabs.filter(this.isAboutPreferences);
tabs = tabs.filter(this.isNotAboutPreferences);
}
return tabs;
@ -377,9 +377,9 @@ var gMainPane = {
/**
* Check to see if a tab is not about:preferences
*/
isAboutPreferences: function (aElement, aIndex, aArray)
isNotAboutPreferences: function (aElement, aIndex, aArray)
{
return (aElement.linkedBrowser.currentURI.spec != "about:preferences");
return (aElement.linkedBrowser.currentURI.spec.startsWith != "about:preferences");
},
/**

View File

@ -139,7 +139,8 @@ html, body {
.theme-light .add,
.theme-light .remove-button {
.theme-light .remove-button,
.theme-light #toggle-presets {
filter: invert(1);
}
@ -217,8 +218,8 @@ html, body {
line-height: 20px;
}
.add {
background: url(chrome://browser/skin/devtools/add.svg);
.add,
#toggle-presets {
background-size: cover;
border: none;
width: 16px;
@ -226,4 +227,18 @@ html, body {
font-size: 0;
vertical-align: middle;
cursor: pointer;
margin: 0 5px;
}
.add {
background: url(chrome://browser/skin/devtools/add.svg);
}
#toggle-presets {
background: url(chrome://browser/skin/devtools/pseudo-class.svg#pseudo-class);
}
.show-presets #toggle-presets {
background: url(chrome://browser/skin/devtools/pseudo-class.svg#pseudo-class-checked);
filter: none;
}

View File

@ -33,14 +33,17 @@ cmd_addFoundEngine=Add "%S"
# grouped in a submenu using cmd_addFoundEngineMenu as a label.
cmd_addFoundEngineMenu=Add search engine
# LOCALIZATION NOTE (searchFor, searchWith):
# These two strings are used to build the header above the list of one-click
# LOCALIZATION NOTE (searchForKeywordsWith):
# This string is used to build the header above the list of one-click
# search providers: "Search for <user-typed keywords> with:"
searchFor=Search for
searchWith= with:
searchForKeywordsWith=Search for %S with:
# LOCALIZATION NOTE (searchWithHeader):
# The wording of this string should be as close as possible to
# searchFor and searchWith. This string will be used instead of
# them when the user has not typed any keyword.
# searchForKeywordsWith. This string will be used when the user
# has not typed anything.
searchWithHeader=Search with:
# LOCALIZATION NOTE (searchSettings):
# This is the label for the button that opens Search preferences.
searchSettings=Change Search Settings

View File

@ -114,8 +114,8 @@ this.ContentSearch = {
}
this._searchSuggestionUIStrings = {};
let searchBundle = Services.strings.createBundle("chrome://browser/locale/search.properties");
let stringNames = ["searchHeader", "searchPlaceholder", "searchFor",
"searchWith", "searchWithHeader"];
let stringNames = ["searchHeader", "searchPlaceholder", "searchForKeywordsWith",
"searchWithHeader", "searchSettings"];
for (let name of stringNames) {
this._searchSuggestionUIStrings[name] = searchBundle.GetStringFromName(name);
}

View File

@ -317,16 +317,21 @@ IterPerformanceStats(JSContext* cx,
}
JSRuntime* rt = JS_GetRuntime(cx);
for (CompartmentsIter c(rt, WithAtoms); !c.done(); c.next()) {
// First report the shared groups
for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
JSCompartment* compartment = c.get();
if (!compartment->performanceMonitoring.isLinked()) {
if (!c->principals()) {
// Compartments without principals could show up here, but
// reporting them doesn't really make sense.
continue;
}
if (!c->performanceMonitoring.hasSharedGroup()) {
// Don't report compartments that do not even have a PerformanceGroup.
continue;
}
js::AutoCompartment autoCompartment(cx, compartment);
PerformanceGroup* group = compartment->performanceMonitoring.getGroup(cx);
PerformanceGroup* group = compartment->performanceMonitoring.getSharedGroup(cx);
if (group->data.ticks == 0) {
// Don't report compartments that have never been used.
continue;
@ -338,7 +343,9 @@ IterPerformanceStats(JSContext* cx,
continue;
}
if (!(*walker)(cx, group->data, group->uid, closure)) {
if (!(*walker)(cx,
group->data, group->uid, nullptr,
closure)) {
// Issue in callback
return false;
}
@ -347,6 +354,35 @@ IterPerformanceStats(JSContext* cx,
return false;
}
}
// Then report the own groups
for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
JSCompartment* compartment = c.get();
if (!c->principals()) {
// Compartments without principals could show up here, but
// reporting them doesn't really make sense.
continue;
}
if (!c->performanceMonitoring.hasOwnGroup()) {
// Don't report compartments that do not even have a PerformanceGroup.
continue;
}
js::AutoCompartment autoCompartment(cx, compartment);
PerformanceGroup* ownGroup = compartment->performanceMonitoring.getOwnGroup(cx);
if (ownGroup->data.ticks == 0) {
// Don't report compartments that have never been used.
continue;
}
PerformanceGroup* sharedGroup = compartment->performanceMonitoring.getSharedGroup(cx);
if (!(*walker)(cx,
ownGroup->data, ownGroup->uid, &sharedGroup->uid,
closure)) {
// Issue in callback
return false;
}
}
// Finally, report the process stats
*processStats = rt->stopwatch.performance;
return true;
}

View File

@ -5409,7 +5409,7 @@ BuildStackString(JSContext* cx, HandleObject stack, MutableHandleString stringp)
namespace js {
struct AutoStopwatch;
class AutoStopwatch;
// Container for performance data
// All values are monotonic.
@ -5540,21 +5540,30 @@ private:
};
//
// Indirection towards a PerformanceGroup.
// This structure handles reference counting for instances of PerformanceGroup.
// Each PerformanceGroupHolder handles:
// - a reference-counted indirection towards a PerformanceGroup shared
// by several compartments
// - a owned PerformanceGroup representing the performance of a single
// compartment.
//
struct PerformanceGroupHolder {
// Get the group.
// Get the shared group.
// On first call, this causes a single Hashtable lookup.
// Successive calls do not require further lookups.
js::PerformanceGroup* getGroup(JSContext*);
js::PerformanceGroup* getSharedGroup(JSContext*);
// `true` if the this holder is currently associated to a
// Get the own group.
js::PerformanceGroup* getOwnGroup(JSContext*);
// `true` if the this holder is currently associated to a shared
// PerformanceGroup, `false` otherwise. Use this method to avoid
// instantiating a PerformanceGroup if you only need to get
// available performance data.
inline bool isLinked() const {
return group_ != nullptr;
inline bool hasSharedGroup() const {
return sharedGroup_ != nullptr;
}
inline bool hasOwnGroup() const {
return ownGroup_ != nullptr;
}
// Remove the link to the PerformanceGroup. This method is designed
@ -5564,10 +5573,12 @@ struct PerformanceGroupHolder {
explicit PerformanceGroupHolder(JSRuntime* runtime)
: runtime_(runtime)
, group_(nullptr)
, sharedGroup_(nullptr)
, ownGroup_(nullptr)
{ }
~PerformanceGroupHolder();
private:
private:
// Return the key representing this PerformanceGroup in
// Runtime::Stopwatch.
// Do not deallocate the key.
@ -5575,10 +5586,11 @@ private:
JSRuntime *runtime_;
// The PerformanceGroup held by this object.
// Initially set to `nullptr` until the first cal to `getGroup`.
// The PerformanceGroups held by this object.
// Initially set to `nullptr` until the first call to `getGroup`.
// May be reset to `nullptr` by a call to `unlink`.
js::PerformanceGroup* group_;
js::PerformanceGroup* sharedGroup_;
js::PerformanceGroup* ownGroup_;
};
/**
@ -5604,6 +5616,10 @@ extern JS_PUBLIC_API(bool)
SetStopwatchIsMonitoringJank(JSRuntime*, bool);
extern JS_PUBLIC_API(bool)
GetStopwatchIsMonitoringJank(JSRuntime*);
extern JS_PUBLIC_API(bool)
SetStopwatchIsMonitoringPerCompartment(JSRuntime*, bool);
extern JS_PUBLIC_API(bool)
GetStopwatchIsMonitoringPerCompartment(JSRuntime*);
extern JS_PUBLIC_API(bool)
IsStopwatchActive(JSRuntime*);
@ -5615,7 +5631,9 @@ extern JS_PUBLIC_API(PerformanceData*)
GetPerformanceData(JSRuntime*);
typedef bool
(PerformanceStatsWalker)(JSContext* cx, const PerformanceData& stats, uint64_t uid, void* closure);
(PerformanceStatsWalker)(JSContext* cx,
const PerformanceData& stats, uint64_t uid,
const uint64_t* parentId, void* closure);
/**
* Extract the performance statistics.

View File

@ -372,15 +372,49 @@ ExecuteState::pushInterpreterFrame(JSContext* cx)
scopeChain_, type_, evalInFrame_);
}
namespace js {
// Implementation of per-performance group performance measurement.
//
//
// All mutable state is stored in `Runtime::stopwatch` (per-process
// performance stats and logistics) and in `PerformanceGroup` (per
// group performance stats).
struct AutoStopwatch final
class AutoStopwatch final
{
// The context with which this object was initialized.
// Non-null.
JSContext* const cx_;
// An indication of the number of times we have entered the event
// loop. Used only for comparison.
uint64_t iteration_;
// `true` if this object is currently used to monitor performance
// for a shared PerformanceGroup, `false` otherwise, i.e. if the
// stopwatch mechanism is off or if another stopwatch is already
// in charge of monitoring for the same PerformanceGroup.
bool isMonitoringForGroup_;
// `true` if this object is currently used to monitor performance
// for a single compartment, `false` otherwise, i.e. if the
// stopwatch mechanism is off or if another stopwatch is already
// in charge of monitoring for the same PerformanceGroup.
bool isMonitoringForSelf_;
// `true` if this stopwatch is the topmost stopwatch on the stack
// for this event, `false` otherwise.
bool isMonitoringForTop_;
// `true` if we are monitoring jank, `false` otherwise.
bool isMonitoringJank_;
// `true` if we are monitoring CPOW, `false` otherwise.
bool isMonitoringCPOW_;
// Timestamps captured while starting the stopwatch.
uint64_t userTimeStart_;
uint64_t systemTimeStart_;
uint64_t CPOWTimeStart_;
public:
// If the stopwatch is active, constructing an instance of
// AutoStopwatch causes it to become the current owner of the
// stopwatch.
@ -389,120 +423,198 @@ struct AutoStopwatch final
explicit inline AutoStopwatch(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: cx_(cx)
, iteration_(0)
, isActive_(false)
, isTop_(false)
, isMonitoringForGroup_(false)
, isMonitoringForSelf_(false)
, isMonitoringForTop_(false)
, isMonitoringJank_(false)
, isMonitoringCPOW_(false)
, userTimeStart_(0)
, systemTimeStart_(0)
, CPOWTimeStart_(0)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
JSRuntime* runtime = JS_GetRuntime(cx_);
if (!runtime->stopwatch.isMonitoringJank())
return;
JSCompartment* compartment = cx_->compartment();
if (compartment->scheduledForDestruction)
return;
JSRuntime* runtime = cx_->runtime();
iteration_ = runtime->stopwatch.iteration;
PerformanceGroup *group = compartment->performanceMonitoring.getGroup(cx);
MOZ_ASSERT(group);
if (group->hasStopwatch(iteration_)) {
// Someone is already monitoring this group during this
// tick, no need for further monitoring.
PerformanceGroup* sharedGroup = compartment->performanceMonitoring.getSharedGroup(cx);
if (!sharedGroup) {
// Either this Runtime is not configured for Performance Monitoring, or we couldn't
// allocate the group, or there was a problem with the hashtable.
return;
}
// Start the stopwatch.
if (!this->getTimes(runtime, &userTimeStart_, &systemTimeStart_))
return;
isActive_ = true;
CPOWTimeStart_ = runtime->stopwatch.performance.totalCPOWTime;
if (!sharedGroup->hasStopwatch(iteration_)) {
// We are now in charge of monitoring this group for the tick,
// until destruction of `this` or until we enter a nested event
// loop and `iteration_` is incremented.
sharedGroup->acquireStopwatch(iteration_, this);
isMonitoringForGroup_ = true;
}
// We are now in charge of monitoring this group for the tick,
// until destruction of `this` or until we enter a nested event
// loop and `iteration_` is incremented.
group->acquireStopwatch(iteration_, this);
PerformanceGroup* ownGroup = nullptr;
if (runtime->stopwatch.isMonitoringPerCompartment()) {
// As above, but for the group representing just this compartment.
ownGroup = compartment->performanceMonitoring.getOwnGroup(cx);
if (!ownGroup->hasStopwatch(iteration_)) {
ownGroup->acquireStopwatch(iteration_, this);
isMonitoringForSelf_ = true;
}
}
if (runtime->stopwatch.isEmpty) {
// This is the topmost stopwatch on the stack.
// It will be in charge of updating the per-process
// performance data.
runtime->stopwatch.isEmpty = false;
runtime->stopwatch.performance.ticks++;
isTop_ = true;
isMonitoringForTop_ = true;
MOZ_ASSERT(isMonitoringForGroup_);
}
}
inline ~AutoStopwatch() {
if (!isActive_) {
if (!isMonitoringForGroup_ && !isMonitoringForSelf_) {
// We are not in charge of monitoring anything.
// (isMonitoringForTop_ implies isMonitoringForGroup_,
// so we do not need to check it)
return;
}
enter();
}
~AutoStopwatch() {
if (!isMonitoringForGroup_ && !isMonitoringForSelf_) {
// We are not in charge of monitoring anything.
// (isMonitoringForTop_ implies isMonitoringForGroup_,
// so we do not need to check it)
return;
}
JSRuntime* runtime = JS_GetRuntime(cx_);
JSCompartment* compartment = cx_->compartment();
MOZ_ASSERT(!compartment->scheduledForDestruction);
if (!runtime->stopwatch.isMonitoringJank()) {
// Monitoring has been stopped while we were
// executing the code. Drop everything.
if (compartment->scheduledForDestruction)
return;
}
JSRuntime* runtime = cx_->runtime();
if (iteration_ != runtime->stopwatch.iteration) {
// We have entered a nested event loop at some point.
// Any information we may have is obsolete.
return;
}
PerformanceGroup *group = compartment->performanceMonitoring.getGroup(cx_);
MOZ_ASSERT(group);
// Finish and commit measures
exit();
// Compute time spent.
group->releaseStopwatch(iteration_, this);
uint64_t userTimeEnd, systemTimeEnd;
if (!this->getTimes(runtime, &userTimeEnd, &systemTimeEnd))
return;
// Now release groups.
if (isMonitoringForGroup_) {
PerformanceGroup* sharedGroup = compartment->performanceMonitoring.getSharedGroup(cx_);
MOZ_ASSERT(sharedGroup);
sharedGroup->releaseStopwatch(iteration_, this);
}
uint64_t userTimeDelta = userTimeEnd - userTimeStart_;
uint64_t systemTimeDelta = systemTimeEnd - systemTimeStart_;
uint64_t CPOWTimeDelta = runtime->stopwatch.performance.totalCPOWTime - CPOWTimeStart_;
group->data.totalUserTime += userTimeDelta;
group->data.totalSystemTime += systemTimeDelta;
group->data.totalCPOWTime += CPOWTimeDelta;
if (isMonitoringForSelf_) {
PerformanceGroup* ownGroup = compartment->performanceMonitoring.getOwnGroup(cx_);
MOZ_ASSERT(ownGroup);
ownGroup->releaseStopwatch(iteration_, this);
}
uint64_t totalTimeDelta = userTimeDelta + systemTimeDelta;
updateDurations(totalTimeDelta, group->data.durations);
group->data.ticks++;
if (isTop_) {
// This is the topmost stopwatch on the stack.
// Record the timing information.
runtime->stopwatch.performance.totalUserTime = userTimeEnd;
runtime->stopwatch.performance.totalSystemTime = systemTimeEnd;
updateDurations(totalTimeDelta, runtime->stopwatch.performance.durations);
if (isMonitoringForTop_)
runtime->stopwatch.isEmpty = true;
}
private:
void enter() {
JSRuntime* runtime = cx_->runtime();
if (runtime->stopwatch.isMonitoringCPOW()) {
CPOWTimeStart_ = runtime->stopwatch.performance.totalCPOWTime;
isMonitoringCPOW_ = true;
}
if (runtime->stopwatch.isMonitoringJank()) {
if (this->getTimes(runtime, &userTimeStart_, &systemTimeStart_)) {
isMonitoringJank_ = true;
}
}
}
void exit() {
JSRuntime* runtime = cx_->runtime();
uint64_t userTimeDelta = 0;
uint64_t systemTimeDelta = 0;
if (isMonitoringJank_ && runtime->stopwatch.isMonitoringJank()) {
// We were monitoring jank when we entered and we still are.
uint64_t userTimeEnd, systemTimeEnd;
if (!this->getTimes(runtime, &userTimeEnd, &systemTimeEnd)) {
// We make no attempt to recover from this error. If
// we bail out here, we lose nothing of value, plus
// I'm nearly sure that this error cannot happen in
// practice.
return;
}
userTimeDelta = userTimeEnd - userTimeStart_;
systemTimeDelta = systemTimeEnd - systemTimeStart_;
}
uint64_t CPOWTimeDelta = 0;
if (isMonitoringCPOW_ && runtime->stopwatch.isMonitoringCPOW()) {
// We were monitoring CPOW when we entered and we still are.
CPOWTimeDelta = runtime->stopwatch.performance.totalCPOWTime - CPOWTimeStart_;
}
commitDeltasToGroups(userTimeDelta, systemTimeDelta, CPOWTimeDelta);
}
void commitDeltasToGroups(uint64_t userTimeDelta,
uint64_t systemTimeDelta,
uint64_t CPOWTimeDelta)
{
JSCompartment* compartment = cx_->compartment();
PerformanceGroup* sharedGroup = compartment->performanceMonitoring.getSharedGroup(cx_);
MOZ_ASSERT(sharedGroup);
applyDeltas(userTimeDelta, systemTimeDelta, CPOWTimeDelta, sharedGroup->data);
if (isMonitoringForSelf_) {
PerformanceGroup* ownGroup = compartment->performanceMonitoring.getOwnGroup(cx_);
MOZ_ASSERT(ownGroup);
applyDeltas(userTimeDelta, systemTimeDelta, CPOWTimeDelta, ownGroup->data);
}
if (isMonitoringForTop_) {
JSRuntime* runtime = cx_->runtime();
applyDeltas(userTimeDelta, systemTimeDelta, CPOWTimeDelta, runtime->stopwatch.performance);
}
}
private:
// Update an array containing the number of times we have missed
// at least 2^0 successive ms, 2^1 successive ms, ...
// 2^i successive ms.
template<int N>
void updateDurations(uint64_t totalTimeDelta, uint64_t (&array)[N]) const {
void applyDeltas(uint64_t userTimeDelta,
uint64_t systemTimeDelta,
uint64_t CPOWTimeDelta,
PerformanceData& data) const {
data.ticks++;
uint64_t totalTimeDelta = userTimeDelta + systemTimeDelta;
data.totalUserTime += userTimeDelta;
data.totalSystemTime += systemTimeDelta;
data.totalCPOWTime += CPOWTimeDelta;
// Update an array containing the number of times we have missed
// at least 2^0 successive ms, 2^1 successive ms, ...
// 2^i successive ms.
// Duration of one frame, i.e. 16ms in museconds
size_t i = 0;
uint64_t duration = 1000;
for (i = 0, duration = 1000;
i < N && duration < totalTimeDelta;
++i, duration *= 2) {
array[i]++;
i < ArrayLength(data.durations) && duration < totalTimeDelta;
++i, duration *= 2)
{
data.durations[i]++;
}
}
@ -585,31 +697,9 @@ struct AutoStopwatch final
return true;
}
private:
// The context with which this object was initialized.
// Non-null.
JSContext* const cx_;
// An indication of the number of times we have entered the event
// loop. Used only for comparison.
uint64_t iteration_;
// `true` if this object is currently used to monitor performance,
// `false` otherwise, i.e. if the stopwatch mechanism is off or if
// another stopwatch is already in charge of monitoring for the
// same PerformanceGroup.
bool isActive_;
// `true` if this stopwatch is the topmost stopwatch on the stack
// for this event, `false` otherwise.
bool isTop_;
// Timestamps captured while starting the stopwatch.
uint64_t userTimeStart_;
uint64_t systemTimeStart_;
uint64_t CPOWTimeStart_;
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
private:
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER;
};
} // namespace js

View File

@ -899,6 +899,17 @@ js::GetStopwatchIsMonitoringCPOW(JSRuntime* rt)
return rt->stopwatch.isMonitoringCPOW();
}
bool
js::SetStopwatchIsMonitoringPerCompartment(JSRuntime* rt, bool value)
{
return rt->stopwatch.setIsMonitoringPerCompartment(value);
}
bool
js::GetStopwatchIsMonitoringPerCompartment(JSRuntime* rt)
{
return rt->stopwatch.isMonitoringPerCompartment();
}
js::PerformanceGroupHolder::~PerformanceGroupHolder()
{
unlink();
@ -918,13 +929,18 @@ js::PerformanceGroupHolder::getHashKey(JSContext* cx)
void
js::PerformanceGroupHolder::unlink()
{
if (!group_) {
if (ownGroup_) {
js_delete(ownGroup_);
ownGroup_ = nullptr;
}
if (!sharedGroup_) {
// The group has never been instantiated.
return;
}
js::PerformanceGroup* group = group_;
group_ = nullptr;
js::PerformanceGroup* group = sharedGroup_;
sharedGroup_ = nullptr;
if (group->decRefCount() > 0) {
// The group has at least another owner.
@ -933,32 +949,51 @@ js::PerformanceGroupHolder::unlink()
JSRuntime::Stopwatch::Groups::Ptr ptr =
runtime_->stopwatch.groups_.lookup(group->key_);
runtime_->stopwatch.groups().lookup(group->key_);
MOZ_ASSERT(ptr);
runtime_->stopwatch.groups_.remove(ptr);
runtime_->stopwatch.groups().remove(ptr);
js_delete(group);
}
PerformanceGroup*
js::PerformanceGroupHolder::getGroup(JSContext* cx)
js::PerformanceGroupHolder::getOwnGroup(JSContext* cx)
{
if (group_)
return group_;
if (ownGroup_)
return ownGroup_;
ownGroup_ = runtime_->new_<PerformanceGroup>(cx, nullptr);
return ownGroup_;
}
PerformanceGroup*
js::PerformanceGroupHolder::getSharedGroup(JSContext* cx)
{
if (sharedGroup_)
return sharedGroup_;
if (!runtime_->stopwatch.groups().initialized())
return nullptr;
void* key = getHashKey(cx);
JSRuntime::Stopwatch::Groups::AddPtr ptr =
runtime_->stopwatch.groups_.lookupForAdd(key);
JSRuntime::Stopwatch::Groups::AddPtr ptr = runtime_->stopwatch.groups().lookupForAdd(key);
if (ptr) {
group_ = ptr->value();
MOZ_ASSERT(group_);
sharedGroup_ = ptr->value();
MOZ_ASSERT(sharedGroup_);
} else {
group_ = runtime_->new_<PerformanceGroup>(cx, key);
runtime_->stopwatch.groups_.add(ptr, key, group_);
sharedGroup_ = runtime_->new_<PerformanceGroup>(cx, key);
if (!sharedGroup_)
return nullptr;
if (!runtime_->stopwatch.groups().add(ptr, key, sharedGroup_)) {
js_delete(sharedGroup_);
sharedGroup_ = nullptr;
return nullptr;
}
}
group_->incRefCount();
sharedGroup_->incRefCount();
return group_;
return sharedGroup_;
}
PerformanceData*

View File

@ -1483,6 +1483,29 @@ struct JSRuntime : public JS::shadow::Runtime,
Performance measurements
------------------------------------------ */
struct Stopwatch {
/**
* A map used to collapse compartments belonging to the same
* add-on (respectively to the same webpage, to the platform)
* into a single group.
*
* Keys: for system compartments, a `JSAddonId*` (which may be
* `nullptr`), and for webpages, a `JSPrincipals*` (which may
* not). Note that compartments may start as non-system
* compartments and become compartments later during their
* lifetime, which requires an invalidation.
*
* This map is meant to be accessed only by instances of
* PerformanceGroupHolder, which handle both reference-counting
* of the values and invalidation of the key/value pairs.
*/
typedef js::HashMap<void*, js::PerformanceGroup*,
js::DefaultHasher<void*>,
js::SystemAllocPolicy> Groups;
Groups& groups() {
return groups_;
}
/**
* The number of times we have entered the event loop.
* Used to reset counters whenever we enter the loop,
@ -1523,6 +1546,7 @@ struct JSRuntime : public JS::shadow::Runtime,
, currentPerfGroupCallback(nullptr)
, isMonitoringJank_(false)
, isMonitoringCPOW_(false)
, isMonitoringPerCompartment_(false)
, idCounter_(0)
{ }
@ -1564,6 +1588,21 @@ struct JSRuntime : public JS::shadow::Runtime,
return isMonitoringJank_;
}
bool setIsMonitoringPerCompartment(bool value) {
if (isMonitoringPerCompartment_ != value)
reset();
if (value && !groups_.initialized()) {
if (!groups_.init(128))
return false;
}
isMonitoringPerCompartment_ = value;
return true;
}
bool isMonitoringPerCompartment() const {
return isMonitoringPerCompartment_;
}
/**
* Activate/deactivate stopwatch measurement of CPOW.
@ -1607,25 +1646,6 @@ struct JSRuntime : public JS::shadow::Runtime,
MonotonicTimeStamp userTimeFix;
private:
/**
* A map used to collapse compartments belonging to the same
* add-on (respectively to the same webpage, to the platform)
* into a single group.
*
* Keys: for system compartments, a `JSAddonId*` (which may be
* `nullptr`), and for webpages, a `JSPrincipals*` (which may
* not). Note that compartments may start as non-system
* compartments and become compartments later during their
* lifetime, which requires an invalidation.
*
* This map is meant to be accessed only by instances of
* PerformanceGroupHolder, which handle both reference-counting
* of the values and invalidation of the key/value pairs.
*/
typedef js::HashMap<void*, js::PerformanceGroup*,
js::DefaultHasher<void*>,
js::SystemAllocPolicy> Groups;
Groups groups_;
friend struct js::PerformanceGroupHolder;
@ -1634,6 +1654,7 @@ struct JSRuntime : public JS::shadow::Runtime,
*/
bool isMonitoringJank_;
bool isMonitoringCPOW_;
bool isMonitoringPerCompartment_;
/**
* A counter used to generate unique identifiers for groups.

View File

@ -478,8 +478,6 @@ public class BrowserApp extends GeckoApp
}
mHideDynamicToolbarOnActionModeEnd = false;
mProgressView.setPrivateMode(tab.isPrivate());
break;
case START:
if (Tabs.getInstance().isSelectedTab(tab)) {

View File

@ -19,9 +19,10 @@ import android.content.Intent;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;
import java.io.UnsupportedEncodingException;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.List;
@ -39,6 +40,9 @@ public final class IntentHelper implements GeckoEventListener {
private static String MARKET_INTENT_URI_PACKAGE_PREFIX = "market://details?id=";
private static String EXTRA_BROWSER_FALLBACK_URL = "browser_fallback_url";
/** A partial URI to an error page - the encoded error URI should be appended before loading. */
private static String UNKNOWN_PROTOCOL_URI_PREFIX = "about:neterror?e=unknownProtocolFound&u=";
private static IntentHelper instance;
private final Activity activity;
@ -133,8 +137,8 @@ public final class IntentHelper implements GeckoEventListener {
final String uri = msg.optString("uri");
if (TextUtils.isEmpty(uri)) {
displayToastCannotOpenLink();
Log.w(LOGTAG, "Received empty URL. Ignoring...");
openUnknownProtocolErrorPage("");
Log.w(LOGTAG, "Received empty URL - loading about:neterror");
return;
}
@ -143,9 +147,14 @@ public final class IntentHelper implements GeckoEventListener {
// TODO (bug 1173626): This will not handle android-app uris on non 5.1 devices.
intent = Intent.parseUri(uri, 0);
} catch (final URISyntaxException e) {
displayToastCannotOpenLink();
try {
openUnknownProtocolErrorPage(URLEncoder.encode(uri, "UTF-8"));
} catch (final UnsupportedEncodingException encodingE) {
openUnknownProtocolErrorPage("");
}
// Don't log the exception to prevent leaking URIs.
Log.w(LOGTAG, "Unable to parse Intent URI");
Log.w(LOGTAG, "Unable to parse Intent URI - loading about:neterror");
return;
}
@ -172,15 +181,21 @@ public final class IntentHelper implements GeckoEventListener {
Tabs.getInstance().loadUrl(fallbackUrl);
} else {
displayToastCannotOpenLink();
openUnknownProtocolErrorPage(intent.getData().toString());
// Don't log the URI to prevent leaking it.
Log.w(LOGTAG, "Unable to handle URI");
Log.w(LOGTAG, "Unable to open URI, default case - loading about:neterror");
}
}
private void displayToastCannotOpenLink() {
final String errText = activity.getResources().getString(R.string.intent_uri_cannot_open);
Toast.makeText(activity, errText, Toast.LENGTH_LONG).show();
/**
* Opens about:neterror with the unknownProtocolFound text.
* @param encodedUri The encoded uri. While the page does not open correctly without specifying
* a uri parameter, it happily accepts the empty String so this argument may
* be the empty String.
*/
private void openUnknownProtocolErrorPage(final String encodedUri) {
final String errorUri = UNKNOWN_PROTOCOL_URI_PREFIX + encodedUri;
Tabs.getInstance().loadUrl(errorUri);
}
private void openWebActivity(JSONObject message) throws JSONException {

View File

@ -502,13 +502,15 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange
}
private void stopZoomDisplay(boolean withAnimation) {
shouldSetVisibleOnUpdate = false;
hideZoomedView(withAnimation);
ThreadUtils.removeCallbacksFromUiThread(requestRenderRunnable);
if (layerView != null) {
layerView.setOnMetricsChangedZoomedViewportListener(null);
layerView.removeZoomedViewListener(this);
layerView = null;
if (getVisibility() == View.VISIBLE) {
shouldSetVisibleOnUpdate = false;
hideZoomedView(withAnimation);
ThreadUtils.removeCallbacksFromUiThread(requestRenderRunnable);
if (layerView != null) {
layerView.setOnMetricsChangedZoomedViewportListener(null);
layerView.removeZoomedViewListener(this);
layerView = null;
}
}
}

View File

@ -534,6 +534,7 @@ public abstract class BrowserToolbar extends ThemedRelativeLayout
private void updateProgressVisibility(Tab selectedTab, int progress) {
if (!isEditing() && selectedTab.getState() == Tab.STATE_LOADING) {
progressBar.setProgress(progress);
progressBar.setPrivateMode(selectedTab.isPrivate());
progressBar.setVisibility(View.VISIBLE);
} else {
progressBar.setVisibility(View.GONE);

View File

@ -129,7 +129,7 @@ public final class WaitHelper {
* returned from hasStateChanged, indicating this change of status.
*/
private interface ChangeVerifier {
public String getLogTag();
String getLogTag();
/**
* Stores the initial state of the system. This system state is used to diff against
@ -137,8 +137,8 @@ public final class WaitHelper {
* (with a timeout), this method could potentially store state inconsistent with
* what is visible to the user.
*/
public void storeState();
public boolean hasStateChanged();
void storeState();
boolean hasStateChanged();
}
private static class ToolbarTitleTextChangeVerifier implements ChangeVerifier {

View File

@ -258,4 +258,25 @@ this.LoginHelper = {
return newLogin;
},
/**
* Open the password manager window.
*
* @param {Window} window
* the window from where we want to open the dialog
*
* @param {string} [filterString=""]
* the filterString parameter to pass to the login manager dialog
*/
openPasswordManager(window, filterString = "") {
let win = Services.wm.getMostRecentWindow("Toolkit:PasswordManager");
if (win) {
win.setFilter(filterString);
win.focus();
} else {
window.openDialog("chrome://passwordmgr/content/passwordManager.xul",
"Toolkit:PasswordManager", "",
{filterString : filterString});
}
},
};

View File

@ -146,6 +146,16 @@ Probe.prototype = {
return this._impl.subtract(a, b);
},
importChildCompartments: function(parent, children) {
if (!Array.isArray(children)) {
throw new TypeError();
}
if (!parent || !(parent instanceof PerformanceData)) {
throw new TypeError();
}
return this._impl.importChildCompartments(parent, children);
},
/**
* The name of the probe.
*/
@ -228,7 +238,8 @@ let Probes = {
}
result.longestDuration = lastNonZero(result.durations);
return result;
}
},
importChildCompartments: function() { /* nothing to do */ },
}),
/**
@ -258,7 +269,8 @@ let Probes = {
return {
totalCPOWTime: a.totalCPOWTime - b.totalCPOWTime
};
}
},
importChildCompartments: function() { /* nothing to do */ },
}),
/**
@ -287,7 +299,8 @@ let Probes = {
return {
ticks: a.ticks - b.ticks
};
}
},
importChildCompartments: function() { /* nothing to do */ },
}),
"jank-content": new Probe("jank-content", {
@ -311,7 +324,8 @@ let Probes = {
},
subtract: function(a, b) {
return null;
}
},
importChildCompartments: function() { /* nothing to do */ },
}),
"cpow-content": new Probe("cpow-content", {
@ -335,15 +349,22 @@ let Probes = {
},
subtract: function(a, b) {
return null;
}
},
importChildCompartments: function() { /* nothing to do */ },
}),
"ticks-content": new Probe("ticks-content", {
_isActive: false,
set isActive(x) {
// Ignore: This probe is always active.
this._isActive = x;
if (x) {
Process.broadcast("acquire", ["ticks"]);
} else {
Process.broadcast("release", ["ticks"]);
}
},
get isActive() {
return true;
return this._isActive;
},
extract: function(xpcom) {
return {};
@ -353,8 +374,30 @@ let Probes = {
},
subtract: function(a, b) {
return null;
}
},
importChildCompartments: function() { /* nothing to do */ },
}),
compartments: new Probe("compartments", {
set isActive(x) {
performanceStatsService.isMonitoringPerCompartment = x;
},
get isActive() {
return performanceStatsService.isMonitoringPerCompartment;
},
extract: function(xpcom) {
return null;
},
isEqual: function(a, b) {
return true;
},
subtract: function(a, b) {
return true;
},
importChildCompartments: function(parent, children) {
parent.children = children;
},
})
};
@ -657,16 +700,32 @@ function PerformanceDiff(current, old = null) {
function Snapshot({xpcom, childProcesses, probes}) {
this.componentsData = [];
// Parent process
// Current process
if (xpcom) {
let children = new Map();
let enumeration = xpcom.getComponentsData().enumerate();
while (enumeration.hasMoreElements()) {
let xpcom = enumeration.getNext().QueryInterface(Ci.nsIPerformanceStats);
this.componentsData.push(new PerformanceData({xpcom, probes}));
let stat = new PerformanceData({xpcom, probes});
if (!stat.parentId) {
this.componentsData.push(stat);
} else {
let siblings = children.get(stat.parentId);
if (!siblings) {
children.set(stat.parentId, (siblings = []));
}
siblings.push(stat);
}
}
for (let parent of this.componentsData) {
for (let probe of probes) {
probe.importChildCompartments(parent, children.get(parent.groupId) || []);
}
}
}
// Content processes
// Child processes
if (childProcesses) {
for (let {componentsData} of childProcesses) {
// We are only interested in `componentsData` for the time being.

View File

@ -17,12 +17,12 @@
/**
* Snapshot of the performance of a component, e.g. an add-on, a web
* page, system built-ins, or the entire process itself.
* page, system built-ins, a module or the entire process itself.
*
* All values are monotonic and are updated only when
* `nsIPerformanceStatsService.isStopwatchActive` is `true`.
*/
[scriptable, uuid(47f8d36d-1d67-43cb-befd-d2f4720ac568)]
[scriptable, uuid(1bc2d016-e9ae-4186-97c6-9478eddda245)]
interface nsIPerformanceStats: nsISupports {
/**
* An identifier unique to the component.
@ -32,6 +32,16 @@ interface nsIPerformanceStats: nsISupports {
*/
readonly attribute AString groupId;
/**
* If this component is part of a larger component, the larger
* component. Otherwise, null.
*
* As of this writing, there can be at most two levels of components:
* - compartments (a single module, iframe, etc.);
* - groups (an entire add-on, an entire webpage, etc.).
*/
readonly attribute AString parentId;
/**
* The name of the component:
* - for the process itself, "<process>";
@ -112,7 +122,7 @@ interface nsIPerformanceSnapshot: nsISupports {
nsIPerformanceStats getProcessData();
};
[scriptable, builtinclass, uuid(0469e6af-95c3-4961-a385-4bc009128939)]
[scriptable, builtinclass, uuid(60973d54-13e2-455c-a3c6-84dea5dfc8b9)]
interface nsIPerformanceStatsService : nsISupports {
/**
* `true` if we should monitor CPOW, `false` otherwise.
@ -124,6 +134,13 @@ interface nsIPerformanceStatsService : nsISupports {
*/
[implicit_jscontext] attribute bool isMonitoringJank;
/**
* `true` if all compartments need to be monitored individually,
* `false` if only performance groups (i.e. entire add-ons, entire
* webpages, etc.) need to be monitored.
*/
[implicit_jscontext] attribute bool isMonitoringPerCompartment;
/**
* Capture a snapshot of the performance data.
*/

View File

@ -30,6 +30,7 @@
class nsPerformanceStats: public nsIPerformanceStats {
public:
nsPerformanceStats(const nsAString& aName,
nsIPerformanceStats* aParent,
const nsAString& aGroupId,
const nsAString& aAddonId,
const nsAString& aTitle,
@ -44,6 +45,10 @@ public:
, mIsSystem(aIsSystem)
, mPerformanceData(aPerformanceData)
{
if (aParent) {
mozilla::DebugOnly<nsresult> rv = aParent->GetGroupId(mParentId);
MOZ_ASSERT(NS_SUCCEEDED(rv));
}
}
explicit nsPerformanceStats() {}
@ -61,6 +66,12 @@ public:
return NS_OK;
};
/* readonly attribute AString parentId; */
NS_IMETHOD GetParentId(nsAString& aParentId) override {
aParentId.Assign(mParentId);
return NS_OK;
};
/* readonly attribute AString addonId; */
NS_IMETHOD GetAddonId(nsAString& aAddonId) override {
aAddonId.Assign(mAddonId);
@ -124,6 +135,7 @@ public:
private:
nsString mName;
nsString mParentId;
nsString mGroupId;
nsString mAddonId;
nsString mTitle;
@ -159,13 +171,18 @@ private:
* entire process, rather than the statistics for a specific set of
* compartments.
*/
already_AddRefed<nsIPerformanceStats> ImportStats(JSContext* cx, const js::PerformanceData& data, uint64_t uid);
already_AddRefed<nsIPerformanceStats> ImportStats(JSContext* cx, const js::PerformanceData& data, uint64_t uid, nsIPerformanceStats* parent);
/**
* Callbacks for iterating through the `PerformanceStats` of a runtime.
*/
bool IterPerformanceStatsCallbackInternal(JSContext* cx, const js::PerformanceData& stats, uint64_t uid);
static bool IterPerformanceStatsCallback(JSContext* cx, const js::PerformanceData& stats, uint64_t uid, void* self);
bool IterPerformanceStatsCallbackInternal(JSContext* cx,
const js::PerformanceData& ownStats, const uint64_t ownId,
const uint64_t* parentId);
static bool IterPerformanceStatsCallback(JSContext* cx,
const js::PerformanceData& ownStats, const uint64_t ownId,
const uint64_t* parentId,
void* self);
// If the context represents a window, extract the title and window ID.
// Otherwise, extract "" and 0.
@ -175,6 +192,11 @@ private:
void GetGroupId(JSContext*,
uint64_t uid,
nsString& groupId);
static void GetName(JSContext*,
JS::Handle<JSObject*> global,
nsString& name);
// If the context presents an add-on, extract the addon ID.
// Otherwise, extract "".
static void GetAddonId(JSContext*,
@ -188,6 +210,7 @@ private:
private:
nsCOMArray<nsIPerformanceStats> mComponentsData;
nsCOMPtr<nsIPerformanceStats> mProcessData;
nsBaseHashtable<nsUint64HashKey, nsCOMPtr<nsIPerformanceStats>, nsCOMPtr<nsIPerformanceStats> > mCachedStats;
uint64_t mProcessId;
};
@ -273,8 +296,44 @@ nsPerformanceSnapshot::GetIsSystem(JSContext*,
return nsContentUtils::IsSystemPrincipal(nsContentUtils::ObjectPrincipal(global));
}
/* static */
void
nsPerformanceSnapshot::GetName(JSContext* cx,
JS::Handle<JSObject*> global,
nsString& name)
{
nsAutoCString cname;
do {
// Attempt to use the URL as name.
nsCOMPtr<nsIPrincipal> principal = nsContentUtils::ObjectPrincipal(global);
if (!principal) {
break;
}
nsCOMPtr<nsIURI> uri;
nsresult rv = principal->GetURI(getter_AddRefs(uri));
if (NS_FAILED(rv) || !uri) {
break;
}
rv = uri->GetSpec(cname);
if (NS_FAILED(rv)) {
break;
}
name.Assign(NS_ConvertUTF8toUTF16(cname));
return;
} while(false);
xpc::GetCurrentCompartmentName(cx, cname);
name.Assign(NS_ConvertUTF8toUTF16(cname));
}
already_AddRefed<nsIPerformanceStats>
nsPerformanceSnapshot::ImportStats(JSContext* cx, const js::PerformanceData& performance, const uint64_t uid) {
nsPerformanceSnapshot::ImportStats(JSContext* cx, const js::PerformanceData& performance, const uint64_t uid, nsIPerformanceStats* parent) {
if (performance.ticks == 0) {
// Ignore compartments with no activity.
return nullptr;
}
JS::RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
if (!global) {
@ -293,28 +352,34 @@ nsPerformanceSnapshot::ImportStats(JSContext* cx, const js::PerformanceData& per
uint64_t windowId;
GetWindowData(cx, title, &windowId);
nsAutoString name;
nsAutoCString cname;
xpc::GetCurrentCompartmentName(cx, cname);
name.Assign(NS_ConvertUTF8toUTF16(cname));
nsString name;
GetName(cx, global, name);
bool isSystem = GetIsSystem(cx, global);
nsCOMPtr<nsIPerformanceStats> result =
new nsPerformanceStats(name, groupId, addonId, title, windowId, isSystem, performance);
new nsPerformanceStats(name, parent, groupId, addonId, title, windowId, isSystem, performance);
return result.forget();
}
/*static*/ bool
nsPerformanceSnapshot::IterPerformanceStatsCallback(JSContext* cx, const js::PerformanceData& stats, const uint64_t uid, void* self) {
return reinterpret_cast<nsPerformanceSnapshot*>(self)->IterPerformanceStatsCallbackInternal(cx, stats, uid);
nsPerformanceSnapshot::IterPerformanceStatsCallback(JSContext* cx,
const js::PerformanceData& stats, const uint64_t id,
const uint64_t* parentId,
void* self) {
return reinterpret_cast<nsPerformanceSnapshot*>(self)->IterPerformanceStatsCallbackInternal(cx, stats, id, parentId);
}
bool
nsPerformanceSnapshot::IterPerformanceStatsCallbackInternal(JSContext* cx, const js::PerformanceData& stats, const uint64_t uid) {
nsCOMPtr<nsIPerformanceStats> result = ImportStats(cx, stats, uid);
nsPerformanceSnapshot::IterPerformanceStatsCallbackInternal(JSContext* cx,
const js::PerformanceData& stats, const uint64_t id,
const uint64_t* parentId) {
nsCOMPtr<nsIPerformanceStats> parent = parentId ? mCachedStats.Get(*parentId) : nullptr;
nsCOMPtr<nsIPerformanceStats> result = ImportStats(cx, stats, id, parent);
if (result) {
mComponentsData.AppendElement(result);
mCachedStats.Put(id, result);
}
return true;
@ -328,8 +393,12 @@ nsPerformanceSnapshot::Init(JSContext* cx, uint64_t processId) {
return NS_ERROR_UNEXPECTED;
}
nsAutoString processGroupId;
processGroupId.AssignLiteral("process: ");
processGroupId.AppendInt(processId);
mProcessData = new nsPerformanceStats(NS_LITERAL_STRING("<process>"), // name
NS_LITERAL_STRING("<process:?>"), // group id
nullptr, // parent
processGroupId, // group id
NS_LITERAL_STRING(""), // add-on id
NS_LITERAL_STRING(""), // title
0, // window id
@ -405,6 +474,20 @@ NS_IMETHODIMP nsPerformanceStatsService::SetIsMonitoringJank(JSContext* cx, bool
}
return NS_OK;
}
NS_IMETHODIMP nsPerformanceStatsService::GetIsMonitoringPerCompartment(JSContext* cx, bool *aIsStopwatchActive)
{
JSRuntime *runtime = JS_GetRuntime(cx);
*aIsStopwatchActive = js::GetStopwatchIsMonitoringPerCompartment(runtime);
return NS_OK;
}
NS_IMETHODIMP nsPerformanceStatsService::SetIsMonitoringPerCompartment(JSContext* cx, bool aIsStopwatchActive)
{
JSRuntime *runtime = JS_GetRuntime(cx);
if (!js::SetStopwatchIsMonitoringPerCompartment(runtime, aIsStopwatchActive)) {
return NS_ERROR_OUT_OF_MEMORY;
}
return NS_OK;
}
/* readonly attribute nsIPerformanceSnapshot snapshot; */
NS_IMETHODIMP nsPerformanceStatsService::GetSnapshot(JSContext* cx, nsIPerformanceSnapshot * *aSnapshot)

View File

@ -28,7 +28,7 @@ function frameScript() {
getService(Ci.nsIPerformanceStatsService);
// Make sure that the stopwatch is now active.
let monitor = PerformanceStats.getMonitor(["jank", "cpow", "ticks"]);
let monitor = PerformanceStats.getMonitor(["jank", "cpow", "ticks", "compartments"]);
addMessageListener("compartments-test:getStatistics", () => {
try {
@ -158,8 +158,10 @@ function monotinicity_tester(source, testName) {
["jank", "totalSystemTime"],
["cpow", "totalCPOWTime"]
]) {
SilentAssert.leq(item[probe][k], snapshot.processData[probe][k],
`Sanity check (${testName}): component has a lower ${k} than process`);
// Note that we cannot expect components data to be always smaller
// than process data, as `getrusage` & co are not monotonic.
SilentAssert.leq(item[probe][k], 2 * snapshot.processData[probe][k],
`Sanity check (${testName}): ${k} of component is not impossibly larger than that of process`);
}
let key = item.groupId;
@ -169,6 +171,24 @@ function monotinicity_tester(source, testName) {
}
map.set(key, item);
}
for (let item of snapshot.componentsData) {
if (!item.parentId) {
continue;
}
let parent = map.get(item.parentId);
SilentAssert.ok(parent, `The parent exists ${item.parentId}`);
for (let [probe, k] of [
["jank", "totalUserTime"],
["jank", "totalSystemTime"],
["cpow", "totalCPOWTime"]
]) {
// Note that we cannot expect components data to be always smaller
// than parent data, as `getrusage` & co are not monotonic.
SilentAssert.leq(item[probe][k], 2 * parent[probe][k],
`Sanity check (${testName}): ${k} of component is not impossibly larger than that of parent`);
}
}
for (let [key, item] of map) {
sanityCheck(previous.componentsMap.get(key), item);
previous.componentsMap.set(key, item);

View File

@ -19,10 +19,12 @@ let promiseStatistics = Task.async(function*(name) {
let componentsData = [];
let componentsEnum = snapshot.getComponentsData().enumerate();
while (componentsEnum.hasMoreElements()) {
componentsData.push(componentsEnum.getNext().QueryInterface(Ci.nsIPerformanceStats));
let data = componentsEnum.getNext().QueryInterface(Ci.nsIPerformanceStats);
let normalized = JSON.parse(JSON.stringify(data));
componentsData.push(data);
}
return {
processData: snapshot.getProcessData(),
processData: JSON.parse(JSON.stringify(snapshot.getProcessData())),
componentsData
};
});
@ -35,11 +37,19 @@ let promiseSetMonitoring = Task.async(function*(to) {
yield Promise.resolve();
});
let promiseSetPerCompartment = Task.async(function*(to) {
let service = Cc["@mozilla.org/toolkit/performance-stats-service;1"].
getService(Ci.nsIPerformanceStatsService);
service.isMonitoringPerCompartment = to;
yield Promise.resolve();
});
function getBuiltinStatistics(name, snapshot) {
let stats = snapshot.componentsData.find(stats =>
stats.isSystem && !stats.addonId
);
do_print(`Built-in statistics for ${name} were ${stats?"":"not "}found`);
do_print(JSON.stringify(snapshot.componentsData, null, "\t"));
return stats;
}
@ -57,17 +67,25 @@ function burnCPU(ms) {
}
function ensureEquals(snap1, snap2, name) {
Assert.equal(
JSON.stringify(snap1.processData),
JSON.stringify(snap2.processData),
"Same process data: " + name);
for (let k of Object.keys(snap1.processData)) {
if (k == "ticks") {
// Ticks monitoring cannot be deactivated
continue;
}
Assert.equal(snap1.processData[k], snap2.processData[k], `Same process data value ${k} (${name})`)
}
let stats1 = snap1.componentsData.sort((a, b) => a.name <= b.name);
let stats2 = snap2.componentsData.sort((a, b) => a.name <= b.name);
Assert.equal(
JSON.stringify(stats1),
JSON.stringify(stats2),
"Same components data: " + name
);
Assert.equal(stats1.length, stats2.length, `Same number of components (${name})`);
for (let i = 0; i < stats1.length; ++i) {
for (let k of Object.keys(stats1[i])) {
if (k == "ticks") {
// Ticks monitoring cannot be deactivated
continue;
}
Assert.equal(stats1[i][k], stats1[i][k], `Same component data value ${i} ${k} (${name})`)
}
}
}
function hasLowPrecision() {
@ -88,6 +106,7 @@ function hasLowPrecision() {
add_task(function* test_measure() {
let skipPrecisionTests = hasLowPrecision();
yield promiseSetPerCompartment(false);
do_print("Burn CPU without the stopwatch");
yield promiseSetMonitoring(false);
@ -137,4 +156,14 @@ add_task(function* test_measure() {
Assert.equal(builtin2.totalCPOWTime, builtin1.totalCPOWTime, "No CPOW for built-in statistics");
Assert.equal(builtin4.totalUserTime, builtin3.totalUserTime, "After deactivating the stopwatch, we didn't count any time for the built-in");
Assert.equal(builtin4.totalCPOWTime, builtin3.totalCPOWTime, "After deactivating the stopwatch, we didn't count any CPOW time for the built-in");
// Ideally, we should be able to look for test_compartments.js, but
// it doesn't have its own compartment.
for (let stats of [stats1, stats2, stats3, stats4]) {
Assert.ok(!stats.componentsData.find(x => x.name.includes("Task.jsm")), "At this stage, Task.jsm doesn't show up in the components data");
}
yield promiseSetPerCompartment(true);
burnCPU(300);
let stats5 = yield promiseStatistics("With per-compartment monitoring");
Assert.ok(stats5.componentsData.find(x => x.name.includes("Task.jsm")), "With per-compartment monitoring, test_compartments.js shows up");
});

View File

@ -3,3 +3,4 @@ head=
tail=
[test_compartments.js]
skip-if = toolkit == 'gonk' # Fails on b2g emulator, bug 1147664