mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge latest green birch changeset and mozilla-central
This commit is contained in:
commit
6c810f6e43
@ -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.
|
||||
*/
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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",
|
||||
|
@ -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.");
|
||||
|
@ -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.");
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
*
|
||||
|
350
browser/devtools/shared/widgets/VariablesViewController.jsm
Normal file
350
browser/devtools/shared/widgets/VariablesViewController.jsm
Normal 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);
|
||||
};
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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>
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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___ */
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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__
|
||||
|
||||
|
@ -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++;
|
||||
|
@ -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__
|
||||
|
||||
|
@ -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__
|
||||
|
||||
|
@ -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__
|
||||
|
||||
|
@ -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__
|
||||
|
||||
|
@ -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__
|
||||
|
@ -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__
|
||||
|
@ -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__
|
||||
|
||||
|
@ -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__
|
||||
|
||||
|
@ -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__
|
||||
|
||||
|
@ -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__
|
||||
|
||||
|
@ -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__
|
||||
|
@ -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__
|
||||
|
||||
|
@ -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__
|
||||
|
@ -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__
|
||||
|
||||
|
@ -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__
|
||||
|
@ -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__
|
||||
|
||||
|
@ -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__
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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__
|
||||
|
||||
|
@ -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__
|
||||
|
@ -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);
|
||||
|
@ -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:
|
||||
|
@ -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",
|
||||
|
@ -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,
|
||||
|
@ -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__
|
||||
|
||||
|
@ -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__
|
||||
|
||||
|
@ -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
|
||||
|
@ -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__
|
||||
|
||||
|
@ -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__
|
||||
|
||||
|
@ -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__
|
||||
|
||||
|
@ -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__
|
||||
|
||||
|
7
js/src/jit-test/tests/basic/bug883623.js
Normal file
7
js/src/jit-test/tests/basic/bug883623.js
Normal file
@ -0,0 +1,7 @@
|
||||
|
||||
(function() {
|
||||
eval("\
|
||||
arguments.valueOf();\
|
||||
with (function(){}){};\
|
||||
");
|
||||
})()
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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_;
|
||||
|
@ -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_)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -315,4 +315,4 @@ JSObject::asDebugScope()
|
||||
return *static_cast<js::DebugScopeObject *>(this);
|
||||
}
|
||||
|
||||
#endif /* CallObject_inl_h___ */
|
||||
#endif /* ScopeObject_inl_h___ */
|
||||
|
@ -892,4 +892,5 @@ InterpreterActivation::~InterpreterActivation()
|
||||
{}
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* Stack_inl_h__ */
|
||||
|
11
layout/reftests/svg/text/dynamic-non-scaling-stroke-ref.svg
Normal file
11
layout/reftests/svg/text/dynamic-non-scaling-stroke-ref.svg
Normal 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 |
31
layout/reftests/svg/text/dynamic-non-scaling-stroke.svg
Normal file
31
layout/reftests/svg/text/dynamic-non-scaling-stroke.svg
Normal 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 |
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -61,6 +61,7 @@ foreignObject {
|
||||
|
||||
*|*::-moz-svg-text {
|
||||
unicode-bidi: inherit;
|
||||
vector-effect: inherit;
|
||||
}
|
||||
|
||||
*[xml|space=preserve] {
|
||||
|
1582
toolkit/devtools/server/actors/inspector.js
Normal file
1582
toolkit/devtools/server/actors/inspector.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -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");
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
|
185
toolkit/devtools/server/tests/mochitest/inspector-helpers.js
Normal file
185
toolkit/devtools/server/tests/mochitest/inspector-helpers.js
Normal 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()();
|
||||
}
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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 ||
|
||||
|
Loading…
Reference in New Issue
Block a user