This commit is contained in:
Tim Taubert 2012-04-05 11:44:09 +02:00
Родитель af8d505179 03e1318874
Коммит 9e6e319f9c
13 изменённых файлов: 539 добавлений и 460 удалений

Просмотреть файл

@ -492,7 +492,8 @@ DebuggerUI.prototype = {
_onLoadSource: function DebuggerUI__onLoadSource(aEvent) {
let gBrowser = this.aWindow.gBrowser;
let url = aEvent.detail;
let url = aEvent.detail.url;
let showOptions = aEvent.detail.options;
let scheme = Services.io.extractScheme(url);
switch (scheme) {
case "file":
@ -505,7 +506,7 @@ DebuggerUI.prototype = {
}
let source = NetUtil.readInputStreamToString(aStream, aStream.available());
aStream.close();
this._onSourceLoaded(url, source);
this._onSourceLoaded(url, source, showOptions);
}.bind(this));
} catch (ex) {
return this.logError(url, ex.name);
@ -529,7 +530,8 @@ DebuggerUI.prototype = {
return this.logError(url, aStatusCode);
}
this._onSourceLoaded(url, chunks.join(""), channel.contentType);
this._onSourceLoaded(url, chunks.join(""), channel.contentType,
showOptions);
}.bind(this)
};
@ -555,20 +557,23 @@ DebuggerUI.prototype = {
/**
* Called when source has been loaded.
*
* @private
* @param string aSourceUrl
* The URL of the source script.
* @param string aSourceText
* The text of the source script.
* @param string aContentType
* The content type of the source script.
* @param object [aOptions]
* Additional options for showing the script (optional). Supported
* options:
* - targetLine: place the editor at the given line number.
*/
_onSourceLoaded: function DebuggerUI__onSourceLoaded(aSourceUrl,
aSourceText,
aContentType) {
aContentType,
aOptions) {
let dbg = this.getDebugger(this.aWindow.gBrowser.selectedTab);
dbg.debuggerWindow.SourceScripts.setEditorMode(aSourceUrl, aContentType);
dbg.editor.setText(aSourceText);
dbg.editor.resetUndo();
let doc = dbg.frame.contentDocument;
let scripts = doc.getElementById("scripts");
let elt = scripts.getElementsByAttribute("value", aSourceUrl)[0];
@ -577,8 +582,8 @@ DebuggerUI.prototype = {
script.text = aSourceText;
script.contentType = aContentType;
elt.setUserData("sourceScript", script, null);
dbg._updateEditorBreakpoints();
dbg.debuggerWindow.StackFrames.updateEditor();
dbg.debuggerWindow.SourceScripts._onShowScript(script, aOptions);
}
};

Просмотреть файл

@ -272,13 +272,13 @@ var StackFrames = {
if (!frame) {
return;
}
// Move the editor's caret to the proper line.
if (DebuggerView.Scripts.isSelected(frame.where.url) && frame.where.line) {
window.editor.setCaretPosition(frame.where.line - 1);
window.editor.setDebugLocation(frame.where.line - 1);
} else if (DebuggerView.Scripts.contains(frame.where.url)) {
DebuggerView.Scripts.selectScript(frame.where.url);
SourceScripts.onChange({ target: DebuggerView.Scripts._scripts });
window.editor.setCaretPosition(frame.where.line - 1);
} else {
window.editor.setDebugLocation(-1);
@ -523,8 +523,7 @@ var SourceScripts = {
return;
}
let script = scripts.selectedItem.getUserData("sourceScript");
this.setEditorMode(script.url, script.contentType);
this._showScript(script);
this.showScript(script);
},
/**
@ -628,28 +627,64 @@ var SourceScripts = {
DebuggerView.Scripts.addScript(this._getScriptLabel(aScript.url), aScript);
if (window.editor.getCharCount() == 0) {
this._showScript(aScript);
this.showScript(aScript);
}
},
/**
* Load the editor with the script text if available, otherwise fire an event
* to load and display the script text.
*
* @param object aScript
* The script object coming from the active thread.
* @param object [aOptions]
* Additional options for showing the script (optional). Supported
* options:
* - targetLine: place the editor at the given line number.
*/
_showScript: function SS_showScript(aScript) {
showScript: function SS_showScript(aScript, aOptions) {
if (!aScript.loaded) {
// Notify the chrome code that we need to load a script file.
var evt = document.createEvent("CustomEvent");
evt.initCustomEvent("Debugger:LoadSource", true, false, aScript.url);
evt.initCustomEvent("Debugger:LoadSource", true, false,
{url: aScript.url, options: aOptions});
document.documentElement.dispatchEvent(evt);
window.editor.setMode(SourceEditor.MODES.TEXT);
window.editor.setText(DebuggerView.getStr("loadingText"));
window.editor.resetUndo();
} else {
window.editor.setText(aScript.text);
window.updateEditorBreakpoints();
StackFrames.updateEditor();
this._onShowScript(aScript, aOptions);
}
},
/**
* Display the script source once it loads.
*
* @private
* @param object aScript
* The script object coming from the active thread.
* @param object [aOptions]
* Additional options for showing the script (optional). Supported
* options:
* - targetLine: place the editor at the given line number.
*/
_onShowScript: function SS__onShowScript(aScript, aOptions) {
aOptions = aOptions || {};
this.setEditorMode(aScript.url, aScript.contentType);
window.editor.setText(aScript.text);
window.updateEditorBreakpoints();
StackFrames.updateEditor();
if (aOptions.targetLine) {
window.editor.setCaretPosition(aOptions.targetLine - 1);
}
window.editor.resetUndo();
}
// Notify the chrome code that we shown script file.
let evt = document.createEvent("CustomEvent");
evt.initCustomEvent("Debugger:ScriptShown", true, false,
{url: aScript.url});
document.documentElement.dispatchEvent(evt);
},
};
SourceScripts.onScripts = SourceScripts.onScripts.bind(SourceScripts);

Просмотреть файл

@ -21,20 +21,39 @@ function test()
let tempScope = {};
Cu.import("resource:///modules/source-editor.jsm", tempScope);
let SourceEditor = tempScope.SourceEditor;
let scriptShown = false;
let framesAdded = false;
debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
gTab = aTab;
gDebuggee = aDebuggee;
gPane = aPane;
gDebugger = gPane.debuggerWindow;
gPane.activeThread.addOneTimeListener("framesadded", function() {
Services.tm.currentThread.dispatch({ run: onScriptsAdded }, 0);
framesAdded = true;
runTest();
});
gDebuggee.firstCall();
});
function onScriptsAdded()
window.addEventListener("Debugger:ScriptShown", function _onEvent(aEvent) {
let url = aEvent.detail.url;
if (url.indexOf("-02.js") != -1) {
scriptShown = true;
window.removeEventListener(aEvent.type, _onEvent);
runTest();
}
});
function runTest()
{
if (scriptShown && framesAdded) {
Services.tm.currentThread.dispatch({ run: onScriptShown }, 0);
}
}
function onScriptShown()
{
gScripts = gDebugger.DebuggerView.Scripts;
@ -48,7 +67,7 @@ function test()
isnot(gEditor.getText().indexOf("debugger"), -1,
"The correct script was loaded initially.");
isnot(gScripts.selected, gScripts.scriptLocations()[0],
"the correct sccript is selected");
"the correct script is selected");
gBreakpoints = gPane.breakpoints;
is(Object.keys(gBreakpoints), 0, "no breakpoints");
@ -56,8 +75,6 @@ function test()
is(gEditor.getBreakpoints().length, 0, "no breakpoints in the editor");
info("add the first breakpoint");
gEditor.addEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE,
onEditorBreakpointAddFirst);
let location = {url: gScripts.selected, line: 6};

