merge m-c to fx-team

This commit is contained in:
Tim Taubert 2012-03-02 09:47:01 +01:00
commit b038fa20c0
37 changed files with 720 additions and 106 deletions

View File

@ -150,35 +150,35 @@ DebuggerView.Stackframes = {
*
* @param number aDepth
* The frame depth specified by the debugger.
* @param string aFrameIdText
* The id to be displayed in the list.
* @param string aFrameNameText
* The name to be displayed in the list.
* @param string aFrameDetailsText
* The details to be displayed in the list.
* @return object
* The newly created html node representing the added frame.
*/
addFrame: function DVF_addFrame(aDepth, aFrameIdText, aFrameNameText) {
addFrame: function DVF_addFrame(aDepth, aFrameNameText, aFrameDetailsText) {
// make sure we don't duplicate anything
if (document.getElementById("stackframe-" + aDepth)) {
return null;
}
let frame = document.createElement("div");
let frameId = document.createElement("span");
let frameName = document.createElement("span");
let frameDetails = document.createElement("span");
// create a list item to be added to the stackframes container
frame.id = "stackframe-" + aDepth;
frame.className = "dbg-stackframe list-item";
// this list should display the id and name of the frame
frameId.className = "dbg-stackframe-id";
// this list should display the name and details for the frame
frameName.className = "dbg-stackframe-name";
frameId.appendChild(document.createTextNode(aFrameIdText));
frameDetails.className = "dbg-stackframe-details";
frameName.appendChild(document.createTextNode(aFrameNameText));
frameDetails.appendChild(document.createTextNode(aFrameDetailsText));
frame.appendChild(frameId);
frame.appendChild(frameName);
frame.appendChild(frameDetails);
this._frames.appendChild(frame);
@ -1074,6 +1074,7 @@ DebuggerView.Scripts = {
*
* @param string aUrl
* The script URL.
* @return boolean
*/
contains: function DVS_contains(aUrl) {
if (this._scripts.getElementsByAttribute("value", aUrl).length > 0) {
@ -1082,6 +1083,21 @@ DebuggerView.Scripts = {
return false;
},
/**
* Checks whether the script with the specified label is among the scripts
* known to the debugger and shown in the list.
*
* @param string aLabel
* The script label.
* @return boolean
*/
containsLabel: function DVS_containsLabel(aLabel) {
if (this._scripts.getElementsByAttribute("label", aLabel).length > 0) {
return true;
}
return false;
},
/**
* Checks whether the script with the specified URL is selected in the list.
*
@ -1109,30 +1125,30 @@ DebuggerView.Scripts = {
break;
}
}
},
},
/**
* Adds a script to the scripts container.
* If the script already exists (was previously added), null is returned.
* Otherwise, the newly created element is returned.
*
* @param string aUrl
* The script url.
* @param string aLabel
* The simplified script location to be shown.
* @param string aScript
* The source script.
* @param string aScriptNameText
* Optional, title displayed instead of url.
* @return object
* The newly created html node representing the added script.
*/
addScript: function DVS_addScript(aUrl, aSource, aScriptNameText) {
addScript: function DVS_addScript(aLabel, aScript) {
// make sure we don't duplicate anything
if (this.contains(aUrl)) {
if (this.containsLabel(aLabel)) {
return null;
}
let script = this._scripts.appendItem(aScriptNameText || aUrl, aUrl);
script.setUserData("sourceScript", aSource, null);
let script = this._scripts.appendItem(aLabel, aScript.url);
script.setAttribute("tooltiptext", aScript.url);
script.setUserData("sourceScript", aScript, null);
this._scripts.selectedItem = script;
return script;
},

View File

@ -67,6 +67,26 @@
overflow: auto;
}
.dbg-stackframe {
display: block;
}
.dbg-stackframe-name {
float: left;
}
.dbg-stackframe-details {
float: right;
}
.dbg-stackframe-name:-moz-locale-dir(rtl) {
float: right;
}
.dbg-stackframe-details:-moz-locale-dir(rtl) {
float: left;
}
/**
* Properties elements
*/

View File

@ -370,10 +370,12 @@ var StackFrames = {
*/
_addFramePanel: function SF_addFramePanel(aFrame) {
let depth = aFrame.depth;
let idText = "#" + aFrame.depth + " ";
let nameText = this._frameTitle(aFrame);
let label = SourceScripts._getScriptLabel(aFrame.where.url);
let panel = DebuggerView.Stackframes.addFrame(depth, idText, nameText);
let startText = this._frameTitle(aFrame);
let endText = label + ":" + aFrame.where.line;
let panel = DebuggerView.Stackframes.addFrame(depth, startText, endText);
if (panel) {
panel.stackFrame = aFrame;
@ -397,7 +399,7 @@ var StackFrames = {
*/
_frameTitle: function SF_frameTitle(aFrame) {
if (aFrame.type == "call") {
return aFrame["calleeName"] ? aFrame["calleeName"] + "()" : "(anonymous)";
return aFrame["calleeName"] ? aFrame["calleeName"] : "(anonymous)";
}
return "(" + aFrame.type + ")";
@ -416,6 +418,7 @@ StackFrames.onClick = StackFrames.onClick.bind(StackFrames);
var SourceScripts = {
pageSize: 25,
activeThread: null,
_labelsCache: null,
/**
* Watch a given thread client.
@ -431,6 +434,7 @@ var SourceScripts = {
aThreadClient.addListener("paused", this.onPaused);
aThreadClient.addListener("scriptsadded", this.onScripts);
aThreadClient.addListener("scriptscleared", this.onScriptsCleared);
this.clearLabelsCache();
this.onScriptsCleared();
aCallback && aCallback();
},
@ -509,26 +513,86 @@ var SourceScripts = {
return;
}
let url = aUrl;
// Trim the query part.
let q = url.indexOf('?');
if (q > -1) {
url = url.slice(0, q);
}
if (url.slice(-3) == ".js") {
if (this._trimUrlQuery(aUrl).slice(-3) == ".js") {
window.editor.setMode(SourceEditor.MODES.JAVASCRIPT);
} else {
window.editor.setMode(SourceEditor.MODES.HTML);
}
},
/**
* Trims the query part of a url string, if necessary.
*
* @param string aUrl
* The script url.
* @return string
*/
_trimUrlQuery: function SS_trimUrlQuery(aUrl) {
let q = aUrl.indexOf('?');
if (q > -1) {
return aUrl.slice(0, q);
}
return aUrl;
},
/**
* Gets a unique, simplified label from a script url.
* ex: a). ici://some.address.com/random/subrandom/
* b). ni://another.address.org/random/subrandom/page.html
* c). san://interesting.address.gro/random/script.js
* d). si://interesting.address.moc/random/another/script.js
* =>
* a). subrandom/
* b). page.html
* c). script.js
* d). another/script.js
*
* @param string aUrl
* The script url.
* @param string aHref
* The content location href to be used. If unspecified, it will
* defalult to debugged panrent window location.
* @return string
* The simplified label.
*/
_getScriptLabel: function SS_getScriptLabel(aUrl, aHref) {
let url = this._trimUrlQuery(aUrl);
if (this._labelsCache[url]) {
return this._labelsCache[url];
}
let href = aHref || window.parent.content.location.href;
let pathElements = url.split("/");
let label = pathElements.pop() || (pathElements.pop() + "/");
// if the label as a leaf name is alreay present in the scripts list
if (DebuggerView.Scripts.containsLabel(label)) {
label = url.replace(href.substring(0, href.lastIndexOf("/") + 1), "");
// if the path/to/script is exactly the same, we're in different domains
if (DebuggerView.Scripts.containsLabel(label)) {
label = url;
}
}
return this._labelsCache[url] = label;
},
/**
* Clears the labels cache, populated by SS_getScriptLabel().
* This should be done every time the content location changes.
*/
clearLabelsCache: function SS_clearLabelsCache() {
this._labelsCache = {};
},
/**
* Add the specified script to the list and display it in the editor if the
* editor is empty.
*/
_addScript: function SS_addScript(aScript) {
DebuggerView.Scripts.addScript(aScript.url, aScript);
DebuggerView.Scripts.addScript(this._getScriptLabel(aScript.url), aScript);
if (window.editor.getCharCount() == 0) {
this._showScript(aScript);

View File

@ -10,9 +10,7 @@ var gTab = null;
var gDebuggee = null;
var gDebugger = null;
const DEBUGGER_TAB_URL = "http://example.com/browser/browser/devtools/" +
"debugger/test/" +
"browser_dbg_debuggerstatement.html";
const DEBUGGER_TAB_URL = EXAMPLE_URL + "browser_dbg_debuggerstatement.html";
function test() {
debug_tab_pane(DEBUGGER_TAB_URL, function(aTab, aDebuggee, aPane) {

View File

@ -8,9 +8,7 @@
var gClient = null;
var gTab = null;
const DEBUGGER_TAB_URL = "http://example.com/browser/browser/devtools/" +
"debugger/test/" +
"browser_dbg_debuggerstatement.html";
const DEBUGGER_TAB_URL = EXAMPLE_URL + "browser_dbg_debuggerstatement.html";
function test()
{

View File

@ -83,7 +83,60 @@ function testSimpleCall() {
function resumeAndFinish() {
gDebugger.StackFrames.activeThread.resume(function() {
removeTab(gTab);
finish();
let vs = gDebugger.DebuggerView.Scripts;
let ss = gDebugger.SourceScripts;
is(ss._trimUrlQuery("a/b/c.d?test=1&random=4"), "a/b/c.d",
"Trimming the url query isn't done properly.");
let urls = [
{ href: "ici://some.address.com/random/", leaf: "subrandom/" },
{ href: "ni://another.address.org/random/subrandom/", leaf: "page.html" },
{ href: "san://interesting.address.gro/random/", leaf: "script.js" },
{ href: "si://interesting.address.moc/random/", leaf: "script.js" },
{ href: "si://interesting.address.moc/random/", leaf: "x/script.js" },
{ href: "si://interesting.address.moc/random/", leaf: "x/y/script.js?a=1" },
{ href: "si://interesting.address.moc/random/x/", leaf: "y/script.js?a=1&b=2" },
{ href: "si://interesting.address.moc/random/x/y/", leaf: "script.js?a=1&b=2&c=3" }
];
vs._scripts.removeEventListener("select", vs._onScriptsChange, false);
urls.forEach(function(url) {
executeSoon(function() {
let loc = url.href + url.leaf;
vs.addScript(ss._getScriptLabel(loc, url.href), { url: loc });
});
});
executeSoon(function() {
for (let i = 0; i < vs._scripts.itemCount; i++) {
let lab = vs._scripts.getItemAtIndex(i).getAttribute("label");
let loc = urls[i].href + urls[i].leaf;
info("label: " + i + " " + lab);
ok(vs.contains(loc), "Script url is incorrect: " + loc);
}
ok(gDebugger.DebuggerView.Scripts.containsLabel("subrandom/"),
"Script (0) label is incorrect.");
ok(gDebugger.DebuggerView.Scripts.containsLabel("page.html"),
"Script (1) label is incorrect.");
ok(gDebugger.DebuggerView.Scripts.containsLabel("script.js"),
"Script (2) label is incorrect.");
ok(gDebugger.DebuggerView.Scripts.containsLabel("si://interesting.address.moc/random/script.js"),
"Script (3) label is incorrect.");
ok(gDebugger.DebuggerView.Scripts.containsLabel("x/script.js"),
"Script (4) label is incorrect.");
ok(gDebugger.DebuggerView.Scripts.containsLabel("x/y/script.js"),
"Script (5) label is incorrect.");
is(vs._scripts.itemCount, 6,
"Got too many script items in the list!");
removeTab(gTab);
finish();
});
});
}

View File

@ -6,8 +6,7 @@
* Make sure that the property view displays function parameters.
*/
const TAB_URL = "http://example.com/browser/browser/devtools/debugger/test/" +
"browser_dbg_frame-parameters.html";
const TAB_URL = EXAMPLE_URL + "browser_dbg_frame-parameters.html";
var gPane = null;
var gTab = null;

View File

@ -6,8 +6,7 @@
* Make sure that the property view displays the properties of objects.
*/
const TAB_URL = "http://example.com/browser/browser/devtools/debugger/test/" +
"browser_dbg_frame-parameters.html";
const TAB_URL = EXAMPLE_URL + "browser_dbg_frame-parameters.html";
var gPane = null;
var gTab = null;

View File

@ -6,8 +6,8 @@
* Make sure that switching the displayed script in the UI works as advertised.
*/
const TAB_URL = "http://example.com/browser/browser/devtools/debugger/" +
"test/browser_dbg_script-switching.html";
const TAB_URL = EXAMPLE_URL + "browser_dbg_script-switching.html";
let tempScope = {};
Cu.import("resource:///modules/source-editor.jsm", tempScope);
let SourceEditor = tempScope.SourceEditor;
@ -40,6 +40,24 @@ function testScriptsDisplay() {
is(gScripts.itemCount, 2, "Found the expected number of scripts.");
for (let i = 0; i < gScripts.itemCount; i++) {
info("label: " + i + " " + gScripts.getItemAtIndex(i).getAttribute("label"));
}
let label1 = "test-script-switching-01.js";
let label2 = "test-script-switching-02.js";
ok(gDebugger.DebuggerView.Scripts.contains(EXAMPLE_URL +
label1), "First script url is incorrect.");
ok(gDebugger.DebuggerView.Scripts.contains(EXAMPLE_URL +
label2), "Second script url is incorrect.");
ok(gDebugger.DebuggerView.Scripts.containsLabel(
label1), "First script label is incorrect.");
ok(gDebugger.DebuggerView.Scripts.containsLabel(
label2), "Second script label is incorrect.");
ok(gDebugger.editor.getText().search(/debugger/) != -1,
"The correct script was loaded initially.");

View File

@ -6,8 +6,8 @@
* pane and highlights the proper line.
*/
const TAB_URL = "http://example.com/browser/browser/devtools/debugger/" +
"test/browser_dbg_script-switching.html";
const TAB_URL = EXAMPLE_URL + "browser_dbg_script-switching.html";
let tempScope = {};
Cu.import("resource:///modules/source-editor.jsm", tempScope);
let SourceEditor = tempScope.SourceEditor;

View File

@ -6,8 +6,8 @@
* and script URIs with extra query parameters also get the right engine.
*/
const TAB_URL = "http://example.com/browser/browser/devtools/debugger/" +
"test/browser_dbg_update-editor-mode.html";
const TAB_URL = EXAMPLE_URL + "browser_dbg_update-editor-mode.html";
let tempScope = {};
Cu.import("resource:///modules/source-editor.jsm", tempScope);
let SourceEditor = tempScope.SourceEditor;

View File

@ -14,11 +14,11 @@ let DebuggerTransport = tempScope.DebuggerTransport;
let DebuggerClient = tempScope.DebuggerClient;
let Services = tempScope.Services;
const TAB1_URL = "http://example.com/browser/browser/devtools/debugger/test/browser_dbg_tab1.html";
const EXAMPLE_URL = "http://example.com/browser/browser/devtools/debugger/test/";
const TAB2_URL = "http://example.com/browser/browser/devtools/debugger/test/browser_dbg_tab2.html";
const STACK_URL = "http://example.com/browser/browser/devtools/debugger/test/browser_dbg_stack.html";
const TAB1_URL = EXAMPLE_URL + "browser_dbg_tab1.html";
const TAB2_URL = EXAMPLE_URL + "browser_dbg_tab2.html";
const STACK_URL = EXAMPLE_URL + "browser_dbg_stack.html";
if (!DebuggerServer.initialized) {
DebuggerServer.init();

View File

@ -4,7 +4,7 @@
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<link rel="stylesheet" href="chrome://browser/skin/inspector.css" type="text/css"/>
<link rel="stylesheet" href="chrome://browser/skin/devtools/htmlpanel.css" type="text/css"/>
</head>
<body role="application">
<div id="attribute-editor">

View File

@ -870,7 +870,8 @@ InspectorUI.prototype = {
let parent = this.selection.parentNode;
// remove the node from the treepanel
this.treePanel.deleteChildBox(selection);
if (this.treePanel.isOpen())
this.treePanel.deleteChildBox(selection);
// remove the node from content
parent.removeChild(selection);
@ -1234,6 +1235,7 @@ InspectorUI.prototype = {
let iframe = this.chromeDoc.createElement("iframe");
iframe.id = "devtools-sidebar-iframe-" + aRegObj.id;
iframe.setAttribute("flex", "1");
iframe.setAttribute("tooltip", "aHTMLTooltip");
this.sidebarDeck.appendChild(iframe);
// wire up button to show the iframe

View File

@ -77,10 +77,18 @@ const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
const RX_UNIVERSAL_SELECTOR = /\s*\*\s*/g;
const RX_NOT = /:not\((.*?)\)/g;
const RX_PSEUDO_CLASS_OR_ELT = /(:[\w-]+\().*?\)/g;
const RX_CONNECTORS = /\s*[\s>+~]\s*/g;
const RX_ID = /\s*#\w+\s*/g;
const RX_CLASS_OR_ATTRIBUTE = /\s*(?:\.\w+|\[.+?\])\s*/g;
const RX_PSEUDO = /\s*:?:([\w-]+)(\(?\)?)\s*/g;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
var EXPORTED_SYMBOLS = ["CssLogic"];
var EXPORTED_SYMBOLS = ["CssLogic", "CssSelector"];
function CssLogic()
{
@ -1431,6 +1439,31 @@ CssSelector.prototype = {
return this._cssRule.line;
},
/**
* Retrieve the pseudo-elements that we support. This list should match the
* elements specified in layout/style/nsCSSPseudoElementList.h
*/
get pseudoElements()
{
if (!CssSelector._pseudoElements) {
let pseudos = CssSelector._pseudoElements = new Set();
pseudos.add("after");
pseudos.add("before");
pseudos.add("first-letter");
pseudos.add("first-line");
pseudos.add("selection");
pseudos.add("-moz-focus-inner");
pseudos.add("-moz-focus-outer");
pseudos.add("-moz-list-bullet");
pseudos.add("-moz-list-number");
pseudos.add("-moz-math-anonymous");
pseudos.add("-moz-math-stretchy");
pseudos.add("-moz-progress-bar");
pseudos.add("-moz-selection");
}
return CssSelector._pseudoElements;
},
/**
* Retrieve specificity information for the current selector.
*
@ -1446,37 +1479,58 @@ CssSelector.prototype = {
return this._specificity;
}
let specificity = {};
let specificity = {
ids: 0,
classes: 0,
tags: 0
};
specificity.ids = 0;
specificity.classes = 0;
specificity.tags = 0;
let text = this.text;
// Split on CSS combinators (section 5.2).
// TODO: We need to properly parse the selector. See bug 592743.
if (!this.elementStyle) {
this.text.split(/[ >+]/).forEach(function(aSimple) {
// The regex leaves empty nodes combinators like ' > '
if (!aSimple) {
return;
}
// See http://www.w3.org/TR/css3-selectors/#specificity
// We can count the IDs by counting the '#' marks.
specificity.ids += (aSimple.match(/#/g) || []).length;
// Similar with class names and attribute matchers
specificity.classes += (aSimple.match(/\./g) || []).length;
specificity.classes += (aSimple.match(/\[/g) || []).length;
// Pseudo elements count as elements.
specificity.tags += (aSimple.match(/:/g) || []).length;
// If we have anything of substance before we get into ids/classes/etc
// then it must be a tag if it isn't '*'.
let tag = aSimple.split(/[#.[:]/)[0];
if (tag && tag != "*") {
// Remove universal selectors as they are not relevant as far as specificity
// is concerned.
text = text.replace(RX_UNIVERSAL_SELECTOR, "");
// not() is ignored but any selectors contained by it are counted. Let's
// remove the not() and keep the contents.
text = text.replace(RX_NOT, " $1");
// Simplify remaining psuedo classes & elements.
text = text.replace(RX_PSEUDO_CLASS_OR_ELT, " $1)");
// Replace connectors with spaces
text = text.replace(RX_CONNECTORS, " ");
text.split(/\s/).forEach(function(aSimple) {
// Count IDs.
aSimple = aSimple.replace(RX_ID, function() {
specificity.ids++;
return "";
});
// Count class names and attribute matchers.
aSimple = aSimple.replace(RX_CLASS_OR_ATTRIBUTE, function() {
specificity.classes++;
return "";
});
aSimple = aSimple.replace(RX_PSEUDO, function(aDummy, aPseudoName) {
if (this.pseudoElements.has(aPseudoName)) {
// Pseudo elements count as tags.
specificity.tags++;
} else {
// Pseudo classes count as classes.
specificity.classes++;
}
return "";
}.bind(this));
if (aSimple) {
specificity.tags++;
}
}, this);
}
this._specificity = specificity;
return this._specificity;

View File

@ -103,6 +103,13 @@ function ElementStyle(aElement, aStore)
{
this.element = aElement;
this.store = aStore || {};
// We don't want to overwrite this.store.userProperties so we only create it
// if it doesn't already exist.
if (!("userProperties" in this.store)) {
this.store.userProperties = new UserProperties();
}
if (this.store.disabled) {
this.store.disabled = aStore.disabled;
} else {
@ -422,6 +429,7 @@ Rule.prototype = {
applyProperties: function Rule_applyProperties()
{
let disabledProps = [];
let store = this.elementStyle.store;
for each (let prop in this.textProps) {
if (!prop.enabled) {
@ -433,10 +441,11 @@ Rule.prototype = {
continue;
}
store.userProperties.setProperty(this.style, prop.name, prop.value);
this.style.setProperty(prop.name, prop.value, prop.priority);
// Refresh the property's value from the style, to reflect
// Refresh the property's priority from the style, to reflect
// any changes made during parsing.
prop.value = this.style.getPropertyValue(prop.name);
prop.priority = this.style.getPropertyPriority(prop.name);
prop.updateComputed();
}
@ -519,6 +528,7 @@ Rule.prototype = {
_getTextProperties: function Rule_getTextProperties()
{
this.textProps = [];
let store = this.elementStyle.store;
let lines = this.style.cssText.match(CSS_LINE_RE);
for each (let line in lines) {
let matches = CSS_PROP_RE.exec(line);
@ -530,8 +540,8 @@ Rule.prototype = {
!this.elementStyle.domUtils.isInheritedProperty(name)) {
continue;
}
let prop = new TextProperty(this, name, matches[2], matches[3] || "");
let value = store.userProperties.getProperty(this.style, name, matches[2]);
let prop = new TextProperty(this, name, value, matches[3] || "");
this.textProps.push(prop);
}
@ -542,8 +552,8 @@ Rule.prototype = {
}
for each (let prop in disabledProps) {
let textProp = new TextProperty(this, prop.name,
prop.value, prop.priority);
let value = store.userProperties.getProperty(this.style, prop.name, prop.value);
let textProp = new TextProperty(this, prop.name, value, prop.priority);
textProp.enabled = false;
this.textProps.push(textProp);
}
@ -993,6 +1003,12 @@ TextPropertyEditor.prototype = {
appendText(this.element, ";");
this.warning = createChild(this.element, "div", {
hidden: "",
class: "ruleview-warning",
title: CssLogic.l10n("rule.warning.title"),
});
// Holds the viewers for the computed properties.
// will be populated in |_updateComputed|.
this.computed = createChild(this.element, "ul", {
@ -1028,6 +1044,7 @@ TextPropertyEditor.prototype = {
val += " !" + this.prop.priority;
}
this.valueSpan.textContent = val;
this.warning.hidden = this._validate();
// Populate the computed styles.
this._updateComputed();
@ -1163,6 +1180,23 @@ TextPropertyEditor.prototype = {
this.prop.setValue(this.committed.value, this.committed.priority);
}
},
/**
* Validate this property.
*
* @returns {Boolean}
* True if the property value is valid, false otherwise.
*/
_validate: function TextPropertyEditor_validate()
{
let name = this.prop.name;
let value = this.prop.value;
let style = this.doc.createElementNS(HTML_NS, "div").style;
style.setProperty(name, value, null);
return !!style.getPropertyValue(name);
},
};
/**
@ -1381,6 +1415,61 @@ InplaceEditor.prototype = {
}
};
/**
* Store of CSSStyleDeclarations mapped to properties that have been changed by
* the user.
*/
function UserProperties()
{
this.weakMap = new WeakMap();
}
UserProperties.prototype = {
/**
* Get a named property for a given CSSStyleDeclaration.
*
* @param {CSSStyleDeclaration} aStyle
* The CSSStyleDeclaration against which the property is mapped.
* @param {String} aName
* The name of the property to get.
* @param {Boolean} aDefault
* Indicates whether the property value is one entered by a user.
* @returns {String}
* The property value if it has previously been set by the user, null
* otherwise.
*/
getProperty: function UP_getProperty(aStyle, aName, aDefault) {
let entry = this.weakMap.get(aStyle, null);
if (entry && aName in entry) {
return entry[aName];
}
return typeof aDefault != "undefined" ? aDefault : null;
},
/**
* Set a named property for a given CSSStyleDeclaration.
*
* @param {CSSStyleDeclaration} aStyle
* The CSSStyleDeclaration against which the property is to be mapped.
* @param {String} aName
* The name of the property to set.
* @param {String} aValue
* The value of the property to set.
*/
setProperty: function UP_setProperty(aStyle, aName, aValue) {
let entry = this.weakMap.get(aStyle, null);
if (entry) {
entry[aName] = aValue;
} else {
let props = {};
props[aName] = aValue;
this.weakMap.set(aStyle, props);
}
},
};
/**
* Helper functions
*/

View File

@ -82,7 +82,7 @@ StyleInspector.prototype = {
this.registrationObject = {
id: "styleinspector",
label: this.l10n("style.highlighter.button.label2"),
tooltiptext: this.l10n("style.highlighter.button.tooltip"),
tooltiptext: this.l10n("style.highlighter.button.tooltip2"),
accesskey: this.l10n("style.highlighter.accesskey2"),
context: this,
get isOpen() isOpen(),

View File

@ -55,11 +55,13 @@ _BROWSER_TEST_FILES = \
browser_bug_692400_element_style.js \
browser_csslogic_inherited.js \
browser_ruleview_editor.js \
browser_ruleview_editor_changedvalues.js \
browser_ruleview_inherit.js \
browser_ruleview_manipulation.js \
browser_ruleview_override.js \
browser_ruleview_ui.js \
browser_bug705707_is_content_stylesheet.js \
browser_bug_592743_specificity.js \
head.js \
$(NULL)

View File

@ -0,0 +1,49 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Tests that CSS specificity is properly calculated.
let tempScope = {};
Cu.import("resource:///modules/devtools/CssLogic.jsm", tempScope);
let CssLogic = tempScope.CssLogic;
let CssSelector = tempScope.CssSelector;
function test()
{
let tests = [
{text: "*", expected: "000"},
{text: "LI", expected: "001"},
{text: "UL LI", expected: "002"},
{text: "UL OL+LI", expected: "003"},
{text: "H1 + *[REL=up]", expected: "011"},
{text: "UL OL LI.red", expected: "013"},
{text: "LI.red.level", expected: "021"},
{text: ".red .level", expected: "020"},
{text: "#x34y", expected: "100"},
{text: "#s12:not(FOO)", expected: "101"},
{text: "body#home div#warning p.message", expected: "213"},
{text: "* body#home div#warning p.message", expected: "213"},
{text: "#footer *:not(nav) li", expected: "102"},
{text: "bar:nth-child(1n+0)", expected: "011"},
{text: "li::-moz-list-number", expected: "002"},
{text: "a:hover", expected: "011"},
];
tests.forEach(function(aTest) {
let selector = new CssSelector(null, aTest.text);
let specificity = selector.specificity;
let result = "" + specificity.ids + specificity.classes + specificity.tags;
is(result, aTest.expected, "selector \"" + aTest.text +
"\" produces expected result");
});
finishUp();
}
function finishUp()
{
CssLogic = CssSelector = null;
finish();
}

View File

@ -0,0 +1,190 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
let tempScope = {};
Cu.import("resource:///modules/devtools/CssRuleView.jsm", tempScope);
let CssRuleView = tempScope.CssRuleView;
let _ElementStyle = tempScope._ElementStyle;
let _editableField = tempScope._editableField;
let doc;
let ruleDialog;
let ruleView;
function waitForEditorFocus(aParent, aCallback)
{
aParent.addEventListener("focus", function onFocus(evt) {
if (evt.target.inplaceEditor) {
aParent.removeEventListener("focus", onFocus, true);
let editor = evt.target.inplaceEditor;
executeSoon(function() {
aCallback(editor);
});
}
}, true);
}
function waitForEditorBlur(aEditor, aCallback)
{
let input = aEditor.input;
input.addEventListener("blur", function onBlur() {
input.removeEventListener("blur", onBlur, false);
executeSoon(function() {
aCallback();
});
}, false);
}
var gRuleViewChanged = false;
function ruleViewChanged()
{
gRuleViewChanged = true;
}
function expectChange()
{
ok(gRuleViewChanged, "Rule view should have fired a change event.");
gRuleViewChanged = false;
}
function startTest()
{
let style = '' +
'#testid {' +
' background-color: blue;' +
'} ' +
'.testclass {' +
' background-color: green;' +
'}';
let styleNode = addStyle(doc, style);
doc.body.innerHTML = '<div id="testid" class="testclass">Styled Node</div>';
let testElement = doc.getElementById("testid");
ruleDialog = openDialog("chrome://browser/content/devtools/cssruleview.xul",
"cssruleviewtest",
"width=200,height=350");
ruleDialog.addEventListener("load", function onLoad(evt) {
ruleDialog.removeEventListener("load", onLoad, true);
let doc = ruleDialog.document;
ruleView = new CssRuleView(doc);
doc.documentElement.appendChild(ruleView.element);
ruleView.element.addEventListener("CssRuleViewChanged", ruleViewChanged, false);
ruleView.highlight(testElement);
waitForFocus(testCancelNew, ruleDialog);
}, true);
}
function testCancelNew()
{
// Start at the beginning: start to add a rule to the element's style
// declaration, but leave it empty.
let elementRuleEditor = ruleView.element.children[0]._ruleEditor;
waitForEditorFocus(elementRuleEditor.element, function onNewElement(aEditor) {
is(elementRuleEditor.newPropSpan.inplaceEditor, aEditor, "Next focused editor should be the new property editor.");
let input = aEditor.input;
waitForEditorBlur(aEditor, function () {
ok(!gRuleViewChanged, "Shouldn't get a change event after a cancel.");
is(elementRuleEditor.rule.textProps.length, 0, "Should have canceled creating a new text property.");
ok(!elementRuleEditor.propertyList.hasChildNodes(), "Should not have any properties.");
testCreateNew();
});
aEditor.input.blur();
});
EventUtils.synthesizeMouse(elementRuleEditor.closeBrace, 1, 1,
{ },
ruleDialog);
}
function testCreateNew()
{
// Create a new property.
let elementRuleEditor = ruleView.element.children[0]._ruleEditor;
waitForEditorFocus(elementRuleEditor.element, function onNewElement(aEditor) {
is(elementRuleEditor.newPropSpan.inplaceEditor, aEditor, "Next focused editor should be the new property editor.");
let input = aEditor.input;
input.value = "background-color";
waitForEditorFocus(elementRuleEditor.element, function onNewValue(aEditor) {
expectChange();
is(elementRuleEditor.rule.textProps.length, 1, "Should have created a new text property.");
is(elementRuleEditor.propertyList.children.length, 1, "Should have created a property editor.");
let textProp = elementRuleEditor.rule.textProps[0];
is(aEditor, textProp.editor.valueSpan.inplaceEditor, "Should be editing the value span now.");
aEditor.input.value = "#XYZ";
waitForEditorBlur(aEditor, function() {
expectChange();
is(textProp.value, "#XYZ", "Text prop should have been changed.");
is(textProp.editor._validate(), false, "#XYZ should not be a valid entry");
testEditProperty();
});
aEditor.input.blur();
});
EventUtils.synthesizeKey("VK_RETURN", {}, ruleDialog);
});
EventUtils.synthesizeMouse(elementRuleEditor.closeBrace, 1, 1,
{ },
ruleDialog);
}
function testEditProperty()
{
let idRuleEditor = ruleView.element.children[1]._ruleEditor;
let propEditor = idRuleEditor.rule.textProps[0].editor;
waitForEditorFocus(propEditor.element, function onNewElement(aEditor) {
is(propEditor.nameSpan.inplaceEditor, aEditor, "Next focused editor should be the name editor.");
let input = aEditor.input;
waitForEditorFocus(propEditor.element, function onNewName(aEditor) {
expectChange();
input = aEditor.input;
is(propEditor.valueSpan.inplaceEditor, aEditor, "Focus should have moved to the value.");
waitForEditorBlur(aEditor, function() {
expectChange();
let value = idRuleEditor.rule.style.getPropertyValue("border-color");
is(value, "red", "border-color should have been set.");
is(propEditor._validate(), true, "red should be a valid entry");
finishTest();
});
for each (let ch in "red;") {
EventUtils.sendChar(ch, ruleDialog);
}
});
for each (let ch in "border-color:") {
EventUtils.sendChar(ch, ruleDialog);
}
});
EventUtils.synthesizeMouse(propEditor.nameSpan, 1, 1,
{ },
ruleDialog);}
function finishTest()
{
ruleView.element.removeEventListener("CssRuleViewChanged", ruleViewChanged, false);
ruleView.clear();
ruleDialog.close();
ruleDialog = ruleView = null;
doc = null;
gBrowser.removeCurrentTab();
finish();
}
function test()
{
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function changedValues_load(evt) {
gBrowser.selectedBrowser.removeEventListener(evt.type, changedValues_load, true);
doc = content.document;
waitForFocus(startTest, content);
}, true);
content.location = "data:text/html,test rule view user changes";
}

View File

@ -94,7 +94,7 @@ tiltTranslateXDesc=X (pixels)
# LOCALIZATION NOTE (tiltTranslateXManual) A fuller description of the 'x'
# parameter to the 'translate' command, displayed when the user asks for help
# on what it does.
tiltTranslateXManual=The ammount in pixels to translate the webpage mesh on the X axis
tiltTranslateXManual=The amount in pixels to translate the webpage mesh on the X axis
# LOCALIZATION NOTE (tiltTranslateYDesc) A very short string to describe the
# 'y' parameter to the 'tilt translate' command, which is displayed in a dialog
@ -104,7 +104,7 @@ tiltTranslateYDesc=Y (pixels)
# LOCALIZATION NOTE (tiltTranslateYManual) A fuller description of the 'y'
# parameter to the 'translate' command, displayed when the user asks for help
# on what it does.
tiltTranslateYManual=The ammount in pixels to translate the webpage mesh on the Y axis
tiltTranslateYManual=The amount in pixels to translate the webpage mesh on the Y axis
# LOCALIZATION NOTE (tiltRotateDesc) A very short description of the 'tilt rotate'
# command. See tiltRotateManual for a fuller description of what it does. This
@ -124,7 +124,7 @@ tiltRotateXDesc=X (degrees)
# LOCALIZATION NOTE (tiltRotateXManual) A fuller description of the 'x'
# parameter to the 'rotate' command, displayed when the user asks for help
# on what it does.
tiltRotateXManual=The ammount in degrees to rotate the webpage mesh along the X axis
tiltRotateXManual=The amount in degrees to rotate the webpage mesh along the X axis
# LOCALIZATION NOTE (tiltRotateYDesc) A very short string to describe the
# 'y' parameter to the 'tilt rotate' command, which is displayed in a dialog
@ -134,7 +134,7 @@ tiltRotateYDesc=Y (degrees)
# LOCALIZATION NOTE (tiltRotateYManual) A fuller description of the 'y'
# parameter to the 'rotate' command, displayed when the user asks for help
# on what it does.
tiltRotateYManual=The ammount in degrees to rotate the webpage mesh along the Y axis
tiltRotateYManual=The amount in degrees to rotate the webpage mesh along the Y axis
# LOCALIZATION NOTE (tiltRotateZDesc) A very short string to describe the
# 'z' parameter to the 'tilt rotate' command, which is displayed in a dialog
@ -144,7 +144,7 @@ tiltRotateZDesc=Z (degrees)
# LOCALIZATION NOTE (tiltRotateZManual) A fuller description of the 'z'
# parameter to the 'rotate' command, displayed when the user asks for help
# on what it does.
tiltRotateZManual=The ammount in degrees to rotate the webpage mesh along the Z axis
tiltRotateZManual=The amount in degrees to rotate the webpage mesh along the Z axis
# LOCALIZATION NOTE (tiltZoomDesc) A very short description of the 'tilt zoom'
# command. See tiltZoomManual for a fuller description of what it does. This

View File

@ -37,10 +37,15 @@ rule.inheritedSource=Inherited from %S (%S)
# "Computed" refers to the Computed Style of the element.
style.highlighter.button.label2=Computed
style.highlighter.accesskey2=C
style.highlighter.button.tooltip=Inspect element computed styles
style.highlighter.button.tooltip2=Inspect element computed styles
# LOCALIZATION NOTE (helpLinkTitle): For each style property
# the user can hover it and get a help link button which allows one to
# quickly jump to the documentation from the Mozilla Developer Network site.
# This is the link title shown in the hover tooltip.
helpLinkTitle=Read the documentation for this property
# LOCALIZATION NOTE (rule.warning.title): When an invalid property value is
# entered into the rule view a warning icon is displayed. This text is used for
# the title attribute of the warning icon.
rule.warning.title=Invalid property value

Binary file not shown.

After

Width:  |  Height:  |  Size: 613 B

View File

@ -217,6 +217,19 @@
padding: 2px 5px;
}
.ruleview-warning {
background: url("chrome://browser/skin/devtools/alerticon-warning.png");
display: inline-block;
-moz-margin-start: 5px;
vertical-align: middle;
width: 13px;
height: 12px;
}
.ruleview-warning[hidden] {
display: none;
}
.ruleview-ruleopen {
-moz-padding-end: 5px;
}

View File

@ -116,8 +116,13 @@ a {
background-color: white;
}
.dbg-stackframe-id {
-moz-padding-end: 1em;
.dbg-stackframe {
-moz-padding-start: 4px;
-moz-padding-end: 4px;
}
.dbg-stackframe-name {
font-weight: 600;
}
/**

View File

@ -13,7 +13,6 @@ browser.jar:
* skin/classic/browser/browser.css (browser.css)
* skin/classic/browser/engineManager.css (engineManager.css)
skin/classic/browser/fullscreen-video.css
skin/classic/browser/inspector.css
skin/classic/browser/Geolocation-16.png
skin/classic/browser/Geolocation-64.png
skin/classic/browser/Go-arrow.png
@ -88,12 +87,14 @@ browser.jar:
skin/classic/browser/devtools/common.css (devtools/common.css)
skin/classic/browser/devtools/arrows.png (devtools/arrows.png)
skin/classic/browser/devtools/commandline.png (devtools/commandline.png)
skin/classic/browser/devtools/alerticon-warning.png (devtools/alerticon-warning.png)
skin/classic/browser/devtools/goto-mdn.png (devtools/goto-mdn.png)
skin/classic/browser/devtools/csshtmltree.css (devtools/csshtmltree.css)
skin/classic/browser/devtools/webconsole.css (devtools/webconsole.css)
skin/classic/browser/devtools/webconsole_networkpanel.css (devtools/webconsole_networkpanel.css)
skin/classic/browser/devtools/webconsole.png (devtools/webconsole.png)
skin/classic/browser/devtools/gcli.css (devtools/gcli.css)
skin/classic/browser/devtools/htmlpanel.css (devtools/htmlpanel.css)
skin/classic/browser/devtools/orion.css (devtools/orion.css)
skin/classic/browser/devtools/orion-container.css (devtools/orion-container.css)
skin/classic/browser/devtools/orion-task.png (devtools/orion-task.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 613 B

View File

@ -219,6 +219,19 @@
padding: 2px 5px;
}
.ruleview-warning {
background: url("chrome://browser/skin/devtools/alerticon-warning.png");
display: inline-block;
-moz-margin-start: 5px;
vertical-align: middle;
width: 13px;
height: 12px;
}
.ruleview-warning[hidden] {
display: none;
}
.ruleview-ruleopen {
-moz-padding-end: 5px;
}

View File

@ -120,8 +120,13 @@ a {
background-color: white;
}
.dbg-stackframe-id {
-moz-padding-end: 1em;
.dbg-stackframe {
-moz-padding-start: 4px;
-moz-padding-end: 4px;
}
.dbg-stackframe-name {
font-weight: 600;
}
/**

View File

@ -58,7 +58,6 @@ browser.jar:
skin/classic/browser/newtab/strip.png (newtab/strip.png)
skin/classic/browser/newtab/toolbar.png (newtab/toolbar.png)
skin/classic/browser/setDesktopBackground.css
skin/classic/browser/inspector.css
skin/classic/browser/monitor.png
skin/classic/browser/monitor_16-10.png
skin/classic/browser/places/allBookmarks.png (places/allBookmarks.png)
@ -127,9 +126,11 @@ browser.jar:
* skin/classic/browser/devtools/common.css (devtools/common.css)
skin/classic/browser/devtools/arrows.png (devtools/arrows.png)
skin/classic/browser/devtools/commandline.png (devtools/commandline.png)
skin/classic/browser/devtools/alerticon-warning.png (devtools/alerticon-warning.png)
skin/classic/browser/devtools/goto-mdn.png (devtools/goto-mdn.png)
skin/classic/browser/devtools/csshtmltree.css (devtools/csshtmltree.css)
skin/classic/browser/devtools/gcli.css (devtools/gcli.css)
skin/classic/browser/devtools/htmlpanel.css (devtools/htmlpanel.css)
skin/classic/browser/devtools/orion.css (devtools/orion.css)
skin/classic/browser/devtools/orion-container.css (devtools/orion-container.css)
skin/classic/browser/devtools/orion-task.png (devtools/orion-task.png)
@ -165,10 +166,10 @@ browser.jar:
skin/classic/browser/devtools/splitview.css (devtools/splitview.css)
skin/classic/browser/devtools/styleeditor.css (devtools/styleeditor.css)
skin/classic/browser/devtools/debugger.css (devtools/debugger.css)
skin/classic/browser/devtools/magnifying-glass.png (devtools/magnifying-glass.png)
skin/classic/browser/devtools/itemToggle.png (devtools/itemToggle.png)
skin/classic/browser/devtools/itemArrow-rtl.png (devtools/itemArrow-rtl.png)
skin/classic/browser/devtools/itemArrow-ltr.png (devtools/itemArrow-ltr.png)
skin/classic/browser/devtools/magnifying-glass.png (devtools/magnifying-glass.png)
skin/classic/browser/devtools/itemToggle.png (devtools/itemToggle.png)
skin/classic/browser/devtools/itemArrow-rtl.png (devtools/itemArrow-rtl.png)
skin/classic/browser/devtools/itemArrow-ltr.png (devtools/itemArrow-ltr.png)
skin/classic/browser/devtools/background-noise-toolbar.png (devtools/background-noise-toolbar.png)
#ifdef MOZ_SERVICES_SYNC
skin/classic/browser/sync-throbber.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 613 B

View File

@ -217,6 +217,19 @@
padding: 2px 5px;
}
.ruleview-warning {
background: url("chrome://browser/skin/devtools/alerticon-warning.png");
display: inline-block;
-moz-margin-start: 5px;
vertical-align: middle;
width: 13px;
height: 12px;
}
.ruleview-warning[hidden] {
display: none;
}
.ruleview-ruleopen {
-moz-padding-end: 5px;
}

View File

@ -116,8 +116,13 @@ a {
background-color: white;
}
.dbg-stackframe-id {
-moz-padding-end: 1em;
.dbg-stackframe {
-moz-padding-start: 4px;
-moz-padding-end: 4px;
}
.dbg-stackframe-name {
font-weight: 600;
}
/**

View File

@ -60,7 +60,6 @@ browser.jar:
skin/classic/browser/newtab/newTab.css (newtab/newTab.css)
skin/classic/browser/newtab/strip.png (newtab/strip.png)
skin/classic/browser/newtab/toolbar.png (newtab/toolbar.png)
skin/classic/browser/inspector.css
skin/classic/browser/places/places.css (places/places.css)
* skin/classic/browser/places/organizer.css (places/organizer.css)
skin/classic/browser/places/bookmark.png (places/bookmark.png)
@ -112,9 +111,11 @@ browser.jar:
skin/classic/browser/devtools/common.css (devtools/common.css)
skin/classic/browser/devtools/arrows.png (devtools/arrows.png)
skin/classic/browser/devtools/commandline.png (devtools/commandline.png)
skin/classic/browser/devtools/alerticon-warning.png (devtools/alerticon-warning.png)
skin/classic/browser/devtools/goto-mdn.png (devtools/goto-mdn.png)
skin/classic/browser/devtools/csshtmltree.css (devtools/csshtmltree.css)
skin/classic/browser/devtools/gcli.css (devtools/gcli.css)
skin/classic/browser/devtools/htmlpanel.css (devtools/htmlpanel.css)
skin/classic/browser/devtools/orion.css (devtools/orion.css)
skin/classic/browser/devtools/orion-container.css (devtools/orion-container.css)
skin/classic/browser/devtools/orion-task.png (devtools/orion-task.png)
@ -229,7 +230,6 @@ browser.jar:
skin/classic/aero/browser/newtab/newTab.css (newtab/newTab.css)
skin/classic/aero/browser/newtab/strip.png (newtab/strip.png)
skin/classic/aero/browser/newtab/toolbar.png (newtab/toolbar.png)
skin/classic/aero/browser/inspector.css
* skin/classic/aero/browser/places/places.css (places/places-aero.css)
* skin/classic/aero/browser/places/organizer.css (places/organizer-aero.css)
skin/classic/aero/browser/places/bookmark.png (places/bookmark.png)
@ -281,9 +281,11 @@ browser.jar:
skin/classic/aero/browser/devtools/common.css (devtools/common.css)
skin/classic/aero/browser/devtools/arrows.png (devtools/arrows.png)
skin/classic/aero/browser/devtools/commandline.png (devtools/commandline.png)
skin/classic/aero/browser/devtools/alerticon-warning.png (devtools/alerticon-warning.png)
skin/classic/aero/browser/devtools/goto-mdn.png (devtools/goto-mdn.png)
skin/classic/aero/browser/devtools/csshtmltree.css (devtools/csshtmltree.css)
skin/classic/aero/browser/devtools/gcli.css (devtools/gcli.css)
skin/classic/aero/browser/devtools/htmlpanel.css (devtools/htmlpanel.css)
skin/classic/aero/browser/devtools/orion.css (devtools/orion.css)
skin/classic/aero/browser/devtools/orion-container.css (devtools/orion-container.css)
skin/classic/aero/browser/devtools/orion-task.png (devtools/orion-task.png)