michael@0: /* vim: set ts=2 et sw=2 tw=80: */ michael@0: /* Any copyright is dedicated to the Public Domain. michael@0: http://creativecommons.org/publicdomain/zero/1.0/ */ michael@0: michael@0: Components.utils.import("resource://gre/modules/Task.jsm"); michael@0: let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}); michael@0: let {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {}); michael@0: michael@0: const TESTCASE_URI_HTML = TEST_BASE + "sourcemaps.html"; michael@0: const TESTCASE_URI_CSS = TEST_BASE + "sourcemap-css/sourcemaps.css"; michael@0: const TESTCASE_URI_CSS2 = TEST_BASE + "sourcemap-css/contained.css"; michael@0: const TESTCASE_URI_REG_CSS = TEST_BASE + "simple.css"; michael@0: const TESTCASE_URI_SCSS = TEST_BASE + "sourcemap-sass/sourcemaps.scss"; michael@0: const TESTCASE_URI_MAP = TEST_BASE + "sourcemap-css/sourcemaps.css.map"; michael@0: const TESTCASE_SCSS_NAME = "sourcemaps.scss"; michael@0: michael@0: const SOURCE_MAP_PREF = "devtools.styleeditor.source-maps-enabled"; michael@0: const TRANSITIONS_PREF = "devtools.styleeditor.transitions"; michael@0: michael@0: const CSS_TEXT = "* { color: blue }"; michael@0: michael@0: const Cc = Components.classes; michael@0: const Ci = Components.interfaces; michael@0: michael@0: let tempScope = {}; michael@0: Components.utils.import("resource://gre/modules/FileUtils.jsm", tempScope); michael@0: Components.utils.import("resource://gre/modules/NetUtil.jsm", tempScope); michael@0: let FileUtils = tempScope.FileUtils; michael@0: let NetUtil = tempScope.NetUtil; michael@0: michael@0: function test() michael@0: { michael@0: waitForExplicitFinish(); michael@0: michael@0: Services.prefs.setBoolPref(SOURCE_MAP_PREF, true); michael@0: Services.prefs.setBoolPref(TRANSITIONS_PREF, false); michael@0: michael@0: Task.spawn(function() { michael@0: // copy all our files over so we don't screw them up for other tests michael@0: let HTMLFile = yield copy(TESTCASE_URI_HTML, ["sourcemaps.html"]); michael@0: let CSSFile = yield copy(TESTCASE_URI_CSS, ["sourcemap-css", "sourcemaps.css"]); michael@0: let CSSFile2 = yield copy(TESTCASE_URI_CSS2, ["sourcemap-css", "contained.css"]); michael@0: yield copy(TESTCASE_URI_SCSS, ["sourcemap-sass", "sourcemaps.scss"]); michael@0: yield copy(TESTCASE_URI_MAP, ["sourcemap-css", "sourcemaps.css.map"]); michael@0: yield copy(TESTCASE_URI_REG_CSS, ["simple.css"]); michael@0: michael@0: let uri = Services.io.newFileURI(HTMLFile); michael@0: let testcaseURI = uri.resolve(""); michael@0: michael@0: let editor = yield openEditor(testcaseURI); michael@0: michael@0: let element = content.document.querySelector("div"); michael@0: let style = content.getComputedStyle(element, null); michael@0: michael@0: is(style.color, "rgb(255, 0, 102)", "div is red before saving file"); michael@0: michael@0: editor.styleSheet.relatedStyleSheet.once("style-applied", function() { michael@0: is(style.color, "rgb(0, 0, 255)", "div is blue after saving file"); michael@0: finishUp(); michael@0: }); michael@0: michael@0: yield pauseForTimeChange(); michael@0: michael@0: // Edit and save Sass in the editor. This will start off a file-watching michael@0: // process waiting for the CSS file to change. michael@0: yield editSCSS(editor); michael@0: michael@0: // We can't run Sass or another compiler, so we fake it by just michael@0: // directly changing the CSS file. michael@0: yield editCSSFile(CSSFile); michael@0: michael@0: info("wrote to CSS file"); michael@0: }) michael@0: } michael@0: michael@0: function openEditor(testcaseURI) { michael@0: let deferred = promise.defer(); michael@0: michael@0: addTabAndOpenStyleEditors(5, panel => { michael@0: let UI = panel.UI; michael@0: michael@0: // wait for 5 editors - 1 for first style sheet, 2 for the michael@0: // generated style sheets, and 2 for original source after it michael@0: // loads and replaces the generated style sheets. michael@0: let editor = UI.editors[1]; michael@0: if (getStylesheetNameFor(editor) != TESTCASE_SCSS_NAME) { michael@0: editor = UI.editors[2]; michael@0: } michael@0: is(getStylesheetNameFor(editor), TESTCASE_SCSS_NAME, "found scss editor"); michael@0: michael@0: let link = getLinkFor(editor); michael@0: link.click(); michael@0: michael@0: editor.getSourceEditor().then(deferred.resolve); michael@0: }); michael@0: content.location = testcaseURI; michael@0: michael@0: return deferred.promise; michael@0: } michael@0: michael@0: function editSCSS(editor) { michael@0: let deferred = promise.defer(); michael@0: michael@0: let pos = {line: 0, ch: 0}; michael@0: editor.sourceEditor.replaceText(CSS_TEXT, pos, pos); michael@0: michael@0: editor.saveToFile(null, function (file) { michael@0: ok(file, "Scss file should be saved"); michael@0: deferred.resolve(); michael@0: }); michael@0: michael@0: return deferred.promise; michael@0: } michael@0: michael@0: function editCSSFile(CSSFile) { michael@0: return write(CSS_TEXT, CSSFile); michael@0: } michael@0: michael@0: function pauseForTimeChange() { michael@0: let deferred = promise.defer(); michael@0: michael@0: // We have to wait for the system time to turn over > 1000 ms so that michael@0: // our file's last change time will show a change. This reflects what michael@0: // would happen in real life with a user manually saving the file. michael@0: setTimeout(deferred.resolve, 2000); michael@0: michael@0: return deferred.promise; michael@0: } michael@0: michael@0: function finishUp() { michael@0: Services.prefs.clearUserPref(SOURCE_MAP_PREF); michael@0: Services.prefs.clearUserPref(TRANSITIONS_PREF); michael@0: finish(); michael@0: } michael@0: michael@0: /* Helpers */ michael@0: michael@0: function getLinkFor(editor) { michael@0: return editor.summary.querySelector(".stylesheet-name"); michael@0: } michael@0: michael@0: function getStylesheetNameFor(editor) { michael@0: return editor.summary.querySelector(".stylesheet-name > label") michael@0: .getAttribute("value") michael@0: } michael@0: michael@0: function copy(aSrcChromeURL, aDestFilePath) michael@0: { michael@0: let destFile = FileUtils.getFile("ProfD", aDestFilePath); michael@0: return write(read(aSrcChromeURL), destFile); michael@0: } michael@0: michael@0: function read(aSrcChromeURL) michael@0: { michael@0: let scriptableStream = Cc["@mozilla.org/scriptableinputstream;1"] michael@0: .getService(Ci.nsIScriptableInputStream); michael@0: michael@0: let channel = Services.io.newChannel(aSrcChromeURL, null, null); michael@0: let input = channel.open(); michael@0: scriptableStream.init(input); michael@0: michael@0: let data = ""; michael@0: while (input.available()) { michael@0: data = data.concat(scriptableStream.read(input.available())); michael@0: } michael@0: scriptableStream.close(); michael@0: input.close(); michael@0: michael@0: return data; michael@0: } michael@0: michael@0: function write(aData, aFile) michael@0: { michael@0: let deferred = promise.defer(); michael@0: michael@0: let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"] michael@0: .createInstance(Ci.nsIScriptableUnicodeConverter); michael@0: michael@0: converter.charset = "UTF-8"; michael@0: michael@0: let istream = converter.convertToInputStream(aData); michael@0: let ostream = FileUtils.openSafeFileOutputStream(aFile); michael@0: michael@0: NetUtil.asyncCopy(istream, ostream, function(status) { michael@0: if (!Components.isSuccessCode(status)) { michael@0: info("Coudln't write to " + aFile.path); michael@0: return; michael@0: } michael@0: deferred.resolve(aFile); michael@0: }); michael@0: michael@0: return deferred.promise; michael@0: }