Просмотреть файл

@ -19,6 +19,8 @@ function test()
let SourceEditor = tempScope.SourceEditor;
let contextMenu = null;
let scriptShown = false;
let framesAdded = false;
debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
gTab = aTab;
@ -27,12 +29,29 @@ function test()
gDebugger = gPane.debuggerWindow;
gPane.activeThread.addOneTimeListener("framesadded", function() {
Services.tm.currentThread.dispatch({ run: onScriptsAdded }, 0);
framesAdded = true;
runTest();
});
gDebuggee.firstCall();
});
function onScriptsAdded()
window.addEventListener("Debugger:ScriptShown", function _onEvent(aEvent) {
let url = aEvent.detail.url;
if (url.indexOf("-02.js") != -1) {
scriptShown = true;
window.removeEventListener(aEvent.type, _onEvent);
runTest();
}
});
function runTest()
{
if (scriptShown && framesAdded) {
Services.tm.currentThread.dispatch({ run: onScriptShown }, 0);
}
}
function onScriptShown()
{
let scripts = gDebugger.DebuggerView.Scripts._scripts;

Просмотреть файл

@ -25,15 +25,27 @@ function test()
function testFrameParameters()
{
dump("Started testFrameParameters!\n");
gDebugger.addEventListener("Debugger:FetchedParameters", function test() {
dump("Entered Debugger:FetchedParameters!\n");
gDebugger.removeEventListener("Debugger:FetchedParameters", test, false);
Services.tm.currentThread.dispatch({ run: function() {
dump("After currentThread.dispatch!\n");
var frames = gDebugger.DebuggerView.Stackframes._frames,
childNodes = frames.childNodes,
localScope = gDebugger.DebuggerView.Properties.localScope,
localNodes = localScope.querySelector(".details").childNodes;
dump("Got our variables:\n");
dump("frames - " + frames.constructor + "\n");
dump("childNodes - " + childNodes.constructor + "\n");
dump("localScope - " + localScope.constructor + "\n");
dump("localNodes - " + localNodes.constructor + "\n");
is(gDebugger.StackFrames.activeThread.state, "paused",
"Should only be getting stack frames while paused.");

Просмотреть файл

@ -25,14 +25,25 @@ function test()
function testFrameParameters()
{
dump("Started testFrameParameters!\n");
gDebugger.addEventListener("Debugger:FetchedParameters", function test() {
dump("Entered Debugger:FetchedParameters!\n");
gDebugger.removeEventListener("Debugger:FetchedParameters", test, false);
Services.tm.currentThread.dispatch({ run: function() {
dump("After currentThread.dispatch!\n");
var frames = gDebugger.DebuggerView.Stackframes._frames,
localScope = gDebugger.DebuggerView.Properties.localScope,
localNodes = localScope.querySelector(".details").childNodes;
dump("Got our variables:\n");
dump("frames - " + frames.constructor + "\n");
dump("localScope - " + localScope.constructor + "\n");
dump("localNodes - " + localNodes.constructor + "\n");
is(gDebugger.StackFrames.activeThread.state, "paused",
"Should only be getting stack frames while paused.");
@ -87,9 +98,9 @@ function testFrameParameters()
}}, 0);
}, false);
EventUtils.synthesizeMouseAtCenter(content.document.querySelector("button"),
{},
content.window);
EventUtils.sendMouseEvent({ type: "click" },
content.document.querySelector("button"),
content.window);
}
function resumeAndFinish() {

Просмотреть файл

@ -15,72 +15,91 @@ var gDebuggee = null;
var gDebugger = null;
function test() {
let scriptShown = false;
let framesAdded = false;
debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
gTab = aTab;
gDebuggee = aDebuggee;
gPane = aPane;
gDebugger = gPane.debuggerWindow;
testRecurse();
gPane.activeThread.addOneTimeListener("framesadded", function() {
framesAdded = true;
runTest();
});
gDebuggee.firstCall();
});
window.addEventListener("Debugger:ScriptShown", function _onEvent(aEvent) {
let url = aEvent.detail.url;
if (url.indexOf("-02.js") != -1) {
scriptShown = true;
window.removeEventListener(aEvent.type, _onEvent);
runTest();
}
});
function runTest()
{
if (scriptShown && framesAdded) {
Services.tm.currentThread.dispatch({ run: testRecurse }, 0);
}
}
}
function testRecurse() {
gPane.activeThread.addOneTimeListener("framesadded", function() {
Services.tm.currentThread.dispatch({ run: function() {
let frames = gDebugger.DebuggerView.Stackframes._frames;
let childNodes = frames.childNodes;
function testRecurse()
{
let frames = gDebugger.DebuggerView.Stackframes._frames;
let childNodes = frames.childNodes;
is(frames.querySelectorAll(".dbg-stackframe").length, 4,
"Correct number of frames.");
is(frames.querySelectorAll(".dbg-stackframe").length, 4,
"Correct number of frames.");
is(childNodes.length, frames.querySelectorAll(".dbg-stackframe").length,
"All children should be frames.");
is(childNodes.length, frames.querySelectorAll(".dbg-stackframe").length,
"All children should be frames.");
ok(frames.querySelector("#stackframe-0").classList.contains("selected"),
"First frame should be selected by default.");
ok(frames.querySelector("#stackframe-0").classList.contains("selected"),
"First frame should be selected by default.");
ok(!frames.querySelector("#stackframe-2").classList.contains("selected"),
"Third frame should not be selected.");
ok(!frames.querySelector("#stackframe-2").classList.contains("selected"),
"Third frame should not be selected.");
is(gDebugger.editor.getDebugLocation(), 5,
"editor debugger location is correct.");
is(gDebugger.editor.getDebugLocation(), 5,
"editor debugger location is correct.");
EventUtils.sendMouseEvent({ type: "click" },
frames.querySelector("#stackframe-2"),
gDebugger);
EventUtils.sendMouseEvent({ type: "click" },
frames.querySelector("#stackframe-2"),
gDebugger);
ok(!frames.querySelector("#stackframe-0").classList.contains("selected"),
"First frame should not be selected after click.");
ok(!frames.querySelector("#stackframe-0").classList.contains("selected"),
"First frame should not be selected after click.");
ok(frames.querySelector("#stackframe-2").classList.contains("selected"),
"Third frame should be selected after click.");
ok(frames.querySelector("#stackframe-2").classList.contains("selected"),
"Third frame should be selected after click.");
is(gDebugger.editor.getDebugLocation(), 4,
"editor debugger location is correct after click.");
is(gDebugger.editor.getDebugLocation(), 4,
"editor debugger location is correct after click.");
EventUtils.sendMouseEvent({ type: "click" },
frames.querySelector("#stackframe-0 .dbg-stackframe-name"),
gDebugger);
EventUtils.sendMouseEvent({ type: "click" },
frames.querySelector("#stackframe-0 .dbg-stackframe-name"),
gDebugger);
ok(frames.querySelector("#stackframe-0").classList.contains("selected"),
"First frame should be selected after click inside the first frame.");
ok(frames.querySelector("#stackframe-0").classList.contains("selected"),
"First frame should be selected after click inside the first frame.");
ok(!frames.querySelector("#stackframe-2").classList.contains("selected"),
"Third frame should not be selected after click inside the first frame.");
ok(!frames.querySelector("#stackframe-2").classList.contains("selected"),
"Third frame should not be selected after click inside the first frame.");
is(gDebugger.editor.getDebugLocation(), 5,
"editor debugger location is correct (frame 0 again).");
is(gDebugger.editor.getDebugLocation(), 5,
"editor debugger location is correct (frame 0 again).");
gDebugger.StackFrames.activeThread.resume(function() {
is(gDebugger.editor.getDebugLocation(), -1,
"editor debugger location is correct after resume.");
closeDebuggerAndFinish(gTab);
});
}}, 0);
gDebugger.StackFrames.activeThread.resume(function() {
is(gDebugger.editor.getDebugLocation(), -1,
"editor debugger location is correct after resume.");
closeDebuggerAndFinish(gTab);
});
gDebuggee.firstCall();
}
registerCleanupFunction(function() {

Просмотреть файл

@ -939,27 +939,6 @@ InspectorUI.prototype = {
this.cssRuleViewBoundCSSLinkClicked = this.ruleViewCSSLinkClicked.bind(this);
this.ruleView.element.addEventListener("CssRuleViewCSSLinkClicked",
this.cssRuleViewBoundCSSLinkClicked);
this.cssRuleViewBoundMouseDown = this.ruleViewMouseDown.bind(this);
this.ruleView.element.addEventListener("mousedown",
this.cssRuleViewBoundMouseDown);
this.cssRuleViewBoundMouseUp = this.ruleViewMouseUp.bind(this);
this.ruleView.element.addEventListener("mouseup",
this.cssRuleViewBoundMouseUp);
this.cssRuleViewBoundMouseMove = this.ruleViewMouseMove.bind(this);
this.cssRuleViewBoundMenuUpdate = this.ruleViewMenuUpdate.bind(this);
this.cssRuleViewBoundCopy = this.ruleViewCopy.bind(this);
iframe.addEventListener("copy", this.cssRuleViewBoundCopy);
this.cssRuleViewBoundCopyRule = this.ruleViewCopyRule.bind(this);
this.cssRuleViewBoundCopyDeclaration =
this.ruleViewCopyDeclaration.bind(this);
this.cssRuleViewBoundCopyProperty = this.ruleViewCopyProperty.bind(this);
this.cssRuleViewBoundCopyPropertyValue =
this.ruleViewCopyPropertyValue.bind(this);
// Add the rule view's context menu.
this.ruleViewAddContextMenu();
doc.documentElement.appendChild(this.ruleView.element);
this.ruleView.highlight(this.selection);
@ -1050,364 +1029,24 @@ InspectorUI.prototype = {
}
},
/**
* This is the mousedown handler for the rule view. We use it to track whether
* text is currently getting selected.
* .
* @param aEvent The event object
*/
ruleViewMouseDown: function IUI_ruleViewMouseDown(aEvent)
{
this.ruleView.element.addEventListener("mousemove",
this.cssRuleViewBoundMouseMove);
},
/**
* This is the mouseup handler for the rule view. We use it to track whether
* text is currently getting selected.
* .
* @param aEvent The event object
*/
ruleViewMouseUp: function IUI_ruleViewMouseUp(aEvent)
{
this.ruleView.element.removeEventListener("mousemove",
this.cssRuleViewBoundMouseMove);
this.ruleView._selectionMode = false;
},
/**
* This is the mousemove handler for the rule view. We use it to track whether
* text is currently getting selected.
* .
* @param aEvent The event object
*/
ruleViewMouseMove: function IUI_ruleViewMouseMove(aEvent)
{
this.ruleView._selectionMode = true;
},
/**
* Add a context menu to the rule view.
*/
ruleViewAddContextMenu: function IUI_ruleViewAddContextMenu()
{
let iframe = this.getToolIframe(this.ruleViewObject);
let popupSet = this.chromeDoc.getElementById("mainPopupSet");
let menu = this.chromeDoc.createElement("menupopup");
menu.addEventListener("popupshowing", this.cssRuleViewBoundMenuUpdate);
menu.id = "rule-view-context-menu";
// Copy selection
let label = styleInspectorStrings
.GetStringFromName("rule.contextmenu.copyselection");
let accessKey = styleInspectorStrings
.GetStringFromName("rule.contextmenu.copyselection.accesskey");
let item = this.chromeDoc.createElement("menuitem");
item.id = "rule-view-copy";
item.setAttribute("label", label);
item.setAttribute("accesskey", accessKey);
item.addEventListener("command", this.cssRuleViewBoundCopy);
menu.appendChild(item);
// Copy rule
label = styleInspectorStrings.
GetStringFromName("rule.contextmenu.copyrule");
accessKey = styleInspectorStrings.
GetStringFromName("rule.contextmenu.copyrule.accesskey");
item = this.chromeDoc.createElement("menuitem");
item.id = "rule-view-copy-rule";
item.setAttribute("label", label);
item.setAttribute("accesskey", accessKey);
item.addEventListener("command", this.cssRuleViewBoundCopyRule);
menu.appendChild(item);
// Copy declaration
label = styleInspectorStrings.
GetStringFromName("rule.contextmenu.copydeclaration");
accessKey = styleInspectorStrings.
GetStringFromName("rule.contextmenu.copydeclaration.accesskey");
item = this.chromeDoc.createElement("menuitem");
item.id = "rule-view-copy-declaration";
item.setAttribute("label", label);
item.setAttribute("accesskey", accessKey);
item.addEventListener("command", this.cssRuleViewBoundCopyDeclaration);
menu.appendChild(item);
// Copy property name
label = styleInspectorStrings.
GetStringFromName("rule.contextmenu.copyproperty");
accessKey = styleInspectorStrings.
GetStringFromName("rule.contextmenu.copyproperty.accesskey");
item = this.chromeDoc.createElement("menuitem");
item.id = "rule-view-copy-property";
item.setAttribute("label", label);
item.setAttribute("accesskey", accessKey);
item.addEventListener("command", this.cssRuleViewBoundCopyProperty);
menu.appendChild(item);
// Copy property value
label = styleInspectorStrings.
GetStringFromName("rule.contextmenu.copypropertyvalue");
accessKey = styleInspectorStrings.
GetStringFromName("rule.contextmenu.copypropertyvalue.accesskey");
item = this.chromeDoc.createElement("menuitem");
item.id = "rule-view-copy-property-value";
item.setAttribute("label", label);
item.setAttribute("accesskey", accessKey);
item.addEventListener("command", this.cssRuleViewBoundCopyPropertyValue);
menu.appendChild(item);
popupSet.appendChild(menu);
iframe.setAttribute("context", menu.id);
},
/**
* Update the rule view's context menu by disabling irrelevant menuitems and
* enabling relevant ones.
*
* @param aEvent The event object
*/
ruleViewMenuUpdate: function IUI_ruleViewMenuUpdate(aEvent)
{
let iframe = this.getToolIframe(this.ruleViewObject);
let win = iframe.contentWindow;
// Copy selection.
let disable = win.getSelection().isCollapsed;
let menuitem = this.chromeDoc.getElementById("rule-view-copy");
menuitem.disabled = disable;
// Copy property, copy property name & copy property value.
let node = this.chromeDoc.popupNode;
if (!node.classList.contains("ruleview-property") &&
!node.classList.contains("ruleview-computed")) {
while (node = node.parentElement) {
if (node.classList.contains("ruleview-property") ||
node.classList.contains("ruleview-computed")) {
break;
}
}
}
let disablePropertyItems = !node || (node &&
!node.classList.contains("ruleview-property") &&
!node.classList.contains("ruleview-computed"));
menuitem = this.chromeDoc.querySelector("#rule-view-copy-declaration");
menuitem.disabled = disablePropertyItems;
menuitem = this.chromeDoc.querySelector("#rule-view-copy-property");
menuitem.disabled = disablePropertyItems;
menuitem = this.chromeDoc.querySelector("#rule-view-copy-property-value");
menuitem.disabled = disablePropertyItems;
},
/**
* Copy selected text from the rule view.
*
* @param aEvent The event object
*/
ruleViewCopy: function IUI_ruleViewCopy(aEvent)
{
let iframe = this.getToolIframe(this.ruleViewObject);
let win = iframe.contentWindow;
let text = win.getSelection().toString();
// Remove any double newlines.
text = text.replace(/(\r?\n)\r?\n/g, "$1");
// Remove "inline"
let inline = styleInspectorStrings.GetStringFromName("rule.sourceInline");
let rx = new RegExp("^" + inline + "\\r?\\n?", "g");
text = text.replace(rx, "");
// Remove file:line
text = text.replace(/[\w\.]+:\d+(\r?\n)/g, "$1");
// Remove inherited from: line
let inheritedFrom = styleInspectorStrings
.GetStringFromName("rule.inheritedSource");
inheritedFrom = inheritedFrom.replace(/\s%S\s\(%S\)/g, "");
rx = new RegExp("(\r?\n)" + inheritedFrom + ".*", "g");
text = text.replace(rx, "$1");
clipboardHelper.copyString(text);
if (aEvent) {
aEvent.preventDefault();
}
},
/**
* Copy a rule from the rule view.
*
* @param aEvent The event object
*/
ruleViewCopyRule: function IUI_ruleViewCopyRule(aEvent)
{
let node = this.chromeDoc.popupNode;
if (node.className != "ruleview-code") {
if (node.className == "ruleview-rule-source") {
node = node.nextElementSibling;
} else {
while (node = node.parentElement) {
if (node.className == "ruleview-code") {
break;
}
}
}
}
if (node.className == "ruleview-code") {
// We need to strip expanded properties from the node because we use
// node.textContent below, which also gets text from hidden nodes. The
// simplest way to do this is to clone the node and remove them from the
// clone.
node = node.cloneNode();
let computed = node.querySelector(".ruleview-computedlist");
if (computed) {
computed.parentNode.removeChild(computed);
}
let autosizer = node.querySelector(".autosizer");
if (autosizer) {
autosizer.parentNode.removeChild(autosizer);
}
}
let text = node.textContent;
// Format the rule
if (osString == "WINNT") {
text = text.replace(/{/g, "{\r\n ");
text = text.replace(/;/g, ";\r\n ");
text = text.replace(/\s*}/g, "\r\n}");
} else {
text = text.replace(/{/g, "{\n ");
text = text.replace(/;/g, ";\n ");
text = text.replace(/\s*}/g, "\n}");
}
clipboardHelper.copyString(text);
},
/**
* Copy a declaration from the rule view.
*
* @param aEvent The event object
*/
ruleViewCopyDeclaration: function IUI_ruleViewCopyDeclaration(aEvent)
{
let node = this.chromeDoc.popupNode;
if (!node.classList.contains("ruleview-property") &&
!node.classList.contains("ruleview-computed")) {
while (node = node.parentElement) {
if (node.classList.contains("ruleview-property") ||
node.classList.contains("ruleview-computed")) {
break;
}
}
}
// We need to strip expanded properties from the node because we use
// node.textContent below, which also gets text from hidden nodes. The
// simplest way to do this is to clone the node and remove them from the
// clone.
node = node.cloneNode();
let computed = node.querySelector(".ruleview-computedlist");
if (computed) {
computed.parentNode.removeChild(computed);
}
clipboardHelper.copyString(node.textContent);
},
/**
* Copy a property name from the rule view.
*
* @param aEvent The event object
*/
ruleViewCopyProperty: function IUI_ruleViewCopyProperty(aEvent)
{
let node = this.chromeDoc.popupNode;
if (!node.classList.contains("ruleview-propertyname")) {
node = node.querySelector(".ruleview-propertyname");
}
if (node) {
clipboardHelper.copyString(node.textContent);
}
},
/**
* Copy a property value from the rule view.
*
* @param aEvent The event object
*/
ruleViewCopyPropertyValue: function IUI_ruleViewCopyPropertyValue(aEvent)
{
let node = this.chromeDoc.popupNode;
if (!node.classList.contains("ruleview-propertyvalue")) {
node = node.querySelector(".ruleview-propertyvalue");
}
if (node) {
clipboardHelper.copyString(node.textContent);
}
},
/**
* Destroy the rule view.
*/
destroyRuleView: function IUI_destroyRuleView()
{
let iframe = this.getToolIframe(this.ruleViewObject);
iframe.removeEventListener("copy", this.cssRuleViewBoundCopy);
iframe.parentNode.removeChild(iframe);
if (this.ruleView) {
let menu = this.chromeDoc.querySelector("#rule-view-context-menu");
if (menu) {
// Copy
let menuitem = this.chromeDoc.querySelector("#rule-view-copy");
menuitem.removeEventListener("command", this.cssRuleViewBoundCopy);
// Copy rule
menuitem = this.chromeDoc.querySelector("#rule-view-copy-rule");
menuitem.removeEventListener("command", this.cssRuleViewBoundCopyRule);
// Copy property
menuitem = this.chromeDoc.querySelector("#rule-view-copy-declaration");
menuitem.removeEventListener("command",
this.cssRuleViewBoundCopyDeclaration);
// Copy property name
menuitem = this.chromeDoc.querySelector("#rule-view-copy-property");
menuitem.removeEventListener("command",
this.cssRuleViewBoundCopyProperty);
// Copy property value
menuitem = this.chromeDoc.querySelector("#rule-view-copy-property-value");
menuitem.removeEventListener("command",
this.cssRuleViewBoundCopyPropertyValue);
menu.removeEventListener("popupshowing", this.cssRuleViewBoundMenuUpdate);
menu.parentNode.removeChild(menu);
}
this.ruleView.element.removeEventListener("CssRuleViewChanged",
this.boundRuleViewChanged);
this.ruleView.element.removeEventListener("CssRuleViewCSSLinkClicked",
this.cssRuleViewBoundCSSLinkClicked);
this.ruleView.element.removeEventListener("mousedown",
this.cssRuleViewBoundMouseDown);
this.ruleView.element.removeEventListener("mouseup",
this.cssRuleViewBoundMouseUp);
this.ruleView.element.removeEventListener("mousemove",
this.cssRuleViewBoundMouseMove);
delete boundRuleViewChanged;
this.ruleView.clear();
delete this.boundRuleViewChanged;
delete this.cssRuleViewBoundCSSLinkClicked;
this.ruleView.destroy();
delete this.ruleView;
}
let iframe = this.getToolIframe(this.ruleViewObject);
iframe.parentNode.removeChild(iframe);
},
/////////////////////////////////////////////////////////////////////////
@ -2631,12 +2270,3 @@ XPCOMUtils.defineLazyGetter(this, "clipboardHelper", function() {
return Cc["@mozilla.org/widget/clipboardhelper;1"].
getService(Ci.nsIClipboardHelper);
});
XPCOMUtils.defineLazyGetter(this, "styleInspectorStrings", function() {
return Services.strings.createBundle(
"chrome://browser/locale/devtools/styleinspector.properties");
});
XPCOMUtils.defineLazyGetter(this, "osString", function() {
return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
});

Просмотреть файл

@ -90,7 +90,9 @@ function inspectorFocusTab1()
is(InspectorUI.selection, div, "selection matches the div element");
ok(InspectorUI.isSidebarOpen, "sidebar is open");
ok(InspectorUI.isRuleViewOpen(), "rule view is open");
is(InspectorUI.ruleView.doc.documentElement.children.length, 1, "RuleView elements.length == 1");
// The rule view element plus its popupSet
is(InspectorUI.ruleView.doc.documentElement.children.length, 2, "RuleView elements.length == 2");
requestLongerTimeout(4);
executeSoon(function() {

Просмотреть файл

@ -40,6 +40,7 @@ function test()
testNew();
testSavedFile();
gBrowser.selectedTab = gBrowser.addTab();
content.location = "data:text/html,<p>test scratchpad save file prompt on closing";
}

Просмотреть файл

@ -24,6 +24,7 @@ function test()
testRestoreFromFileSaved();
testRestoreFromFileUnsaved();
gBrowser.selectedTab = gBrowser.addTab();
content.location = "data:text/html,<p>test star* UI for unsaved file changes";
}

Просмотреть файл

@ -64,6 +64,7 @@ const CSS_PROP_RE = /\s*([^:\s]*)\s*:\s*(.*?)\s*(?:! (important))?;?$/;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource:///modules/devtools/CssLogic.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
var EXPORTED_SYMBOLS = ["CssRuleView",
"_ElementStyle",
@ -699,12 +700,55 @@ function CssRuleView(aDoc, aStore)
this.element.setAttribute("tabindex", "0");
this.element.classList.add("ruleview");
this.element.flex = 1;
this._selectionMode = false;
this._boundMouseDown = this._onMouseDown.bind(this);
this.element.addEventListener("mousedown",
this._boundMouseDown);
this._boundMouseUp = this._onMouseUp.bind(this);
this.element.addEventListener("mouseup",
this._boundMouseUp);
this._boundMouseMove = this._onMouseMove.bind(this);
this._boundCopy = this._onCopy.bind(this);
this.element.addEventListener("copy", this._boundCopy);
this._createContextMenu();
}
CssRuleView.prototype = {
// The element that we're inspecting.
_viewedElement: null,
destroy: function CssRuleView_destroy()
{
this.clear();
this.element.removeEventListener("copy", this._boundCopy);
this._copyItem.removeEventListener("command", this._boundCopy);
delete this._boundCopy;
this._ruleItem.removeEventListener("command", this._boundCopyRule);
delete this._boundCopyRule;
this._declarationItem.removeEventListener("command", this._boundCopyDeclaration);
delete this._boundCopyDeclaration;
this._propertyItem.removeEventListener("command", this._boundCopyProperty);
delete this._boundCopyProperty;
this._propertyValueItem.removeEventListener("command", this._boundCopyPropertyValue);
delete this._boundCopyPropertyValue;
this._contextMenu.removeEventListener("popupshowing", this._boundMenuUpdate);
delete this._boundMenuUpdate;
delete this._contextMenu;
if (this.element.parentNode) {
this.element.parentNode.removeChild(this.element);
}
},
/**
* Update the highlighted element.
*
@ -813,6 +857,262 @@ CssRuleView.prototype = {
this.element.appendChild(editor.element);
}
},
/**
* Add a context menu to the rule view.
*/
_createContextMenu: function CssRuleView_createContextMenu()
{
let popupSet = this.doc.createElement("popupset");
this.doc.documentElement.appendChild(popupSet);
let menu = this.doc.createElement("menupopup");
menu.id = "rule-view-context-menu";
this._boundMenuUpdate = this._onMenuUpdate.bind(this);
menu.addEventListener("popupshowing", this._boundMenuUpdate);
// Copy selection
this._copyItem = createMenuItem(menu, {
label: "rule.contextmenu.copyselection",
accesskey: "rule.contextmenu.copyselection.accesskey",
command: this._boundCopy
});
// Copy rule
this._boundCopyRule = this._onCopyRule.bind(this);
this._ruleItem = createMenuItem(menu, {
label: "rule.contextmenu.copyrule",
accesskey: "rule.contextmenu.copyrule.accesskey",
command: this._boundCopyRule
});
// Copy declaration
this._boundCopyDeclaration = this._onCopyDeclaration.bind(this);
this._declarationItem = createMenuItem(menu, {
label: "rule.contextmenu.copydeclaration",
accesskey: "rule.contextmenu.copydeclaration.accesskey",
command: this._boundCopyDeclaration
});
this._boundCopyProperty = this._onCopyProperty.bind(this);
this._propertyItem = createMenuItem(menu, {
label: "rule.contextmenu.copyproperty",
accesskey: "rule.contextmenu.copyproperty.accesskey",
command: this._boundCopyProperty
});
this._boundCopyPropertyValue = this._onCopyPropertyValue.bind(this);
this._propertyValueItem = createMenuItem(menu,{
label: "rule.contextmenu.copypropertyvalue",
accesskey: "rule.contextmenu.copypropertyvalue.accesskey",
command: this._boundCopyPropertyValue
});
popupSet.appendChild(menu);
this.element.setAttribute("context", menu.id);
this._contextMenu = menu;
},
/**
* Update the rule view's context menu by disabling irrelevant menuitems and
* enabling relevant ones.
*
* @param aEvent The event object
*/
_onMenuUpdate: function CssRuleView_onMenuUpdate(aEvent)
{
// Copy selection.
let disable = this.doc.defaultView.getSelection().isCollapsed;
this._copyItem.disabled = disable;
// Copy property, copy property name & copy property value.
let node = this.doc.popupNode;
if (!node.classList.contains("ruleview-property") &&
!node.classList.contains("ruleview-computed")) {
while (node = node.parentElement) {
if (node.classList.contains("ruleview-property") ||
node.classList.contains("ruleview-computed")) {
break;
}
}
}
let disablePropertyItems = !node || (node &&
!node.classList.contains("ruleview-property") &&
!node.classList.contains("ruleview-computed"));
this._declarationItem.disabled = disablePropertyItems;
this._propertyItem.disabled = disablePropertyItems;
this._propertyValueItem.disabled = disablePropertyItems;
dump("Done updating menu!\n");
},
_onMouseDown: function CssRuleView_onMouseDown()
{
this.element.addEventListener("mousemove", this._boundMouseMove);
},
_onMouseUp: function CssRuleView_onMouseUp()
{
this.element.removeEventListener("mousemove", this._boundMouseMove);
this._selectionMode = false;
},
_onMouseMove: function CssRuleView_onMouseMove()
{
this._selectionMode = true;
},
/**
* Copy selected text from the rule view.
*
* @param aEvent The event object
*/
_onCopy: function CssRuleView_onCopy(aEvent)
{
let win = this.doc.defaultView;
let text = win.getSelection().toString();
// Remove any double newlines.
text = text.replace(/(\r?\n)\r?\n/g, "$1");
// Remove "inline"
let inline = _strings.GetStringFromName("rule.sourceInline");
let rx = new RegExp("^" + inline + "\\r?\\n?", "g");
text = text.replace(rx, "");
// Remove file:line
text = text.replace(/[\w\.]+:\d+(\r?\n)/g, "$1");
// Remove inherited from: line
let inheritedFrom = _strings.
GetStringFromName("rule.inheritedSource");
inheritedFrom = inheritedFrom.replace(/\s%S\s\(%S\)/g, "");
rx = new RegExp("(\r?\n)" + inheritedFrom + ".*", "g");
text = text.replace(rx, "$1");
clipboardHelper.copyString(text);
if (aEvent) {
aEvent.preventDefault();
}
},
/**
* Copy a rule from the rule view.
*
* @param aEvent The event object
*/
_onCopyRule: function CssRuleView_onCopyRule(aEvent)
{
let node = this.doc.popupNode;
if (node.className != "ruleview-code") {
if (node.className == "ruleview-rule-source") {
node = node.nextElementSibling;
} else {
while (node = node.parentElement) {
if (node.className == "ruleview-code") {
break;
}
}
}
}
if (node.className == "ruleview-code") {
// We need to strip expanded properties from the node because we use
// node.textContent below, which also gets text from hidden nodes. The
// simplest way to do this is to clone the node and remove them from the
// clone.
node = node.cloneNode();
let computed = node.querySelector(".ruleview-computedlist");
if (computed) {
computed.parentNode.removeChild(computed);
}
}
let text = node.textContent;
// Format the rule
if (osString == "WINNT") {
text = text.replace(/{/g, "{\r\n ");
text = text.replace(/;/g, ";\r\n ");
text = text.replace(/\s*}/g, "\r\n}");
} else {
text = text.replace(/{/g, "{\n ");
text = text.replace(/;/g, ";\n ");
text = text.replace(/\s*}/g, "\n}");
}
clipboardHelper.copyString(text);
},
/**
* Copy a declaration from the rule view.
*
* @param aEvent The event object
*/
_onCopyDeclaration: function CssRuleView_onCopyDeclaration(aEvent)
{
let node = this.doc.popupNode;
if (!node.classList.contains("ruleview-property") &&
!node.classList.contains("ruleview-computed")) {
while (node = node.parentElement) {
if (node.classList.contains("ruleview-property") ||
node.classList.contains("ruleview-computed")) {
break;
}
}
}
// We need to strip expanded properties from the node because we use
// node.textContent below, which also gets text from hidden nodes. The
// simplest way to do this is to clone the node and remove them from the
// clone.
node = node.cloneNode();
let computed = node.querySelector(".ruleview-computedlist");
if (computed) {
computed.parentNode.removeChild(computed);
}
clipboardHelper.copyString(node.textContent);
},
/**
* Copy a property name from the rule view.
*
* @param aEvent The event object
*/
_onCopyProperty: function CssRuleView_onCopyProperty(aEvent)
{
let node = this.doc.popupNode;
if (!node.classList.contains("ruleview-propertyname")) {
node = node.querySelector(".ruleview-propertyname");
}
if (node) {
clipboardHelper.copyString(node.textContent);
}
},
/**
* Copy a property value from the rule view.
*
* @param aEvent The event object
*/
_onCopyPropertyValue: function CssRuleView_onCopyPropertyValue(aEvent)
{
let node = this.doc.popupNode;
if (!node.classList.contains("ruleview-propertyvalue")) {
node = node.querySelector(".ruleview-propertyvalue");
}
if (node) {
clipboardHelper.copyString(node.textContent);
}
}
};
/**
@ -1569,6 +1869,18 @@ function createChild(aParent, aTag, aAttributes)
return elt;
}
function createMenuItem(aMenu, aAttributes)
{
let item = aMenu.ownerDocument.createElementNS(XUL_NS, "menuitem");
item.setAttribute("label", _strings.GetStringFromName(aAttributes.label));
item.setAttribute("accesskey", _strings.GetStringFromName(aAttributes.accesskey));
item.addEventListener("command", aAttributes.command);
aMenu.appendChild(item);
return item;
}
/**
* Append a text node to an element.
*/
@ -1598,3 +1910,18 @@ function moveFocus(aWin, aDirection)
let fm = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
fm.moveFocus(aWin, null, aDirection, 0);
}
XPCOMUtils.defineLazyGetter(this, "clipboardHelper", function() {
return Cc["@mozilla.org/widget/clipboardhelper;1"].
getService(Ci.nsIClipboardHelper);
});
XPCOMUtils.defineLazyGetter(this, "_strings", function() {
return Services.strings.createBundle(
"chrome://browser/locale/devtools/styleinspector.properties");
});
XPCOMUtils.defineLazyGetter(this, "osString", function() {
return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
});

Просмотреть файл

@ -70,7 +70,7 @@ function testClip()
InspectorUI.INSPECTOR_NOTIFICATIONS.RULEVIEWREADY, false);
executeSoon(function() {
info("Checking that InspectorUI.ruleViewCopyRule() returns " +
info("Checking that _onCopyRule() returns " +
"the correct clipboard value");
let expectedPattern = "element {[\\r\\n]+" +
" margin: 10em;[\\r\\n]+" +
@ -106,12 +106,12 @@ function checkCopyRule() {
EventUtils.synthesizeMouse(prop, 1, 1, { type: "contextmenu", button: 2 },
ruleView.contentWindow);
InspectorUI.ruleViewCopyRule();
InspectorUI.ruleView._boundCopyRule();
}
function checkCopyProperty()
{
info("Checking that InspectorUI.cssRuleViewBoundCopyDeclaration() returns " +
info("Checking that _onCopyDeclaration() returns " +
"the correct clipboard value");
let expectedPattern = "font-family: helvetica,sans-serif;";
info("Expected pattern: " + expectedPattern);
@ -119,13 +119,13 @@ function checkCopyProperty()
SimpleTest.waitForClipboard(function IUI_boundCopyPropCheck() {
return checkClipboardData(expectedPattern);
},
InspectorUI.cssRuleViewBoundCopyDeclaration,
InspectorUI.ruleView._boundCopyDeclaration,
checkCopyPropertyName, checkCopyPropertyName);
}
function checkCopyPropertyName()
{
info("Checking that InspectorUI.cssRuleViewBoundCopyProperty() returns " +
info("Checking that _onCopyProperty() returns " +
"the correct clipboard value");
let expectedPattern = "font-family";
info("Expected pattern: " + expectedPattern);
@ -133,13 +133,13 @@ function checkCopyPropertyName()
SimpleTest.waitForClipboard(function IUI_boundCopyPropNameCheck() {
return checkClipboardData(expectedPattern);
},
InspectorUI.cssRuleViewBoundCopyProperty,
InspectorUI.ruleView._boundCopyProperty,
checkCopyPropertyValue, checkCopyPropertyValue);
}
function checkCopyPropertyValue()
{
info("Checking that InspectorUI.cssRuleViewBoundCopyPropertyValue() " +
info("Checking that _onCopyPropertyValue() " +
" returns the correct clipboard value");
let expectedPattern = "helvetica,sans-serif";
info("Expected pattern: " + expectedPattern);
@ -147,7 +147,7 @@ function checkCopyPropertyValue()
SimpleTest.waitForClipboard(function IUI_boundCopyPropValueCheck() {
return checkClipboardData(expectedPattern);
},
InspectorUI.cssRuleViewBoundCopyPropertyValue,
InspectorUI.ruleView._boundCopyPropertyValue,
checkCopySelection, checkCopySelection);
}
@ -162,7 +162,7 @@ function checkCopySelection()
range.setEnd(props[4], 8);
ruleView.contentWindow.getSelection().addRange(range);
info("Checking that InspectorUI.cssRuleViewBoundCopy() returns the correct" +
info("Checking that _onCopy() returns the correct" +
"clipboard value");
let expectedPattern = " margin: 10em;[\\r\\n]+" +
" font-size: 14pt;[\\r\\n]+" +
@ -175,7 +175,7 @@ function checkCopySelection()
SimpleTest.waitForClipboard(function IUI_boundCopyCheck() {
return checkClipboardData(expectedPattern);
},InspectorUI.cssRuleViewBoundCopy, finishup, finishup);
},InspectorUI.ruleView._boundCopy, finishup, finishup);
}
function checkClipboardData(aExpectedPattern)