Merge latest green birch changeset and mozilla-central

This commit is contained in:
Ed Morley 2013-06-17 09:35:37 +01:00
commit 6c810f6e43
80 changed files with 4155 additions and 1075 deletions

View File

@ -24,6 +24,7 @@ Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
Cu.import("resource:///modules/devtools/BreadcrumbsWidget.jsm");
Cu.import("resource:///modules/devtools/SideMenuWidget.jsm");
Cu.import("resource:///modules/devtools/VariablesView.jsm");
Cu.import("resource:///modules/devtools/VariablesViewController.jsm");
Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Parser",
@ -73,6 +74,24 @@ let DebuggerController = {
DebuggerView.initialize(() => {
DebuggerView._isInitialized = true;
VariablesViewController.attach(DebuggerView.Variables, {
getGripClient: aObject => {
return this.activeThread.pauseGrip(aObject);
}
});
// Relay events from the VariablesView.
DebuggerView.Variables.on("fetched", (aEvent, aType) => {
switch (aType) {
case "variables":
window.dispatchEvent(document, "Debugger:FetchedVariables");
break;
case "properties":
window.dispatchEvent(document, "Debugger:FetchedProperties");
break;
}
});
// Chrome debugging needs to initiate the connection by itself.
if (window._isChromeDebugger) {
this.connect().then(deferred.resolve);
@ -403,6 +422,7 @@ ThreadState.prototype = {
}
};
/**
* Keeps the stack frame list up-to-date, using the thread client's
* stack frame cache.
@ -413,9 +433,6 @@ function StackFrames() {
this._onFrames = this._onFrames.bind(this);
this._onFramesCleared = this._onFramesCleared.bind(this);
this._afterFramesCleared = this._afterFramesCleared.bind(this);
this._fetchScopeVariables = this._fetchScopeVariables.bind(this);
this._fetchVarProperties = this._fetchVarProperties.bind(this);
this._addVarExpander = this._addVarExpander.bind(this);
this.evaluate = this.evaluate.bind(this);
}
@ -588,7 +605,12 @@ StackFrames.prototype = {
DebuggerView.StackFrames.empty();
for (let frame of this.activeThread.cachedFrames) {
this._addFrame(frame);
let depth = frame.depth;
let { url, line } = frame.where;
let frameLocation = NetworkHelper.convertToUnicode(unescape(url));
let frameTitle = StackFrameUtils.getFrameTitle(frame);
DebuggerView.StackFrames.addFrame(frameTitle, frameLocation, line, depth);
}
if (this.currentFrame == null) {
DebuggerView.StackFrames.selectedDepth = 0;
@ -661,6 +683,7 @@ StackFrames.prototype = {
// Clear existing scopes and create each one dynamically.
DebuggerView.Variables.empty();
// If watch expressions evaluation results are available, create a scope
// to contain all the values.
if (this.syncedWatchExpressions && watchExpressionsEvaluation) {
@ -684,18 +707,20 @@ StackFrames.prototype = {
// Create a scope to contain all the inspected variables.
let label = StackFrameUtils.getScopeLabel(environment);
let scope = DebuggerView.Variables.addScope(label);
let innermost = environment == frame.environment;
// Handle additions to the innermost scope.
if (environment == frame.environment) {
// Handle special additions to the innermost scope.
if (innermost) {
this._insertScopeFrameReferences(scope, frame);
this._addScopeExpander(scope, environment);
// Always expand the innermost scope by default.
scope.expand();
}
// Lazily add nodes for every other environment scope.
else {
this._addScopeExpander(scope, environment);
this.autoScopeExpand && scope.expand();
DebuggerView.Variables.controller.addExpander(scope, environment);
// The innermost scope is always automatically expanded, because it
// contains the variables in the current stack frame which are likely to
// be inspected.
if (innermost || this.autoScopeExpand) {
scope.expand();
}
} while ((environment = environment.parent));
@ -704,49 +729,6 @@ StackFrames.prototype = {
DebuggerView.Variables.commitHierarchy();
},
/**
* Adds an 'onexpand' callback for a scope, lazily handling
* the addition of new variables.
*
* @param Scope aScope
* The scope where the variables will be placed into.
* @param object aEnv
* The scope's environment.
*/
_addScopeExpander: function(aScope, aEnv) {
aScope._sourceEnvironment = aEnv;
// It's a good idea to be prepared in case of an expansion.
aScope.addEventListener("mouseover", this._fetchScopeVariables, false);
// Make sure that variables are always available on expansion.
aScope.onexpand = this._fetchScopeVariables;
},
/**
* Adds an 'onexpand' callback for a variable, lazily handling
* the addition of new properties.
*
* @param Variable aVar
* The variable where the properties will be placed into.
* @param any aGrip
* The grip of the variable.
*/
_addVarExpander: function(aVar, aGrip) {
// No need for expansion for primitive values.
if (VariablesView.isPrimitive({ value: aGrip })) {
return;
}
aVar._sourceGrip = aGrip;
// Some variables are likely to contain a very large number of properties.
// It's a good idea to be prepared in case of an expansion.
if (aVar.name == "window" || aVar.name == "this") {
aVar.addEventListener("mouseover", this._fetchVarProperties, false);
}
// Make sure that properties are always available on expansion.
aVar.onexpand = this._fetchVarProperties;
},
/**
* Adds the watch expressions evaluation results to a scope in the view.
*
@ -770,8 +752,8 @@ StackFrames.prototype = {
for (let i = 0; i < totalExpressions; i++) {
let name = DebuggerView.WatchExpressions.getExpression(i);
let expVal = ownProperties[i].value;
let expRef = aScope.addVar(name, ownProperties[i]);
this._addVarExpander(expRef, expVal);
let expRef = aScope.addItem(name, ownProperties[i]);
DebuggerView.Variables.controller.addExpander(expRef, expVal);
// Revert some of the custom watch expressions scope presentation flags.
expRef.switch = null;
@ -786,51 +768,6 @@ StackFrames.prototype = {
});
},
/**
* Adds variables to a scope in the view. Triggered when a scope is
* expanded or is hovered. It does not expand the scope.
*
* @param Scope aScope
* The scope where the variables will be placed into.
*/
_fetchScopeVariables: function(aScope) {
// Fetch the variables only once.
if (aScope._fetched) {
return;
}
aScope._fetched = true;
let env = aScope._sourceEnvironment;
switch (env.type) {
case "with":
case "object":
// Add nodes for every variable in scope.
this.activeThread.pauseGrip(env.object).getPrototypeAndProperties((aResponse) => {
let { ownProperties, safeGetterValues } = aResponse;
this._mergeSafeGetterValues(ownProperties, safeGetterValues);
this._insertScopeVariables(ownProperties, aScope);
// Signal that variables have been fetched.
window.dispatchEvent(document, "Debugger:FetchedVariables");
DebuggerView.Variables.commitHierarchy();
});
break;
case "block":
case "function":
// Add nodes for every argument and every other variable in scope.
this._insertScopeArguments(env.bindings.arguments, aScope);
this._insertScopeVariables(env.bindings.variables, aScope);
// No need to signal that variables have been fetched, since
// the scope arguments and variables are already attached to the
// environment bindings, so pausing the active thread is unnecessary.
break;
default:
Cu.reportError("Unknown Debugger.Environment type: " + env.type);
break;
}
},
/**
* Add nodes for special frame references in the innermost scope.
*
@ -842,154 +779,21 @@ StackFrames.prototype = {
_insertScopeFrameReferences: function(aScope, aFrame) {
// Add any thrown exception.
if (this.currentException) {
let excRef = aScope.addVar("<exception>", { value: this.currentException });
this._addVarExpander(excRef, this.currentException);
let excRef = aScope.addItem("<exception>", { value: this.currentException });
DebuggerView.Variables.controller.addExpander(excRef, this.currentException);
}
// Add any returned value.
if (this.currentReturnedValue) {
let retRef = aScope.addVar("<return>", { value: this.currentReturnedValue });
this._addVarExpander(retRef, this.currentReturnedValue);
let retRef = aScope.addItem("<return>", { value: this.currentReturnedValue });
DebuggerView.Variables.controller.addExpander(retRef, this.currentReturnedValue);
}
// Add "this".
if (aFrame.this) {
let thisRef = aScope.addVar("this", { value: aFrame.this });
this._addVarExpander(thisRef, aFrame.this);
let thisRef = aScope.addItem("this", { value: aFrame.this });
DebuggerView.Variables.controller.addExpander(thisRef, aFrame.this);
}
},
/**
* Add nodes for every argument in scope.
*
* @param object aArguments
* The map of names to arguments, as specified in the protocol.
* @param Scope aScope
* The scope where the nodes will be placed into.
*/
_insertScopeArguments: function(aArguments, aScope) {
if (!aArguments) {
return;
}
for (let argument of aArguments) {
let name = Object.getOwnPropertyNames(argument)[0];
let argRef = aScope.addVar(name, argument[name]);
let argVal = argument[name].value;
this._addVarExpander(argRef, argVal);
}
},
/**
* Add nodes for every variable in scope.
*
* @param object aVariables
* The map of names to variables, as specified in the protocol.
* @param Scope aScope
* The scope where the nodes will be placed into.
*/
_insertScopeVariables: function(aVariables, aScope) {
if (!aVariables) {
return;
}
let variableNames = Object.keys(aVariables);
// Sort all of the variables before adding them, if preferred.
if (Prefs.variablesSortingEnabled) {
variableNames.sort();
}
// Add the variables to the specified scope.
for (let name of variableNames) {
let varRef = aScope.addVar(name, aVariables[name]);
let varVal = aVariables[name].value;
this._addVarExpander(varRef, varVal);
}
},
/**
* Adds properties to a variable in the view. Triggered when a variable is
* expanded or certain variables are hovered. It does not expand the variable.
*
* @param Variable aVar
* The variable where the properties will be placed into.
*/
_fetchVarProperties: function(aVar) {
// Fetch the properties only once.
if (aVar._fetched) {
return;
}
aVar._fetched = true;
let grip = aVar._sourceGrip;
this.activeThread.pauseGrip(grip).getPrototypeAndProperties((aResponse) => {
let { ownProperties, prototype, safeGetterValues } = aResponse;
let sortable = VariablesView.NON_SORTABLE_CLASSES.indexOf(grip.class) == -1;
this._mergeSafeGetterValues(ownProperties, safeGetterValues);
// Add all the variable properties.
if (ownProperties) {
aVar.addProperties(ownProperties, {
// Not all variables need to force sorted properties.
sorted: sortable,
// Expansion handlers must be set after the properties are added.
callback: this._addVarExpander
});
}
// Add the variable's __proto__.
if (prototype && prototype.type != "null") {
aVar.addProperty("__proto__", { value: prototype });
// Expansion handlers must be set after the properties are added.
this._addVarExpander(aVar.get("__proto__"), prototype);
}
// Mark the variable as having retrieved all its properties.
aVar._retrieved = true;
// Signal that properties have been fetched.
window.dispatchEvent(document, "Debugger:FetchedProperties");
DebuggerView.Variables.commitHierarchy();
});
},
/**
* Merge the safe getter values descriptors into the "own properties" object
* that comes from a "prototypeAndProperties" response packet. This is needed
* for Variables View.
*
* @private
* @param object aOwnProperties
* The |ownProperties| object that will get the new safe getter values.
* @param object aSafeGetterValues
* The |safeGetterValues| object.
*/
_mergeSafeGetterValues: function(aOwnProperties, aSafeGetterValues) {
// Merge the safe getter values into one object such that we can use it
// in VariablesView.
for (let name of Object.keys(aSafeGetterValues)) {
if (name in aOwnProperties) {
aOwnProperties[name].getterValue = aSafeGetterValues[name].getterValue;
aOwnProperties[name].getterPrototypeLevel =
aSafeGetterValues[name].getterPrototypeLevel;
} else {
aOwnProperties[name] = aSafeGetterValues[name];
}
}
},
/**
* Adds the specified stack frame to the list.
*
* @param object aFrame
* The new frame to add.
*/
_addFrame: function(aFrame) {
let depth = aFrame.depth;
let { url, line } = aFrame.where;
let frameLocation = NetworkHelper.convertToUnicode(unescape(url));
let frameTitle = StackFrameUtils.getFrameTitle(aFrame);
DebuggerView.StackFrames.addFrame(frameTitle, frameLocation, line, depth);
},
/**
* Loads more stack frames from the debugger server cache.
*/

View File

@ -24,9 +24,9 @@ function testNonEnumProperties() {
Services.tm.currentThread.dispatch({ run: function() {
let testScope = gDebugger.DebuggerView.Variables.addScope("test-scope");
let testVar = testScope.addVar("foo");
let testVar = testScope.addItem("foo");
testVar.addProperties({
testVar.addItems({
foo: {
value: "bar",
enumerable: true

View File

@ -24,8 +24,8 @@ function testSimpleCall() {
Services.tm.currentThread.dispatch({ run: function() {
let testScope = gDebugger.DebuggerView.Variables.addScope("test-scope");
let testVar = testScope.addVar("something");
let duplVar = testScope.addVar("something");
let testVar = testScope.addItem("something");
let duplVar = testScope.addItem("something");
info("Scope id: " + testScope.target.id);
info("Scope name: " + testScope.target.name);
@ -61,8 +61,8 @@ function testSimpleCall() {
"Any new variable should have a details container with no child nodes.");
let properties = testVar.addProperties({ "child": { "value": { "type": "object",
"class": "Object" } } });
let properties = testVar.addItems({ "child": { "value": { "type": "object",
"class": "Object" } } });
ok(!testVar.expanded,

View File

@ -24,9 +24,9 @@ function testSimpleCall() {
Services.tm.currentThread.dispatch({ run: function() {
let testScope = gDebugger.DebuggerView.Variables.addScope("test");
let testVar = testScope.addVar("something");
let testVar = testScope.addItem("something");
let properties = testVar.addProperties({
let properties = testVar.addItems({
"child": {
"value": {
"type": "object",
@ -43,7 +43,7 @@ function testSimpleCall() {
"The added detail property should be accessible from the variable.");
let properties2 = testVar.get("child").addProperties({
let properties2 = testVar.get("child").addItems({
"grandchild": {
"value": {
"type": "object",

View File

@ -24,7 +24,7 @@ function testSimpleCall() {
Services.tm.currentThread.dispatch({ run: function() {
let testScope = gDebugger.DebuggerView.Variables.addScope("test");
let testVar = testScope.addVar("something");
let testVar = testScope.addItem("something");
testVar.setGrip(1.618);
@ -44,32 +44,32 @@ function testSimpleCall() {
"The information for the variable wasn't set correctly.");
testVar.addProperties({ "helloWorld": { "value": "hello world", "enumerable": true } });
testVar.addItems({ "helloWorld": { "value": "hello world", "enumerable": true } });
is(testVar.target.querySelector(".variables-view-element-details").childNodes.length, 1,
"A new detail node should have been added in the variable tree.");
testVar.addProperties({ "helloWorld": { "value": "hello jupiter", "enumerable": true } });
testVar.addItems({ "helloWorld": { "value": "hello jupiter", "enumerable": true } });
is(testVar.target.querySelector(".variables-view-element-details").childNodes.length, 1,
"Shouldn't be able to duplicate nodes added in the variable tree.");
testVar.addProperties({ "someProp0": { "value": "random string", "enumerable": true },
"someProp1": { "value": "another string", "enumerable": true } });
testVar.addItems({ "someProp0": { "value": "random string", "enumerable": true },
"someProp1": { "value": "another string", "enumerable": true } });
is(testVar.target.querySelector(".variables-view-element-details").childNodes.length, 3,
"Two new detail nodes should have been added in the variable tree.");
testVar.addProperties({ "someProp2": { "value": { "type": "null" }, "enumerable": true },
"someProp3": { "value": { "type": "undefined" }, "enumerable": true },
"someProp4": {
"value": { "type": "object", "class": "Object" },
"enumerable": true
}
});
testVar.addItems({ "someProp2": { "value": { "type": "null" }, "enumerable": true },
"someProp3": { "value": { "type": "undefined" }, "enumerable": true },
"someProp4": {
"value": { "type": "object", "class": "Object" },
"enumerable": true
}
});
is(testVar.target.querySelector(".variables-view-element-details").childNodes.length, 6,
"Three new detail nodes should have been added in the variable tree.");

View File

@ -26,14 +26,14 @@ function testSimpleCall() {
let globalScope = gDebugger.DebuggerView.Variables.addScope("Test-Global");
let localScope = gDebugger.DebuggerView.Variables.addScope("Test-Local");
let windowVar = globalScope.addVar("window");
let documentVar = globalScope.addVar("document");
let localVar0 = localScope.addVar("localVariable");
let localVar1 = localScope.addVar("localVar1");
let localVar2 = localScope.addVar("localVar2");
let localVar3 = localScope.addVar("localVar3");
let localVar4 = localScope.addVar("localVar4");
let localVar5 = localScope.addVar("localVar5");
let windowVar = globalScope.addItem("window");
let documentVar = globalScope.addItem("document");
let localVar0 = localScope.addItem("localVariable");
let localVar1 = localScope.addItem("localVar1");
let localVar2 = localScope.addItem("localVar2");
let localVar3 = localScope.addItem("localVar3");
let localVar4 = localScope.addItem("localVar4");
let localVar5 = localScope.addItem("localVar5");
localVar0.setGrip(42);
localVar1.setGrip(true);
@ -43,36 +43,36 @@ function testSimpleCall() {
localVar4.setGrip({ "type": "null" });
localVar5.setGrip({ "type": "object", "class": "Object" });
localVar5.addProperties({ "someProp0": { "value": 42, "enumerable": true },
"someProp1": { "value": true , "enumerable": true},
"someProp2": { "value": "nasu", "enumerable": true},
"someProp3": { "value": { "type": "undefined" }, "enumerable": true},
"someProp4": { "value": { "type": "null" }, "enumerable": true },
"someProp5": {
"value": { "type": "object", "class": "Object" },
"enumerable": true
}
});
localVar5.addItems({ "someProp0": { "value": 42, "enumerable": true },
"someProp1": { "value": true , "enumerable": true},
"someProp2": { "value": "nasu", "enumerable": true},
"someProp3": { "value": { "type": "undefined" }, "enumerable": true},
"someProp4": { "value": { "type": "null" }, "enumerable": true },
"someProp5": {
"value": { "type": "object", "class": "Object" },
"enumerable": true
}
});
localVar5.get("someProp5").addProperties({ "someProp0": { "value": 42, "enumerable": true },
"someProp1": { "value": true, "enumerable": true },
"someProp2": { "value": "nasu", "enumerable": true },
"someProp3": { "value": { "type": "undefined" }, "enumerable": true },
"someProp4": { "value": { "type": "null" }, "enumerable": true },
"someAccessor": { "get": { "type": "object", "class": "Function" },
"set": { "type": "undefined" },
"enumerable": true } });
localVar5.get("someProp5").addItems({ "someProp0": { "value": 42, "enumerable": true },
"someProp1": { "value": true, "enumerable": true },
"someProp2": { "value": "nasu", "enumerable": true },
"someProp3": { "value": { "type": "undefined" }, "enumerable": true },
"someProp4": { "value": { "type": "null" }, "enumerable": true },
"someAccessor": { "get": { "type": "object", "class": "Function" },
"set": { "type": "undefined" }, "enumerable": true }
});
windowVar.setGrip({ "type": "object", "class": "Window" });
windowVar.addProperties({ "helloWorld": { "value": "hello world" } });
windowVar.addItems({ "helloWorld": { "value": "hello world" } });
documentVar.setGrip({ "type": "object", "class": "HTMLDocument" });
documentVar.addProperties({ "onload": { "value": { "type": "null" } },
"onunload": { "value": { "type": "null" } },
"onfocus": { "value": { "type": "null" } },
"onblur": { "value": { "type": "null" } },
"onclick": { "value": { "type": "null" } },
"onkeypress": { "value": { "type": "null" } } });
documentVar.addItems({ "onload": { "value": { "type": "null" } },
"onunload": { "value": { "type": "null" } },
"onfocus": { "value": { "type": "null" } },
"onblur": { "value": { "type": "null" } },
"onclick": { "value": { "type": "null" } },
"onkeypress": { "value": { "type": "null" } } });
ok(windowVar, "The windowVar hasn't been created correctly.");

View File

@ -75,11 +75,11 @@ function testVariablesView()
testIntegrity(arr, obj);
let fooScope = gVariablesView.addScope("foo");
let anonymousVar = fooScope.addVar();
let anonymousVar = fooScope.addItem();
let anonymousScope = gVariablesView.addScope();
let barVar = anonymousScope.addVar("bar");
let bazProperty = barVar.addProperty("baz");
let barVar = anonymousScope.addItem("bar");
let bazProperty = barVar.addItem("baz");
testAnonymousHeaders(fooScope, anonymousVar, anonymousScope, barVar, bazProperty);
testPropertyInheritance(fooScope, anonymousVar, anonymousScope, barVar, bazProperty);

View File

@ -110,8 +110,9 @@ function testVariablesFiltering()
is(gSearchBox.value, "*",
"Searchbox value is incorrect after 3 backspaces");
is(innerScope.querySelectorAll(".variables-view-variable:not([non-match])").length, 3,
"There should be 3 variables displayed in the inner scope");
// variable count includes `__proto__` for object scopes
is(innerScope.querySelectorAll(".variables-view-variable:not([non-match])").length, 4,
"There should be 4 variables displayed in the inner scope");
isnot(mathScope.querySelectorAll(".variables-view-variable:not([non-match])").length, 0,
"There should be some variables displayed in the math scope");
isnot(testScope.querySelectorAll(".variables-view-variable:not([non-match])").length, 0,
@ -140,8 +141,9 @@ function testVariablesFiltering()
is(gSearchBox.value, "",
"Searchbox value is incorrect after 1 backspace");
is(innerScope.querySelectorAll(".variables-view-variable:not([non-match])").length, 3,
"There should be 3 variables displayed in the inner scope");
// variable count includes `__proto__` for object scopes
is(innerScope.querySelectorAll(".variables-view-variable:not([non-match])").length, 4,
"There should be 4 variables displayed in the inner scope");
isnot(mathScope.querySelectorAll(".variables-view-variable:not([non-match])").length, 0,
"There should be some variables displayed in the math scope");
isnot(testScope.querySelectorAll(".variables-view-variable:not([non-match])").length, 0,

View File

@ -1446,7 +1446,7 @@ NetworkDetailsView.prototype = {
headersScope.expanded = true;
for (let header of aResponse.headers) {
let headerVar = headersScope.addVar(header.name, { null: true }, true);
let headerVar = headersScope.addItem(header.name, { null: true }, 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.addVar(cookie.name, { null: true }, true);
let cookieVar = cookiesScope.addItem(cookie.name, { null: true }, 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.addVar(param.name, { null: true }, true);
let headerVar = paramsScope.addItem(param.name, { null: true }, true);
headerVar.setGrip(param.value);
}
},
@ -1634,7 +1634,7 @@ NetworkDetailsView.prototype = {
: L10N.getStr("jsonScopeName");
let jsonScope = this._json.addScope(jsonScopeName);
jsonScope.addVar().populate(jsonObject, { expanded: true });
jsonScope.addItem().populate(jsonObject, { expanded: true });
jsonScope.expanded = true;
}
// Malformed JSON.

View File

@ -20,6 +20,8 @@ const SEARCH_ACTION_MAX_DELAY = 300; // ms
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
Cu.import("resource:///modules/devtools/shared/event-emitter.js");
Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js");
XPCOMUtils.defineLazyModuleGetter(this, "NetworkHelper",
"resource://gre/modules/devtools/NetworkHelper.jsm");
@ -74,6 +76,8 @@ this.VariablesView = function VariablesView(aParentNode, aFlags = {}) {
for (let name in aFlags) {
this[name] = aFlags[name];
}
EventEmitter.decorate(this);
};
VariablesView.prototype = {
@ -86,7 +90,7 @@ VariablesView.prototype = {
*/
set rawObject(aObject) {
this.empty();
this.addScope().addVar().populate(aObject);
this.addScope().addItem().populate(aObject);
},
/**
@ -180,6 +184,11 @@ VariablesView.prototype = {
}, aTimeout);
},
/**
* The controller for this VariablesView, if it has one.
*/
controller: null,
/**
* The amount of time (in milliseconds) it takes to empty this view lazily.
*/
@ -587,7 +596,8 @@ VariablesView.prototype = {
*/
getScopeForNode: function(aNode) {
let item = this._itemsByElement.get(aNode);
if (item && !(item instanceof Variable) && !(item instanceof Property)) {
// Match only Scopes, not Variables or Properties.
if (item && !(item instanceof Variable)) {
return item;
}
return null;
@ -790,9 +800,8 @@ VariablesView.prototype = {
case e.DOM_VK_RETURN:
case e.DOM_VK_ENTER:
// Start editing the value or name of the variable or property.
if (item instanceof Variable ||
item instanceof Property) {
// Start editing the value or name of the Variable or Property.
if (item instanceof Variable) {
if (e.metaKey || e.altKey || e.shiftKey) {
item._activateNameInput();
} else {
@ -803,9 +812,8 @@ VariablesView.prototype = {
case e.DOM_VK_DELETE:
case e.DOM_VK_BACK_SPACE:
// Delete the variable or property if allowed.
if (item instanceof Variable ||
item instanceof Property) {
// Delete the Variable or Property if allowed.
if (item instanceof Variable) {
item._onDelete(e);
}
return;
@ -902,6 +910,7 @@ VariablesView.NON_SORTABLE_CLASSES = [
"Array",
"Int8Array",
"Uint8Array",
"Uint8ClampedArray",
"Int16Array",
"Uint16Array",
"Int32Array",
@ -910,6 +919,16 @@ VariablesView.NON_SORTABLE_CLASSES = [
"Float64Array"
];
/**
* Determine whether an object's properties should be sorted based on its class.
*
* @param string aClassName
* The class of the object.
*/
VariablesView.isSortable = function(aClassName) {
return VariablesView.NON_SORTABLE_CLASSES.indexOf(aClassName) == -1;
};
/**
* Generates the string evaluated when performing simple value changes.
*
@ -917,11 +936,13 @@ VariablesView.NON_SORTABLE_CLASSES = [
* The current variable or property.
* @param string aCurrentString
* The trimmed user inputted string.
* @param string aPrefix [optional]
* Prefix for the symbolic name.
* @return string
* The string to be evaluated.
*/
VariablesView.simpleValueEvalMacro = function(aItem, aCurrentString) {
return aItem._symbolicName + "=" + aCurrentString;
VariablesView.simpleValueEvalMacro = function(aItem, aCurrentString, aPrefix = "") {
return aPrefix + aItem._symbolicName + "=" + aCurrentString;
};
/**
@ -932,12 +953,14 @@ VariablesView.simpleValueEvalMacro = function(aItem, aCurrentString) {
* The current getter or setter property.
* @param string aCurrentString
* The trimmed user inputted string.
* @param string aPrefix [optional]
* Prefix for the symbolic name.
* @return string
* The string to be evaluated.
*/
VariablesView.overrideValueEvalMacro = function(aItem, aCurrentString) {
VariablesView.overrideValueEvalMacro = function(aItem, aCurrentString, aPrefix = "") {
let property = "\"" + aItem._nameString + "\"";
let parent = aItem.ownerView._symbolicName || "this";
let parent = aPrefix + aItem.ownerView._symbolicName || "this";
return "Object.defineProperty(" + parent + "," + property + "," +
"{ value: " + aCurrentString +
@ -954,15 +977,17 @@ VariablesView.overrideValueEvalMacro = function(aItem, aCurrentString) {
* The current getter or setter property.
* @param string aCurrentString
* The trimmed user inputted string.
* @param string aPrefix [optional]
* Prefix for the symbolic name.
* @return string
* The string to be evaluated.
*/
VariablesView.getterOrSetterEvalMacro = function(aItem, aCurrentString) {
VariablesView.getterOrSetterEvalMacro = function(aItem, aCurrentString, aPrefix = "") {
let type = aItem._nameString;
let propertyObject = aItem.ownerView;
let parentObject = propertyObject.ownerView;
let property = "\"" + propertyObject._nameString + "\"";
let parent = parentObject._symbolicName || "this";
let parent = aPrefix + parentObject._symbolicName || "this";
switch (aCurrentString) {
case "":
@ -976,7 +1001,7 @@ VariablesView.getterOrSetterEvalMacro = function(aItem, aCurrentString) {
if ((type == "set" && propertyObject.getter.type == "undefined") ||
(type == "get" && propertyObject.setter.type == "undefined")) {
// Make sure the right getter/setter to value override macro is applied to the target object.
return propertyObject.evaluationMacro(propertyObject, "undefined");
return propertyObject.evaluationMacro(propertyObject, "undefined", aPrefix);
}
// Construct and return the getter/setter removal evaluation string.
@ -995,16 +1020,16 @@ VariablesView.getterOrSetterEvalMacro = function(aItem, aCurrentString) {
default:
// Wrap statements inside a function declaration if not already wrapped.
if (aCurrentString.indexOf("function") != 0) {
if (!aCurrentString.startsWith("function")) {
let header = "function(" + (type == "set" ? "value" : "") + ")";
let body = "";
// If there's a return statement explicitly written, always use the
// standard function definition syntax
if (aCurrentString.indexOf("return ") != -1) {
if (aCurrentString.contains("return ")) {
body = "{" + aCurrentString + "}";
}
// If block syntax is used, use the whole string as the function body.
else if (aCurrentString.indexOf("{") == 0) {
else if (aCurrentString.startsWith("{")) {
body = aCurrentString;
}
// Prefer an expression closure.
@ -1042,6 +1067,7 @@ VariablesView.getterOrSetterDeleteCallback = function(aItem) {
return true; // Don't hide the element.
};
/**
* A Scope is an object holding Variable instances.
* Iterable via "for (let [name, variable] in instance) { }".
@ -1083,12 +1109,31 @@ function Scope(aView, aName, aFlags = {}) {
Scope.prototype = {
/**
* Adds a variable to contain any inspected properties.
* Whether this Scope should be prefetched when it is remoted.
*/
shouldPrefetch: true,
/**
* Create a new Variable that is a child of this Scope.
*
* @param string aName
* The variable's name.
* The name of the new Property.
* @param object aDescriptor
* Specifies the value and/or type & class of the variable,
* The variable's descriptor.
* @return Variable
* The newly created child Variable.
*/
_createChild: function(aName, aDescriptor) {
return new Variable(this, aName, aDescriptor);
},
/**
* Adds a child to contain any inspected properties.
*
* @param string aName
* The child's name.
* @param object aDescriptor
* Specifies the value and/or type & class of the child,
* or 'get' & 'set' accessor properties. If the type is implicit,
* it will be inferred from the value.
* e.g. - { value: 42 }
@ -1104,17 +1149,56 @@ Scope.prototype = {
* @return Variable
* The newly created Variable instance, null if it already exists.
*/
addVar: function(aName = "", aDescriptor = {}, aRelaxed = false) {
addItem: function(aName = "", aDescriptor = {}, aRelaxed = false) {
if (this._store.has(aName) && !aRelaxed) {
return null;
}
let variable = new Variable(this, aName, aDescriptor);
this._store.set(aName, variable);
this._variablesView._itemsByElement.set(variable._target, variable);
this._variablesView._currHierarchy.set(variable._absoluteName, variable);
variable.header = !!aName;
return variable;
let child = this._createChild(aName, aDescriptor);
this._store.set(aName, child);
this._variablesView._itemsByElement.set(child._target, child);
this._variablesView._currHierarchy.set(child._absoluteName, child);
child.header = !!aName;
return child;
},
/**
* Adds items for this variable.
*
* @param object aItems
* An object containing some { name: descriptor } data properties,
* specifying the value and/or type & class of the variable,
* or 'get' & 'set' accessor properties. If the type is implicit,
* it will be inferred from the value.
* e.g. - { someProp0: { value: 42 },
* someProp1: { value: true },
* someProp2: { value: "nasu" },
* someProp3: { value: { type: "undefined" } },
* someProp4: { value: { type: "null" } },
* someProp5: { value: { type: "object", class: "Object" } },
* someProp6: { get: { type: "object", class: "Function" },
* set: { type: "undefined" } } }
* @param object aOptions [optional]
* Additional options for adding the properties. Supported options:
* - sorted: true to sort all the properties before adding them
* - callback: function invoked after each item is added
*/
addItems: function(aItems, aOptions = {}) {
let names = Object.keys(aItems);
// Sort all of the properties before adding them, if preferred.
if (aOptions.sorted) {
names.sort();
}
// Add the properties to the current scope.
for (let name of names) {
let descriptor = aItems[name];
let item = this.addItem(name, descriptor);
if (aOptions.callback) {
aOptions.callback(item, descriptor.value);
}
}
},
/**
@ -1179,11 +1263,13 @@ Scope.prototype = {
if (this.isChildOf(aParent)) {
return true;
}
if (this.ownerView instanceof Scope ||
this.ownerView instanceof Variable ||
this.ownerView instanceof Property) {
// Recurse to parent if it is a Scope, Variable, or Property.
if (this.ownerView instanceof Scope) {
return this.ownerView.isDescendantOf(aParent);
}
return false;
},
/**
@ -1405,10 +1491,9 @@ Scope.prototype = {
}
// Check if all parent objects are expanded.
let item = this;
while ((item = item.ownerView) && /* Parent object exists. */
(item instanceof Scope ||
item instanceof Variable ||
item instanceof Property)) {
// Recurse while parent is a Scope, Variable, or Property
while ((item = item.ownerView) && item instanceof Scope) {
if (!item._isExpanded) {
return false;
}
@ -1722,14 +1807,11 @@ Scope.prototype = {
variable._wasToggled = true;
}
// If the variable is contained in another scope (variable or property),
// If the variable is contained in another Scope, Variable, or Property,
// the parent may not be a match, thus hidden. It should be visible
// ("expand upwards").
while ((variable = variable.ownerView) && /* Parent object exists. */
(variable instanceof Scope ||
variable instanceof Variable ||
variable instanceof Property)) {
variable instanceof Scope) {
// Show and expand the parent, as it is certainly accessible.
variable._matched = true;
@ -1971,79 +2053,24 @@ function Variable(aScope, aName, aDescriptor) {
ViewHelpers.create({ constructor: Variable, proto: Scope.prototype }, {
/**
* Adds a property for this variable.
*
* @param string aName
* The property's name.
* @param object aDescriptor
* Specifies the value and/or type & class of the property,
* or 'get' & 'set' accessor properties. If the type is implicit,
* it will be inferred from the value.
* e.g. - { value: 42 }
* - { value: true }
* - { value: "nasu" }
* - { value: { type: "undefined" } }
* - { value: { type: "null" } }
* - { value: { type: "object", class: "Object" } }
* - { get: { type: "object", class: "Function" },
* set: { type: "undefined" } }
* - { get: { type "object", class: "Function" },
* getterValue: "foo", getterPrototypeLevel: 2 }
* @param boolean aRelaxed
* True if name duplicates should be allowed.
* @return Property
* The newly created Property instance, null if it already exists.
* Whether this Scope should be prefetched when it is remoted.
*/
addProperty: function(aName = "", aDescriptor = {}, aRelaxed = false) {
if (this._store.has(aName) && !aRelaxed) {
return null;
}
let property = new Property(this, aName, aDescriptor);
this._store.set(aName, property);
this._variablesView._itemsByElement.set(property._target, property);
this._variablesView._currHierarchy.set(property._absoluteName, property);
property.header = !!aName;
return property;
get shouldPrefetch(){
return this.name == "window" || this.name == "this";
},
/**
* Adds properties for this variable.
* Create a new Property that is a child of Variable.
*
* @param object aProperties
* An object containing some { name: descriptor } data properties,
* specifying the value and/or type & class of the variable,
* or 'get' & 'set' accessor properties. If the type is implicit,
* it will be inferred from the value.
* e.g. - { someProp0: { value: 42 },
* someProp1: { value: true },
* someProp2: { value: "nasu" },
* someProp3: { value: { type: "undefined" } },
* someProp4: { value: { type: "null" } },
* someProp5: { value: { type: "object", class: "Object" } },
* someProp6: { get: { type: "object", class: "Function" },
* set: { type: "undefined" } } }
* @param object aOptions [optional]
* Additional options for adding the properties. Supported options:
* - sorted: true to sort all the properties before adding them
* - callback: function invoked after each property is added
* @param string aName
* The name of the new Property.
* @param object aDescriptor
* The property's descriptor.
* @return Property
* The newly created child Property.
*/
addProperties: function(aProperties, aOptions = {}) {
let propertyNames = Object.keys(aProperties);
// Sort all of the properties before adding them, if preferred.
if (aOptions.sorted) {
propertyNames.sort();
}
// Add the properties to the current scope.
for (let name of propertyNames) {
let descriptor = aProperties[name];
let property = this.addProperty(name, descriptor);
if (aOptions.callback) {
aOptions.callback(property, descriptor.value);
}
}
_createChild: function(aName, aDescriptor) {
return new Property(this, aName, aDescriptor);
},
/**
@ -2122,7 +2149,7 @@ ViewHelpers.create({ constructor: Variable, proto: Scope.prototype }, {
let descriptor = Object.create(aDescriptor);
descriptor.value = VariablesView.getGrip(aValue);
let propertyItem = this.addProperty(aName, descriptor);
let propertyItem = this.addItem(aName, descriptor);
propertyItem._sourceValue = aValue;
// Add an 'onexpand' callback for the property, lazily handling
@ -2149,7 +2176,7 @@ ViewHelpers.create({ constructor: Variable, proto: Scope.prototype }, {
descriptor.get = VariablesView.getGrip(aDescriptor.get);
descriptor.set = VariablesView.getGrip(aDescriptor.set);
return this.addProperty(aName, descriptor);
return this.addItem(aName, descriptor);
},
/**
@ -2311,8 +2338,8 @@ ViewHelpers.create({ constructor: Variable, proto: Scope.prototype }, {
this.evaluationMacro = null;
}
let getter = this.addProperty("get", { value: descriptor.get });
let setter = this.addProperty("set", { value: descriptor.set });
let getter = this.addItem("get", { value: descriptor.get });
let setter = this.addItem("set", { value: descriptor.set });
getter.evaluationMacro = VariablesView.getterOrSetterEvalMacro;
setter.evaluationMacro = VariablesView.getterOrSetterEvalMacro;
@ -2852,9 +2879,8 @@ VariablesView.prototype.commitHierarchy = function() {
if (prevVariable) {
expanded = prevVariable._isExpanded;
// Only analyze variables and properties for displayed value changes.
if (currVariable instanceof Variable ||
currVariable instanceof Property) {
// Only analyze Variables and Properties for displayed value changes.
if (currVariable instanceof Variable) {
changed = prevVariable._valueString != currVariable._valueString;
}
}
@ -2974,6 +3000,16 @@ VariablesView.isFalsy = function(aDescriptor) {
return false;
};
/**
* Returns true if the value is an instance of Variable or Property.
*
* @param any aValue
* The value to test.
*/
VariablesView.isVariable = function(aValue) {
return aValue instanceof Variable;
};
/**
* Returns a standard grip for a value.
*

View File

@ -0,0 +1,350 @@
/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* 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 { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js");
Cu.import("resource:///modules/devtools/VariablesView.jsm");
Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
Cu.import("resource://gre/modules/devtools/WebConsoleUtils.jsm");
XPCOMUtils.defineLazyGetter(this, "VARIABLES_SORTING_ENABLED", () =>
Services.prefs.getBoolPref("devtools.debugger.ui.variables-sorting-enabled")
);
const MAX_LONG_STRING_LENGTH = 200000;
this.EXPORTED_SYMBOLS = ["VariablesViewController"];
/**
* Controller for a VariablesView that handles interfacing with the debugger
* protocol. Is able to populate scopes and variables via the protocol as well
* as manage actor lifespans.
*
* @param VariablesView aView
* The view to attach to.
* @param object aOptions
* Options for configuring the controller. Supported options:
* - getGripClient: callback for creating an object grip client
* - getLongStringClient: callback for creating a long string grip client
* - releaseActor: callback for releasing an actor when it's no longer needed
* - overrideValueEvalMacro: callback for creating an overriding eval macro
* - getterOrSetterEvalMacro: callback for creating a getter/setter eval macro
* - simpleValueEvalMacro: callback for creating a simple value eval macro
*/
function VariablesViewController(aView, aOptions) {
this.addExpander = this.addExpander.bind(this);
this._getGripClient = aOptions.getGripClient;
this._getLongStringClient = aOptions.getLongStringClient;
this._releaseActor = aOptions.releaseActor;
if (aOptions.overrideValueEvalMacro) {
this._overrideValueEvalMacro = aOptions.overrideValueEvalMacro;
}
if (aOptions.getterOrSetterEvalMacro) {
this._getterOrSetterEvalMacro = aOptions.getterOrSetterEvalMacro;
}
if (aOptions.simpleValueEvalMacro) {
this._simpleValueEvalMacro = aOptions.simpleValueEvalMacro;
}
this._actors = new Set();
this.view = aView;
this.view.controller = this;
}
VariablesViewController.prototype = {
/**
* The default getter/setter evaluation macro.
*/
_getterOrSetterEvalMacro: VariablesView.getterOrSetterEvalMacro,
/**
* The default override value evaluation macro.
*/
_overrideValueEvalMacro: VariablesView.overrideValueEvalMacro,
/**
* The default simple value evaluation macro.
*/
_simpleValueEvalMacro: VariablesView.simpleValueEvalMacro,
/**
* Populate a long string into a target using a grip.
*
* @param Variable aTarget
* The target Variable/Property to put the retrieved string into.
* @param LongStringActor aGrip
* The long string grip that use to retrieve the full string.
* @return Promise
* The promise that will be resolved when the string is retrieved.
*/
_populateFromLongString: function(aTarget, aGrip){
let deferred = Promise.defer();
let from = aGrip.initial.length;
let to = Math.min(aGrip.length, MAX_LONG_STRING_LENGTH);
this._getLongStringClient(aGrip).substring(from, to, aResponse => {
// Stop tracking the actor because it's no longer needed.
this.releaseActor(aGrip);
// Replace the preview with the full string and make it non-expandable.
aTarget.onexpand = null;
aTarget.setGrip(aGrip.initial + aResponse.substring);
aTarget.hideArrow();
// Mark the string as having retrieved.
aTarget._retrieved = true;
deferred.resolve();
});
return deferred.promise;
},
/**
* Adds properties to a Scope, Variable, or Property in the view. Triggered
* when a scope is expanded or certain variables are hovered.
*
* @param Scope aTarget
* The Scope where the properties will be placed into.
* @param object aGrip
* The grip to use to populate the target.
*/
_populateFromObject: function(aTarget, aGrip) {
let deferred = Promise.defer();
this._getGripClient(aGrip).getPrototypeAndProperties(aResponse => {
let { ownProperties, prototype, safeGetterValues } = aResponse;
let sortable = VariablesView.isSortable(aGrip.class);
// Merge the safe getter values into one object such that we can use it
// in VariablesView.
for (let name of Object.keys(safeGetterValues)) {
if (name in ownProperties) {
ownProperties[name].getterValue = safeGetterValues[name].getterValue;
ownProperties[name].getterPrototypeLevel = safeGetterValues[name]
.getterPrototypeLevel;
} else {
ownProperties[name] = safeGetterValues[name];
}
}
// Add all the variable properties.
if (ownProperties) {
aTarget.addItems(ownProperties, {
// Not all variables need to force sorted properties.
sorted: sortable,
// Expansion handlers must be set after the properties are added.
callback: this.addExpander
});
}
// Add the variable's __proto__.
if (prototype && prototype.type != "null") {
let proto = aTarget.addItem("__proto__", { value: prototype });
// Expansion handlers must be set after the properties are added.
this.addExpander(proto, prototype);
}
// Mark the variable as having retrieved all its properties.
aTarget._retrieved = true;
this.view.commitHierarchy();
deferred.resolve();
});
return deferred.promise;
},
/**
* Adds an 'onexpand' callback for a variable, lazily handling
* the addition of new properties.
*
* @param Variable aVar
* The variable where the properties will be placed into.
* @param any aSource
* The source to use to populate the target.
*/
addExpander: function(aTarget, aSource) {
// Attach evaluation macros as necessary.
if (aTarget.getter || aTarget.setter) {
aTarget.evaluationMacro = this._overrideValueEvalMacro;
let getter = aTarget.get("get");
if (getter) {
getter.evaluationMacro = this._getterOrSetterEvalMacro;
}
let setter = aTarget.get("set");
if (setter) {
setter.evaluationMacro = this._getterOrSetterEvalMacro;
}
} else {
aTarget.evaluationMacro = this._simpleValueEvalMacro;
}
// If the source is primitive then an expander is not needed.
if (VariablesView.isPrimitive({ value: aSource })) {
return;
}
// If the source is a long string then show the arrow.
if (WebConsoleUtils.isActorGrip(aSource) && aSource.type == "longString") {
aTarget.showArrow();
}
// Make sure that properties are always available on expansion.
aTarget.onexpand = () => this.expand(aTarget, aSource);
// Some variables are likely to contain a very large number of properties.
// It's a good idea to be prepared in case of an expansion.
if (aTarget.shouldPrefetch) {
aTarget.addEventListener("mouseover", aTarget.onexpand, false);
}
// Register all the actors that this controller now depends on.
for (let grip of [aTarget.value, aTarget.getter, aTarget.setter]) {
if (WebConsoleUtils.isActorGrip(grip)) {
this._actors.add(grip.actor);
}
}
},
/**
* Adds properties to a Scope, Variable, or Property in the view. Triggered
* when a scope is expanded or certain variables are hovered.
*
* @param Scope aTarget
* The Scope to be expanded.
* @param object aSource
* The source to use to populate the target.
* @return Promise
* The promise that is resolved once the target has been expanded.
*/
expand: function(aTarget, aSource) {
// Fetch the variables only once.
if (aTarget._fetched) {
return aTarget._fetched;
}
let deferred = Promise.defer();
aTarget._fetched = deferred.promise;
if (!aSource) {
throw new Error("No actor grip was given for the variable.");
}
// If the target a Variable or Property then we're fetching properties
if (VariablesView.isVariable(aTarget)) {
this._populateFromObject(aTarget, aSource).then(() => {
deferred.resolve();
// Signal that properties have been fetched.
this.view.emit("fetched", "properties", aTarget);
});
return deferred.promise;
}
switch (aSource.type) {
case "longString":
this._populateFromLongString(aTarget, aSource).then(() => {
deferred.resolve();
// Signal that a long string has been fetched.
this.view.emit("fetched", "longString", aTarget);
});
break;
case "with":
case "object":
this._populateFromObject(aTarget, aSource.object).then(() => {
deferred.resolve();
// Signal that variables have been fetched.
this.view.emit("fetched", "variables", aTarget);
});
break;
case "block":
case "function":
// Add nodes for every argument and every other variable in scope.
let args = aSource.bindings.arguments;
if (args) {
for (let arg of args) {
let name = Object.getOwnPropertyNames(arg)[0];
let ref = aTarget.addItem(name, arg[name]);
let val = arg[name].value;
this.addExpander(ref, val);
}
}
aTarget.addItems(aSource.bindings.variables, {
// Not all variables need to force sorted properties.
sorted: VARIABLES_SORTING_ENABLED,
// Expansion handlers must be set after the properties are added.
callback: this.addExpander
});
// No need to signal that variables have been fetched, since
// the scope arguments and variables are already attached to the
// environment bindings, so pausing the active thread is unnecessary.
deferred.resolve();
break;
default:
let error = "Unknown Debugger.Environment type: " + aSource.type;
Cu.reportError(error);
deferred.reject(error);
}
return deferred.promise;
},
/**
* Release an actor from the controller.
*
* @param object aActor
* The actor to release.
*/
releaseActor: function(aActor){
if (this._releaseActor) {
this._releaseActor(aActor);
}
this._actors.delete(aActor);
},
/**
* Release all the actors referenced by the controller, optionally filtered.
*
* @param function aFilter [optional]
* Callback to filter which actors are released.
*/
releaseActors: function(aFilter) {
for (let actor of this._actors) {
if (!aFilter || aFilter(actor)) {
this.releaseActor(actor);
}
}
},
};
/**
* Attaches a VariablesViewController to a VariablesView if it doesn't already
* have one.
*
* @param VariablesView aView
* The view to attach to.
* @param object aOptions
* The options to use in creating the controller.
* @return VariablesViewController
*/
VariablesViewController.attach = function(aView, aOptions) {
if (aView.controller) {
return aView.controller;
}
return new VariablesViewController(aView, aOptions);
};

View File

@ -37,6 +37,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "Promise",
XPCOMUtils.defineLazyModuleGetter(this, "VariablesView",
"resource:///modules/devtools/VariablesView.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "VariablesViewController",
"resource:///modules/devtools/VariablesViewController.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "EventEmitter",
"resource:///modules/devtools/shared/event-emitter.js");
@ -2104,13 +2107,9 @@ WebConsoleFrame.prototype = {
}
else if (aNode.classList.contains("webconsole-msg-inspector")) {
let view = aNode._variablesView;
let actors = view ?
this.jsterm._objectActorsInVariablesViews.get(view) :
new Set();
for (let actor of actors) {
this._releaseObject(actor);
if (view) {
view.controller.releaseActors();
}
actors.clear();
aNode._variablesView = null;
}
@ -2743,6 +2742,35 @@ WebConsoleFrame.prototype = {
},
};
/**
* @see VariablesView.simpleValueEvalMacro
*/
function simpleValueEvalMacro(aItem, aCurrentString)
{
return VariablesView.simpleValueEvalMacro(aItem, aCurrentString, "_self");
};
/**
* @see VariablesView.overrideValueEvalMacro
*/
function overrideValueEvalMacro(aItem, aCurrentString)
{
return VariablesView.overrideValueEvalMacro(aItem, aCurrentString, "_self");
};
/**
* @see VariablesView.getterOrSetterEvalMacro
*/
function getterOrSetterEvalMacro(aItem, aCurrentString)
{
return VariablesView.getterOrSetterEvalMacro(aItem, aCurrentString, "_self");
}
/**
* Create a JSTerminal (a JavaScript command line). This is attached to an
* existing HeadsUpDisplay (a Web Console instance). This code is responsible
@ -2771,8 +2799,6 @@ function JSTerm(aWebConsoleFrame)
this._keyPress = this.keyPress.bind(this);
this._inputEventHandler = this.inputEventHandler.bind(this);
this._fetchVarProperties = this._fetchVarProperties.bind(this);
this._fetchVarLongString = this._fetchVarLongString.bind(this);
this._onKeypressInVariablesView = this._onKeypressInVariablesView.bind(this);
EventEmitter.decorate(this);
@ -3282,7 +3308,27 @@ JSTerm.prototype = {
view.searchEnabled = !aOptions.hideFilterInput;
view.lazyEmpty = this._lazyVariablesView;
view.lazyAppend = this._lazyVariablesView;
this._objectActorsInVariablesViews.set(view, new Set());
VariablesViewController.attach(view, {
getGripClient: aGrip => {
return new GripClient(this.hud.proxy.client, aGrip);
},
getLongStringClient: aGrip => {
return this.webConsoleClient.longString(aGrip);
},
releaseActor: aActor => {
this.hud._releaseObject(aActor);
},
simpleValueEvalMacro: simpleValueEvalMacro,
overrideValueEvalMacro: overrideValueEvalMacro,
getterOrSetterEvalMacro: getterOrSetterEvalMacro,
});
// Relay events from the VariablesView.
view.on("fetched", (aEvent, aType, aVar) => {
this.emit("variablesview-fetched", aVar);
});
return view;
},
@ -3304,16 +3350,11 @@ JSTerm.prototype = {
view.createHierarchy();
view.empty();
let actors = this._objectActorsInVariablesViews.get(view);
for (let actor of actors) {
// We need to avoid pruning the object inspection starting point.
// That one is pruned when the console message is removed.
if (view._consoleLastObjectActor != actor) {
this.hud._releaseObject(actor);
}
}
actors.clear();
// We need to avoid pruning the object inspection starting point.
// That one is pruned when the console message is removed.
view.controller.releaseActors(aActor => {
return view._consoleLastObjectActor != aActor;
});
if (aOptions.objectActor) {
// Make sure eval works in the correct context.
@ -3331,11 +3372,11 @@ JSTerm.prototype = {
scope.expanded = true;
scope.locked = true;
let container = scope.addVar();
container.evaluationMacro = this._variablesViewSimpleValueEvalMacro;
let container = scope.addItem();
container.evaluationMacro = simpleValueEvalMacro;
if (aOptions.objectActor) {
this._fetchVarProperties(container, aOptions.objectActor);
view.controller.expand(container, aOptions.objectActor);
view._consoleLastObjectActor = aOptions.objectActor.actor;
}
else if (aOptions.rawObject) {
@ -3374,80 +3415,6 @@ JSTerm.prototype = {
this.requestEvaluation(aString, evalOptions).then(onEval, onEval);
},
/**
* Generates the string evaluated when performing simple value changes in the
* variables view.
*
* @private
* @param Variable | Property aItem
* The current variable or property.
* @param string aCurrentString
* The trimmed user inputted string.
* @return string
* The string to be evaluated.
*/
_variablesViewSimpleValueEvalMacro:
function JST__variablesViewSimpleValueEvalMacro(aItem, aCurrentString)
{
return "_self" + aItem.symbolicName + "=" + aCurrentString;
},
/**
* Generates the string evaluated when overriding getters and setters with
* plain values in the variables view.
*
* @private
* @param Property aItem
* The current getter or setter property.
* @param string aCurrentString
* The trimmed user inputted string.
* @return string
* The string to be evaluated.
*/
_variablesViewOverrideValueEvalMacro:
function JST__variablesViewOverrideValueEvalMacro(aItem, aCurrentString)
{
let parent = aItem.ownerView;
let symbolicName = parent.symbolicName;
if (symbolicName.indexOf("_self") != 0) {
parent._symbolicName = "_self" + symbolicName;
}
let result = VariablesView.overrideValueEvalMacro.apply(this, arguments);
parent._symbolicName = symbolicName;
return result;
},
/**
* Generates the string evaluated when performing getters and setters changes
* in the variables view.
*
* @private
* @param Property aItem
* The current getter or setter property.
* @param string aCurrentString
* The trimmed user inputted string.
* @return string
* The string to be evaluated.
*/
_variablesViewGetterOrSetterEvalMacro:
function JST__variablesViewGetterOrSetterEvalMacro(aItem, aCurrentString)
{
let propertyObject = aItem.ownerView;
let parentObject = propertyObject.ownerView;
let parent = parentObject.symbolicName;
parentObject._symbolicName = "_self" + parent;
let result = VariablesView.getterOrSetterEvalMacro.apply(this, arguments);
parentObject._symbolicName = parent;
return result;
},
/**
* The property deletion function used by the variables view when a property
* is deleted.
@ -3556,144 +3523,7 @@ JSTerm.prototype = {
aCallback && aCallback(aResponse);
},
/**
* Adds properties to a variable in the view. Triggered when a variable is
* expanded. It does not expand the variable.
*
* @param object aVar
* The VariablseView Variable instance where the properties get added.
* @param object [aGrip]
* Optional, the object actor grip of the variable. If the grip is not
* provided, then the aVar.value is used as the object actor grip.
*/
_fetchVarProperties: function JST__fetchVarProperties(aVar, aGrip)
{
// Retrieve the properties only once.
if (aVar._fetched) {
return;
}
aVar._fetched = true;
let grip = aGrip || aVar.value;
if (!grip) {
throw new Error("No object actor grip was given for the variable.");
}
let view = aVar._variablesView;
let actors = this._objectActorsInVariablesViews.get(view);
function addActorForDescriptor(aGrip) {
if (WebConsoleUtils.isActorGrip(aGrip)) {
actors.add(aGrip.actor);
}
}
let onNewProperty = (aProperty) => {
if (aProperty.getter || aProperty.setter) {
aProperty.evaluationMacro = this._variablesViewOverrideValueEvalMacro;
let getter = aProperty.get("get");
let setter = aProperty.get("set");
if (getter) {
getter.evaluationMacro = this._variablesViewGetterOrSetterEvalMacro;
}
if (setter) {
setter.evaluationMacro = this._variablesViewGetterOrSetterEvalMacro;
}
}
else {
aProperty.evaluationMacro = this._variablesViewSimpleValueEvalMacro;
}
let grips = [aProperty.value, aProperty.getter, aProperty.setter];
grips.forEach(addActorForDescriptor);
let inspectable = !VariablesView.isPrimitive({ value: aProperty.value });
let longString = WebConsoleUtils.isActorGrip(aProperty.value) &&
aProperty.value.type == "longString";
if (inspectable) {
aProperty.onexpand = this._fetchVarProperties;
}
else if (longString) {
aProperty.onexpand = this._fetchVarLongString;
aProperty.showArrow();
}
};
let client = new GripClient(this.hud.proxy.client, grip);
client.getPrototypeAndProperties((aResponse) => {
let { ownProperties, prototype, safeGetterValues } = aResponse;
let sortable = VariablesView.NON_SORTABLE_CLASSES.indexOf(grip.class) == -1;
// Merge the safe getter values into one object such that we can use it
// in VariablesView.
for (let name of Object.keys(safeGetterValues)) {
if (name in ownProperties) {
ownProperties[name].getterValue = safeGetterValues[name].getterValue;
ownProperties[name].getterPrototypeLevel = safeGetterValues[name]
.getterPrototypeLevel;
}
else {
ownProperties[name] = safeGetterValues[name];
}
}
// Add all the variable properties.
if (ownProperties) {
aVar.addProperties(ownProperties, {
sorted: sortable,
callback: onNewProperty,
});
}
// Add the variable's __proto__.
if (prototype && prototype.type != "null") {
let proto = aVar.addProperty("__proto__", { value: prototype });
onNewProperty(proto);
}
aVar._retrieved = true;
view.commitHierarchy();
this.emit("variablesview-fetched", aVar);
});
},
/**
* Fetch the full string for a given variable that displays a long string.
*
* @param object aVar
* The VariablesView Variable instance where the properties get added.
*/
_fetchVarLongString: function JST__fetchVarLongString(aVar)
{
if (aVar._fetched) {
return;
}
aVar._fetched = true;
let grip = aVar.value;
if (!grip) {
throw new Error("No long string actor grip was given for the variable.");
}
let client = this.webConsoleClient.longString(grip);
let toIndex = Math.min(grip.length, MAX_LONG_STRING_LENGTH);
client.substring(grip.initial.length, toIndex, (aResponse) => {
if (aResponse.error) {
Cu.reportError("JST__fetchVarLongString substring failure: " +
aResponse.error + ": " + aResponse.message);
return;
}
aVar.onexpand = null;
aVar.setGrip(grip.initial + aResponse.substring);
aVar.hideArrow();
aVar._retrieved = true;
if (toIndex != grip.length) {
this.hud.logWarningAboutStringTooLong();
}
});
},
/**
* Writes a JS object to the JSTerm outputNode.
@ -4358,11 +4188,7 @@ JSTerm.prototype = {
_sidebarDestroy: function JST__sidebarDestroy()
{
if (this._variablesView) {
let actors = this._objectActorsInVariablesViews.get(this._variablesView);
for (let actor of actors) {
this.hud._releaseObject(actor);
}
actors.clear();
this._variablesView.controller.releaseActors();
this._variablesView = null;
}

View File

@ -574,7 +574,7 @@ var BrowserUI = {
this._sslDiskCacheEnabled = Services.prefs.getBoolPref(aData);
break;
case "browser.urlbar.formatting.enabled":
this._formattingEnabled = Services.prefs.getBookPref(aData);
this._formattingEnabled = Services.prefs.getBoolPref(aData);
break;
case "browser.urlbar.trimURLs":
this._mayTrimURLs = Services.prefs.getBoolPref(aData);

View File

@ -23,6 +23,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=416317
<script class="testbody" type="text/javascript">
/** Test for Bug 416317 **/
SimpleTest.requestLongerTimeout(2);
// Subframe handles the test
</script>
</pre>

View File

@ -46,241 +46,5 @@
#define CRASH() MOZ_CRASH()
#define COMPILE_ASSERT(exp, name) MOZ_STATIC_ASSERT(exp, #name)
#endif
#endif // WTF_Assertions_h
#if 0
/*
no namespaces because this file has to be includable from C and Objective-C
Note, this file uses many GCC extensions, but it should be compatible with
C, Objective C, C++, and Objective C++.
For non-debug builds, everything is disabled by default.
Defining any of the symbols explicitly prevents this from having any effect.
MSVC7 note: variadic macro support was added in MSVC8, so for now we disable
those macros in MSVC7. For more info, see the MSDN document on variadic
macros here:
http://msdn2.microsoft.com/en-us/library/ms177415(VS.80).aspx
*/
#include "Platform.h"
#if WTF_COMPILER_MSVC
#include <stddef.h>
#else
#include <inttypes.h>
#endif
#if WTF_OS_SYMBIAN
#include <e32def.h>
#include <e32debug.h>
#endif
#ifdef NDEBUG
#define ASSERTIONS_DISABLED_DEFAULT 1
#else
#define ASSERTIONS_DISABLED_DEFAULT 0
#endif
#ifndef ASSERT_DISABLED
#define ASSERT_DISABLED ASSERTIONS_DISABLED_DEFAULT
#endif
#ifndef ASSERT_ARG_DISABLED
#define ASSERT_ARG_DISABLED ASSERTIONS_DISABLED_DEFAULT
#endif
#ifndef FATAL_DISABLED
#define FATAL_DISABLED ASSERTIONS_DISABLED_DEFAULT
#endif
#ifndef ERROR_DISABLED
#define ERROR_DISABLED ASSERTIONS_DISABLED_DEFAULT
#endif
#ifndef LOG_DISABLED
#define LOG_DISABLED ASSERTIONS_DISABLED_DEFAULT
#endif
#if WTF_COMPILER_GCC
#define WTF_PRETTY_FUNCTION __PRETTY_FUNCTION__
#else
#define WTF_PRETTY_FUNCTION __FUNCTION__
#endif
/* WTF logging functions can process %@ in the format string to log a NSObject* but the printf format attribute
emits a warning when %@ is used in the format string. Until <rdar://problem/5195437> is resolved we can't include
the attribute when being used from Objective-C code in case it decides to use %@. */
#if WTF_COMPILER_GCC && !defined(__OBJC__)
#define WTF_ATTRIBUTE_PRINTF(formatStringArgument, extraArguments) __attribute__((__format__(printf, formatStringArgument, extraArguments)))
#else
#define WTF_ATTRIBUTE_PRINTF(formatStringArgument, extraArguments)
#endif
/* These helper functions are always declared, but not necessarily always defined if the corresponding function is disabled. */
#ifdef __cplusplus
extern "C" {
#endif
typedef enum { WTFLogChannelOff, WTFLogChannelOn } WTFLogChannelState;
typedef struct {
unsigned mask;
const char *defaultName;
WTFLogChannelState state;
} WTFLogChannel;
void WTFReportAssertionFailure(const char* file, int line, const char* function, const char* assertion);
void WTFReportAssertionFailureWithMessage(const char* file, int line, const char* function, const char* assertion, const char* format, ...) WTF_ATTRIBUTE_PRINTF(5, 6);
void WTFReportArgumentAssertionFailure(const char* file, int line, const char* function, const char* argName, const char* assertion);
void WTFReportFatalError(const char* file, int line, const char* function, const char* format, ...) WTF_ATTRIBUTE_PRINTF(4, 5);
void WTFReportError(const char* file, int line, const char* function, const char* format, ...) WTF_ATTRIBUTE_PRINTF(4, 5);
void WTFLog(WTFLogChannel* channel, const char* format, ...) WTF_ATTRIBUTE_PRINTF(2, 3);
void WTFLogVerbose(const char* file, int line, const char* function, WTFLogChannel* channel, const char* format, ...) WTF_ATTRIBUTE_PRINTF(5, 6);
#ifdef __cplusplus
}
#endif
/* CRASH -- gets us into the debugger or the crash reporter -- signals are ignored by the crash reporter so we must do better */
#ifndef CRASH
#if WTF_OS_SYMBIAN
#define CRASH() do { \
__DEBUGGER(); \
User::Panic(_L("Webkit CRASH"),0); \
} while(false)
#else
#define CRASH() do { \
*(int *)(uintptr_t)0xbbadbeef = 0; \
((void(*)())0)(); /* More reliable, but doesn't say BBADBEEF */ \
} while(false)
#endif
#endif
/* ASSERT, ASSERT_WITH_MESSAGE, ASSERT_NOT_REACHED */
#if WTF_PLATFORM_WIN || WTF_OS_SYMBIAN
/* FIXME: Change to use something other than ASSERT to avoid this conflict with the underlying platform */
#undef ASSERT
#endif
#if ASSERT_DISABLED
#define ASSERT(assertion) ((void)0)
#if WTF_COMPILER_MSVC7
#define ASSERT_WITH_MESSAGE(assertion) ((void)0)
#elif WTF_COMPILER_WINSCW
#define ASSERT_WITH_MESSAGE(assertion, arg...) ((void)0)
#else
#define ASSERT_WITH_MESSAGE(assertion, ...) ((void)0)
#endif /* COMPILER(MSVC7) */
#define ASSERT_NOT_REACHED() ((void)0)
#define ASSERT_UNUSED(variable, assertion) ((void)variable)
#else
#define ASSERT(assertion) do \
if (!(assertion)) { \
WTFReportAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #assertion); \
CRASH(); \
} \
while (0)
#if WTF_COMPILER_MSVC7
#define ASSERT_WITH_MESSAGE(assertion) ((void)0)
#elif WTF_COMPILER_WINSCW
#define ASSERT_WITH_MESSAGE(assertion, arg...) ((void)0)
#else
#define ASSERT_WITH_MESSAGE(assertion, ...) do \
if (!(assertion)) { \
WTFReportAssertionFailureWithMessage(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #assertion, __VA_ARGS__); \
CRASH(); \
} \
while (0)
#endif /* COMPILER(MSVC7) */
#define ASSERT_NOT_REACHED() do { \
WTFReportAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, 0); \
CRASH(); \
} while (0)
#define ASSERT_UNUSED(variable, assertion) ASSERT(assertion)
#endif
/* ASSERT_ARG */
#if ASSERT_ARG_DISABLED
#define ASSERT_ARG(argName, assertion) ((void)0)
#else
#define ASSERT_ARG(argName, assertion) do \
if (!(assertion)) { \
WTFReportArgumentAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #argName, #assertion); \
CRASH(); \
} \
while (0)
#endif
/* COMPILE_ASSERT */
#ifndef COMPILE_ASSERT
#define COMPILE_ASSERT(exp, name) typedef int dummy##name [(exp) ? 1 : -1]
#endif
/* FATAL */
#if FATAL_DISABLED && !WTF_COMPILER_MSVC7 && !WTF_COMPILER_WINSCW
#define FATAL(...) ((void)0)
#elif WTF_COMPILER_MSVC7
#define FATAL() ((void)0)
#else
#define FATAL(...) do { \
WTFReportFatalError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, __VA_ARGS__); \
CRASH(); \
} while (0)
#endif
/* LOG_ERROR */
#if ERROR_DISABLED && !WTF_COMPILER_MSVC7 && !WTF_COMPILER_WINSCW
#define LOG_ERROR(...) ((void)0)
#elif WTF_COMPILER_MSVC7
#define LOG_ERROR() ((void)0)
#elif WTF_COMPILER_WINSCW
#define LOG_ERROR(arg...) ((void)0)
#else
#define LOG_ERROR(...) WTFReportError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, __VA_ARGS__)
#endif
/* LOG */
#if LOG_DISABLED && !WTF_COMPILER_MSVC7 && !WTF_COMPILER_WINSCW
#define LOG(channel, ...) ((void)0)
#elif WTF_COMPILER_MSVC7
#define LOG() ((void)0)
#elif WTF_COMPILER_WINSCW
#define LOG(arg...) ((void)0)
#else
#define LOG(channel, ...) WTFLog(&JOIN_LOG_CHANNEL_WITH_PREFIX(LOG_CHANNEL_PREFIX, channel), __VA_ARGS__)
#define JOIN_LOG_CHANNEL_WITH_PREFIX(prefix, channel) JOIN_LOG_CHANNEL_WITH_PREFIX_LEVEL_2(prefix, channel)
#define JOIN_LOG_CHANNEL_WITH_PREFIX_LEVEL_2(prefix, channel) prefix ## channel
#endif
/* LOG_VERBOSE */
#if LOG_DISABLED && !WTF_COMPILER_MSVC7 && !WTF_COMPILER_WINSCW
#define LOG_VERBOSE(channel, ...) ((void)0)
#elif WTF_COMPILER_MSVC7
#define LOG_VERBOSE(channel) ((void)0)
#elif WTF_COMPILER_WINSCW
#define LOG_VERBOSE(channel, arg...) ((void)0)
#else
#define LOG_VERBOSE(channel, ...) WTFLogVerbose(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, &JOIN_LOG_CHANNEL_WITH_PREFIX(LOG_CHANNEL_PREFIX, channel), __VA_ARGS__)
#endif
#endif /* WTF_Assertions_h */

View File

@ -76,6 +76,52 @@ CheckArgumentsWithinEval(JSContext *cx, Parser<FullParseHandler> &parser, Handle
return true;
}
static bool
MaybeCheckEvalFreeVariables(JSContext *cx, HandleScript evalCaller, HandleObject scopeChain,
Parser<FullParseHandler> &parser,
ParseContext<FullParseHandler> &pc)
{
if (!evalCaller || !evalCaller->functionOrCallerFunction())
return true;
// Watch for uses of 'arguments' within the evaluated script, both as
// free variables and as variables redeclared with 'var'.
RootedFunction fun(cx, evalCaller->functionOrCallerFunction());
HandlePropertyName arguments = cx->names().arguments;
for (AtomDefnRange r = pc.lexdeps->all(); !r.empty(); r.popFront()) {
if (r.front().key() == arguments) {
if (!CheckArgumentsWithinEval(cx, parser, fun))
return false;
}
}
for (AtomDefnListMap::Range r = pc.decls().all(); !r.empty(); r.popFront()) {
if (r.front().key() == arguments) {
if (!CheckArgumentsWithinEval(cx, parser, fun))
return false;
}
}
// If the eval'ed script contains any debugger statement, force construction
// of arguments objects for the caller script and any other scripts it is
// transitively nested inside. The debugger can access any variable on the
// scope chain.
if (pc.sc->hasDebuggerStatement()) {
RootedObject scope(cx, scopeChain);
while (scope->isScope() || scope->isDebugScope()) {
if (scope->isCall() && !scope->asCall().isForEval()) {
RootedScript script(cx, scope->asCall().callee().nonLazyScript());
if (script->argumentsHasVarBinding()) {
if (!JSScript::argumentsOptimizationFailed(cx, script))
return false;
}
}
scope = scope->enclosingScope();
}
}
return true;
}
inline bool
CanLazilyParse(JSContext *cx, const CompileOptions &options)
{
@ -235,6 +281,12 @@ frontend::CompileScript(JSContext *cx, HandleObject scopeChain,
// be ambiguous.
parser.clearAbortedSyntaxParse();
parser.tokenStream.seek(pos);
// Destroying the parse context will destroy its free
// variables, so check if any deoptimization is needed.
if (!MaybeCheckEvalFreeVariables(cx, evalCaller, scopeChain, parser, pc.ref()))
return NULL;
pc.destroy();
pc.construct(&parser, (GenericParseContext *) NULL, &globalsc,
staticLevel, /* bodyid = */ 0);
@ -265,44 +317,11 @@ frontend::CompileScript(JSContext *cx, HandleObject scopeChain,
parser.handler.freeTree(pn);
}
if (!SetSourceMap(cx, parser.tokenStream, ss, script))
if (!MaybeCheckEvalFreeVariables(cx, evalCaller, scopeChain, parser, pc.ref()))
return NULL;
if (evalCaller && evalCaller->functionOrCallerFunction()) {
// Watch for uses of 'arguments' within the evaluated script, both as
// free variables and as variables redeclared with 'var'.
RootedFunction fun(cx, evalCaller->functionOrCallerFunction());
HandlePropertyName arguments = cx->names().arguments;
for (AtomDefnRange r = pc.ref().lexdeps->all(); !r.empty(); r.popFront()) {
if (r.front().key() == arguments) {
if (!CheckArgumentsWithinEval(cx, parser, fun))
return NULL;
}
}
for (AtomDefnListMap::Range r = pc.ref().decls().all(); !r.empty(); r.popFront()) {
if (r.front().key() == arguments) {
if (!CheckArgumentsWithinEval(cx, parser, fun))
return NULL;
}
}
// If the eval'ed script contains any debugger statement, force construction
// of arguments objects for the caller script and any other scripts it is
// transitively nested inside.
if (pc.ref().sc->hasDebuggerStatement()) {
RootedObject scope(cx, scopeChain);
while (scope->isScope() || scope->isDebugScope()) {
if (scope->isCall() && !scope->asCall().isForEval()) {
RootedScript script(cx, scope->asCall().callee().nonLazyScript());
if (script->argumentsHasVarBinding()) {
if (!JSScript::argumentsOptimizationFailed(cx, script))
return NULL;
}
}
scope = scope->enclosingScope();
}
}
}
if (!SetSourceMap(cx, parser.tokenStream, ss, script))
return NULL;
/*
* Nowadays the threaded interpreter needs a stop instruction, so we

View File

@ -3505,7 +3505,6 @@ Parser<ParseHandler>::switchStatement()
while ((tt = tokenStream.getToken()) != TOK_RC) {
uint32_t caseBegin = tokenStream.currentToken().pos.begin;
ParseNodeKind caseKind;
Node caseExpr;
switch (tt) {
case TOK_DEFAULT:
@ -3514,12 +3513,10 @@ Parser<ParseHandler>::switchStatement()
return null();
}
seenDefault = true;
caseKind = PNK_DEFAULT;
caseExpr = null();
break;
case TOK_CASE:
caseKind = PNK_CASE;
caseExpr = expr();
if (!caseExpr)
return null();

View File

@ -4,10 +4,11 @@
* 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/. */
#ifdef JSGC_GENERATIONAL
#ifndef jsgc_storebuffer_h___
#define jsgc_storebuffer_h___
#ifdef JSGC_GENERATIONAL
#ifndef JSGC_USE_EXACT_ROOTING
# error "Generational GC requires exact rooting."
#endif
@ -509,5 +510,6 @@ class StoreBuffer
} /* namespace gc */
} /* namespace js */
#endif /* jsgc_storebuffer_h___ */
#endif /* JSGC_GENERATIONAL */
#endif /* jsgc_storebuffer_h___ */

View File

@ -5316,7 +5316,10 @@ GenerateEntries(ModuleCompiler &m)
static inline bool
TryEnablingIon(JSContext *cx, AsmJSModule::ExitDatum *exitDatum, int32_t argc, Value *argv)
{
JSScript *script = exitDatum->fun->maybeNonLazyScript();
if (!exitDatum->fun->hasScript())
return true;
JSScript *script = exitDatum->fun->nonLazyScript();
if (!script)
return true;

View File

@ -4,7 +4,7 @@
* 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/. */
#if !defined(jsion_asmjs_h__)
#ifndef jsion_asmjs_h__
#define jsion_asmjs_h__
#ifdef XP_MACOSX

View File

@ -4,9 +4,11 @@
* 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/. */
#if !defined(jsion_asmjsmodule_h__) && defined(JS_ION)
#ifndef jsion_asmjsmodule_h__
#define jsion_asmjsmodule_h__
#ifdef JS_ION
#include "gc/Marking.h"
#include "ion/RegisterSets.h"
@ -746,5 +748,7 @@ SetAsmJSModuleObject(JSFunction *moduleFun, JSObject *moduleObj);
} // namespace js
#endif // JS_ION
#endif // jsion_asmjsmodule_h__

View File

@ -1060,7 +1060,7 @@ ion::BailoutIonToBaseline(JSContext *cx, JitActivation *activation, IonBailoutIt
RootedFunction callee(cx, iter.maybeCallee());
if (callee) {
IonSpew(IonSpew_BaselineBailouts, " Callee function (%s:%u)",
callee->nonLazyScript()->filename(), callee->nonLazyScript()->lineno);
callee->getExistingScript()->filename(), callee->getExistingScript()->lineno);
} else {
IonSpew(IonSpew_BaselineBailouts, " No callee!");
}
@ -1098,7 +1098,7 @@ ion::BailoutIonToBaseline(JSContext *cx, JitActivation *activation, IonBailoutIt
caller = scr;
callerPC = callPC;
fun = nextCallee;
scr = fun->nonLazyScript();
scr = fun->getExistingScript();
snapIter.nextFrame();
frameNo++;

View File

@ -4,9 +4,11 @@
* 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/. */
#if !defined(jsion_baseline_compiler_h__) && defined(JS_ION)
#ifndef jsion_baseline_compiler_h__
#define jsion_baseline_compiler_h__
#ifdef JS_ION
#include "jscntxt.h"
#include "jscompartment.h"
#include "IonCode.h"
@ -265,5 +267,7 @@ class BaselineCompiler : public BaselineCompilerSpecific
} // namespace ion
} // namespace js
#endif
#endif // JS_ION
#endif // jsion_baseline_compiler_h__

View File

@ -4,9 +4,11 @@
* 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/. */
#if !defined(jsion_baseline_frame_inl_h__) && defined(JS_ION)
#ifndef jsion_baseline_frame_inl_h__
#define jsion_baseline_frame_inl_h__
#ifdef JS_ION
#include "jscntxt.h"
#include "jscompartment.h"
@ -78,5 +80,7 @@ BaselineFrame::callObj() const
} // namespace ion
} // namespace js
#endif
#endif // JS_ION
#endif // jsion_baseline_frame_inl_h__

View File

@ -4,9 +4,11 @@
* 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/. */
#if !defined(jsion_baseline_frame_h__) && defined(JS_ION)
#ifndef jsion_baseline_frame_h__
#define jsion_baseline_frame_h__
#ifdef JS_ION
#include "jscntxt.h"
#include "jscompartment.h"
@ -396,5 +398,7 @@ JS_STATIC_ASSERT(((sizeof(BaselineFrame) + BaselineFrame::FramePointerOffset) %
} // namespace ion
} // namespace js
#endif
#endif // JS_ION
#endif // jsion_baseline_frame_h__

View File

@ -4,9 +4,11 @@
* 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/. */
#if !defined(jsion_baseline_frameinfo_h__) && defined(JS_ION)
#ifndef jsion_baseline_frameinfo_h__
#define jsion_baseline_frameinfo_h__
#ifdef JS_ION
#include "jscntxt.h"
#include "jscompartment.h"
@ -329,5 +331,7 @@ class FrameInfo
} // namespace ion
} // namespace js
#endif
#endif // JS_ION
#endif // jsion_baseline_frameinfo_h__

View File

@ -4,9 +4,11 @@
* 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/. */
#if !defined(jsion_baseline_helpers_h__) && defined(JS_ION)
#ifndef jsion_baseline_helpers_h__
#define jsion_baseline_helpers_h__
#ifdef JS_ION
#if defined(JS_CPU_X86)
# include "x86/BaselineHelpers-x86.h"
#elif defined(JS_CPU_X64)
@ -23,5 +25,6 @@ namespace ion {
} // namespace ion
} // namespace js
#endif
#endif // JS_ION
#endif // jsion_baseline_helpers_h__

View File

@ -4,9 +4,11 @@
* 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/. */
#if !defined(jsion_baseline_ic_h__) && defined(JS_ION)
#ifndef jsion_baseline_ic_h__
#define jsion_baseline_ic_h__
#ifdef JS_ION
#include "jscntxt.h"
#include "jscompartment.h"
#include "jsgc.h"
@ -5522,4 +5524,6 @@ class ICRest_Fallback : public ICFallbackStub
} // namespace ion
} // namespace js
#endif
#endif // JS_ION
#endif // jsion_baseline_ic_h__

View File

@ -4,9 +4,11 @@
* 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/. */
#if !defined(jsion_baseline_inspector_h__) && defined(JS_ION)
#ifndef jsion_baseline_inspector_h__
#define jsion_baseline_inspector_h__
#ifdef JS_ION
#include "jscntxt.h"
#include "jscompartment.h"
@ -111,5 +113,7 @@ class BaselineInspector
} // namespace ion
} // namespace js
#endif
#endif // JS_ION
#endif // jsion_baseline_inspector_h__

View File

@ -4,9 +4,11 @@
* 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/. */
#if !defined(jsion_baseline_jit_h__) && defined(JS_ION)
#ifndef jsion_baseline_jit_h__
#define jsion_baseline_jit_h__
#ifdef JS_ION
#include "jscntxt.h"
#include "jscompartment.h"
@ -330,5 +332,7 @@ MarkActiveBaselineScripts(Zone *zone);
} // namespace ion
} // namespace js
#endif
#endif // JS_ION
#endif // jsion_baseline_jit_h__

View File

@ -4,9 +4,11 @@
* 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/. */
#if !defined(jsion_baseline_registers_h__) && defined(JS_ION)
#ifndef jsion_baseline_registers_h__
#define jsion_baseline_registers_h__
#ifdef JS_ION
#if defined(JS_CPU_X86)
# include "x86/BaselineRegisters-x86.h"
#elif defined(JS_CPU_X64)
@ -21,5 +23,7 @@ namespace ion {
} // namespace ion
} // namespace js
#endif
#endif // JS_ION
#endif // jsion_baseline_registers_h__

View File

@ -4,9 +4,11 @@
* 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/. */
#if !defined(jsion_ion_gc_h__) && defined(JS_ION)
#ifndef jsion_ion_gc_h__
#define jsion_ion_gc_h__
#ifdef JS_ION
#include "jscntxt.h"
#include "js/RootingAPI.h"
@ -61,5 +63,7 @@ typedef CompilerRoot<Value> CompilerRootValue;
} // namespace ion
} // namespace js
#endif // JS_ION
#endif // jsion_ion_gc_h__

View File

@ -4,9 +4,11 @@
* 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/. */
#if !defined(jsion_executionmodeinlines_h__) && defined(JS_ION)
#ifndef jsion_executionmodeinlines_h__
#define jsion_executionmodeinlines_h__
#ifdef JS_ION
namespace js {
namespace ion {
@ -102,7 +104,9 @@ CompilerOutputKind(ExecutionMode cmode)
return types::CompilerOutput::Ion;
}
}
}
} // namespace ion
} // namespace js
#endif // JS_ION
#endif // jsion_executionmodeinlines_h__

View File

@ -4,9 +4,11 @@
* 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/. */
#if !defined(jsion_ion_h__) && defined(JS_ION)
#ifndef jsion_ion_h__
#define jsion_ion_h__
#ifdef JS_ION
#include "jscntxt.h"
#include "jscompartment.h"
#include "IonCode.h"
@ -350,5 +352,7 @@ void TraceIonScripts(JSTracer* trc, JSScript *script);
} // namespace ion
} // namespace js
#endif // JS_ION
#endif // jsion_ion_h__

View File

@ -4,9 +4,11 @@
* 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/. */
#if !defined(jsion_bytecode_analyzer_h__) && defined(JS_ION)
#ifndef jsion_bytecode_analyzer_h__
#define jsion_bytecode_analyzer_h__
#ifdef JS_ION
// This file declares the data structures for building a MIRGraph from a
// JSScript.
@ -798,4 +800,6 @@ bool NeedsPostBarrier(CompileInfo &info, MDefinition *value);
} // namespace ion
} // namespace js
#endif // JS_ION
#endif // jsion_bytecode_analyzer_h__

View File

@ -4,9 +4,11 @@
* 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/. */
#if !defined(jsion_ion_compartment_h__) && defined(JS_ION)
#ifndef jsion_ion_compartment_h__
#define jsion_ion_compartment_h__
#ifdef JS_ION
#include "IonCode.h"
#include "jsweakcache.h"
#include "js/Value.h"
@ -316,5 +318,7 @@ void FinishInvalidation(FreeOp *fop, JSScript *script);
} // namespace ion
} // namespace js
#endif // JS_ION
#endif // jsion_ion_compartment_h__

View File

@ -4,9 +4,11 @@
* 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/. */
#if !defined(jsion_frame_iterator_inl_h__) && defined(JS_ION)
#ifndef jsion_frame_iterator_inl_h__
#define jsion_frame_iterator_inl_h__
#ifdef JS_ION
#include "ion/BaselineFrame.h"
#include "ion/IonFrameIterator.h"
#include "ion/Bailouts.h"
@ -236,4 +238,6 @@ IonFrameIterator::baselineFrame() const
} // namespace ion
} // namespace js
#endif // JS_ION
#endif // jsion_frame_iterator_inl_h__

View File

@ -4,9 +4,11 @@
* 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/. */
#if !defined(jsion_frame_iterator_h__) && defined(JS_ION)
#ifndef jsion_frame_iterator_h__
#define jsion_frame_iterator_h__
#ifdef JS_ION
#include "jstypes.h"
#include "IonCode.h"
#include "SnapshotReader.h"
@ -331,5 +333,7 @@ typedef InlineFrameIteratorMaybeGC<NoGC> InlineFrameIteratorNoGC;
} // namespace ion
} // namespace js
#endif // JS_ION
#endif // jsion_frames_iterator_h__

View File

@ -4,9 +4,11 @@
* 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/. */
#if !defined(jsion_frames_inl_h__) && defined(JS_ION)
#ifndef jsion_frames_inl_h__
#define jsion_frames_inl_h__
#ifdef JS_ION
#include "ion/IonFrames.h"
#include "ion/IonFrameIterator.h"
#include "ion/LIR.h"
@ -147,5 +149,7 @@ GetTopBaselineFrame(JSContext *cx)
} // namespace ion
} // namespace js
#endif // JS_ION
#endif // jsion_frames_inl_h__

View File

@ -1267,7 +1267,12 @@ InlineFrameIteratorMaybeGC<allowGC>::findNextFrame()
si_.nextFrame();
callee_ = funval.toObject().toFunction();
script_ = callee_->nonLazyScript();
// Inlined functions may be clones that still point to the lazy script
// for the executed script, if they are clones. The actual script
// exists though, just make sure the function points to it.
script_ = callee_->getExistingScript();
pc_ = script_->code + si_.pcOffset();
}

View File

@ -4,9 +4,11 @@
* 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/. */
#if !defined(jsion_frames_h__) && defined(JS_ION)
#ifndef jsion_frames_h__
#define jsion_frames_h__
#ifdef JS_ION
#include "mozilla/DebugOnly.h"
#include "jsfun.h"
@ -330,5 +332,7 @@ MarkCalleeToken(JSTracer *trc, CalleeToken token);
} /* namespace ion */
} /* namespace js */
#endif // JS_ION
#endif // jsion_frames_h__

View File

@ -4,9 +4,11 @@
* 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/. */
#if !defined(jsion_macro_assembler_h__) && defined(JS_ION)
#ifndef jsion_macro_assembler_h__
#define jsion_macro_assembler_h__
#ifdef JS_ION
#if defined(JS_CPU_X86)
# include "ion/x86/MacroAssembler-x86.h"
#elif defined(JS_CPU_X64)
@ -996,4 +998,6 @@ class ABIArgIter
} // namespace ion
} // namespace js
#endif // JS_ION
#endif // jsion_macro_assembler_h__

View File

@ -160,8 +160,6 @@ IonSpewer::spewPass(const char *pass)
{
if (!isSpewingFunction())
return;
if (!GetIonContext()->cx)
return;
c1Spewer.spewPass(pass);
jsonSpewer.beginPass(pass);
@ -175,8 +173,6 @@ IonSpewer::spewPass(const char *pass, LinearScanAllocator *ra)
{
if (!isSpewingFunction())
return;
if (!GetIonContext()->cx)
return;
c1Spewer.spewPass(pass);
c1Spewer.spewIntervals(pass, ra);

View File

@ -1395,7 +1395,7 @@ bool
LIRGenerator::visitToDouble(MToDouble *convert)
{
MDefinition *opd = convert->input();
MToDouble::ConversionKind conversion = convert->conversion();
mozilla::DebugOnly<MToDouble::ConversionKind> conversion = convert->conversion();
switch (opd->type()) {
case MIRType_Value:

View File

@ -425,11 +425,11 @@ ion::PropagateParallelAbort(JSScript *outermostScript,
void
ion::ParCallToUncompiledScript(JSFunction *func)
{
static const int max_bound_function_unrolling = 5;
JS_ASSERT(InParallelSection());
#ifdef DEBUG
static const int max_bound_function_unrolling = 5;
if (func->hasScript()) {
JSScript *script = func->nonLazyScript();
Spew(SpewBailouts, "Call to uncompiled script: %p:%s:%d",

View File

@ -24,29 +24,32 @@ using namespace js::ion;
#define PERF_MODE_FUNC 2
#define PERF_MODE_BLOCK 3
static bool PerfChecked = false;
#ifdef JS_ION_PERF
static uint32_t PerfMode = 0;
static bool PerfChecked = false;
void
js::ion::CheckPerf() {
const char *env = getenv("IONPERF");
if (env == NULL) {
PerfMode = PERF_MODE_NONE;
} else if (!strcmp(env, "none")) {
PerfMode = PERF_MODE_NONE;
} else if (!strcmp(env, "block")) {
PerfMode = PERF_MODE_BLOCK;
} else if (!strcmp(env, "func")) {
PerfMode = PERF_MODE_FUNC;
} else {
fprintf(stderr, "Use IONPERF=func to record at basic block granularity\n");
fprintf(stderr, "Use IONPERF=block to record at basic block granularity\n");
fprintf(stderr, "\n");
fprintf(stderr, "Be advised that using IONPERF will cause all scripts\n");
fprintf(stderr, "to be leaked.\n");
exit(0);
if (!PerfChecked) {
const char *env = getenv("IONPERF");
if (env == NULL) {
PerfMode = PERF_MODE_NONE;
} else if (!strcmp(env, "none")) {
PerfMode = PERF_MODE_NONE;
} else if (!strcmp(env, "block")) {
PerfMode = PERF_MODE_BLOCK;
} else if (!strcmp(env, "func")) {
PerfMode = PERF_MODE_FUNC;
} else {
fprintf(stderr, "Use IONPERF=func to record at basic block granularity\n");
fprintf(stderr, "Use IONPERF=block to record at basic block granularity\n");
fprintf(stderr, "\n");
fprintf(stderr, "Be advised that using IONPERF will cause all scripts\n");
fprintf(stderr, "to be leaked.\n");
exit(0);
}
PerfChecked = true;
}
}
@ -135,13 +138,16 @@ PerfSpewer::writeProfile(JSScript *script,
uint32_t thisFunctionIndex = nextFunctionIndex++;
if (PerfFuncEnabled()) {
fprintf(fp_,
"%lx %lx %s:%d: Func%02d\n",
reinterpret_cast<uintptr_t>(code->raw()),
(unsigned long) code->instructionsSize(),
script->filename(),
script->lineno,
thisFunctionIndex);
unsigned long size = (unsigned long) code->instructionsSize();
if (size > 0) {
fprintf(fp_,
"%lx %lx %s:%d: Func%02d\n",
reinterpret_cast<uintptr_t>(code->raw()),
size,
script->filename(),
script->lineno,
thisFunctionIndex);
}
} else if (PerfBlockEnabled()) {
uintptr_t funcStart = uintptr_t(code->raw());
uintptr_t funcEnd = funcStart + code->instructionsSize();
@ -163,11 +169,15 @@ PerfSpewer::writeProfile(JSScript *script,
}
cur = blockEnd;
fprintf(fp_,
"%lx %lx %s:%d:%d: Func%02d-Block%d\n",
blockStart, blockEnd - blockStart,
r.filename, r.lineNumber, r.columnNumber,
thisFunctionIndex, r.id);
unsigned long size = blockEnd - blockStart;
if (size > 0) {
fprintf(fp_,
"%lx %lx %s:%d:%d: Func%02d-Block%d\n",
blockStart, size,
r.filename, r.lineNumber, r.columnNumber,
thisFunctionIndex, r.id);
}
}
// Any stuff after the basic blocks is presumably OOL code,

View File

@ -4,9 +4,11 @@
* 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/. */
#if !defined(jsion_baseline_helpers_arm_h__) && defined(JS_ION)
#ifndef jsion_baseline_helpers_arm_h__
#define jsion_baseline_helpers_arm_h__
#ifdef JS_ION
#include "ion/IonMacroAssembler.h"
#include "ion/BaselineFrame.h"
#include "ion/BaselineRegisters.h"
@ -303,5 +305,7 @@ EmitStubGuardFailure(MacroAssembler &masm)
} // namespace ion
} // namespace js
#endif
#endif // JS_ION
#endif // jsion_baseline_helpers_arm_h__

View File

@ -4,9 +4,11 @@
* 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/. */
#if !defined(jsion_baseline_registers_arm_h__) && defined(JS_ION)
#ifndef jsion_baseline_registers_arm_h__
#define jsion_baseline_registers_arm_h__
#ifdef JS_ION
#include "ion/IonMacroAssembler.h"
namespace js {
@ -53,5 +55,7 @@ static const FloatRegister FloatReg1 = d2;
} // namespace ion
} // namespace js
#endif
#endif // JS_ION
#endif // jsion_baseline_registers_arm_h__

View File

@ -684,7 +684,7 @@ CodeGeneratorX86Shared::visitDivPowTwoI(LDivPowTwoI *ins)
{
Register lhs = ToRegister(ins->numerator());
Register lhsCopy = ToRegister(ins->numeratorCopy());
Register output = ToRegister(ins->output());
mozilla::DebugOnly<Register> output = ToRegister(ins->output());
int32_t shift = ins->shift();
// We use defineReuseInput so these should always be the same, which is

View File

@ -4,9 +4,11 @@
* 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/. */
#if !defined(jsion_baseline_helpers_x64_h__) && defined(JS_ION)
#ifndef jsion_baseline_helpers_x64_h__
#define jsion_baseline_helpers_x64_h__
#ifdef JS_ION
#include "ion/IonMacroAssembler.h"
#include "ion/BaselineFrame.h"
#include "ion/BaselineRegisters.h"
@ -274,5 +276,7 @@ EmitStubGuardFailure(MacroAssembler &masm)
} // namespace ion
} // namespace js
#endif
#endif // JS_ION
#endif // jsion_baseline_helpers_x64_h__

View File

@ -4,9 +4,11 @@
* 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/. */
#if !defined(jsion_baseline_registers_x64_h__) && defined(JS_ION)
#ifndef jsion_baseline_registers_x64_h__
#define jsion_baseline_registers_x64_h__
#ifdef JS_ION
#include "ion/IonMacroAssembler.h"
namespace js {
@ -34,5 +36,7 @@ static const FloatRegister FloatReg1 = xmm1;
} // namespace ion
} // namespace js
#endif
#endif // JS_ION
#endif // jsion_baseline_registers_x64_h__

View File

@ -4,9 +4,11 @@
* 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/. */
#if !defined(jsion_baseline_helpers_x86_h__) && defined(JS_ION)
#ifndef jsion_baseline_helpers_x86_h__
#define jsion_baseline_helpers_x86_h__
#ifdef JS_ION
#include "ion/IonMacroAssembler.h"
#include "ion/BaselineFrame.h"
#include "ion/BaselineRegisters.h"
@ -280,5 +282,7 @@ EmitStubGuardFailure(MacroAssembler &masm)
} // namespace ion
} // namespace js
#endif
#endif // JS_ION
#endif // jsion_baseline_helpers_x86_h__

View File

@ -4,9 +4,11 @@
* 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/. */
#if !defined(jsion_baseline_registers_x86_h__) && defined(JS_ION)
#ifndef jsion_baseline_registers_x86_h__
#define jsion_baseline_registers_x86_h__
#ifdef JS_ION
#include "ion/IonMacroAssembler.h"
namespace js {
@ -35,5 +37,7 @@ static const FloatRegister FloatReg1 = xmm1;
} // namespace ion
} // namespace js
#endif
#endif // JS_ION
#endif // jsion_baseline_registers_x86_h__

View File

@ -0,0 +1,7 @@
(function() {
eval("\
arguments.valueOf();\
with (function(){}){};\
");
})()

View File

@ -634,8 +634,8 @@ CreateLazyScriptsForCompartment(JSContext *cx)
if (obj->compartment() == cx->compartment() && obj->isFunction()) {
JSFunction *fun = obj->toFunction();
if (fun->isInterpretedLazy()) {
LazyScript *lazy = fun->lazyScript();
if (lazy->sourceObject() && !lazy->maybeScript()) {
LazyScript *lazy = fun->lazyScriptOrNull();
if (lazy && lazy->sourceObject() && !lazy->maybeScript()) {
if (!lazyFunctions.append(fun))
return false;
}
@ -666,8 +666,11 @@ CreateLazyScriptsForCompartment(JSContext *cx)
JSObject *obj = i.get<JSObject>();
if (obj->compartment() == cx->compartment() && obj->isFunction()) {
JSFunction *fun = obj->toFunction();
if (fun->isInterpretedLazy() && fun->lazyScript()->maybeScript())
JS_ALWAYS_TRUE(fun->getOrCreateScript(cx));
if (fun->isInterpretedLazy()) {
LazyScript *lazy = fun->lazyScriptOrNull();
if (lazy && lazy->maybeScript())
fun->getExistingScript();
}
}
}

View File

@ -208,6 +208,27 @@ class JSFunction : public JSObject
static bool createScriptForLazilyInterpretedFunction(JSContext *cx, js::HandleFunction fun);
// Function Scripts
//
// Interpreted functions may either have an explicit JSScript (hasScript())
// or be lazy with sufficient information to construct the JSScript if
// necessary (isInterpretedLazy()).
//
// A lazy function will have a LazyScript if the function came from parsed
// source, or NULL if the function is a clone of a self hosted function.
//
// There are several methods to get the script of an interpreted function:
//
// - For all interpreted functions, getOrCreateScript() will get the
// JSScript, delazifying the function if necessary. This is the safest to
// use, but has extra checks, requires a cx and may trigger a GC.
//
// - For functions which may have a LazyScript but whose JSScript is known
// to exist, getExistingScript() will get the script and delazify the
// function if necessary.
//
// - For functions known to have a JSScript, nonLazyScript() will get it.
JSScript *getOrCreateScript(JSContext *cx) {
JS_ASSERT(isInterpreted());
JS_ASSERT(cx);
@ -222,35 +243,18 @@ class JSFunction : public JSObject
return u.i.s.script_;
}
static bool maybeGetOrCreateScript(JSContext *cx, js::HandleFunction fun,
js::MutableHandle<JSScript*> script)
{
if (fun->isNative()) {
script.set(NULL);
return true;
}
script.set(fun->getOrCreateScript(cx));
return fun->hasScript();
}
inline JSScript *getExistingScript();
JSScript *nonLazyScript() const {
JS_ASSERT(hasScript());
return JS::HandleScript::fromMarkedLocation(&u.i.s.script_);
}
JSScript *maybeNonLazyScript() const {
return hasScript() ? nonLazyScript() : NULL;
}
js::HeapPtrScript &mutableScript() {
JS_ASSERT(isInterpreted());
return *(js::HeapPtrScript *)&u.i.s.script_;
}
// A lazily interpreted function will have an associated LazyScript if the
// script has not yet been parsed. For functions whose scripts are lazily
// cloned from self hosted code, there is no LazyScript.
js::LazyScript *lazyScript() const {
JS_ASSERT(isInterpretedLazy() && u.i.s.lazy_);
return u.i.s.lazy_;

View File

@ -223,6 +223,25 @@ CloneFunctionObjectIfNotSingleton(JSContext *cx, HandleFunction fun, HandleObjec
} /* namespace js */
inline JSScript *
JSFunction::getExistingScript()
{
JS_ASSERT(isInterpreted());
if (isInterpretedLazy()) {
js::LazyScript *lazy = lazyScript();
JS_ASSERT(lazy->maybeScript());
if (zone()->needsBarrier())
js::LazyScript::writeBarrierPre(lazy);
flags &= ~INTERPRETED_LAZY;
flags |= INTERPRETED;
initScript(lazy->maybeScript());
}
JS_ASSERT(hasScript());
return u.i.s.script_;
}
inline void
JSFunction::setScript(JSScript *script_)
{

View File

@ -41,6 +41,8 @@ main(int argc, char **argv)
perror("fopen");
return EXIT_FAILURE;
}
fputs("#ifndef JSAUTOOPLEN_H___\n", fp);
fputs("#define JSAUTOOPLEN_H___\n", fp);
fputs("/*\n"
" * Automatically generated header with JS opcode length constants.\n"
" *\n"
@ -78,6 +80,8 @@ main(int argc, char **argv)
}
}
fputs("#endif // JSAUTOOPLEN_H___\n", fp);
if (fclose(fp)) {
perror("fclose");
return EXIT_FAILURE;

View File

@ -1444,11 +1444,15 @@ ValueToScript(JSContext *cx, jsval v, JSFunction **funp = NULL)
else
break;
}
RootedScript script(cx);
JSFunction::maybeGetOrCreateScript(cx, fun, &script);
if (!script)
if (!fun->isInterpreted()) {
JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_SCRIPTS_ONLY);
return NULL;
}
JSScript *script = fun->getOrCreateScript(cx);
if (!script)
return NULL;
if (fun && funp)
*funp = fun;
@ -1891,13 +1895,14 @@ DisassembleScript(JSContext *cx, HandleScript script, HandleFunction fun, bool l
JSObject *obj = objects->vector[i];
if (obj->isFunction()) {
Sprint(sp, "\n");
RootedFunction f(cx, obj->toFunction());
RootedScript script(cx);
JSFunction::maybeGetOrCreateScript(cx, f, &script);
if (!script)
RootedFunction fun(cx, obj->toFunction());
if (fun->isInterpreted()) {
RootedScript script(cx, fun->getOrCreateScript(cx));
if (!script || !DisassembleScript(cx, script, fun, lines, recursive, sp))
return false;
} else {
Sprint(sp, "[native code]\n");
else if (!DisassembleScript(cx, script, fun, lines, recursive, sp))
return false;
}
}
}
}

View File

@ -581,7 +581,9 @@ RegExpShared::executeMatchOnly(JSContext *cx, const jschar *chars, size_t length
if (!compileMatchOnlyIfNecessary(cx))
return RegExpRunStatus_Error;
#ifdef DEBUG
const size_t origLength = length;
#endif
size_t start = *lastIndex;
size_t displacement = 0;

View File

@ -315,4 +315,4 @@ JSObject::asDebugScope()
return *static_cast<js::DebugScopeObject *>(this);
}
#endif /* CallObject_inl_h___ */
#endif /* ScopeObject_inl_h___ */

View File

@ -892,4 +892,5 @@ InterpreterActivation::~InterpreterActivation()
{}
} /* namespace js */
#endif /* Stack_inl_h__ */

View File

@ -0,0 +1,11 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<svg xmlns="http://www.w3.org/2000/svg" style="font: 32px sans-serif; text-rendering: geometricPrecision">
<text x="100" y="100" stroke="blue" stroke-width="5"><tspan>hello</tspan> there</text>
<text x="100" y="160" stroke="blue" stroke-width="5"><tspan>hello</tspan> there</text>
<text x="100" y="220" stroke="blue" stroke-width="5"><tspan>hello</tspan> there</text>
</svg>

After

Width:  |  Height:  |  Size: 501 B

View File

@ -0,0 +1,31 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<svg xmlns="http://www.w3.org/2000/svg" class="reftest-wait">
<title>Test non-scaling-stroke repainting when ancestor transforms change</title>
<!-- From https://bugzilla.mozilla.org/show_bug.cgi?id=875069 -->
<style>
.noscale {
vector-effect: non-scaling-stroke;
}
</style>
<script>
function doTest() {
document.getElementById("g").setAttribute("transform", "scale(2)");
document.documentElement.removeAttribute('class');
}
document.addEventListener("MozReftestInvalidate", doTest, false);
setTimeout(doTest, 4000); // fallback for running outside reftest
</script>
<g id="g" style="font: 16px sans-serif; text-rendering: geometricPrecision">
<text x="50" y="50" stroke="blue" stroke-width="5" class="noscale"><tspan class="noscale">hello</tspan> there</text>
<text x="50" y="80" stroke="blue" stroke-width="2.5"><tspan class="noscale" stroke-width="5">hello</tspan> there</text>
<text x="50" y="110" stroke="blue" stroke-width="5" class="noscale"><tspan stroke-width="2.5">hello</tspan> there</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -157,6 +157,7 @@ HTTP(../..) == simple-transform-rotate.svg simple-transform-rotate-ref.svg
== dynamic-font-size-4.svg dynamic-font-size-4-ref.svg
== dynamic-dominant-baseline.svg dynamic-dominant-baseline-ref.svg
== dynamic-multiple-x.svg dynamic-multiple-x-ref.svg
fuzzy-if(!d2d,14,2) == dynamic-non-scaling-stroke.svg dynamic-non-scaling-stroke-ref.svg
# text and masks
HTTP(../..) == mask-applied.svg mask-applied-ref.svg

View File

@ -3232,6 +3232,12 @@ nsSVGTextFrame2::NotifySVGChanged(uint32_t aFlags)
needNewBounds = true;
needGlyphMetricsUpdate = true;
}
if (StyleSVGReset()->mVectorEffect ==
NS_STYLE_VECTOR_EFFECT_NON_SCALING_STROKE) {
// Stroke currently contributes to our mRect, and our stroke depends on
// the transform to our outer-<svg> if |vector-effect:non-scaling-stroke|.
needNewBounds = true;
}
}
if (needNewBounds) {

View File

@ -1334,13 +1334,13 @@ nsSVGUtils::GetFirstNonAAncestorFrame(nsIFrame* aStartFrame)
gfxMatrix
nsSVGUtils::GetStrokeTransform(nsIFrame *aFrame)
{
if (aFrame->GetContent()->IsNodeOfType(nsINode::eTEXT)) {
aFrame = aFrame->GetParent();
}
if (aFrame->StyleSVGReset()->mVectorEffect ==
NS_STYLE_VECTOR_EFFECT_NON_SCALING_STROKE) {
if (aFrame->GetContent()->IsNodeOfType(nsINode::eTEXT)) {
aFrame = aFrame->GetParent();
}
nsIContent *content = aFrame->GetContent();
NS_ABORT_IF_FALSE(content->IsSVG(), "bad cast");
@ -1732,14 +1732,10 @@ GetStrokeDashData(nsIFrame* aFrame,
*aDashOffset = aObjectPaint->GetStrokeDashOffset();
} else {
*aDashOffset = nsSVGUtils::CoordToFloat(presContext,
ctx,
style->mStrokeDashoffset);
ctx,
style->mStrokeDashoffset);
}
if (content->IsNodeOfType(nsINode::eTEXT)) {
content = content->GetParent();
}
return (totalLength > 0.0);
}

View File

@ -61,6 +61,7 @@ foreignObject {
*|*::-moz-svg-text {
unicode-bidi: inherit;
vector-effect: inherit;
}
*[xml|space=preserve] {

File diff suppressed because it is too large Load Diff

View File

@ -293,6 +293,7 @@ var DebuggerServer = {
this.addActors("resource://gre/modules/devtools/server/actors/styleeditor.js");
this.addActors("resource://gre/modules/devtools/server/actors/webapps.js");
this.registerModule("devtools/server/actors/inspector");
},
/**

View File

@ -11,9 +11,17 @@ relativesrcdir = @relativesrcdir@
include $(DEPTH)/config/autoconf.mk
MOCHITEST_CHROME_FILES = \
test_unsafeDereference.html \
nonchrome_unsafeDereference.html \
MOCHITEST_CHROME_FILES = \
inspector-helpers.js \
inspector-traversal-data.html \
test_inspector-mutations-attr.html \
test_inspector-mutations-childlist.html \
test_inspector-mutations-frameload.html \
test_inspector-mutations-value.html \
test_inspector-release.html \
test_inspector-traversal.html \
test_unsafeDereference.html \
nonchrome_unsafeDereference.html \
$(NULL)
include $(topsrcdir)/config/rules.mk

View File

@ -0,0 +1,185 @@
var Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
// Always log packets when running tests.
Services.prefs.setBoolPref("devtools.debugger.log", true);
SimpleTest.registerCleanupFunction(function() {
Services.prefs.clearUserPref("devtools.debugger.log");
});
Cu.import("resource://gre/modules/devtools/Loader.jsm");
Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
Cu.import("resource://gre/modules/devtools/dbg-server.jsm");
const {_documentWalker} = devtools.require("devtools/server/actors/inspector");
if (!DebuggerServer.initialized) {
DebuggerServer.init(() => true);
DebuggerServer.addBrowserActors();
SimpleTest.registerCleanupFunction(function() {
DebuggerServer.destroy();
});
}
var gAttachCleanups = [];
SimpleTest.registerCleanupFunction(function() {
for (let cleanup of gAttachCleanups) {
cleanup();
}
});
/**
* Open a tab, load the url, wait for it to signal its readiness,
* find the tab with the debugger server, and call the callback.
*
* Returns a function which can be called to close the opened ta
* and disconnect its debugger client.
*/
function attachURL(url, callback) {
var win = window.open(url, "_blank");
var client = null;
let cleanup = () => {
if (client) {
client.close();
client = null;
}
if (win) {
win.close();
win = null;
}
};
gAttachCleanups.push(cleanup);
window.addEventListener("message", function loadListener(event) {
if (event.data === "ready") {
client = new DebuggerClient(DebuggerServer.connectPipe());
client.connect((applicationType, traits) => {
client.listTabs(response => {
for (let tab of response.tabs) {
if (tab.url === url) {
window.removeEventListener("message", loadListener, false);
try {
callback(null, client, tab, win.document);
} catch(ex) {
Cu.reportError(ex);
dump(ex);
}
break;
}
}
});
});
}
}, false);
return cleanup;
}
function sortOwnershipChildren(children) {
return children.sort((a, b) => a.name.localeCompare(b.name));
}
function serverOwnershipSubtree(walker, node) {
let actor = walker._refMap.get(node);
if (!actor) {
return undefined;
}
let children = [];
let docwalker = _documentWalker(node);
let child = docwalker.firstChild();
while (child) {
let item = serverOwnershipSubtree(walker, child);
if (item) {
children.push(item);
}
child = docwalker.nextSibling();
}
return {
name: actor.actorID,
children: sortOwnershipChildren(children)
}
}
function serverOwnershipTree(walker) {
let serverConnection = walker.conn._transport._serverConnection;
let serverWalker = serverConnection.getActor(walker.actorID);
return {
root: serverOwnershipSubtree(serverWalker, serverWalker.rootDoc ),
orphaned: [serverOwnershipSubtree(serverWalker, o.rawNode) for (o of serverWalker._orphaned)]
};
}
function clientOwnershipSubtree(node) {
return {
name: node.actorID,
children: sortOwnershipChildren([clientOwnershipSubtree(child) for (child of node.treeChildren())])
}
}
function clientOwnershipTree(walker) {
return {
root: clientOwnershipSubtree(walker.rootNode),
orphaned: [clientOwnershipSubtree(o) for (o of walker._orphaned)]
}
}
function ownershipTreeSize(tree) {
let size = 1;
for (let child of tree.children) {
size += ownershipTreeSize(child);
}
return size;
}
function assertOwnershipTrees(walker) {
let serverTree = serverOwnershipTree(walker);
let clientTree = clientOwnershipTree(walker);
is(JSON.stringify(clientTree, null, ' '), JSON.stringify(serverTree, null, ' '), "Server and client ownership trees should match.");
return ownershipTreeSize(clientTree.root);
}
// Verify that an actorID is inaccessible both from the client library and the server.
function checkMissing(client, actorID) {
let deferred = Promise.defer();
let front = client.getActor(actorID);
ok(!front, "Front shouldn't be accessible from the client for actorID: " + actorID);
let deferred = Promise.defer();
client.request({
to: actorID,
type: "request",
}, response => {
is(response.error, "noSuchActor", "node list actor should no longer be contactable.");
deferred.resolve(undefined);
});
return deferred.promise;
}
function promiseDone(promise) {
promise.then(null, err => {
ok(false, "Promise failed: " + err);
if (err.stack) {
dump(err.stack);
}
SimpleTest.finish();
});
}
var _tests = [];
function addTest(test) {
_tests.push(test);
}
function runNextTest() {
if (_tests.length == 0) {
SimpleTest.finish()
return;
}
_tests.shift()();
}

View File

@ -0,0 +1,54 @@
<html>
<head>
<script type="text/javascript">
window.onload = function() {
// Put a copy of the body in an iframe to test frame traversal.
var body = document.querySelector("body");
var data = "data:text/html,<html>" + body.outerHTML + "<html>";
var iframe = document.createElement("iframe");
iframe.setAttribute("id", "childFrame");
iframe.onload = function() {
window.opener.postMessage('ready', '*')
};
iframe.src = data;
body.appendChild(iframe);
}
</script>
<body>
<h1>Inspector Actor Tests</h1>
<span id="longstring">longlonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglong</span>
<span id="shortstring">short</span>
<span id="empty"></span>
<div id="longlist" data-test="exists">
<div id="a">a</div>
<div id="b">b</div>
<div id="c">c</div>
<div id="d">d</div>
<div id="e">e</div>
<div id="f">f</div>
<div id="g">g</div>
<div id="h">h</div>
<div id="i">i</div>
<div id="j">j</div>
<div id="k">k</div>
<div id="l">l</div>
<div id="m">m</div>
<div id="n">n</div>
<div id="o">o</div>
<div id="p">p</div>
<div id="q">q</div>
<div id="r">r</div>
<div id="s">s</div>
<div id="t">t</div>
<div id="u">u</div>
<div id="v">v</div>
<div id="w">w</div>
<div id="x">x</div>
<div id="y">y</div>
<div id="z">z</div>
</div>
<div id="longlist-sibling">
<div id="longlist-sibling-firstchild"></div>
</div>
</body>
</html>

View File

@ -0,0 +1,128 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=
-->
<head>
<meta charset="utf-8">
<title>Test for Bug </title>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
<script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
<script type="application/javascript;version=1.8">
Components.utils.import("resource://gre/modules/devtools/Loader.jsm");
const Promise = devtools.require("sdk/core/promise");
const inspector = devtools.require("devtools/server/actors/inspector");
window.onload = function() {
SimpleTest.waitForExplicitFinish();
runNextTest();
}
var gInspectee = null;
var gWalker = null;
var gClient = null;
var attrNode;
var attrFront;
addTest(function setup() {
let url = document.getElementById("inspectorContent").href;
attachURL(url, function(err, client, tab, doc) {
gInspectee = doc;
let {InspectorFront} = devtools.require("devtools/server/actors/inspector");
let inspector = InspectorFront(client, tab);
promiseDone(inspector.getWalker().then(walker => {
ok(walker, "getWalker() should return an actor.");
gClient = client;
gWalker = walker;
}).then(runNextTest));
});
});
addTest(setupAttrTest);
addTest(testAddAttribute);
addTest(testChangeAttribute);
addTest(testRemoveAttribute);
addTest(setupFrameAttrTest);
addTest(testAddAttribute);
addTest(testChangeAttribute);
addTest(testRemoveAttribute);
function setupAttrTest() {
attrNode = gInspectee.querySelector("#a")
promiseDone(gWalker.querySelector(gWalker.rootNode, "#a").then(node => {
attrFront = node;
}).then(runNextTest));
}
function setupFrameAttrTest() {
let frame = gInspectee.querySelector('#childFrame');
attrNode = frame.contentDocument.querySelector("#a");
promiseDone(gWalker.querySelector(gWalker.rootNode, "#childFrame").then(childFrame => {
return gWalker.children(childFrame);
}).then(children => {
let nodes = children.nodes;
ok(nodes.length, 1, "There should be only one child of the iframe");
is(nodes[0].nodeType, Node.DOCUMENT_NODE, "iframe child should be a document node");
return gWalker.querySelector(nodes[0], "#a");
}).then(node => {
attrFront = node;
}).then(runNextTest));
}
function testAddAttribute() {
attrNode.setAttribute("data-newattr", "newvalue");
attrNode.setAttribute("data-newattr2", "newvalue");
gWalker.once("mutations", () => {
is(attrFront.attributes.length, 3, "Should have id and two new attributes.");
is(attrFront.getAttribute("data-newattr"), "newvalue", "Node front should have the first new attribute");
is(attrFront.getAttribute("data-newattr2"), "newvalue", "Node front should have the second new attribute.");
runNextTest();
});
}
function testChangeAttribute() {
attrNode.setAttribute("data-newattr", "changedvalue");
gWalker.once("mutations", () => {
is(attrFront.attributes.length, 3, "Should have id and two new attributes.");
is(attrFront.getAttribute("data-newattr"), "changedvalue", "Node front should have the changed first value");
is(attrFront.getAttribute("data-newattr2"), "newvalue", "Second value should remain unchanged.");
runNextTest();
});
}
function testRemoveAttribute() {
attrNode.removeAttribute("data-newattr2");
gWalker.once("mutations", () => {
is(attrFront.attributes.length, 2, "Should have id and one remaining attribute.");
is(attrFront.getAttribute("data-newattr"), "changedvalue", "Node front should still have the first value");
ok(!attrFront.hasAttribute("data-newattr2"), "Second value should be removed.");
runNextTest();
})
}
addTest(function cleanup() {
delete gInspectee;
delete gWalker;
delete gClient;
runNextTest();
});
</script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a>
<a id="inspectorContent" target="_blank" href="inspector-traversal-data.html">Test Document</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
</body>
</html>

View File

@ -0,0 +1,313 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=
-->
<head>
<meta charset="utf-8">
<title>Test for Bug </title>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
<script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
<script type="application/javascript;version=1.8">
Components.utils.import("resource://gre/modules/devtools/Loader.jsm");
const Promise = devtools.require("sdk/core/promise");
const inspector = devtools.require("devtools/server/actors/inspector");
window.onload = function() {
SimpleTest.waitForExplicitFinish();
runNextTest();
}
var gInspectee = null;
var gWalker = null;
var gClient = null;
var gCleanupConnection = null;
function setup(callback) {
let url = document.getElementById("inspectorContent").href;
gCleanupConnection = attachURL(url, function(err, client, tab, doc) {
gInspectee = doc;
let {InspectorFront} = devtools.require("devtools/server/actors/inspector");
let inspector = InspectorFront(client, tab);
promiseDone(inspector.getWalker().then(walker => {
gClient = client;
gWalker = walker;
}).then(callback));
});
}
function teardown() {
gWalker = null;
gClient = null;
gInspectee = null;
if (gCleanupConnection) {
gCleanupConnection();
gCleanupConnection = null;
}
}
function assertOwnership() {
let num = assertOwnershipTrees(gWalker);
}
function setParent(nodeSelector, newParentSelector) {
let node = gInspectee.querySelector(nodeSelector);
if (newParentSelector) {
let newParent = gInspectee.querySelector(newParentSelector);
newParent.appendChild(node);
} else {
node.parentNode.removeChild(node);
}
}
function loadSelector(selector) {
return gWalker.querySelectorAll(gWalker.rootNode, selector).then(nodeList => {
return nodeList.items();
});
}
function loadSelectors(selectors) {
return Promise.all([loadSelector(sel) for (sel of selectors)]);
}
function doMoves(moves) {
for (let move of moves) {
setParent(move[0], move[1]);
}
}
/**
* Test a set of tree rearrangements and make sure they cause the expected changes.
*/
var gDummySerial = 0;
function mutationTest(testSpec) {
return function() {
setup(() => {
promiseDone(loadSelectors(testSpec.load || ["html"]).then(() => {
gWalker.autoCleanup = !!testSpec.autoCleanup;
if (testSpec.preCheck) {
testSpec.preCheck();
}
doMoves(testSpec.moves || []);
// Some of these moves will trigger no mutation events,
// so do a dummy change to the root node to trigger
// a mutation event anyway.
gInspectee.documentElement.setAttribute("data-dummy", gDummySerial++);
gWalker.once("mutations", (mutations) => {
// Filter out our dummy mutation.
let mutations = mutations.filter(change => {
if (change.type == "attributes" &&
change.attributeName == "data-dummy") {
return false;
}
return true;
});
assertOwnership();
if (testSpec.postCheck) {
testSpec.postCheck(mutations);
}
teardown();
runNextTest();
});
}));
})
}
}
// Verify that our dummy mutation works.
addTest(mutationTest({
autoCleanup: false,
postCheck: function(mutations) {
is(mutations.length, 0, "Dummy mutation is filtered out.");
}
}));
// Test a simple move to a different location in the sibling list for the same
// parent.
addTest(mutationTest({
autoCleanup: false,
load: ["#longlist div"],
moves: [
["#a", "#longlist"]
],
postCheck: function(mutations) {
let remove = mutations[0];
is(remove.type, "childList", "First mutation should be a childList.")
ok(remove.removed.length > 0, "First mutation should be a removal.")
let add = mutations[1];
is(add.type, "childList", "Second mutation should be a childList removal.")
ok(add.added.length > 0, "Second mutation should be an addition.")
let a = add.added[0];
is(a.id, "a", "Added node should be #a");
is(a.parentNode(), remove.target, "Should still be a child of longlist.");
is(remove.target, add.target, "First and second mutations should be against the same node.");
}
}));
// Test a move to another location that is within our ownership tree.
addTest(mutationTest({
autoCleanup: false,
load: ["#longlist div", "#longlist-sibling"],
moves: [
["#a", "#longlist-sibling"]
],
postCheck: function(mutations) {
let remove = mutations[0];
is(remove.type, "childList", "First mutation should be a childList.")
ok(remove.removed.length > 0, "First mutation should be a removal.")
let add = mutations[1];
is(add.type, "childList", "Second mutation should be a childList removal.")
ok(add.added.length > 0, "Second mutation should be an addition.")
let a = add.added[0];
is(a.id, "a", "Added node should be #a");
is(a.parentNode(), add.target, "Should still be a child of longlist.");
is(add.target.id, "longlist-sibling", "long-sibling should be the target.");
}
}));
// Move an unseen node with a seen parent into our ownership tree - should generate a
// childList pair with no adds or removes.
addTest(mutationTest({
autoCleanup: false,
load: ["#longlist"],
moves: [
["#longlist-sibling", "#longlist"]
],
postCheck: function(mutations) {
is(mutations.length, 2, "Should generate two mutations");
is(mutations[0].type, "childList", "Should be childList mutations.");
is(mutations[0].added.length, 0, "Should have no adds.");
is(mutations[0].removed.length, 0, "Should have no removes.");
is(mutations[1].type, "childList", "Should be childList mutations.");
is(mutations[1].added.length, 0, "Should have no adds.");
is(mutations[1].removed.length, 0, "Should have no removes.");
}
}));
// Move an unseen node with an unseen parent into our ownership tree. Should only
// generate one childList mutation with no adds or removes.
addTest(mutationTest({
autoCleanup: false,
load: ["#longlist div"],
moves: [
["#longlist-sibling-firstchild", "#longlist"]
],
postCheck: function(mutations) {
is(mutations.length, 1, "Should generate two mutations");
is(mutations[0].type, "childList", "Should be childList mutations.");
is(mutations[0].added.length, 0, "Should have no adds.");
is(mutations[0].removed.length, 0, "Should have no removes.");
}
}));
// Move a node between unseen nodes, should generate no mutations.
addTest(mutationTest({
autoCleanup: false,
load: ["html"],
moves: [
["#longlist-sibling", "#longlist"]
],
postCheck: function(mutations) {
is(mutations.length, 0, "Should generate no mutations.");
}
}));
// Orphan a node and don't clean it up
addTest(mutationTest({
autoCleanup: false,
load: ["#longlist div"],
moves: [
["#longlist", null]
],
postCheck: function(mutations) {
is(mutations.length, 1, "Should generate one mutation.");
let change = mutations[0];
is(change.type, "childList", "Should be a childList.");
is(change.removed.length, 1, "Should have removed a child.");
let ownership = clientOwnershipTree(gWalker);
is(ownership.orphaned.length, 1, "Should have one orphaned subtree.");
is(ownershipTreeSize(ownership.orphaned[0]), 27, "Should have orphaned longlist and 26 children.");
}
}));
// Orphan a node, and do clean it up.
addTest(mutationTest({
autoCleanup: true,
load: ["#longlist div"],
moves: [
["#longlist", null]
],
postCheck: function(mutations) {
is(mutations.length, 1, "Should generate one mutation.");
let change = mutations[0];
is(change.type, "childList", "Should be a childList.");
is(change.removed.length, 1, "Should have removed a child.");
let ownership = clientOwnershipTree(gWalker);
is(ownership.orphaned.length, 0, "Should have no orphaned subtrees.");
}
}));
// Orphan a node by moving it into the tree but out of our visible subtree.
addTest(mutationTest({
autoCleanup: false,
load: ["#longlist div"],
moves: [
["#longlist", "#longlist-sibling"]
],
postCheck: function(mutations) {
is(mutations.length, 1, "Should generate one mutation.");
let change = mutations[0];
is(change.type, "childList", "Should be a childList.");
is(change.removed.length, 1, "Should have removed a child.");
let ownership = clientOwnershipTree(gWalker);
is(ownership.orphaned.length, 1, "Should have one orphaned subtree.");
is(ownershipTreeSize(ownership.orphaned[0]), 27, "Should have orphaned longlist and 26 children.");
}
}));
// Orphan a node by moving it into the tree but out of our visible subtree, and clean it up.
addTest(mutationTest({
autoCleanup: true,
load: ["#longlist div"],
moves: [
["#longlist", "#longlist-sibling"]
],
postCheck: function(mutations) {
is(mutations.length, 1, "Should generate one mutation.");
let change = mutations[0];
is(change.type, "childList", "Should be a childList.");
is(change.removed.length, 1, "Should have removed a child.");
let ownership = clientOwnershipTree(gWalker);
is(ownership.orphaned.length, 0, "Should have no orphaned subtrees.");
}
}));
addTest(function cleanup() {
delete gInspectee;
delete gWalker;
delete gClient;
runNextTest();
});
</script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a>
<a id="inspectorContent" target="_blank" href="inspector-traversal-data.html">Test Document</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
</body>
</html>

View File

@ -0,0 +1,273 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=
-->
<head>
<meta charset="utf-8">
<title>Test for Bug </title>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
<script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
<script type="application/javascript;version=1.8">
Components.utils.import("resource://gre/modules/devtools/Loader.jsm");
const Promise = devtools.require("sdk/core/promise");
const inspector = devtools.require("devtools/server/actors/inspector");
window.onload = function() {
SimpleTest.waitForExplicitFinish();
runNextTest();
}
var gInspectee = null;
var gWalker = null;
var gClient = null;
var gChildFrame = null;
var gChildDocument = null;
var gCleanupConnection = null;
function setup(callback) {
let url = document.getElementById("inspectorContent").href;
gCleanupConnection = attachURL(url, function(err, client, tab, doc) {
gInspectee = doc;
let {InspectorFront} = devtools.require("devtools/server/actors/inspector");
let inspector = InspectorFront(client, tab);
promiseDone(inspector.getWalker().then(walker => {
gClient = client;
gWalker = walker;
}).then(callback));
});
}
function teardown() {
gWalker = null;
gClient = null;
gInspectee = null;
gChildFrame = null;
if (gCleanupConnection) {
gCleanupConnection();
gCleanupConnection = null;
}
}
function assertOwnership() {
return assertOwnershipTrees(gWalker);
}
function loadChildSelector(selector) {
return gWalker.querySelector(gWalker.rootNode, "#childFrame").then(frame => {
gChildFrame = frame;
return gWalker.children(frame);
}).then(children => {
return gWalker.querySelectorAll(children.nodes[0], selector);
}).then(nodeList => {
return nodeList.items();
});
}
function isSrcChange(change) {
return (change.type === "attributes" && change.attributeName === "src");
}
function assertAndStrip(mutations, message, test) {
let size = mutations.length;
mutations = mutations.filter(test);
ok((mutations.size != size), message);
return mutations;
}
function isSrcChange(change) {
return change.type === "attributes" && change.attributeName === "src";
}
function isUnload(change) {
return change.type === "documentUnload";
}
function isFrameLoad(change) {
return change.type === "frameLoad";
}
// Make sure an iframe's src attribute changed and then
// strip that mutation out of the list.
function assertSrcChange(mutations) {
return assertAndStrip(mutations, "Should have had an iframe source change.", isSrcChange);
}
// Make sure there's an unload in the mutation list and strip
// that mutation out of the list
function assertUnload(mutations) {
return assertAndStrip(mutations, "Should have had a document unload change.", isUnload);
}
// Make sure there's a frame load in the mutation list and strip
// that mutation out of the list
function assertFrameLoad(mutations) {
return assertAndStrip(mutations, "Should have had a frame load change.", isFrameLoad);
}
// Load mutations aren't predictable, so keep accumulating mutations until
// the one we're looking for shows up.
function waitForMutation(walker, test, mutations=[]) {
let deferred = Promise.defer();
for (let change of mutations) {
if (test(change)) {
deferred.resolve(mutations);
}
}
walker.once("mutations", newMutations => {
waitForMutation(walker, test, mutations.concat(newMutations)).then(finalMutations => {
deferred.resolve(finalMutations);
})
});
return deferred.promise;
}
function getUnloadedDoc(mutations) {
for (let change of mutations) {
if (isUnload(change)) {
return change.target;
}
}
return null;
}
addTest(function loadNewChild() {
setup(() => {
let beforeUnloadSize = 0;
// Load a bunch of fronts for actors inside the child frame.
promiseDone(loadChildSelector("#longlist div").then(() => {
let childFrame = gInspectee.querySelector("#childFrame");
childFrame.src = "data:text/html,<html>new child</html>";
return waitForMutation(gWalker, isFrameLoad);
}).then(mutations => {
let unloaded = getUnloadedDoc(mutations);
mutations = assertSrcChange(mutations);
mutations = assertUnload(mutations);
mutations = assertFrameLoad(mutations);
is(mutations.length, 0, "Got the expected mutations.");
assertOwnership();
return checkMissing(gClient, unloaded);
}).then(() => {
teardown();
}).then(runNextTest));
});
});
addTest(function loadNewChildTwice() {
setup(() => {
let beforeUnloadSize = 0;
// Load a bunch of fronts for actors inside the child frame.
promiseDone(loadChildSelector("#longlist div").then(() => {
let childFrame = gInspectee.querySelector("#childFrame");
childFrame.src = "data:text/html,<html>new child</html>";
return waitForMutation(gWalker, isFrameLoad);
}).then(mutations => {
// The first load went through as expected (as tested in loadNewChild)
// Now change the source again, but this time we *don't* expect
// an unload, because we haven't seen the new child document yet.
let childFrame = gInspectee.querySelector("#childFrame");
childFrame.src = "data:text/html,<html>second new child</html>";
return waitForMutation(gWalker, isFrameLoad);
}).then(mutations => {
mutations = assertSrcChange(mutations);
mutations = assertFrameLoad(mutations);
ok(!getUnloadedDoc(mutations), "Should not have gotten an unload.");
is(mutations.length, 0, "Got the expected mutations.");
assertOwnership();
}).then(() => {
teardown();
}).then(runNextTest));
});
});
addTest(function loadNewChildTwiceAndCareAboutIt() {
setup(() => {
let beforeUnloadSize = 0;
// Load a bunch of fronts for actors inside the child frame.
promiseDone(loadChildSelector("#longlist div").then(() => {
let childFrame = gInspectee.querySelector("#childFrame");
childFrame.src = "data:text/html,<html>new child</html>";
return waitForMutation(gWalker, isFrameLoad);
}).then(mutations => {
// Read the new child
return loadChildSelector("#longlist div");
}).then(() => {
// Now change the source again, and expect the same results as loadNewChild.
let childFrame = gInspectee.querySelector("#childFrame");
childFrame.src = "data:text/html,<html>second new child</html>";
return waitForMutation(gWalker, isFrameLoad);
}).then(mutations => {
let unloaded = getUnloadedDoc(mutations);
mutations = assertSrcChange(mutations);
mutations = assertUnload(mutations);
mutations = assertFrameLoad(mutations);
is(mutations.length, 0, "Got the expected mutations.");
assertOwnership();
return checkMissing(gClient, unloaded);
}).then(() => {
teardown();
}).then(runNextTest));
});
});
addTest(function testBack() {
setup(() => {
let beforeUnloadSize = 0;
// Load a bunch of fronts for actors inside the child frame.
promiseDone(loadChildSelector("#longlist div").then(() => {
let childFrame = gInspectee.querySelector("#childFrame");
childFrame.src = "data:text/html,<html>new child</html>";
return waitForMutation(gWalker, isFrameLoad);
}).then(mutations => {
// Read the new child
return loadChildSelector("#longlist div");
}).then(() => {
// Now use history.back to change the source, and expect the same results as loadNewChild.
let childFrame = gInspectee.querySelector("#childFrame");
childFrame.contentWindow.history.back();
return waitForMutation(gWalker, isFrameLoad);
}).then(mutations => {
let unloaded = getUnloadedDoc(mutations);
mutations = assertSrcChange(mutations);
mutations = assertUnload(mutations);
mutations = assertFrameLoad(mutations);
is(mutations.length, 0, "Got the expected mutations.");
assertOwnership();
return checkMissing(gClient, unloaded);
}).then(() => {
teardown();
}).then(runNextTest));
});
});
</script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a>
<a id="inspectorContent" target="_blank" href="inspector-traversal-data.html">Test Document</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
</body>
</html>

View File

@ -0,0 +1,151 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=
-->
<head>
<meta charset="utf-8">
<title>Test for Bug </title>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
<script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
<script type="application/javascript;version=1.8">
Components.utils.import("resource://gre/modules/devtools/Loader.jsm");
const Promise = devtools.require("sdk/core/promise");
const inspector = devtools.require("devtools/server/actors/inspector");
window.onload = function() {
SimpleTest.waitForExplicitFinish();
runNextTest();
}
const testSummaryLength = 10;
inspector.setValueSummaryLength(testSummaryLength);
SimpleTest.registerCleanupFunction(function() {
inspector.setValueSummaryLength(inspector.DEFAULT_VALUE_SUMMARY_LENGTH);
});
var gInspectee = null;
var gWalker = null;
var gClient = null;
var valueNode;
var valueFront;
var longString = "stringstringstringstringstringstringstringstringstringstringstring";
var truncatedLongString = longString.substring(0, testSummaryLength);
var shortString = "str";
var shortString2 = "str2";
addTest(function setup() {
let url = document.getElementById("inspectorContent").href;
attachURL(url, function(err, client, tab, doc) {
gInspectee = doc;
let {InspectorFront} = devtools.require("devtools/server/actors/inspector");
let inspector = InspectorFront(client, tab);
promiseDone(inspector.getWalker().then(walker => {
ok(walker, "getWalker() should return an actor.");
gClient = client;
gWalker = walker;
}).then(runNextTest));
});
});
addTest(setupValueTest);
addTest(testKeepLongValue);
addTest(testSetShortValue);
addTest(testKeepShortValue);
addTest(testSetLongValue);
addTest(setupFrameValueTest);
addTest(testKeepLongValue);
addTest(testSetShortValue);
addTest(testKeepShortValue);
addTest(testSetLongValue);
function setupValueTest() {
valueNode = gInspectee.querySelector("#longstring").firstChild;
promiseDone(gWalker.querySelector(gWalker.rootNode, "#longstring").then(node => {
return gWalker.children(node);
}).then(children => {
valueFront = children.nodes[0];
}).then(runNextTest));
}
function setupFrameValueTest() {
let frame = gInspectee.querySelector('#childFrame');
valueNode = frame.contentDocument.querySelector("#longstring").firstChild;
promiseDone(gWalker.querySelector(gWalker.rootNode, "#childFrame").then(childFrame => {
return gWalker.children(childFrame);
}).then(children => {
let nodes = children.nodes;
ok(nodes.length, 1, "There should be only one child of the iframe");
is(nodes[0].nodeType, Node.DOCUMENT_NODE, "iframe child should be a document node");
return gWalker.querySelector(nodes[0], "#longstring");
}).then(node => {
return gWalker.children(node);
}).then(children => {
valueFront = children.nodes[0];
}).then(runNextTest));
}
function testKeepLongValue() {
// After first setup we should have a long string in the node
is(valueFront.shortValue.length, testSummaryLength, "After setup the test node should be truncated.");
ok(valueFront.incompleteValue, "After setup the node value should be incomplete.");
valueNode.nodeValue = longString;
gWalker.once("mutations", () => {
is(valueFront.shortValue, truncatedLongString, "Value should have changed to a truncated value");
ok(valueFront.incompleteValue, "Node value should stay incomplete.");
runNextTest();
});
}
function testSetShortValue() {
valueNode.nodeValue = shortString;
gWalker.once("mutations", () => {
is(valueFront.shortValue, shortString, "Value should not be truncated.");
ok(!valueFront.incompleteValue, "Value should not be incomplete.");
runNextTest();
});
}
function testKeepShortValue() {
valueNode.nodeValue = shortString2;
gWalker.once("mutations", () => {
is(valueFront.shortValue, shortString2, "Value should not be truncated.");
ok(!valueFront.incompleteValue, "Value should not be incomplete.");
runNextTest();
});
}
function testSetLongValue() {
valueNode.nodeValue = longString;
gWalker.once("mutations", () => {
is(valueFront.shortValue, truncatedLongString, "Value should have changed to a truncated value");
ok(valueFront.incompleteValue, "Node value should stay incomplete.");
runNextTest();
});
}
addTest(function cleanup() {
delete gInspectee;
delete gWalker;
delete gClient;
runNextTest();
});
</script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a>
<a id="inspectorContent" target="_blank" href="inspector-traversal-data.html">Test Document</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
</body>
</html>

View File

@ -0,0 +1,94 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=
-->
<head>
<meta charset="utf-8">
<title>Test for Bug </title>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
<script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
<script type="application/javascript;version=1.8">
Components.utils.import("resource://gre/modules/devtools/Loader.jsm");
const Promise = devtools.require("sdk/core/promise");
const inspector = devtools.require("devtools/server/actors/inspector");
window.onload = function() {
SimpleTest.waitForExplicitFinish();
runNextTest();
}
var gWalker = null;
var gClient = null;
function assertOwnership() {
return assertOwnershipTrees(gWalker);
}
addTest(function setup() {
let url = document.getElementById("inspectorContent").href;
attachURL(url, function(err, client, tab, doc) {
gInspectee = doc;
let {InspectorFront} = devtools.require("devtools/server/actors/inspector");
let inspector = InspectorFront(client, tab);
promiseDone(inspector.getWalker().then(walker => {
ok(walker, "getWalker() should return an actor.");
gClient = client;
gWalker = walker;
}).then(runNextTest));
});
});
addTest(function testReleaseSubtree() {
let originalOwnershipSize = 0;
let longlist = null;
let firstChild = null;
promiseDone(gWalker.querySelectorAll(gWalker.rootNode, "#longlist div").then(list => {
// Make sure we have the 26 children of longlist in our ownership tree.
is(list.length, 26, "Expect 26 div children.");
// Make sure we've read in all those children and incorporated them in our ownership tree.
return list.items();
}).then((items)=> {
originalOwnershipSize = assertOwnership();
ok(originalOwnershipSize > 26, "Should have at least 26 items in our ownership tree");
firstChild = items[0].actorID;
}).then(() => {
// Now get the longlist and release it from the ownership tree.
return gWalker.querySelector(gWalker.rootNode, "#longlist");
}).then(node => {
longlist = node.actorID;
return gWalker.releaseNode(node);
}).then(() => {
// Our ownership size should now be 27 fewer (we forgot about #longlist + 26 children)
let newOwnershipSize = assertOwnership();
is(newOwnershipSize, originalOwnershipSize - 27, "Ownership tree should have dropped by 27 nodes");
// Now verify that some nodes have gone away
return checkMissing(gClient, longlist);
}).then(() => {
return checkMissing(gClient, firstChild);
}).then(runNextTest));
});
addTest(function cleanup() {
delete gWalker;
delete gClient;
runNextTest();
});
</script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a>
<a id="inspectorContent" target="_blank" href="inspector-traversal-data.html">Test Document</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
</body>
</html>

View File

@ -0,0 +1,287 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=
-->
<head>
<meta charset="utf-8">
<title>Test for Bug </title>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
<script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
<script type="application/javascript;version=1.8">
Components.utils.import("resource://gre/modules/devtools/Loader.jsm");
const Promise = devtools.require("sdk/core/promise");
const inspector = devtools.require("devtools/server/actors/inspector");
window.onload = function() {
SimpleTest.waitForExplicitFinish();
runNextTest();
}
var gInspectee = null;
var gClient = null;
var gWalker = null;
var checkActorIDs = [];
function assertOwnership() {
assertOwnershipTrees(gWalker);
}
addTest(function setup() {
let url = document.getElementById("inspectorContent").href;
attachURL(url, function(err, client, tab, doc) {
gInspectee = doc;
let {InspectorFront} = devtools.require("devtools/server/actors/inspector");
let inspector = InspectorFront(client, tab);
promiseDone(inspector.getWalker().then(walker => {
ok(walker, "getWalker() should return an actor.");
gClient = client;
gWalker = walker;
}).then(runNextTest));
});
});
addTest(function testWalkerRoot() {
// Make sure that refetching the root document of the walker returns the same
// actor as the getWalker returned.
promiseDone(gWalker.document().then(root => {
ok(root === gWalker.rootNode, "Re-fetching the document node should match the root document node.");
checkActorIDs.push(root.actorID);
assertOwnership();
}).then(runNextTest));
});
addTest(function testQuerySelector() {
promiseDone(gWalker.querySelector(gWalker.rootNode, "#longlist").then(node => {
is(node.getAttribute("data-test"), "exists", "should have found the right node");
assertOwnership();
}).then(() => {
return gWalker.querySelector(gWalker.rootNode, "unknownqueryselector").then(node => {
ok(!node, "Should not find a node here.");
assertOwnership();
});
}).then(runNextTest));
});
addTest(function testQuerySelectors() {
let nodeList = null;
let firstNode = null;
let nodeListID = null;
promiseDone(gWalker.querySelectorAll(gWalker.rootNode, "#longlist div").then(list => {
nodeList = list;
is(nodeList.length, 26, "Expect 26 div children.");
assertOwnership();
return nodeList.item(0);
}).then(node => {
firstNode = node;
checkActorIDs.push(node.actorID);
is(node.id, "a", "First child should be a");
assertOwnership();
return nodeList.items();
}).then(nodes => {
is(nodes.length, 26, "Expect 26 nodes");
is(nodes[0], firstNode, "First node should be reused.");
ok(nodes[0]._parent, "Parent node should be set.");
ok(nodes[0]._next || nodes[0]._prev, "Siblings should be set.");
ok(nodes[25]._next || nodes[25]._prev, "Siblings of " + nodes[25] + " should be set.");
assertOwnership();
return nodeList.items(-1);
}).then(nodes => {
is(nodes.length, 1, "Expect 1 node")
is(nodes[0].id, "z", "Expect it to be the last node.");
checkActorIDs.push(nodes[0].actorID);
// Save the node list ID so we can ensure it was destroyed.
nodeListID = nodeList.actorID;
assertOwnership();
return nodeList.release();
}).then(() => {
ok(!nodeList.actorID, "Actor should have been destroyed.");
assertOwnership();
return checkMissing(gClient, nodeListID);
}).then(runNextTest));
});
// Helper to check the response of requests that return hasFirst/hasLast/nodes
// node lists (like `children` and `siblings`)
function nodeArrayChecker(first, last, ids) {
return function(response) {
is(response.hasFirst, first, "Should " + (first ? "" : "not ") + " have the first node.");
is(response.hasLast, last, "Should " + (last ? "" : "not ") + " have the last node.");
is(response.nodes.length, ids.length, "Should have " + ids.length + " children listed.");
let responseIds = '';
for (node of response.nodes) {
responseIds += node.id;
}
is(responseIds, ids, "Correct nodes were returned.");
assertOwnership();
}
}
addTest(function testNoChildren() {
promiseDone(gWalker.querySelector(gWalker.rootNode, "#empty").then(empty => {
assertOwnership();
return gWalker.children(empty).then(nodeArrayChecker(true, true, ""));
}).then(runNextTest));
});
addTest(function testLongListTraversal() {
var longList;
var allChildren;
promiseDone(gWalker.querySelector(gWalker.rootNode, "#longlist").then(node => {
longList = node;
// First call with no options, expect all children.
assertOwnership();
return gWalker.children(longList).then(response => {
nodeArrayChecker(true, true, "abcdefghijklmnopqrstuvwxyz")(response);
allChildren = response.nodes;
assertOwnership();
});
}).then(() => {
// maxNodes should limit us to the first 5 nodes.
assertOwnership();
return gWalker.children(longList, { maxNodes: 5 }).then(nodeArrayChecker(true, false, 'abcde'));
}).then(() => {
assertOwnership();
// maxNodes with the second item centered should still give us the first 5 nodes.
return gWalker.children(longList, { maxNodes: 5, center: allChildren[1] }).then(
nodeArrayChecker(true, false, 'abcde')
);
}).then(() => {
// maxNodes with a center in the middle of the list should put that item in the middle
let center = allChildren[13];
is(center.id, 'n', "Make sure I know how to count letters.");
return gWalker.children(longList, { maxNodes: 5, center: center }).then(
nodeArrayChecker(false, false, 'lmnop')
);
}).then(() => {
// maxNodes with the second-to-last item centered should give us the last 5 nodes.
return gWalker.children(longList, { maxNodes: 5, center: allChildren[24] }).then(
nodeArrayChecker(false, true, 'vwxyz')
);
}).then(() => {
// maxNodes with a start in the middle should start at that node and fetch 5
let start = allChildren[13];
is(start.id, 'n', "Make sure I know how to count letters.")
return gWalker.children(longList, { maxNodes: 5, start: start }).then(
nodeArrayChecker(false, false, 'nopqr')
);
}).then(() => {
// maxNodes near the end should only return what's left
return gWalker.children(longList, { maxNodes: 5, start: allChildren[24] }).then(
nodeArrayChecker(false, true, 'yz')
);
}).then(runNextTest));
});
addTest(function testSiblings() {
promiseDone(gWalker.querySelector(gWalker.rootNode, "#a").then(a => {
return gWalker.siblings(a, { maxNodes: 5, center: a }).then(nodeArrayChecker(true, false, "abcde"));
}).then(() => {
return gWalker.siblings(gWalker.rootNode).then(response => {
ok(response.hasFirst && response.hasLast, "Has first and last.");
is(response.nodes.length, 1, "Has only the document element.");
ok(response.nodes[0] === gWalker.rootNode, "Document element is its own sibling.");
});
}).then(runNextTest));
})
addTest(function testFrameTraversal() {
promiseDone(gWalker.querySelector(gWalker.rootNode, "#childFrame").then(childFrame => {
return gWalker.children(childFrame);
}).then(children => {
let nodes = children.nodes;
ok(nodes.length, 1, "There should be only one child of the iframe");
is(nodes[0].nodeType, Node.DOCUMENT_NODE, "iframe child should be a document node");
return gWalker.querySelector(nodes[0], "#z");
}).then(childDocumentZ => {
return gWalker.parents(childDocumentZ);
}).then(parents => {
// Expected set of parent tag names for this item:
let expectedParents = ['DIV', 'BODY', 'HTML', '#document', 'IFRAME', 'BODY', 'HTML', '#document'];
for (let parent of parents) {
let expected = expectedParents.shift();
is(parent.nodeName, expected, "Got expected parent");
}
}).then(runNextTest));
});
addTest(function testLongValue() {
const testSummaryLength = 10;
inspector.setValueSummaryLength(testSummaryLength);
SimpleTest.registerCleanupFunction(function() {
inspector.setValueSummaryLength(inspector.DEFAULT_VALUE_SUMMARY_LENGTH);
});
let longstringText = gInspectee.getElementById("longstring").firstChild.nodeValue;
promiseDone(gWalker.querySelector(gWalker.rootNode, "#longstring").then(node => {
// Now we need to get the text node child...
return gWalker.children(node, { maxNodes: 1 });
}).then(children => {
let textNode = children.nodes[0];
is(textNode.nodeType, Node.TEXT_NODE, "Value should be a text node");
is(textNode.shortValue.length, 10, "Value summary should be limited to the summary value length");
ok(textNode.incompleteValue, "Value should be incomplete");
return textNode;
}).then(textNode => {
return textNode.getNodeValue();
}).then(value => {
return value.string();
}).then(valueStr => {
is(valueStr, longstringText, "Full node value should match the string from the document.");
}).then(runNextTest));
});
addTest(function testShortValue() {
let shortstringText = gInspectee.getElementById("shortstring").firstChild.nodeValue;
promiseDone(gWalker.querySelector(gWalker.rootNode, "#shortstring").then(node => {
// Now we need to get the text node child...
return gWalker.children(node, { maxNodes: 1 });
}).then(children => {
let textNode = children.nodes[0];
is(textNode.nodeType, Node.TEXT_NODE, "Value should be a text node");
is(textNode.shortValue, shortstringText, "Value should be complete");
ok(!textNode.incompleteValue, "Value should be complete");
return textNode;
}).then(textNode => {
return textNode.getNodeValue();
}).then(value => {
return value.string();
}).then(valueStr => {
is(valueStr, shortstringText, "Full node value should match the string from the document.");
}).then(runNextTest));
});
addTest(function testReleaseWalker() {
checkActorIDs.push(gWalker.actorID);
promiseDone(gWalker.release().then(() => {
let promises = [checkMissing(gClient, id) for (id of checkActorIDs)];
return Promise.all(promises)
}).then(runNextTest));
});
addTest(function cleanup() {
delete gWalker;
delete gInspectee;
delete gClient;
runNextTest();
});
</script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a>
<a id="inspectorContent" target="_blank" href="inspector-traversal-data.html">Test Document</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
</body>
</html>

View File

@ -472,11 +472,6 @@ GfxInfo::GetFeatureStatusImpl(int32_t aFeature,
// All Sony devices (Bug 845734)
bool isBlocklisted =
cModel.Find("SCH-I535", true) != -1 ||
cModel.Find("SGH-I747", true) != -1 ||
cModel.Find("SGH-T999", true) != -1 ||
cModel.Find("SPH-L710", true) != -1 ||
cModel.Find("GT-I8190", true) != -1 ||
cModel.Find("GT-P3100", true) != -1 ||
cModel.Find("GT-P3110", true) != -1 ||
cModel.Find("GT-P3113", true) != -1 ||