--- a/browser/devtools/styleinspector/CssRuleView.jsm
+++ b/browser/devtools/styleinspector/CssRuleView.jsm
@@ -863,25 +863,16 @@ TextProperty.prototype = {
function CssRuleView(aDoc, aStore)
{
this.doc = aDoc;
this.store = aStore;
this.element = this.doc.createElementNS(XUL_NS, "vbox");
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();
this._showEmpty();
}
@@ -943,34 +934,16 @@ CssRuleView.prototype = {
}
this._elementStyle = new ElementStyle(aElement, this.store);
this._elementStyle.onChanged = function() {
this._changed();
}.bind(this);
this._createEditors();
-
- // When creating a new property, we fake the normal property
- // editor behavior (focusing a property's value after entering its
- // name) by responding to the name's blur event, creating the
- // value editor, and grabbing focus to the value editor. But if
- // focus has already moved to another document, we won't be able
- // to move focus to the new editor.
- // Create a focusable item at the end of the editors to catch these
- // cases.
- this._focusBackstop = createChild(this.element, "div", {
- tabindex: 0,
- });
- this._backstopHandler = function() {
- // If this item is actually focused long enough to get the focus
- // event, allow focus to move on out of this document.
- moveFocus(this.doc.defaultView, FOCUS_FORWARD);
- }.bind(this);
- this._focusBackstop.addEventListener("focus", this._backstopHandler, false);
},
/**
* Update the rules for the currently highlighted element.
*/
nodeChanged: function CssRuleView_nodeChanged()
{
// Repopulate the element style.
@@ -1008,22 +981,16 @@ CssRuleView.prototype = {
/**
* Clear the rule view.
*/
clear: function CssRuleView_clear()
{
this._clearRules();
this._viewedElement = null;
this._elementStyle = null;
-
- if (this._focusBackstop) {
- this._focusBackstop.removeEventListener("focus", this._backstopHandler, false);
- this._backstopHandler = null;
- this._focusBackstop = null;
- }
},
/**
* Called when the user has made changes to the ElementStyle.
* Emits an event that clients can listen to.
*/
_changed: function CssRuleView_changed()
{
@@ -1147,32 +1114,16 @@ CssRuleView.prototype = {
!node.classList.contains("ruleview-property") &&
!node.classList.contains("ruleview-computed"));
this._declarationItem.disabled = disablePropertyItems;
this._propertyItem.disabled = disablePropertyItems;
this._propertyValueItem.disabled = disablePropertyItems;
},
- _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;
@@ -1337,30 +1288,30 @@ CssRuleView.prototype = {
}
}
};
/**
* Create a RuleEditor.
*
* @param CssRuleView aRuleView
- * The CssRuleView containg the document holding this rule editor and the
- * _selectionMode flag.
+ * The CssRuleView containg the document holding this rule editor.
* @param Rule aRule
* The Rule object we're editing.
* @constructor
*/
function RuleEditor(aRuleView, aRule)
{
this.ruleView = aRuleView;
this.doc = this.ruleView.doc;
this.rule = aRule;
this.rule.editor = this;
this._onNewProperty = this._onNewProperty.bind(this);
+ this._newPropertyDestroy = this._newPropertyDestroy.bind(this);
this._create();
}
RuleEditor.prototype = {
_create: function RuleEditor_create()
{
this.element = this.doc.createElementNS(HTML_NS, "div");
@@ -1411,29 +1362,20 @@ RuleEditor.prototype = {
this.populate();
this.closeBrace = createChild(code, "div", {
class: "ruleview-ruleclose",
tabindex: "0",
textContent: "}"
});
- // We made the close brace focusable, tabbing to it
- // or clicking on it should start the new property editor.
- this.closeBrace.addEventListener("focus", function(aEvent) {
- if (!this.ruleView._selectionMode) {
- this.newProperty();
- }
- }.bind(this), true);
- this.closeBrace.addEventListener("mousedown", function(aEvent) {
- aEvent.preventDefault();
- }.bind(this), true);
- this.closeBrace.addEventListener("click", function(aEvent) {
- this.closeBrace.focus();
- }.bind(this), true);
+ // Create a property editor when the close brace is clicked.
+ editableItem(this.closeBrace, function(aElement) {
+ this.newProperty();
+ }.bind(this));
},
/**
* Update the rule editor with the contents of the rule.
*/
populate: function RuleEditor_populate()
{
this.selectorText.textContent = this.rule.selectorText;
@@ -1475,45 +1417,62 @@ RuleEditor.prototype = {
// close brace for now.
this.closeBrace.removeAttribute("tabindex");
this.newPropItem = createChild(this.propertyList, "li", {
class: "ruleview-property ruleview-newproperty",
});
this.newPropSpan = createChild(this.newPropItem, "span", {
- class: "ruleview-propertyname"
+ class: "ruleview-propertyname",
+ tabindex: "0"
});
new InplaceEditor({
element: this.newPropSpan,
done: this._onNewProperty,
+ destroy: this._newPropertyDestroy,
advanceChars: ":"
});
},
- _onNewProperty: function RuleEditor_onNewProperty(aValue, aCommit)
+ /**
+ * Called when the new property input has been dismissed.
+ * Will create a new TextProperty if necessary.
+ *
+ * @param string aValue
+ * The value in the editor.
+ * @param bool aCommit
+ * True if the value should be committed.
+ */
+ _onNewProperty: function RuleEditor__onNewProperty(aValue, aCommit)
{
- // We're done, make the close brace focusable again.
- this.closeBrace.setAttribute("tabindex", "0");
-
- this.propertyList.removeChild(this.newPropItem);
- delete this.newPropItem;
- delete this.newPropSpan;
-
if (!aValue || !aCommit) {
return;
}
// Create an empty-valued property and start editing it.
let prop = this.rule.createProperty(aValue, "", "");
let editor = new TextPropertyEditor(this, prop);
this.propertyList.appendChild(editor.element);
- editor.valueSpan.focus();
+ editor.valueSpan.click();
},
+
+ /**
+ * Called when the new property editor is destroyed.
+ */
+ _newPropertyDestroy: function RuleEditor__newPropertyDestroy()
+ {
+ // We're done, make the close brace focusable again.
+ this.closeBrace.setAttribute("tabindex", "0");
+
+ this.propertyList.removeChild(this.newPropItem);
+ delete this.newPropItem;
+ delete this.newPropSpan;
+ }
};
/**
* Create a TextPropertyEditor.
*
* @param {RuleEditor} aRuleEditor
* The rule editor that owns this TextPropertyEditor.
* @param {TextProperty} aProperty
@@ -1819,54 +1778,94 @@ TextPropertyEditor.prototype = {
* {function} start:
* Will be called when the inplace editor is initialized.
* {function} change:
* Will be called when the text input changes. Will be called
* with the current value of the text input.
* {function} done:
* Called when input is committed or blurred. Called with
* current value and a boolean telling the caller whether to
- * commit the change. This function is called after the editor
+ * commit the change. This function is called before the editor
* has been torn down.
+ * {function} destroy:
+ * Called when the editor is destroyed and has been torn down.
* {string} advanceChars:
* If any characters in advanceChars are typed, focus will advance
* to the next element.
*/
function editableField(aOptions)
{
- aOptions.element.addEventListener("focus", function() {
+ editableItem(aOptions.element, function(aElement) {
new InplaceEditor(aOptions);
- }, false);
+ });
+}
- // In order to allow selection on the element, prevent focus on
- // mousedown. Focus on click instead.
- aOptions.element.addEventListener("mousedown", function(evt) {
- evt.preventDefault();
- }, false);
- aOptions.element.addEventListener("click", function(evt) {
+/**
+ * Handle events for an element that should respond to
+ * clicks and sit in the editing tab order, and call
+ * a callback when it is activated.
+ *
+ * @param DOMElement aElement
+ * The DOM element.
+ * @param function aCallback
+ * Called when the editor is activated.
+ */
+
+function editableItem(aElement, aCallback)
+{
+ aElement.addEventListener("click", function() {
let win = this.ownerDocument.defaultView;
let selection = win.getSelection();
if (selection.isCollapsed) {
- aOptions.element.focus();
- } else {
- selection.removeAllRanges();
+ aCallback(aElement);
}
}, false);
+
+ // If focused by means other than a click, start editing by
+ // pressing enter or space.
+ aElement.addEventListener("keypress", function(evt) {
+ if (evt.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_RETURN ||
+ evt.charCode === Ci.nsIDOMKeyEvent.DOM_VK_SPACE) {
+ aCallback(aElement);
+ }
+ }, true);
+
+ // Ugly workaround - the element is focused on mousedown but
+ // the editor is activated on click/mouseup. This leads
+ // to an ugly flash of the focus ring before showing the editor.
+ // So hide the focus ring while the mouse is down.
+ aElement.addEventListener("mousedown", function(evt) {
+ let cleanup = function() {
+ aElement.style.removeProperty("outline-style");
+ aElement.removeEventListener("mouseup", cleanup, false);
+ aElement.removeEventListener("mouseout", cleanup, false);
+ };
+ aElement.style.setProperty("outline-style", "none");
+ aElement.addEventListener("mouseup", cleanup, false);
+ aElement.addEventListener("mouseout", cleanup, false);
+ }, false);
+
+ // Mark the element editable field for tab
+ // navigation while editing.
+ aElement._editable = true;
}
+
var _editableField = editableField;
function InplaceEditor(aOptions)
{
this.elt = aOptions.element;
+ let doc = this.elt.ownerDocument;
+ this.doc = doc;
this.elt.inplaceEditor = this;
this.change = aOptions.change;
this.done = aOptions.done;
+ this.destroy = aOptions.destroy;
this.initial = aOptions.initial ? aOptions.initial : this.elt.textContent;
- this.doc = this.elt.ownerDocument;
this._onBlur = this._onBlur.bind(this);
this._onKeyPress = this._onKeyPress.bind(this);
this._onInput = this._onInput.bind(this);
this._createInput();
this._autosize();
@@ -1906,23 +1905,34 @@ InplaceEditor.prototype = {
copyTextStyles(this.elt, this.input);
},
/**
* Get rid of the editor.
*/
_clear: function InplaceEditor_clear()
{
+ if (!this.input) {
+ // Already cleared.
+ return;
+ }
+
this.input.removeEventListener("blur", this._onBlur, false);
this.input.removeEventListener("keypress", this._onKeyPress, false);
this.input.removeEventListener("oninput", this._onInput, false);
this._stopAutosize();
+ this.elt.style.display = this.originalDisplay;
+ this.elt.focus();
+
+ if (this.destroy) {
+ this.destroy();
+ }
+
this.elt.parentNode.removeChild(this.input);
- this.elt.style.display = this.originalDisplay;
this.input = null;
delete this.elt.inplaceEditor;
delete this.elt;
},
/**
* Keeps the editor close to the size of its input string. This is pretty
@@ -1975,44 +1985,81 @@ InplaceEditor.prototype = {
// any letter that could be typed, otherwise we'll scroll before
// we get a chance to resize. Yuck.
let width = this._measurement.offsetWidth + 10;
this.input.style.width = width + "px";
},
/**
- * Handle loss of focus by calling the client's done handler and
- * clearing out.
+ * Call the client's done handler and clear out.
+ */
+ _apply: function InplaceEditor_apply(aEvent)
+ {
+ if (this._applied) {
+ return;
+ }
+
+ this._applied = true;
+
+ if (this.done) {
+ let val = this.input.value.trim();
+ return this.done(this.cancelled ? this.initial : val, !this.cancelled);
+ }
+ return null;
+ },
+
+ /**
+ * Handle loss of focus by calling done if it hasn't been called yet.
*/
_onBlur: function InplaceEditor_onBlur(aEvent)
{
- let val = this.input.value.trim();
+ this._apply();
this._clear();
- if (this.done) {
- this.done(this.cancelled ? this.initial : val, !this.cancelled);
- }
},
_onKeyPress: function InplaceEditor_onKeyPress(aEvent)
{
let prevent = false;
if (aEvent.charCode in this._advanceCharCodes
- || aEvent.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_RETURN) {
- // Focus the next element, triggering a blur which
- // will eventually shut us down (making return roughly equal
- // tab).
+ || aEvent.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_RETURN
+ || aEvent.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_TAB) {
prevent = true;
- moveFocus(this.input.ownerDocument.defaultView, FOCUS_FORWARD);
+
+ let direction = FOCUS_FORWARD;
+ if (aEvent.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_TAB &&
+ aEvent.shiftKey) {
+ this.cancelled = true;
+ direction = FOCUS_BACKWARD;
+ }
+
+ let input = this.input;
+
+ this._apply();
+
+ let fm = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
+ if (fm.focusedElement === input) {
+ // If the focused element wasn't changed by the done callback,
+ // move the focus as requested.
+ let next = moveFocus(this.doc.defaultView, direction);
+
+ // If the next node to be focused has been tagged as an editable
+ // node, send it a click event to trigger
+ if (next && next.ownerDocument === this.doc && next._editable) {
+ next.click();
+ }
+ }
+
+ this._clear();
} else if (aEvent.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_ESCAPE) {
- // Cancel and blur ourselves. |_onBlur| will call the user's
- // done handler for us.
+ // Cancel and blur ourselves.
prevent = true;
this.cancelled = true;
- this.input.blur();
+ this._apply();
+ this._clear();
aEvent.stopPropagation();
} else if (aEvent.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_SPACE) {
// No need for leading spaces here. This is particularly
// noticable when adding a property: it's very natural to type
// <name>: (which advances to the next property) then spacebar.
prevent = !this.input.value;
}
@@ -2189,17 +2236,17 @@ function copyTextStyles(aFrom, aTo)
}
/**
* Trigger a focus change similar to pressing tab/shift-tab.
*/
function moveFocus(aWin, aDirection)
{
let fm = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
- fm.moveFocus(aWin, null, aDirection, 0);
+ return 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() {
--- a/browser/devtools/styleinspector/test/browser_ruleview_bug_703643_context_menu_copy.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_bug_703643_context_menu_copy.js
@@ -46,40 +46,16 @@ function openInspector()
ok(!InspectorUI.inspecting, "Inspector is not highlighting");
ok(InspectorUI.store.isEmpty(), "Inspector.store is empty");
Services.obs.addObserver(inspectorUIOpen,
InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
InspectorUI.openInspectorUI();
}
-function waitForEditorFocus(aParent, aCallback)
-{
- aParent.addEventListener("focus", function onFocus(evt) {
- if (inplaceEditor(evt.target)) {
- aParent.removeEventListener("focus", onFocus, true);
- let editor = inplaceEditor(evt.target);
- 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);
-}
-
function inspectorUIOpen()
{
Services.obs.removeObserver(inspectorUIOpen,
InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
// Make sure the inspector is open.
ok(InspectorUI.inspecting, "Inspector is highlighting");
ok(!InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is not open");
--- a/browser/devtools/styleinspector/test/browser_ruleview_editor.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_editor.js
@@ -45,33 +45,33 @@ function testReturnCommit()
initial: "explicit initial",
start: function() {
is(inplaceEditor(span).input.value, "explicit initial", "Explicit initial value should be used.");
inplaceEditor(span).input.value = "Test Value";
EventUtils.sendKey("return");
},
done: expectDone("Test Value", true, testBlurCommit)
});
- span.focus();
+ span.click();
}
function testBlurCommit()
{
clearBody();
let span = createSpan();
_editableField({
element: span,
start: function() {
is(inplaceEditor(span).input.value, "Edit Me!", "textContent of the span used.");
inplaceEditor(span).input.value = "Test Value";
inplaceEditor(span).input.blur();
},
done: expectDone("Test Value", true, testAdvanceCharCommit)
});
- span.focus();
+ span.click();
}
function testAdvanceCharCommit()
{
clearBody();
let span = createSpan();
_editableField({
element: span,
@@ -79,33 +79,33 @@ function testAdvanceCharCommit()
start: function() {
let input = inplaceEditor(span).input;
for each (let ch in "Test:") {
EventUtils.sendChar(ch);
}
},
done: expectDone("Test", true, testEscapeCancel)
});
- span.focus();
+ span.click();
}
function testEscapeCancel()
{
clearBody();
let span = createSpan();
_editableField({
element: span,
initial: "initial text",
start: function() {
inplaceEditor(span).input.value = "Test Value";
EventUtils.sendKey("escape");
},
done: expectDone("initial text", false, finishTest)
});
- span.focus();
+ span.click();
}
function finishTest()
{
doc = null;
gBrowser.removeCurrentTab();
finish();
--- a/browser/devtools/styleinspector/test/browser_ruleview_editor_changedvalues.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_editor_changedvalues.js
@@ -8,40 +8,16 @@ let CssRuleView = tempScope.CssRuleView;
let _ElementStyle = tempScope._ElementStyle;
let _editableField = tempScope._editableField;
let inplaceEditor = tempScope._getInplaceEditorForSpan;
let doc;
let ruleDialog;
let ruleView;
-function waitForEditorFocus(aParent, aCallback)
-{
- aParent.addEventListener("focus", function onFocus(evt) {
- if (inplaceEditor(evt.target)) {
- aParent.removeEventListener("focus", onFocus, true);
- let editor = inplaceEditor(evt.target);
- 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()
{
@@ -110,17 +86,16 @@ function testCreateNew()
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, inplaceEditor(textProp.editor.valueSpan), "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();
--- a/browser/devtools/styleinspector/test/browser_ruleview_focus.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_focus.js
@@ -11,29 +11,16 @@ let inplaceEditor = tempScope._getInplac
let doc;
let stylePanel;
function waitForRuleView(aCallback)
{
InspectorUI.currentInspector.once("sidebaractivated-ruleview", aCallback);
}
-function waitForEditorFocus(aParent, aCallback)
-{
- aParent.addEventListener("focus", function onFocus(evt) {
- if (inplaceEditor(evt.target)) {
- aParent.removeEventListener("focus", onFocus, true);
- let editor = inplaceEditor(evt.target);
- executeSoon(function() {
- aCallback(editor);
- });
- }
- }, true);
-}
-
function openRuleView()
{
Services.obs.addObserver(function onOpened() {
Services.obs.removeObserver(onOpened,
InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
// Highlight a node.
let node = content.document.getElementsByTagName("h1")[0];
@@ -68,17 +55,17 @@ function testFocus()
aEditor.input.blur();
finishUp();
});
EventUtils.sendKey("return");
});
EventUtils.sendKey("return");
});
- brace.focus();
+ brace.click();
}
function finishUp()
{
InspectorUI.sidebar.hide();
InspectorUI.closeInspectorUI();
doc = stylePanel = null;
gBrowser.removeCurrentTab();
--- a/browser/devtools/styleinspector/test/browser_ruleview_ui.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_ui.js
@@ -8,40 +8,16 @@ let CssRuleView = tempScope.CssRuleView;
let _ElementStyle = tempScope._ElementStyle;
let _editableField = tempScope._editableField;
let inplaceEditor = tempScope._getInplaceEditorForSpan;
let doc;
let ruleDialog;
let ruleView;
-function waitForEditorFocus(aParent, aCallback)
-{
- aParent.addEventListener("focus", function onFocus(evt) {
- if (inplaceEditor(evt.target)) {
- aParent.removeEventListener("focus", onFocus, true);
- let editor = inplaceEditor(evt.target);
- 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()
{
--- a/browser/devtools/styleinspector/test/head.js
+++ b/browser/devtools/styleinspector/test/head.js
@@ -300,12 +300,36 @@ ComputedViewPanel.prototype = {
},
};
function ruleView()
{
return InspectorUI.sidebar._toolContext("ruleview").view;
}
+function waitForEditorFocus(aParent, aCallback)
+{
+ aParent.addEventListener("focus", function onFocus(evt) {
+ if (inplaceEditor(evt.target) && evt.target.tagName == "input") {
+ aParent.removeEventListener("focus", onFocus, true);
+ let editor = inplaceEditor(evt.target);
+ 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);
+}
+
registerCleanupFunction(tearDown);
waitForExplicitFinish();