Bug 881209 - Scrolling in the variables view is much choppier after bug 760370, r=past

This commit is contained in:
Victor Porof 2013-06-21 17:33:56 +03:00
parent 5d176875e9
commit af8328e211
6 changed files with 212 additions and 195 deletions

View File

@ -47,13 +47,13 @@ const DEFAULT_EDITOR_CONFIG = {
showLineNumbers: true
};
const GENERIC_VARIABLES_VIEW_SETTINGS = {
lazyEmpty: false,
lazyEmpty: true,
lazyEmptyDelay: 10, // ms
searchEnabled: true,
descriptorTooltip: false,
editableValueTooltip: "",
editableNameTooltip: "",
preventDisableOnChage: true,
preventDescriptorModifiers: true,
eval: () => {},
switch: () => {}
};
@ -1446,7 +1446,7 @@ NetworkDetailsView.prototype = {
headersScope.expanded = true;
for (let header of aResponse.headers) {
let headerVar = headersScope.addItem(header.name, { null: true }, true);
let headerVar = headersScope.addItem(header.name, {}, true);
gNetwork.getString(header.value).then((aString) => headerVar.setGrip(aString));
}
},
@ -1489,7 +1489,7 @@ NetworkDetailsView.prototype = {
cookiesScope.expanded = true;
for (let cookie of aResponse.cookies) {
let cookieVar = cookiesScope.addItem(cookie.name, { null: true }, true);
let cookieVar = cookiesScope.addItem(cookie.name, {}, true);
gNetwork.getString(cookie.value).then((aString) => cookieVar.setGrip(aString));
// By default the cookie name and value are shown. If this is the only
@ -1591,7 +1591,7 @@ NetworkDetailsView.prototype = {
paramsScope.expanded = true;
for (let param of paramsArray) {
let headerVar = paramsScope.addItem(param.name, { null: true }, true);
let headerVar = paramsScope.addItem(param.name, {}, true);
headerVar.setGrip(param.value);
}
},

View File

@ -245,6 +245,16 @@ VariablesView.prototype = {
*/
preventDisableOnChage: false,
/**
* Specifies if, whenever a variable or property descriptor is available,
* configurable, enumerable, writable, frozen, sealed and extensible
* attributes should not affect presentation.
*
* This flag is applied recursively onto each scope in this view and
* affects only the child nodes when they're created.
*/
preventDescriptorModifiers: false,
/**
* The tooltip text shown on a variable or property's value if an |eval|
* function is provided, in order to change the variable or property's value.
@ -282,15 +292,6 @@ VariablesView.prototype = {
*/
deleteButtonTooltip: STR.GetStringFromName("variablesCloseButtonTooltip"),
/**
* Specifies if the configurable, enumerable or writable tooltip should be
* shown whenever a variable or property descriptor is available.
*
* This flag is applied recursively onto each scope in this view and
* affects only the child nodes when they're created.
*/
descriptorTooltip: true,
/**
* Specifies the context menu attribute set on variables and properties.
*
@ -1086,7 +1087,6 @@ function Scope(aView, aName, aFlags = {}) {
this._openEnum = this._openEnum.bind(this);
this._openNonEnum = this._openNonEnum.bind(this);
this._batchAppend = this._batchAppend.bind(this);
this._batchItems = [];
// Inherit properties and flags from the parent view. You can override
// each of these directly onto any scope, variable or property instance.
@ -1097,13 +1097,18 @@ function Scope(aView, aName, aFlags = {}) {
this.editableNameTooltip = aView.editableNameTooltip;
this.editButtonTooltip = aView.editButtonTooltip;
this.deleteButtonTooltip = aView.deleteButtonTooltip;
this.descriptorTooltip = aView.descriptorTooltip;
this.preventDescriptorModifiers = aView.preventDescriptorModifiers;
this.contextMenuId = aView.contextMenuId;
this.separatorStr = aView.separatorStr;
this._store = new Map();
this._enumItems = [];
this._nonEnumItems = [];
// Creating maps and arrays thousands of times for variables or properties
// with a large number of children fills up a lot of memory. Make sure
// these are instantiated only if needed.
XPCOMUtils.defineLazyGetter(this, "_store", () => new Map());
XPCOMUtils.defineLazyGetter(this, "_enumItems", () => []);
XPCOMUtils.defineLazyGetter(this, "_nonEnumItems", () => []);
XPCOMUtils.defineLazyGetter(this, "_batchItems", () => []);
this._init(aName.trim(), aFlags);
}
@ -1991,7 +1996,7 @@ Scope.prototype = {
editableNameTooltip: "",
editButtonTooltip: "",
deleteButtonTooltip: "",
descriptorTooltip: true,
preventDescriptorModifiers: false,
contextMenuId: "",
separatorStr: "",
@ -2034,7 +2039,7 @@ Scope.prototype = {
* The variable's descriptor.
*/
function Variable(aScope, aName, aDescriptor) {
this._displayTooltip = this._displayTooltip.bind(this);
this._setTooltips = this._setTooltips.bind(this);
this._activateNameInput = this._activateNameInput.bind(this);
this._activateValueInput = this._activateValueInput.bind(this);
@ -2265,7 +2270,7 @@ Variable.prototype = Heritage.extend(Scope.prototype, {
if (this._nameString) {
this._displayVariable();
this._customizeVariable();
this._prepareTooltip();
this._prepareTooltips();
this._setAttributes();
this._addEventListeners();
}
@ -2353,7 +2358,10 @@ Variable.prototype = Heritage.extend(Scope.prototype, {
* Adds specific nodes for this variable based on custom flags.
*/
_customizeVariable: function() {
if (this.ownerView.eval) {
let ownerView = this.ownerView;
let descriptor = this._initialDescriptor;
if (ownerView.eval) {
if (!this._isUndefined && (this.getter || this.setter)) {
let editNode = this._editNode = this.document.createElement("toolbarbutton");
editNode.className = "plain variables-view-edit";
@ -2361,61 +2369,95 @@ Variable.prototype = Heritage.extend(Scope.prototype, {
this._title.appendChild(editNode);
}
}
if (this.ownerView.delete) {
if (!this._isUndefined || !(this.ownerView.getter && this.ownerView.setter)) {
if (ownerView.delete) {
if (!this._isUndefined || !(ownerView.getter && ownerView.setter)) {
let deleteNode = this._deleteNode = this.document.createElement("toolbarbutton");
deleteNode.className = "plain variables-view-delete";
deleteNode.addEventListener("click", this._onDelete.bind(this), false);
this._title.appendChild(deleteNode);
}
}
if (this.ownerView.contextMenuId) {
this._title.setAttribute("context", this.ownerView.contextMenuId);
if (ownerView.contextMenuId) {
this._title.setAttribute("context", ownerView.contextMenuId);
}
},
/**
* Prepares a tooltip for this variable.
*/
_prepareTooltip: function() {
this._target.addEventListener("mouseover", this._displayTooltip, false);
},
if (ownerView.preventDescriptorModifiers) {
return;
}
/**
* Creates a tooltip for this variable.
*/
_displayTooltip: function() {
this._target.removeEventListener("mouseover", this._displayTooltip, false);
if (this.ownerView.descriptorTooltip) {
let document = this.document;
let tooltip = document.createElement("tooltip");
tooltip.id = "tooltip-" + this._idString;
tooltip.setAttribute("orient", "horizontal");
let labels = ["configurable", "enumerable", "writable", "native-getter",
"frozen", "sealed", "non-extensible"];
for (let label of labels) {
let labelElement = document.createElement("label");
labelElement.setAttribute("value", label);
tooltip.appendChild(labelElement);
if (!descriptor.writable && !ownerView.getter && !ownerView.setter) {
let nonWritableIcon = this.document.createElement("hbox");
nonWritableIcon.className = "variable-or-property-non-writable-icon";
this._title.appendChild(nonWritableIcon);
}
if (descriptor.value && typeof descriptor.value == "object") {
if (descriptor.value.frozen) {
let frozenLabel = this.document.createElement("label");
frozenLabel.className = "plain variable-or-property-frozen-label";
frozenLabel.setAttribute("value", "F");
this._title.appendChild(frozenLabel);
}
if (descriptor.value.sealed) {
let sealedLabel = this.document.createElement("label");
sealedLabel.className = "plain variable-or-property-sealed-label";
sealedLabel.setAttribute("value", "S");
this._title.appendChild(sealedLabel);
}
if (!descriptor.value.extensible) {
let nonExtensibleLabel = this.document.createElement("label");
nonExtensibleLabel.className = "plain variable-or-property-non-extensible-label";
nonExtensibleLabel.setAttribute("value", "N");
this._title.appendChild(nonExtensibleLabel);
}
}
},
this._target.appendChild(tooltip);
this._target.setAttribute("tooltip", tooltip.id);
/**
* Prepares all tooltips for this variable.
*/
_prepareTooltips: function() {
this._target.addEventListener("mouseover", this._setTooltips, false);
},
/**
* Sets all tooltips for this variable.
*/
_setTooltips: function() {
this._target.removeEventListener("mouseover", this._setTooltips, false);
let ownerView = this.ownerView;
if (ownerView.preventDescriptorModifiers) {
return;
}
if (this.ownerView.eval && !this._isUndefined && (this.getter || this.setter)) {
this._editNode.setAttribute("tooltiptext", this.ownerView.editButtonTooltip);
let tooltip = this.document.createElement("tooltip");
tooltip.id = "tooltip-" + this._idString;
tooltip.setAttribute("orient", "horizontal");
let labels = [
"configurable", "enumerable", "writable",
"frozen", "sealed", "extensible", "native-getter"];
for (let label of labels) {
let labelElement = this.document.createElement("label");
labelElement.setAttribute("value", label);
tooltip.appendChild(labelElement);
}
if (this.ownerView.eval) {
this._valueLabel.setAttribute("tooltiptext", this.ownerView.editableValueTooltip);
this._target.appendChild(tooltip);
this._target.setAttribute("tooltip", tooltip.id);
if (this._editNode && ownerView.eval) {
this._editNode.setAttribute("tooltiptext", ownerView.editButtonTooltip);
}
if (this.ownerView.switch) {
this._name.setAttribute("tooltiptext", this.ownerView.editableNameTooltip);
if (this._valueLabel && ownerView.eval) {
this._valueLabel.setAttribute("tooltiptext", ownerView.editableValueTooltip);
}
if (this.ownerView.delete) {
this._deleteNode.setAttribute("tooltiptext", this.ownerView.deleteButtonTooltip);
if (this._name && ownerView.switch) {
this._name.setAttribute("tooltiptext", ownerView.editableNameTooltip);
}
if (this._deleteNode && ownerView.delete) {
this._deleteNode.setAttribute("tooltiptext", ownerView.deleteButtonTooltip);
}
},
@ -2424,48 +2466,56 @@ Variable.prototype = Heritage.extend(Scope.prototype, {
* and specifies if it's a 'this', '<exception>' or '__proto__' reference.
*/
_setAttributes: function() {
let ownerView = this.ownerView;
if (ownerView.preventDescriptorModifiers) {
return;
}
let descriptor = this._initialDescriptor;
let target = this._target;
let name = this._nameString;
if (this.ownerView.eval) {
this._target.setAttribute("editable", "");
if (ownerView.eval) {
target.setAttribute("editable", "");
}
if (!descriptor.null) {
if (!descriptor.configurable) {
this._target.setAttribute("non-configurable", "");
if (!descriptor.configurable) {
target.setAttribute("non-configurable", "");
}
if (!descriptor.enumerable) {
target.setAttribute("non-enumerable", "");
}
if (!descriptor.writable && !ownerView.getter && !ownerView.setter) {
target.setAttribute("non-writable", "");
}
if (descriptor.value && typeof descriptor.value == "object") {
if (descriptor.value.frozen) {
target.setAttribute("frozen", "");
}
if (!descriptor.enumerable) {
this._target.setAttribute("non-enumerable", "");
if (descriptor.value.sealed) {
target.setAttribute("sealed", "");
}
if (!descriptor.writable && !this.ownerView.getter && !this.ownerView.setter) {
this._target.setAttribute("non-writable", "");
}
if (descriptor.value && typeof descriptor.value == "object") {
if (descriptor.value.frozen) {
this._target.setAttribute("frozen", "");
}
if (descriptor.value.sealed) {
this._target.setAttribute("sealed", "");
}
if (!descriptor.value.extensible) {
this._target.setAttribute("non-extensible", "");
}
if (!descriptor.value.extensible) {
target.setAttribute("non-extensible", "");
}
}
if (descriptor && "getterValue" in descriptor) {
this._target.setAttribute("safe-getter", "");
target.setAttribute("safe-getter", "");
}
if (name == "this") {
this._target.setAttribute("self", "");
target.setAttribute("self", "");
}
else if (name == "<exception>") {
this._target.setAttribute("exception", "");
target.setAttribute("exception", "");
}
else if (name == "<return>") {
this._target.setAttribute("return", "");
target.setAttribute("return", "");
}
else if (name == "__proto__") {
this._target.setAttribute("proto", "");
target.setAttribute("proto", "");
}
},
@ -2793,7 +2843,7 @@ Property.prototype = Heritage.extend(Variable.prototype, {
if (this._nameString) {
this._displayVariable();
this._customizeVariable();
this._prepareTooltip();
this._prepareTooltips();
this._setAttributes();
this._addEventListeners();
}
@ -2937,11 +2987,11 @@ VariablesView.isPrimitive = function(aDescriptor) {
// As described in the remote debugger protocol, the value grip
// must be contained in a 'value' property.
let grip = aDescriptor.value;
if (!grip || typeof grip != "object") {
if (typeof grip != "object") {
return true;
}
// For convenience, undefined, null and long strings are considered primitives.
// For convenience, undefined, null and long strings are considered types.
let type = grip.type;
if (type == "undefined" || type == "null" || type == "longString") {
return true;
@ -2968,9 +3018,8 @@ VariablesView.isUndefined = function(aDescriptor) {
// As described in the remote debugger protocol, the value grip
// must be contained in a 'value' property.
// For convenience, undefined is considered a type.
let grip = aDescriptor.value;
if (grip && grip.type == "undefined") {
if (typeof grip == "object" && grip.type == "undefined") {
return true;
}

View File

@ -50,3 +50,10 @@
.variable-or-property[non-match] > .title {
display: none;
}
.variable-or-property:not([safe-getter]) > tooltip > label[value=native-getter],
.variable-or-property:not([non-extensible]) > tooltip > label[value=extensible],
.variable-or-property:not([frozen]) > tooltip > label[value=frozen],
.variable-or-property:not([sealed]) > tooltip > label[value=sealed] {
display: none;
}

View File

@ -491,9 +491,9 @@
color: #333;
}
/* Non enumerable, configurable and writable variables and properties */
/* Custom configurable/enumerable/writable or frozen/sealed/extensible
* variables and properties */
.variable-or-property[proto] > .title > .name,
.variable-or-property[non-enumerable]:not([self]):not([exception]):not([return]) > .title > .name {
opacity: 0.5;
}
@ -502,17 +502,11 @@
border-bottom: 1px dashed #99f;
}
.variable-or-property[non-configurable][non-writable] > .title > .name {
.variable-or-property[non-writable] > .title > .name {
border-bottom: 1px dashed #f99;
}
.variable-or-property[safe-getter] > .title > .name {
border-bottom: 1px dashed #8b0;
}
.variable-or-property[non-writable] > .title:after {
content: " ";
display: inline-block;
.variable-or-property-non-writable-icon {
background: url("chrome://browser/skin/identity-icons-https.png") no-repeat;
width: 16px;
height: 16px;
@ -520,12 +514,25 @@
}
@media (min-resolution: 2dppx) {
.variable-or-property[non-writable] > .title:after {
.variable-or-property-non-writable-icon {
background-image: url("chrome://browser/skin/identity-icons-https@2x.png");
background-size: 32px;
}
}
.variable-or-property-frozen-label,
.variable-or-property-sealed-label,
.variable-or-property-non-extensible-label {
-moz-padding-end: 4px;
color: #666;
}
/* Special variables and properties */
.variable-or-property[safe-getter] > .title > .name {
border-bottom: 1px dashed #8b0;
}
.variable-or-property[exception]:not(:focus) > .title > .name {
color: #a00;
text-shadow: 0 0 8px #fcc;
@ -536,21 +543,6 @@
text-shadow: 0 0 8px #cfc;
}
.variable-or-property[non-extensible]:not([non-writable]) > .title:after {
content: "N";
display: inline-block;
}
.variable-or-property[sealed]:not([non-writable]) > .title:after {
content: "S";
display: inline-block;
}
.variable-or-property[frozen]:not([non-writable]) > .title:after {
content: "F";
display: inline-block;
}
/* Variables and properties tooltips */
.variable-or-property > tooltip > label {
@ -559,17 +551,12 @@
.variable-or-property[non-enumerable] > tooltip > label[value=enumerable],
.variable-or-property[non-configurable] > tooltip > label[value=configurable],
.variable-or-property[non-writable] > tooltip > label[value=writable] {
.variable-or-property[non-writable] > tooltip > label[value=writable],
.variable-or-property[non-extensible] > tooltip > label[value=extensible] {
color: #800;
text-decoration: line-through;
}
.variable-or-property:not([safe-getter]) > tooltip > label[value=native-getter],
.variable-or-property:not([non-extensible]) > tooltip > label[value=non-extensible],
.variable-or-property:not([frozen]) > tooltip > label[value=frozen],
.variable-or-property:not([sealed]) > tooltip > label[value=sealed] {
display: none;
}
/* Variables and properties editing */
.variables-view-delete {

View File

@ -491,9 +491,9 @@
color: #333;
}
/* Non enumerable, configurable and writable variables and properties */
/* Custom configurable/enumerable/writable or frozen/sealed/extensible
* variables and properties */
.variable-or-property[proto] > .title > .name,
.variable-or-property[non-enumerable]:not([self]):not([exception]):not([return]) > .title > .name {
opacity: 0.5;
}
@ -502,17 +502,11 @@
border-bottom: 1px dashed #99f;
}
.variable-or-property[non-configurable][non-writable] > .title > .name {
.variable-or-property[non-writable] > .title > .name {
border-bottom: 1px dashed #f99;
}
.variable-or-property[safe-getter] > .title > .name {
border-bottom: 1px dashed #8b0;
}
.variable-or-property[non-writable] > .title:after {
content: " ";
display: inline-block;
.variable-or-property-non-writable-icon {
background: url("chrome://browser/skin/identity-icons-https.png") no-repeat;
width: 16px;
height: 16px;
@ -520,12 +514,25 @@
}
@media (min-resolution: 2dppx) {
.variable-or-property[non-writable] > .title:after {
.variable-or-property-non-writable-icon {
background-image: url("chrome://browser/skin/identity-icons-https@2x.png");
background-size: 32px;
}
}
.variable-or-property-frozen-label,
.variable-or-property-sealed-label,
.variable-or-property-non-extensible-label {
-moz-padding-end: 4px;
color: #666;
}
/* Special variables and properties */
.variable-or-property[safe-getter] > .title > .name {
border-bottom: 1px dashed #8b0;
}
.variable-or-property[exception]:not(:focus) > .title > .name {
color: #a00;
text-shadow: 0 0 8px #fcc;
@ -536,21 +543,6 @@
text-shadow: 0 0 8px #cfc;
}
.variable-or-property[non-extensible]:not([non-writable]) > .title:after {
content: "N";
display: inline-block;
}
.variable-or-property[sealed]:not([non-writable]) > .title:after {
content: "S";
display: inline-block;
}
.variable-or-property[frozen]:not([non-writable]) > .title:after {
content: "F";
display: inline-block;
}
/* Variables and properties tooltips */
.variable-or-property > tooltip > label {
@ -559,17 +551,12 @@
.variable-or-property[non-enumerable] > tooltip > label[value=enumerable],
.variable-or-property[non-configurable] > tooltip > label[value=configurable],
.variable-or-property[non-writable] > tooltip > label[value=writable] {
.variable-or-property[non-writable] > tooltip > label[value=writable],
.variable-or-property[non-extensible] > tooltip > label[value=extensible] {
color: #800;
text-decoration: line-through;
}
.variable-or-property:not([safe-getter]) > tooltip > label[value=native-getter],
.variable-or-property:not([non-extensible]) > tooltip > label[value=non-extensible],
.variable-or-property:not([frozen]) > tooltip > label[value=frozen],
.variable-or-property:not([sealed]) > tooltip > label[value=sealed] {
display: none;
}
/* Variables and properties editing */
.variables-view-delete {

View File

@ -494,9 +494,9 @@
color: #333;
}
/* Non enumerable, configurable and writable variables and properties */
/* Custom configurable/enumerable/writable or frozen/sealed/extensible
* variables and properties */
.variable-or-property[proto] > .title > .name,
.variable-or-property[non-enumerable]:not([self]):not([exception]):not([return]) > .title > .name {
opacity: 0.5;
}
@ -505,17 +505,11 @@
border-bottom: 1px dashed #99f;
}
.variable-or-property[non-configurable][non-writable] > .title > .name {
.variable-or-property[non-writable] > .title > .name {
border-bottom: 1px dashed #f99;
}
.variable-or-property[safe-getter] > .title > .name {
border-bottom: 1px dashed #8b0;
}
.variable-or-property[non-writable] > .title:after {
content: " ";
display: inline-block;
.variable-or-property-non-writable-icon {
background: url("chrome://browser/skin/identity-icons-https.png") no-repeat;
width: 16px;
height: 16px;
@ -523,12 +517,25 @@
}
@media (min-resolution: 2dppx) {
.variable-or-property[non-writable] > .title:after {
.variable-or-property-non-writable-icon {
background-image: url("chrome://browser/skin/identity-icons-https@2x.png");
background-size: 32px;
}
}
.variable-or-property-frozen-label,
.variable-or-property-sealed-label,
.variable-or-property-non-extensible-label {
-moz-padding-end: 4px;
color: #666;
}
/* Special variables and properties */
.variable-or-property[safe-getter] > .title > .name {
border-bottom: 1px dashed #8b0;
}
.variable-or-property[exception]:not(:focus) > .title > .name {
color: #a00;
text-shadow: 0 0 8px #fcc;
@ -539,21 +546,6 @@
text-shadow: 0 0 8px #cfc;
}
.variable-or-property[non-extensible]:not([non-writable]) > .title:after {
content: "N";
display: inline-block;
}
.variable-or-property[sealed]:not([non-writable]) > .title:after {
content: "S";
display: inline-block;
}
.variable-or-property[frozen]:not([non-writable]) > .title:after {
content: "F";
display: inline-block;
}
/* Variables and properties tooltips */
.variable-or-property > tooltip > label {
@ -562,17 +554,12 @@
.variable-or-property[non-enumerable] > tooltip > label[value=enumerable],
.variable-or-property[non-configurable] > tooltip > label[value=configurable],
.variable-or-property[non-writable] > tooltip > label[value=writable] {
.variable-or-property[non-writable] > tooltip > label[value=writable],
.variable-or-property[non-extensible] > tooltip > label[value=extensible] {
color: #800;
text-decoration: line-through;
}
.variable-or-property:not([safe-getter]) > tooltip > label[value=native-getter],
.variable-or-property:not([non-extensible]) > tooltip > label[value=non-extensible],
.variable-or-property:not([frozen]) > tooltip > label[value=frozen],
.variable-or-property:not([sealed]) > tooltip > label[value=sealed] {
display: none;
}
/* Variables and properties editing */
.variables-view-delete {