зеркало из https://github.com/mozilla/gecko-dev.git
Bug 700893 - API for tracking unsaved/saved state in source editor; r=rcampbell f=fayearthur
This commit is contained in:
Родитель
30a7711db3
Коммит
a25f68694c
|
@ -75,6 +75,8 @@ const BUTTON_POSITION_DONT_SAVE = 2;
|
|||
* The scratchpad object handles the Scratchpad window functionality.
|
||||
*/
|
||||
var Scratchpad = {
|
||||
_initialWindowTitle: document.title,
|
||||
|
||||
/**
|
||||
* The script execution context. This tells Scratchpad in which context the
|
||||
* script shall execute.
|
||||
|
@ -151,7 +153,22 @@ var Scratchpad = {
|
|||
*/
|
||||
setFilename: function SP_setFilename(aFilename)
|
||||
{
|
||||
document.title = this.filename = aFilename;
|
||||
this.filename = aFilename;
|
||||
this._updateTitle();
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the Scratchpad window title based on the current state.
|
||||
* @private
|
||||
*/
|
||||
_updateTitle: function SP__updateTitle()
|
||||
{
|
||||
if (this.filename) {
|
||||
document.title = (this.editor && this.editor.dirty ? "*" : "") +
|
||||
this.filename;
|
||||
} else {
|
||||
document.title = this._initialWindowTitle;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -168,7 +185,7 @@ var Scratchpad = {
|
|||
filename: this.filename,
|
||||
text: this.getText(),
|
||||
executionContext: this.executionContext,
|
||||
saved: this.saved
|
||||
saved: !this.editor.dirty,
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -184,7 +201,9 @@ var Scratchpad = {
|
|||
if (aState.filename) {
|
||||
this.setFilename(aState.filename);
|
||||
}
|
||||
this.saved = aState.saved;
|
||||
if (this.editor) {
|
||||
this.editor.dirty = !aState.saved;
|
||||
}
|
||||
|
||||
if (aState.executionContext == SCRATCHPAD_CONTEXT_BROWSER) {
|
||||
this.setBrowserContext();
|
||||
|
@ -638,7 +657,7 @@ var Scratchpad = {
|
|||
fp.defaultString = "";
|
||||
if (fp.show() != Ci.nsIFilePicker.returnCancel) {
|
||||
this.setFilename(fp.file.path);
|
||||
this.importFromFile(fp.file, false, this.onTextSaved.bind(this));
|
||||
this.importFromFile(fp.file, false);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -658,7 +677,9 @@ var Scratchpad = {
|
|||
file.initWithPath(this.filename);
|
||||
|
||||
this.exportToFile(file, true, false, function(aStatus) {
|
||||
this.onTextSaved();
|
||||
if (Components.isSuccessCode(aStatus)) {
|
||||
this.editor.dirty = false;
|
||||
}
|
||||
if (aCallback) {
|
||||
aCallback(aStatus);
|
||||
}
|
||||
|
@ -681,7 +702,9 @@ var Scratchpad = {
|
|||
this.setFilename(fp.file.path);
|
||||
|
||||
this.exportToFile(fp.file, true, false, function(aStatus) {
|
||||
this.onTextSaved();
|
||||
if (Components.isSuccessCode(aStatus)) {
|
||||
this.editor.dirty = false;
|
||||
}
|
||||
if (aCallback) {
|
||||
aCallback(aStatus);
|
||||
}
|
||||
|
@ -783,7 +806,6 @@ var Scratchpad = {
|
|||
if (aEvent.target != document) {
|
||||
return;
|
||||
}
|
||||
|
||||
let chrome = Services.prefs.getBoolPref(DEVTOOLS_CHROME_ENABLED);
|
||||
if (chrome) {
|
||||
let environmentMenu = document.getElementById("sp-environment-menu");
|
||||
|
@ -794,10 +816,11 @@ var Scratchpad = {
|
|||
errorConsoleCommand.removeAttribute("disabled");
|
||||
}
|
||||
|
||||
let state = null;
|
||||
let initialText = this.strings.GetStringFromName("scratchpadIntro");
|
||||
if ("arguments" in window &&
|
||||
window.arguments[0] instanceof Ci.nsIDialogParamBlock) {
|
||||
let state = JSON.parse(window.arguments[0].GetString(0));
|
||||
state = JSON.parse(window.arguments[0].GetString(0));
|
||||
this.setState(state);
|
||||
initialText = state.text;
|
||||
}
|
||||
|
@ -811,29 +834,32 @@ var Scratchpad = {
|
|||
};
|
||||
|
||||
let editorPlaceholder = document.getElementById("scratchpad-editor");
|
||||
this.editor.init(editorPlaceholder, config, this.onEditorLoad.bind(this));
|
||||
this.editor.init(editorPlaceholder, config,
|
||||
this._onEditorLoad.bind(this, state));
|
||||
},
|
||||
|
||||
/**
|
||||
* The load event handler for the source editor. This method does post-load
|
||||
* editor initialization.
|
||||
*
|
||||
* @private
|
||||
* @param object aState
|
||||
* The initial Scratchpad state object.
|
||||
*/
|
||||
onEditorLoad: function SP_onEditorLoad()
|
||||
_onEditorLoad: function SP__onEditorLoad(aState)
|
||||
{
|
||||
this.editor.addEventListener(SourceEditor.EVENTS.CONTEXT_MENU,
|
||||
this.onContextMenu);
|
||||
this.editor.addEventListener(SourceEditor.EVENTS.DIRTY_CHANGED,
|
||||
this._onDirtyChanged);
|
||||
this.editor.focus();
|
||||
this.editor.setCaretOffset(this.editor.getCharCount());
|
||||
if (aState) {
|
||||
this.editor.dirty = !aState.saved;
|
||||
}
|
||||
|
||||
this.initialized = true;
|
||||
|
||||
if (this.filename && !this.saved) {
|
||||
this.onTextChanged();
|
||||
}
|
||||
else if (this.filename && this.saved) {
|
||||
this.onTextSaved();
|
||||
}
|
||||
|
||||
this._triggerObservers("Ready");
|
||||
},
|
||||
|
||||
|
@ -866,6 +892,20 @@ var Scratchpad = {
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The Source Editor DirtyChanged event handler. This function updates the
|
||||
* Scratchpad window title to show an asterisk when there are unsaved changes.
|
||||
*
|
||||
* @private
|
||||
* @see SourceEditor.EVENTS.DIRTY_CHANGED
|
||||
* @param object aEvent
|
||||
* The DirtyChanged event object.
|
||||
*/
|
||||
_onDirtyChanged: function SP__onDirtyChanged(aEvent)
|
||||
{
|
||||
Scratchpad._updateTitle();
|
||||
},
|
||||
|
||||
/**
|
||||
* The popupshowing event handler for the Edit menu. This method updates the
|
||||
* enabled/disabled state of the Undo and Redo commands, based on the editor
|
||||
|
@ -899,36 +939,6 @@ var Scratchpad = {
|
|||
this.editor.redo();
|
||||
},
|
||||
|
||||
/**
|
||||
* This method adds a listener to the editor for text changes. Called when
|
||||
* a scratchpad is saved, opened from file, or restored from a saved file.
|
||||
*/
|
||||
onTextSaved: function SP_onTextSaved(aStatus)
|
||||
{
|
||||
if (aStatus && !Components.isSuccessCode(aStatus)) {
|
||||
return;
|
||||
}
|
||||
if (!document || !this.initialized) {
|
||||
return; // file saved to disk after window has closed
|
||||
}
|
||||
document.title = document.title.replace(/^\*/, "");
|
||||
this.saved = true;
|
||||
this.editor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
|
||||
this.onTextChanged);
|
||||
},
|
||||
|
||||
/**
|
||||
* The scratchpad handler for editor text change events. This handler
|
||||
* indicates that there are unsaved changes in the UI.
|
||||
*/
|
||||
onTextChanged: function SP_onTextChanged()
|
||||
{
|
||||
document.title = "*" + document.title;
|
||||
Scratchpad.saved = false;
|
||||
Scratchpad.editor.removeEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
|
||||
Scratchpad.onTextChanged);
|
||||
},
|
||||
|
||||
/**
|
||||
* The Scratchpad window unload event handler. This method unloads/destroys
|
||||
* the source editor.
|
||||
|
@ -942,6 +952,8 @@ var Scratchpad = {
|
|||
}
|
||||
|
||||
this.resetContext();
|
||||
this.editor.removeEventListener(SourceEditor.EVENTS.DIRTY_CHANGED,
|
||||
this._onDirtyChanged);
|
||||
this.editor.removeEventListener(SourceEditor.EVENTS.CONTEXT_MENU,
|
||||
this.onContextMenu);
|
||||
this.editor.destroy();
|
||||
|
@ -953,13 +965,18 @@ var Scratchpad = {
|
|||
* Prompt to save scratchpad if it has unsaved changes.
|
||||
*
|
||||
* @param function aCallback
|
||||
* Optional function you want to call when file is saved
|
||||
* Optional function you want to call when file is saved. The callback
|
||||
* receives three arguments:
|
||||
* - toClose (boolean) - tells if the window should be closed.
|
||||
* - saved (boolen) - tells if the file has been saved.
|
||||
* - status (number) - the file save status result (if the file was
|
||||
* saved).
|
||||
* @return boolean
|
||||
* Whether the window should be closed
|
||||
*/
|
||||
promptSave: function SP_promptSave(aCallback)
|
||||
{
|
||||
if (this.filename && !this.saved) {
|
||||
if (this.filename && this.editor.dirty) {
|
||||
let ps = Services.prompt;
|
||||
let flags = ps.BUTTON_POS_0 * ps.BUTTON_TITLE_SAVE +
|
||||
ps.BUTTON_POS_1 * ps.BUTTON_TITLE_CANCEL +
|
||||
|
@ -971,12 +988,25 @@ var Scratchpad = {
|
|||
flags, null, null, null, null, {});
|
||||
|
||||
if (button == BUTTON_POSITION_CANCEL) {
|
||||
if (aCallback) {
|
||||
aCallback(false, false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (button == BUTTON_POSITION_SAVE) {
|
||||
this.saveFile(aCallback);
|
||||
this.saveFile(function(aStatus) {
|
||||
if (aCallback) {
|
||||
aCallback(true, true, aStatus);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (aCallback) {
|
||||
aCallback(true, false);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
|
@ -988,10 +1018,22 @@ var Scratchpad = {
|
|||
*/
|
||||
onClose: function SP_onClose(aEvent)
|
||||
{
|
||||
let toClose = this.promptSave();
|
||||
if (!toClose) {
|
||||
aEvent.preventDefault();
|
||||
if (this._skipClosePrompt) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.promptSave(function(aShouldClose, aSaved, aStatus) {
|
||||
let shouldClose = aShouldClose;
|
||||
if (aSaved && !Components.isSuccessCode(aStatus)) {
|
||||
shouldClose = false;
|
||||
}
|
||||
|
||||
if (shouldClose) {
|
||||
this._skipClosePrompt = true;
|
||||
window.close();
|
||||
}
|
||||
}.bind(this));
|
||||
aEvent.preventDefault();
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1003,10 +1045,20 @@ var Scratchpad = {
|
|||
*/
|
||||
close: function SP_close(aCallback)
|
||||
{
|
||||
let toClose = this.promptSave(aCallback);
|
||||
if (toClose) {
|
||||
window.close();
|
||||
}
|
||||
this.promptSave(function(aShouldClose, aSaved, aStatus) {
|
||||
let shouldClose = aShouldClose;
|
||||
if (aSaved && !Components.isSuccessCode(aStatus)) {
|
||||
shouldClose = false;
|
||||
}
|
||||
|
||||
if (shouldClose) {
|
||||
this._skipClosePrompt = true;
|
||||
window.close();
|
||||
}
|
||||
if (aCallback) {
|
||||
aCallback();
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
_observers: [],
|
||||
|
|
|
@ -46,9 +46,10 @@ function test()
|
|||
function testNew()
|
||||
{
|
||||
openScratchpad(function(win) {
|
||||
win.Scratchpad.close();
|
||||
ok(win.closed, "new scratchpad window should close without prompting")
|
||||
done();
|
||||
win.Scratchpad.close(function() {
|
||||
ok(win.closed, "new scratchpad window should close without prompting")
|
||||
done();
|
||||
});
|
||||
}, {noFocus: true});
|
||||
}
|
||||
|
||||
|
@ -56,11 +57,11 @@ function testSavedFile()
|
|||
{
|
||||
openScratchpad(function(win) {
|
||||
win.Scratchpad.filename = "test.js";
|
||||
win.Scratchpad.saved = true;
|
||||
win.Scratchpad.close();
|
||||
|
||||
ok(win.closed, "scratchpad from file with no changes should close")
|
||||
done();
|
||||
win.Scratchpad.editor.dirty = false;
|
||||
win.Scratchpad.close(function() {
|
||||
ok(win.closed, "scratchpad from file with no changes should close")
|
||||
done();
|
||||
});
|
||||
}, {noFocus: true});
|
||||
}
|
||||
|
||||
|
@ -74,17 +75,16 @@ function testUnsaved()
|
|||
function testUnsavedFileCancel()
|
||||
{
|
||||
openScratchpad(function(win) {
|
||||
win.Scratchpad.filename = "test.js";
|
||||
win.Scratchpad.saved = false;
|
||||
win.Scratchpad.setFilename("test.js");
|
||||
win.Scratchpad.editor.dirty = true;
|
||||
|
||||
promptButton = win.BUTTON_POSITION_CANCEL;
|
||||
|
||||
win.Scratchpad.close();
|
||||
|
||||
ok(!win.closed, "cancelling dialog shouldn't close scratchpad");
|
||||
|
||||
win.close();
|
||||
done();
|
||||
win.Scratchpad.close(function() {
|
||||
ok(!win.closed, "cancelling dialog shouldn't close scratchpad");
|
||||
win.close();
|
||||
done();
|
||||
});
|
||||
}, {noFocus: true});
|
||||
}
|
||||
|
||||
|
@ -92,8 +92,7 @@ function testUnsavedFileSave()
|
|||
{
|
||||
openScratchpad(function(win) {
|
||||
win.Scratchpad.importFromFile(gFile, true, function(status, content) {
|
||||
win.Scratchpad.filename = gFile.path;
|
||||
win.Scratchpad.onTextSaved();
|
||||
win.Scratchpad.setFilename(gFile.path);
|
||||
|
||||
let text = "new text";
|
||||
win.Scratchpad.setText(text);
|
||||
|
@ -101,13 +100,12 @@ function testUnsavedFileSave()
|
|||
promptButton = win.BUTTON_POSITION_SAVE;
|
||||
|
||||
win.Scratchpad.close(function() {
|
||||
ok(win.closed, 'pressing "Save" in dialog should close scratchpad');
|
||||
readFile(gFile, function(savedContent) {
|
||||
is(savedContent, text, 'prompted "Save" worked when closing scratchpad');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
ok(win.closed, 'pressing "Save" in dialog should close scratchpad');
|
||||
});
|
||||
}, {noFocus: true});
|
||||
}
|
||||
|
@ -115,15 +113,15 @@ function testUnsavedFileSave()
|
|||
function testUnsavedFileDontSave()
|
||||
{
|
||||
openScratchpad(function(win) {
|
||||
win.Scratchpad.filename = gFile.path;
|
||||
win.Scratchpad.saved = false;
|
||||
win.Scratchpad.setFilename(gFile.path);
|
||||
win.Scratchpad.editor.dirty = true;
|
||||
|
||||
promptButton = win.BUTTON_POSITION_DONT_SAVE;
|
||||
|
||||
win.Scratchpad.close();
|
||||
|
||||
ok(win.closed, 'pressing "Don\'t Save" in dialog should close scratchpad');
|
||||
done();
|
||||
win.Scratchpad.close(function() {
|
||||
ok(win.closed, 'pressing "Don\'t Save" in dialog should close scratchpad');
|
||||
done();
|
||||
});
|
||||
}, {noFocus: true});
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// only finish() when correct number of tests are done
|
||||
const expected = 5;
|
||||
const expected = 4;
|
||||
var count = 0;
|
||||
function done()
|
||||
{
|
||||
|
@ -20,7 +20,6 @@ function test()
|
|||
waitForExplicitFinish();
|
||||
|
||||
testListeners();
|
||||
testErrorStatus();
|
||||
testRestoreNotFromFile();
|
||||
testRestoreFromFileSaved();
|
||||
testRestoreFromFileUnsaved();
|
||||
|
@ -34,36 +33,30 @@ function testListeners()
|
|||
aScratchpad.setText("new text");
|
||||
ok(!isStar(aWin), "no star if scratchpad isn't from a file");
|
||||
|
||||
aScratchpad.onTextSaved();
|
||||
aScratchpad.editor.dirty = false;
|
||||
ok(!isStar(aWin), "no star before changing text");
|
||||
|
||||
aScratchpad.setFilename("foo.js");
|
||||
aScratchpad.setText("new text2");
|
||||
ok(isStar(aWin), "shows star if scratchpad text changes");
|
||||
|
||||
aScratchpad.onTextSaved();
|
||||
aScratchpad.editor.dirty = false;
|
||||
ok(!isStar(aWin), "no star if scratchpad was just saved");
|
||||
|
||||
aScratchpad.setText("new text3");
|
||||
ok(isStar(aWin), "shows star if scratchpad has more changes");
|
||||
|
||||
aScratchpad.undo();
|
||||
ok(isStar(aWin), "star if scratchpad undo");
|
||||
ok(!isStar(aWin), "no star if scratchpad undo to save point");
|
||||
|
||||
aScratchpad.undo();
|
||||
ok(isStar(aWin), "star if scratchpad undo past save point");
|
||||
|
||||
aWin.close();
|
||||
done();
|
||||
}, {noFocus: true});
|
||||
}
|
||||
|
||||
function testErrorStatus()
|
||||
{
|
||||
openScratchpad(function(aWin, aScratchpad) {
|
||||
aScratchpad.onTextSaved(Components.results.NS_ERROR_FAILURE);
|
||||
aScratchpad.setText("new text");
|
||||
ok(!isStar(aWin), "no star if file save failed");
|
||||
|
||||
aWin.close();
|
||||
done();
|
||||
}, {noFocus: true});
|
||||
}
|
||||
|
||||
|
||||
function testRestoreNotFromFile()
|
||||
{
|
||||
let session = [{
|
||||
|
|
|
@ -146,6 +146,7 @@ function SourceEditor() {
|
|||
Services.prefs.getBoolPref(SourceEditor.PREFS.EXPAND_TAB);
|
||||
|
||||
this._onOrionSelection = this._onOrionSelection.bind(this);
|
||||
this._onTextChanged = this._onTextChanged.bind(this);
|
||||
|
||||
this._eventTarget = {};
|
||||
this._eventListenersQueue = [];
|
||||
|
@ -172,6 +173,7 @@ SourceEditor.prototype = {
|
|||
_iframeWindow: null,
|
||||
_eventTarget: null,
|
||||
_eventListenersQueue: null,
|
||||
_dirty: false,
|
||||
|
||||
/**
|
||||
* The Source Editor user interface manager.
|
||||
|
@ -279,8 +281,11 @@ SourceEditor.prototype = {
|
|||
|
||||
this._view.addEventListener("Load", onOrionLoad);
|
||||
if (config.highlightCurrentLine || Services.appinfo.OS == "Linux") {
|
||||
this._view.addEventListener("Selection", this._onOrionSelection);
|
||||
this.addEventListener(SourceEditor.EVENTS.SELECTION,
|
||||
this._onOrionSelection);
|
||||
}
|
||||
this.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
|
||||
this._onTextChanged);
|
||||
|
||||
let KeyBinding = window.require("orion/textview/keyBinding").KeyBinding;
|
||||
let TextDND = window.require("orion/textview/textDND").TextDND;
|
||||
|
@ -587,6 +592,28 @@ SourceEditor.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The TextChanged event handler which tracks the dirty state of the editor.
|
||||
*
|
||||
* @see SourceEditor.EVENTS.TEXT_CHANGED
|
||||
* @see SourceEditor.EVENTS.DIRTY_CHANGED
|
||||
* @see SourceEditor.dirty
|
||||
* @private
|
||||
*/
|
||||
_onTextChanged: function SE__onTextChanged()
|
||||
{
|
||||
this._updateDirty();
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the dirty state of the editor based on the undo stack.
|
||||
* @private
|
||||
*/
|
||||
_updateDirty: function SE__updateDirty()
|
||||
{
|
||||
this.dirty = !this._undoStack.isClean();
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the X11 PRIMARY buffer to hold the current selection.
|
||||
* @private
|
||||
|
@ -903,11 +930,53 @@ SourceEditor.prototype = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Reset the Undo stack
|
||||
* Reset the Undo stack.
|
||||
*/
|
||||
resetUndo: function SE_resetUndo()
|
||||
{
|
||||
this._undoStack.reset();
|
||||
this._updateDirty();
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the "dirty" state of the editor. Set this to false when you save the
|
||||
* text being edited. The dirty state will become true once the user makes
|
||||
* changes to the text.
|
||||
*
|
||||
* @param boolean aNewValue
|
||||
* The new dirty state: true if the text is not saved, false if you
|
||||
* just saved the text.
|
||||
*/
|
||||
set dirty(aNewValue)
|
||||
{
|
||||
if (aNewValue == this._dirty) {
|
||||
return;
|
||||
}
|
||||
|
||||
let event = {
|
||||
type: SourceEditor.EVENTS.DIRTY_CHANGED,
|
||||
oldValue: this._dirty,
|
||||
newValue: aNewValue,
|
||||
};
|
||||
|
||||
this._dirty = aNewValue;
|
||||
if (!this._dirty && !this._undoStack.isClean()) {
|
||||
this._undoStack.markClean();
|
||||
}
|
||||
this._dispatchEvent(event);
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the editor "dirty" state. This tells if the text is considered saved or
|
||||
* not.
|
||||
*
|
||||
* @see SourceEditor.EVENTS.DIRTY_CHANGED
|
||||
* @return boolean
|
||||
* True if there are changes which are not saved, false otherwise.
|
||||
*/
|
||||
get dirty()
|
||||
{
|
||||
return this._dirty;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1326,10 +1395,15 @@ SourceEditor.prototype = {
|
|||
destroy: function SE_destroy()
|
||||
{
|
||||
if (this._config.highlightCurrentLine || Services.appinfo.OS == "Linux") {
|
||||
this._view.removeEventListener("Selection", this._onOrionSelection);
|
||||
this.removeEventListener(SourceEditor.EVENTS.SELECTION,
|
||||
this._onOrionSelection);
|
||||
}
|
||||
this._onOrionSelection = null;
|
||||
|
||||
this.removeEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
|
||||
this._onTextChanged);
|
||||
this._onTextChanged = null;
|
||||
|
||||
if (this._primarySelectionTimeout) {
|
||||
let window = this.parentElement.ownerDocument.defaultView;
|
||||
window.clearTimeout(this._primarySelectionTimeout);
|
||||
|
|
|
@ -282,6 +282,15 @@ SourceEditor.EVENTS = {
|
|||
* condition.
|
||||
*/
|
||||
BREAKPOINT_CHANGE: "BreakpointChange",
|
||||
|
||||
/**
|
||||
* The DirtyChanged event is fired when the dirty state of the editor is
|
||||
* changed. The dirty state of the editor tells if the are text changes that
|
||||
* have not been saved yet. Event object properties: oldValue and newValue.
|
||||
* Both are booleans telling the old dirty state and the new state,
|
||||
* respectively.
|
||||
*/
|
||||
DIRTY_CHANGED: "DirtyChanged",
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -58,6 +58,7 @@ _BROWSER_TEST_FILES = \
|
|||
browser_bug725388_mouse_events.js \
|
||||
browser_bug707987_debugger_breakpoints.js \
|
||||
browser_bug712982_line_ruler_click.js \
|
||||
browser_bug700893_dirty_state.js \
|
||||
head.js \
|
||||
|
||||
libs:: $(_BROWSER_TEST_FILES)
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
function test() {
|
||||
|
||||
let temp = {};
|
||||
Cu.import("resource:///modules/source-editor.jsm", temp);
|
||||
let SourceEditor = temp.SourceEditor;
|
||||
|
||||
let component = Services.prefs.getCharPref(SourceEditor.PREFS.COMPONENT);
|
||||
if (component == "textarea") {
|
||||
ok(true, "skip test for bug 700893: only applicable for non-textarea components");
|
||||
return;
|
||||
}
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
let editor;
|
||||
|
||||
const windowUrl = "data:text/xml,<?xml version='1.0'?>" +
|
||||
"<window xmlns='http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul'" +
|
||||
" title='test for bug 700893' width='600' height='500'><hbox flex='1'/></window>";
|
||||
const windowFeatures = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
|
||||
|
||||
let testWin = Services.ww.openWindow(null, windowUrl, "_blank", windowFeatures, null);
|
||||
testWin.addEventListener("load", function onWindowLoad() {
|
||||
testWin.removeEventListener("load", onWindowLoad, false);
|
||||
waitForFocus(initEditor, testWin);
|
||||
}, false);
|
||||
|
||||
function initEditor()
|
||||
{
|
||||
let hbox = testWin.document.querySelector("hbox");
|
||||
editor = new SourceEditor();
|
||||
editor.init(hbox, {initialText: "foobar"}, editorLoaded);
|
||||
}
|
||||
|
||||
function editorLoaded()
|
||||
{
|
||||
editor.focus();
|
||||
|
||||
is(editor.dirty, false, "editory is not dirty");
|
||||
|
||||
let event = null;
|
||||
let eventHandler = function(aEvent) {
|
||||
event = aEvent;
|
||||
};
|
||||
editor.addEventListener(SourceEditor.EVENTS.DIRTY_CHANGED, eventHandler);
|
||||
|
||||
editor.setText("omg");
|
||||
|
||||
is(editor.dirty, true, "editor is dirty");
|
||||
ok(event, "DirtyChanged event fired")
|
||||
is(event.oldValue, false, "event.oldValue is correct");
|
||||
is(event.newValue, true, "event.newValue is correct");
|
||||
|
||||
event = null;
|
||||
editor.setText("foo 2");
|
||||
ok(!event, "no DirtyChanged event fired");
|
||||
|
||||
editor.dirty = false;
|
||||
|
||||
is(editor.dirty, false, "editor marked as clean");
|
||||
ok(event, "DirtyChanged event fired")
|
||||
is(event.oldValue, true, "event.oldValue is correct");
|
||||
is(event.newValue, false, "event.newValue is correct");
|
||||
|
||||
event = null;
|
||||
editor.setText("foo 3");
|
||||
|
||||
is(editor.dirty, true, "editor is dirty after changes");
|
||||
ok(event, "DirtyChanged event fired")
|
||||
is(event.oldValue, false, "event.oldValue is correct");
|
||||
is(event.newValue, true, "event.newValue is correct");
|
||||
|
||||
editor.undo();
|
||||
is(editor.dirty, false, "editor is not dirty after undo");
|
||||
ok(event, "DirtyChanged event fired")
|
||||
is(event.oldValue, true, "event.oldValue is correct");
|
||||
is(event.newValue, false, "event.newValue is correct");
|
||||
|
||||
editor.removeEventListener(SourceEditor.EVENTS.DIRTY_CHANGED, eventHandler);
|
||||
|
||||
editor.destroy();
|
||||
|
||||
testWin.close();
|
||||
testWin = editor = null;
|
||||
|
||||
waitForFocus(finish, window);
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче