Bug 751744 - Add a Revert to Saved menu item to the scratchpad; r=harth
authorGirish Sharma <scrapmachines@gmail.com>
Thu, 06 Sep 2012 09:50:05 +0530
changeset 104693 008527c74b67d92433abea6d595f04c4ce0d9577
parent 104692 aaa9837d68b3761deff184ae7ac9197fd34c7c38
child 104694 5a46e13426b90237c015e1a02e3f57a083faa1cc
push id14635
push useremorley@mozilla.com
push dateMon, 10 Sep 2012 14:05:48 +0000
treeherdermozilla-inbound@e7b3c24ca2b8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersharth
bugs751744
milestone18.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 751744 - Add a Revert to Saved menu item to the scratchpad; r=harth
browser/devtools/scratchpad/scratchpad.js
browser/devtools/scratchpad/scratchpad.xul
browser/devtools/scratchpad/test/Makefile.in
browser/devtools/scratchpad/test/browser_scratchpad_bug_751744_revert_to_saved.js
browser/locales/en-US/chrome/browser/devtools/scratchpad.dtd
browser/locales/en-US/chrome/browser/devtools/scratchpad.properties
--- a/browser/devtools/scratchpad/scratchpad.js
+++ b/browser/devtools/scratchpad/scratchpad.js
@@ -30,16 +30,17 @@ Cu.import("resource://gre/modules/jsdebu
 const SCRATCHPAD_CONTEXT_CONTENT = 1;
 const SCRATCHPAD_CONTEXT_BROWSER = 2;
 const SCRATCHPAD_L10N = "chrome://browser/locale/devtools/scratchpad.properties";
 const DEVTOOLS_CHROME_ENABLED = "devtools.chrome.enabled";
 const PREF_RECENT_FILES_MAX = "devtools.scratchpad.recentFilesMax";
 const BUTTON_POSITION_SAVE = 0;
 const BUTTON_POSITION_CANCEL = 1;
 const BUTTON_POSITION_DONT_SAVE = 2;
+const BUTTON_POSITION_REVERT=0;
 
 let keysbundle = Services.strings.createBundle("chrome://global-platform/locale/platformKeys.properties");
 
 
 function SP_Pretty_Key(aElemKey) {
 
   let elemString = "";
   let elemMod = aElemKey.getAttribute("modifiers");
@@ -918,16 +919,82 @@ var Scratchpad = {
         if (aCallback) {
           aCallback(aStatus);
         }
       });
     }
   },
 
   /**
+   * Restore content from saved version of current file.
+   *
+   * @param function aCallback
+   *        Optional function you want to call when file is saved
+   */
+  revertFile: function SP_revertFile(aCallback)
+  {
+    
+    let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
+    file.initWithPath(this.filename);
+
+    if (!file.exists()) {
+      return;
+    }
+
+    this.importFromFile(file, false, function(aStatus, aContent) {
+      if (aCallback) {
+        aCallback(aStatus);
+      }
+    });
+  },
+
+  /**
+   * Prompt to revert scratchpad if it has unsaved changes.
+   *
+   * @param function aCallback
+   *        Optional function you want to call when file is saved. The callback
+   *        receives three arguments:
+   *          - aRevert (boolean) - tells if the file has been reverted.
+   *          - status (number) - the file revert status result (if the file was
+   *          saved).
+   */
+  promptRevert: function SP_promptRervert(aCallback)
+  {
+    if (this.filename) {
+      let ps = Services.prompt;
+      let flags = ps.BUTTON_POS_0 * ps.BUTTON_TITLE_REVERT +
+                  ps.BUTTON_POS_1 * ps.BUTTON_TITLE_CANCEL;
+
+      let button = ps.confirmEx(window,
+                          this.strings.GetStringFromName("confirmRevert.title"),
+                          this.strings.GetStringFromName("confirmRevert"),
+                          flags, null, null, null, null, {});
+      if (button == BUTTON_POSITION_CANCEL) {
+        if (aCallback) {
+          aCallback(false);
+        }
+
+        return;
+      }
+      if (button == BUTTON_POSITION_REVERT) {
+        this.revertFile(function(aStatus) {
+          if(aCallback){
+            aCallback(true, aStatus);
+          }
+        });
+
+        return;
+      }
+    }
+    if (aCallback) {
+      aCallback(false);
+    }
+  },
+
+  /**
    * Open the Error Console.
    */
   openErrorConsole: function SP_openErrorConsole()
   {
     this.browserWindow.toJavaScriptConsole();
   },
 
   /**
@@ -1102,16 +1169,24 @@ var Scratchpad = {
    * @private
    * @see SourceEditor.EVENTS.DIRTY_CHANGED
    * @param object aEvent
    *        The DirtyChanged event object.
    */
   _onDirtyChanged: function SP__onDirtyChanged(aEvent)
   {
     Scratchpad._updateTitle();
+    if (Scratchpad.filename) {
+      if (Scratchpad.editor.dirty) {
+        document.getElementById("sp-cmd-revert").removeAttribute("disabled");
+      }
+      else {
+        document.getElementById("sp-cmd-revert").setAttribute("disabled", true);
+      }
+    }
   },
 
   /**
    * Undo the last action of the user.
    */
   undo: function SP_undo()
   {
     this.editor.undo();
--- a/browser/devtools/scratchpad/scratchpad.xul
+++ b/browser/devtools/scratchpad/scratchpad.xul
@@ -28,16 +28,17 @@
 <commandset id="sourceEditorCommands"/>
 
 <commandset id="sp-commandset">
   <command id="sp-cmd-newWindow" oncommand="Scratchpad.openScratchpad();"/>
   <command id="sp-cmd-openFile" oncommand="Scratchpad.openFile();"/>
   <command id="sp-cmd-clearRecentFiles" oncommand="Scratchpad.clearRecentFiles();"/>
   <command id="sp-cmd-save" oncommand="Scratchpad.saveFile();"/>
   <command id="sp-cmd-saveas" oncommand="Scratchpad.saveFileAs();"/>
+  <command id="sp-cmd-revert" oncommand="Scratchpad.promptRevert();" disabled="true"/>
 
   <!-- TODO: bug 650340 - implement printFile()
   <command id="sp-cmd-printFile" oncommand="Scratchpad.printFile();" disabled="true"/>
  -->
 
   <command id="sp-cmd-close" oncommand="Scratchpad.close();"/>
   <command id="sp-cmd-run" oncommand="Scratchpad.run();"/>
   <command id="sp-cmd-inspect" oncommand="Scratchpad.inspect();"/>
@@ -127,16 +128,20 @@
                 label="&saveFileCmd.label;"
                 accesskey="&saveFileCmd.accesskey;"
                 key="sp-key-save"
                 command="sp-cmd-save"/>
       <menuitem id="sp-menu-saveas"
                 label="&saveFileAsCmd.label;"
                 accesskey="&saveFileAsCmd.accesskey;"
                 command="sp-cmd-saveas"/>
+      <menuitem id="sp-menu-revert"
+                label="&revertCmd.label;"
+                accesskey="&revertCmd.accesskey;"
+                command="sp-cmd-revert"/>
       <menuseparator/>
 
       <!-- TODO: bug 650340 - implement printFile
       <menuitem id="sp-menu-print"
                 label="&printCmd.label;"
                 accesskey="&printCmd.accesskey;"
                 command="sp-cmd-printFile"/>
       <menuseparator/>
--- a/browser/devtools/scratchpad/test/Makefile.in
+++ b/browser/devtools/scratchpad/test/Makefile.in
@@ -28,11 +28,12 @@ MOCHITEST_BROWSER_FILES = \
 		browser_scratchpad_bug_653427_confirm_close.js \
 		browser_scratchpad_bug684546_reset_undo.js \
 		browser_scratchpad_bug690552_display_outputs_errors.js \
 		browser_scratchpad_bug650345_find_ui.js \
 		browser_scratchpad_bug714942_goto_line_ui.js \
 		browser_scratchpad_bug_650760_help_key.js \
 		browser_scratchpad_bug_651942_recent_files.js \
 		browser_scratchpad_bug756681_display_non_error_exceptions.js \
+		browser_scratchpad_bug_751744_revert_to_saved.js \
 		head.js \
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_bug_751744_revert_to_saved.js
@@ -0,0 +1,137 @@
+/* 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://gre/modules/NetUtil.jsm", tempScope);
+Cu.import("resource://gre/modules/FileUtils.jsm", tempScope);
+let NetUtil = tempScope.NetUtil;
+let FileUtils = tempScope.FileUtils;
+
+// Reference to the Scratchpad object.
+let gScratchpad;
+
+// Reference to the temporary nsIFiles.
+let gFile;
+
+// Temporary file name.
+let gFileName = "testFileForBug751744.tmp"
+
+
+// Content for the temporary file.
+let gFileContent = "/* this file is already saved */\n" +
+                   "function foo() { alert('bar') }";
+let gLength = gFileContent.length;
+
+// Reference to the menu entry.
+let menu;
+
+function startTest()
+{
+  gScratchpad = gScratchpadWindow.Scratchpad;
+  menu = gScratchpadWindow.document.getElementById("sp-menu-revert");
+  createAndLoadTemporaryFile();
+}
+
+function testAfterSaved() {
+  // Check if the revert menu is disabled as the file is at saved state.
+  ok(menu.hasAttribute("disabled"), "The revert menu entry is disabled.");
+
+  // chancging the text in the file
+  gScratchpad.setText("\nfoo();", gLength, gLength);
+  // Checking the text got changed
+  is(gScratchpad.getText(), gFileContent + "\nfoo();",
+     "The text changed the first time.");
+
+  // Revert menu now should be enabled.
+  ok(!menu.hasAttribute("disabled"),
+     "The revert menu entry is enabled after changing text first time");
+
+  // reverting back to last saved state.
+  gScratchpad.revertFile(testAfterRevert);
+}
+
+function testAfterRevert() {
+  // Check if the file's text got reverted
+  is(gScratchpad.getText(), gFileContent,
+     "The text reverted back to original text.");
+  // The revert menu should be disabled again.
+  ok(menu.hasAttribute("disabled"),
+     "The revert menu entry is disabled after reverting.");
+
+  // chancging the text in the file again
+  gScratchpad.setText("\nalert(foo.toSource());", gLength, gLength);
+  // Saving the file.
+  gScratchpad.saveFile(testAfterSecondSave);
+}
+
+function testAfterSecondSave() {
+  // revert menu entry should be disabled.
+  ok(menu.hasAttribute("disabled"),
+     "The revert menu entry is disabled after saving.");
+
+  // changing the text.
+  gScratchpad.setText("\nfoo();", gLength + 23, gLength + 23);
+
+  // revert menu entry should get enabled yet again.
+  ok(!menu.hasAttribute("disabled"),
+     "The revert menu entry is enabled after changing text third time");
+
+  // reverting back to last saved state.
+  gScratchpad.revertFile(testAfterSecondRevert);
+}
+
+function testAfterSecondRevert() {
+  // Check if the file's text got reverted
+  is(gScratchpad.getText(), gFileContent + "\nalert(foo.toSource());",
+     "The text reverted back to the changed saved text.");
+  // The revert menu should be disabled again.
+  ok(menu.hasAttribute("disabled"),
+     "Revert menu entry is disabled after reverting to changed saved state.");
+  gFile.remove(false);
+  gFile = null;
+  gScratchpad = null;
+}
+
+function createAndLoadTemporaryFile()
+{
+  // Create a temporary file.
+  gFile = FileUtils.getFile("TmpD", [gFileName]);
+  gFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
+
+  // Write the temporary file.
+  let fout = Cc["@mozilla.org/network/file-output-stream;1"].
+             createInstance(Ci.nsIFileOutputStream);
+  fout.init(gFile.QueryInterface(Ci.nsILocalFile), 0x02 | 0x08 | 0x20,
+            0644, fout.DEFER_OPEN);
+
+  let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
+                  createInstance(Ci.nsIScriptableUnicodeConverter);
+  converter.charset = "UTF-8";
+  let fileContentStream = converter.convertToInputStream(gFileContent);
+
+  NetUtil.asyncCopy(fileContentStream, fout, tempFileSaved);
+}
+
+function tempFileSaved(aStatus)
+{
+  ok(Components.isSuccessCode(aStatus),
+     "the temporary file was saved successfully");
+
+  // Import the file into Scratchpad.
+  gScratchpad.setFilename(gFile.path);
+  gScratchpad.importFromFile(gFile.QueryInterface(Ci.nsILocalFile),  true,
+                             testAfterSaved);
+}
+
+function test()
+{
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
+    gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
+    openScratchpad(startTest);
+  }, true);
+
+  content.location = "data:text/html,<p>test reverting to last saved state of" +
+                     " a file </p>";
+}
--- a/browser/locales/en-US/chrome/browser/devtools/scratchpad.dtd
+++ b/browser/locales/en-US/chrome/browser/devtools/scratchpad.dtd
@@ -31,16 +31,19 @@
 
 <!ENTITY openFileCmd.label            "Open File…">
 <!ENTITY openFileCmd.accesskey        "O">
 <!ENTITY openFileCmd.commandkey       "o">
 
 <!ENTITY openRecentMenu.label         "Open Recent">
 <!ENTITY openRecentMenu.accesskey     "R">
 
+<!ENTITY revertCmd.label              "Revert…">
+<!ENTITY revertCmd.accesskey          "t">
+
 <!ENTITY saveFileCmd.label            "Save">
 <!ENTITY saveFileCmd.accesskey        "S">
 <!ENTITY saveFileCmd.commandkey       "s">
 
 <!ENTITY saveFileAsCmd.label          "Save As…">
 <!ENTITY saveFileAsCmd.accesskey      "A">
 
 <!ENTITY closeCmd.label               "Close">
--- a/browser/locales/en-US/chrome/browser/devtools/scratchpad.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/scratchpad.properties
@@ -49,16 +49,24 @@ saveFile.failed=The file save operation 
 # LOCALIZATION NOTE  (confirmClose): This is message in the prompt dialog when
 # you try to close a scratchpad with unsaved changes.
 confirmClose=Do you want to save the changes you made to this scratchpad?
 
 # LOCALIZATION NOTE  (confirmClose.title): This is title of the prompt dialog when
 # you try to close a scratchpad with unsaved changes.
 confirmClose.title=Unsaved Changes
 
+# LOCALIZATION NOTE  (confirmRevert): This is message in the prompt dialog when
+# you try to revert unsaved content of scratchpad.
+confirmRevert=Do you want to revert the changes you made to this scratchpad?
+
+# LOCALIZATION NOTE  (confirmRevert.title): This is title of the prompt dialog when
+# you try to revert unsaved content of scratchpad.
+confirmRevert.title=Revert Changes
+
 # LOCALIZATION NOTE  (scratchpadIntro): This is a multi-line comment explaining
 # how to use the Scratchpad. Note that this should be a valid JavaScript
 # comment inside /* and */.
 scratchpadIntro1=/*\n * This is a JavaScript Scratchpad.\n *\n * Enter some JavaScript, then Right Click or choose from the Execute Menu:\n * 1. Run to evaluate the selected text (%1$S),\n * 2. Inspect to bring up an Object Inspector on the result (%2$S), or,\n * 3. Display to insert the result in a comment after the selection. (%3$S)\n */\n\n
 
 # LOCALIZATION NOTE  (notification.browserContext): This is the message displayed
 # over the top of the editor when the user has switched to browser context.
 browserContext.notification=This scratchpad executes in the Browser context.