Bug 583041 - Style Editor integration; part 5 - tests; r=rcampbell
authorCedric Vivier <cedricv@neonux.com>
Wed, 26 Oct 2011 17:52:17 +0800
changeset 80390 1cac8411fdc68be72bbfdeccf44f360039d5cfa0
parent 80389 4119bee2221dea719ddaf61fd09103902c41a5e9
child 80391 d756772d3ed0185f475303692403e1e988d6c9cb
push id21489
push userbmcbride@mozilla.com
push dateFri, 18 Nov 2011 01:56:06 +0000
treeherdermozilla-central@169516414349 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrcampbell
bugs583041
milestone11.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 583041 - Style Editor integration; part 5 - tests; r=rcampbell
browser/devtools/styleeditor/test/Makefile.in
browser/devtools/styleeditor/test/browser_styleeditor_enabled.js
browser/devtools/styleeditor/test/browser_styleeditor_import.js
browser/devtools/styleeditor/test/browser_styleeditor_init.js
browser/devtools/styleeditor/test/browser_styleeditor_loading.js
browser/devtools/styleeditor/test/browser_styleeditor_new.js
browser/devtools/styleeditor/test/browser_styleeditor_pretty.js
browser/devtools/styleeditor/test/browser_styleeditor_readonly.js
browser/devtools/styleeditor/test/browser_styleeditor_reopen.js
browser/devtools/styleeditor/test/browser_styleeditor_sv_filter.js
browser/devtools/styleeditor/test/browser_styleeditor_sv_keynav.js
browser/devtools/styleeditor/test/browser_styleeditor_sv_resize.js
browser/devtools/styleeditor/test/four.html
browser/devtools/styleeditor/test/head.js
browser/devtools/styleeditor/test/media-small.css
browser/devtools/styleeditor/test/media.html
browser/devtools/styleeditor/test/minified.html
browser/devtools/styleeditor/test/simple.css
browser/devtools/styleeditor/test/simple.css.gz
browser/devtools/styleeditor/test/simple.css.gz^headers^
browser/devtools/styleeditor/test/simple.gz.html
browser/devtools/styleeditor/test/simple.html
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleeditor/test/Makefile.in
@@ -0,0 +1,72 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is Style Editor code.
+#
+# The Initial Developer of the Original Code is Mozilla Foundation.
+#
+# Portions created by the Initial Developer are Copyright (C) 2007
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Cedric Vivier <cedricv@neonux.com> (original author)
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either of the GNU General Public License Version 2 or later (the "GPL"),
+# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH		= ../../../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+relativesrcdir  = browser/devtools/styleeditor/test
+
+include $(DEPTH)/config/autoconf.mk
+include $(topsrcdir)/config/rules.mk
+
+_BROWSER_TEST_FILES = \
+                 browser_styleeditor_enabled.js \
+                 browser_styleeditor_import.js \
+                 browser_styleeditor_init.js \
+                 browser_styleeditor_loading.js \
+                 browser_styleeditor_new.js \
+                 browser_styleeditor_pretty.js \
+                 browser_styleeditor_readonly.js \
+                 browser_styleeditor_reopen.js \
+                 browser_styleeditor_sv_filter.js \
+                 browser_styleeditor_sv_keynav.js \
+                 browser_styleeditor_sv_resize.js \
+                 four.html \
+                 head.js \
+                 media.html \
+                 media-small.css \
+                 minified.html \
+                 simple.css \
+                 simple.css.gz \
+                 simple.css.gz^headers^ \
+                 simple.gz.html \
+                 simple.html \
+                 $(NULL)
+
+libs::	$(_BROWSER_TEST_FILES)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_enabled.js
@@ -0,0 +1,101 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// https rather than chrome to improve coverage
+const TESTCASE_URI = TEST_BASE_HTTPS + "simple.html";
+
+
+function test()
+{
+  waitForExplicitFinish();
+
+  let count = 0;
+  addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
+    aChrome.addChromeListener({
+      onEditorAdded: function (aChrome, aEditor) {
+        count++;
+        if (count == 2) {
+          // we test against first stylesheet after all are ready
+          let editor = aChrome.editors[0];
+          if (!editor.sourceEditor) {
+            editor.addActionListener({
+              onAttach: function (aEditor) {
+                run(aChrome, aEditor);
+              }
+            });
+          } else {
+            run(aChrome, editor);
+          }
+        }
+      }
+    });
+  });
+
+  content.location = TESTCASE_URI;
+}
+
+function run(aChrome, aEditor)
+{
+  testEnabledToggle(aChrome, aEditor);
+}
+
+function testEnabledToggle(aChrome, aEditor)
+{
+  is(aEditor, aChrome.editors[0],
+     "stylesheet with index 0 is the first stylesheet listed in the UI");
+
+  let firstStyleSheetEditor = aEditor;
+  let firstStyleSheetUI = aChrome.getSummaryElementForEditor(aEditor);
+  let enabledToggle = firstStyleSheetUI.querySelector(".stylesheet-enabled");
+
+  is(firstStyleSheetEditor.contentDocument.styleSheets[0].disabled, false,
+     "first stylesheet is initially enabled");
+  is(firstStyleSheetEditor.hasFlag("disabled"), false,
+     "first stylesheet is initially enabled, it does not have DISABLED flag");
+  is(firstStyleSheetUI.classList.contains("disabled"), false,
+     "first stylesheet is initially enabled, UI does not have DISABLED class");
+
+  let disabledToggleCount = 0;
+  firstStyleSheetEditor.addActionListener({
+    onFlagChange: function (aEditor, aFlagName) {
+      if (aFlagName != "disabled") {
+        return;
+      }
+      disabledToggleCount++;
+
+      if (disabledToggleCount == 1) {
+        is(firstStyleSheetEditor, aEditor,
+           "FlagChange handler triggered for DISABLED flag on the first editor");
+        is(firstStyleSheetEditor.styleSheet.disabled, true,
+           "first stylesheet is now disabled");
+        is(firstStyleSheetEditor.hasFlag("disabled"), true,
+           "first stylesheet is now disabled, it has DISABLED flag");
+        is(firstStyleSheetUI.classList.contains("disabled"), true,
+           "first stylesheet is now disabled, UI has DISABLED class");
+
+        // now toggle it back to enabled
+        waitForFocus(function () {
+          EventUtils.synthesizeMouseAtCenter(enabledToggle, {}, gChromeWindow);
+        }, gChromeWindow);
+        return;
+      }
+
+      // disabledToggleCount == 2
+      is(firstStyleSheetEditor, aEditor,
+         "FlagChange handler triggered for DISABLED flag on the first editor (2)");
+      is(firstStyleSheetEditor.styleSheet.disabled, false,
+         "first stylesheet is now enabled again");
+      is(firstStyleSheetEditor.hasFlag("disabled"), false,
+         "first stylesheet is now enabled again, it does not have DISABLED flag");
+      is(firstStyleSheetUI.classList.contains("disabled"), false,
+         "first stylesheet is now enabled again, UI does not have DISABLED class");
+
+      finish();
+    }
+  });
+
+  waitForFocus(function () {
+    EventUtils.synthesizeMouseAtCenter(enabledToggle, {}, gChromeWindow);
+  }, gChromeWindow);
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_import.js
@@ -0,0 +1,87 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// http rather than chrome to improve coverage
+const TESTCASE_URI = TEST_BASE_HTTP + "simple.html";
+
+Components.utils.import("resource://gre/modules/FileUtils.jsm");
+const FILENAME = "styleeditor-import-test.css";
+const SOURCE = "body{background:red;}";
+
+
+function test()
+{
+  waitForExplicitFinish();
+
+  addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
+    aChrome.addChromeListener({
+      onContentAttach: run,
+      onEditorAdded: testEditorAdded
+    });
+    if (aChrome.isContentAttached) {
+      run(aChrome);
+    }
+  });
+
+  content.location = TESTCASE_URI;
+}
+
+function run(aChrome)
+{
+  is(aChrome.editors.length, 2,
+     "there is 2 stylesheets initially");
+}
+
+function testImport(aChrome, aEditor)
+{
+  // create file to import first
+  let file = FileUtils.getFile("ProfD", [FILENAME]);
+  let ostream = FileUtils.openSafeFileOutputStream(file);
+  let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
+                    .createInstance(Ci.nsIScriptableUnicodeConverter);
+  converter.charset = "UTF-8";
+  let istream = converter.convertToInputStream(SOURCE);
+  NetUtil.asyncCopy(istream, ostream, function (status) {
+    FileUtils.closeSafeFileOutputStream(ostream);
+
+    // click the import button now that the file to import is ready
+    aChrome._mockImportFile = file;
+
+    waitForFocus(function () {
+      let document = gChromeWindow.document
+      let importButton = document.querySelector(".style-editor-importButton");
+      EventUtils.synthesizeMouseAtCenter(importButton, {}, gChromeWindow);
+    }, gChromeWindow);
+  });
+}
+
+let gAddedCount = 0;
+function testEditorAdded(aChrome, aEditor)
+{
+  if (++gAddedCount == 2) {
+    // test import after the 2 initial stylesheets have been loaded
+    if (!aChrome.editors[0].sourceEditor) {
+      aChrome.editors[0].addActionListener({
+        onAttach: function () {
+          testImport(aChrome);
+        }
+      });
+    } else {
+      testImport(aChrome);
+    }
+  }
+
+  if (!aEditor.hasFlag("imported")) {
+    return;
+  }
+
+  ok(!aEditor.hasFlag("inline"),
+     "imported stylesheet does not have INLINE flag");
+  ok(aEditor.savedFile,
+     "imported stylesheet will be saved directly into the same file");
+  is(aEditor.getFriendlyName(), FILENAME,
+     "imported stylesheet has the same name as the filename");
+
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_init.js
@@ -0,0 +1,126 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const TESTCASE_URI = TEST_BASE + "simple.html";
+
+
+function test()
+{
+  waitForExplicitFinish();
+
+  addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
+    aChrome.addChromeListener({
+      onContentAttach: run,
+      onEditorAdded: testEditorAdded
+    });
+    if (aChrome.isContentAttached) {
+      run(aChrome);
+    }
+  });
+
+  content.location = TESTCASE_URI;
+}
+
+let gContentAttachHandled = false;
+function run(aChrome)
+{
+  gContentAttachHandled = true;
+  is(aChrome.contentWindow.document.readyState, "complete",
+     "content document is complete");
+
+  let SEC = gChromeWindow.styleEditorChrome;
+  is(SEC, aChrome, "StyleEditorChrome object exists as new window property");
+
+  ok(gChromeWindow.document.title.indexOf("simple testcase") >= 0,
+     "the Style Editor window title contains the document's title");
+
+  // check editors are instantiated
+  is(SEC.editors.length, 2,
+     "there is two StyleEditor instances managed");
+  ok(SEC.editors[0].styleSheetIndex < SEC.editors[1].styleSheetIndex,
+     "editors are ordered by styleSheetIndex");
+
+  // check StyleEditorChrome is a singleton wrt to the same DOMWindow
+  let chromeWindow = StyleEditor.openChrome();
+  is(chromeWindow, gChromeWindow,
+     "attempt to edit the same document returns the same Style Editor window");
+}
+
+let gEditorAddedCount = 0;
+function testEditorAdded(aChrome, aEditor)
+{
+  if (!gEditorAddedCount) {
+    is(gContentAttachHandled, true,
+       "ContentAttach event triggered before EditorAdded");
+  }
+
+  if (aEditor.styleSheetIndex == 0) {
+    gEditorAddedCount++;
+    testFirstStyleSheetEditor(aChrome, aEditor);
+  }
+  if (aEditor.styleSheetIndex == 1) {
+    gEditorAddedCount++;
+    testSecondStyleSheetEditor(aChrome, aEditor);
+  }
+
+  if (gEditorAddedCount == 2) {
+    finish();
+  }
+}
+
+function testFirstStyleSheetEditor(aChrome, aEditor)
+{
+  //testing TESTCASE's simple.css stylesheet
+  is(aEditor.styleSheetIndex, 0,
+     "first stylesheet is at index 0");
+
+  is(aEditor, aChrome.editors[0],
+     "first stylesheet corresponds to StyleEditorChrome.editors[0]");
+
+  ok(!aEditor.hasFlag("inline"),
+     "first stylesheet does not have INLINE flag");
+
+  let summary = aChrome.getSummaryElementForEditor(aEditor);
+  ok(!summary.classList.contains("inline"),
+     "first stylesheet UI does not have INLINE class");
+
+  let name = summary.querySelector(".stylesheet-name").textContent;
+  is(name, "simple.css",
+     "first stylesheet's name is `simple.css`");
+
+  let ruleCount = summary.querySelector(".stylesheet-rule-count").textContent;
+  is(parseInt(ruleCount), 1,
+     "first stylesheet UI shows rule count as 1");
+
+  ok(summary.classList.contains("splitview-active"),
+     "first stylesheet UI is focused/active");
+}
+
+function testSecondStyleSheetEditor(aChrome, aEditor)
+{
+  //testing TESTCASE's inline stylesheet
+  is(aEditor.styleSheetIndex, 1,
+     "second stylesheet is at index 1");
+
+  is(aEditor, aChrome.editors[1],
+     "second stylesheet corresponds to StyleEditorChrome.editors[1]");
+
+  ok(aEditor.hasFlag("inline"),
+     "second stylesheet has INLINE flag");
+
+  let summary = aChrome.getSummaryElementForEditor(aEditor);
+  ok(summary.classList.contains("inline"),
+     "second stylesheet UI has INLINE class");
+
+  let name = summary.querySelector(".stylesheet-name").textContent;
+  ok(/^<.*>$/.test(name),
+     "second stylesheet's name is surrounded by `<>`");
+
+  let ruleCount = summary.querySelector(".stylesheet-rule-count").textContent;
+  is(parseInt(ruleCount), 3,
+     "second stylesheet UI shows rule count as 3");
+
+  ok(!summary.classList.contains("splitview-active"),
+     "second stylesheet UI is NOT focused/active");
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_loading.js
@@ -0,0 +1,39 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const TESTCASE_URI = TEST_BASE + "simple.html";
+
+
+function test()
+{
+  waitForExplicitFinish();
+
+  gBrowser.selectedTab = gBrowser.addTab();
+
+  // launch Style Editor right when the tab is created (before load)
+  // this checks that the Style Editor still launches correctly when it is opened
+  // *while* the page is still loading
+  launchStyleEditorChrome(function (aChrome) {
+    isnot(gBrowser.selectedBrowser.contentWindow.document.readyState, "complete",
+          "content document is still loading");
+
+    if (!aChrome.isContentAttached) {
+      aChrome.addChromeListener({
+        onContentAttach: run
+      });
+    } else {
+      run(aChrome);
+    }
+  });
+
+  content.location = TESTCASE_URI;
+}
+
+function run(aChrome)
+{
+  is(aChrome.contentWindow.document.readyState, "complete",
+     "content document is complete");
+
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_new.js
@@ -0,0 +1,117 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const TESTCASE_URI = TEST_BASE + "simple.html";
+
+
+function test()
+{
+  waitForExplicitFinish();
+
+  addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
+    aChrome.addChromeListener({
+      onContentAttach: run,
+      onEditorAdded: testEditorAdded
+    });
+    if (aChrome.isContentAttached) {
+      run(aChrome);
+    }
+  });
+
+  content.location = TESTCASE_URI;
+}
+
+function run(aChrome)
+{
+  is(aChrome.editors.length, 2,
+     "there is 2 stylesheets initially");
+}
+
+let gAddedCount = 0;  // to add new stylesheet after the 2 initial stylesheets
+let gNewEditor;       // to make sure only one new stylesheet got created
+let gCommitCount = 0; // to make sure only one Commit event is triggered
+
+function testEditorAdded(aChrome, aEditor)
+{
+  gAddedCount++;
+  if (gAddedCount == 2) {
+    waitForFocus(function () { // create a new style sheet
+      let newButton = gChromeWindow.document.querySelector(".style-editor-newButton");
+      EventUtils.synthesizeMouseAtCenter(newButton, {}, gChromeWindow);
+    }, gChromeWindow);
+  }
+  if (gAddedCount != 3) {
+    return;
+  }
+
+  ok(!gNewEditor, "creating a new stylesheet triggers one EditorAdded event");
+  gNewEditor = aEditor; // above test will fail if we get a duplicate event
+
+  is(aChrome.editors.length, 3,
+     "creating a new stylesheet added a new StyleEditor instance");
+
+  let listener = {
+    onAttach: function (aEditor) {
+      waitForFocus(function () {
+        ok(aEditor.isLoaded,
+           "new editor is loaded when attached");
+        ok(aEditor.hasFlag("new"),
+           "new editor has NEW flag");
+        ok(!aEditor.hasFlag("unsaved"),
+           "new editor does not have UNSAVED flag");
+
+        ok(aEditor.inputElement,
+           "new editor has an input element attached");
+
+        ok(aEditor.sourceEditor.hasFocus(),
+           "new editor has focus");
+
+        let summary = aChrome.getSummaryElementForEditor(aEditor);
+        let ruleCount = summary.querySelector(".stylesheet-rule-count").textContent;
+        is(parseInt(ruleCount), 0,
+           "new editor initially shows 0 rules");
+
+        let computedStyle = content.getComputedStyle(content.document.body, null);
+        is(computedStyle.backgroundColor, "rgb(255, 255, 255)",
+           "content's background color is initially white");
+
+        for each (let c in "body{background-color:red;}") {
+          EventUtils.synthesizeKey(c, {}, gChromeWindow);
+        }
+      }, gChromeWindow) ;
+    },
+
+    onCommit: function (aEditor) {
+      gCommitCount++;
+
+      ok(aEditor.hasFlag("new"),
+         "new editor still has NEW flag");
+      ok(aEditor.hasFlag("unsaved"),
+         "new editor has UNSAVED flag after modification");
+
+      let summary = aChrome.getSummaryElementForEditor(aEditor);
+      let ruleCount = summary.querySelector(".stylesheet-rule-count").textContent;
+      is(parseInt(ruleCount), 1,
+         "new editor shows 1 rule after modification");
+
+      let computedStyle = content.getComputedStyle(content.document.body, null);
+      is(computedStyle.backgroundColor, "rgb(255, 0, 0)",
+         "content's background color has been updated to red");
+
+      executeSoon(function () {
+        is(gCommitCount, 1, "received only one Commit event (throttle)");
+
+        aEditor.removeActionListener(listener);
+
+        gNewEditor = null;
+        finish();
+      });
+    }
+  };
+
+  aEditor.addActionListener(listener);
+  if (aEditor.sourceEditor) {
+    listener.onAttach(aEditor);
+  }
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_pretty.js
@@ -0,0 +1,56 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const TESTCASE_URI = TEST_BASE + "minified.html";
+
+
+function test()
+{
+  waitForExplicitFinish();
+
+  addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
+    aChrome.addChromeListener({
+      onEditorAdded: function (aChrome, aEditor) {
+        if (aEditor.sourceEditor) {
+          run(aEditor); // already attached to input element
+        } else {
+          aEditor.addActionListener({
+            onAttach: run
+          });
+        }
+      }
+    });
+  });
+
+  content.location = TESTCASE_URI;
+}
+
+let editorTestedCount = 0;
+function run(aEditor)
+{
+  if (aEditor.styleSheetIndex == 0) {
+    let prettifiedSource = "body\{\r?\n\tbackground\:white;\r?\n\}\r?\n\r?\ndiv\{\r?\n\tfont\-size\:4em;\r?\n\tcolor\:red\r?\n\}\r?\n";
+    let prettifiedSourceRE = new RegExp(prettifiedSource);
+
+    ok(prettifiedSourceRE.test(aEditor.sourceEditor.getText()),
+       "minified source has been prettified automatically");
+    editorTestedCount++;
+    let chrome = gChromeWindow.styleEditorChrome;
+    let summary = chrome.getSummaryElementForEditor(chrome.editors[1]);
+    EventUtils.synthesizeMouseAtCenter(summary, {}, gChromeWindow);
+  }
+
+  if (aEditor.styleSheetIndex == 1) {
+    let originalSource = "body \{ background\: red; \}\r?\ndiv \{\r?\nfont\-size\: 5em;\r?\ncolor\: red\r?\n\}";
+    let originalSourceRE = new RegExp(originalSource);
+
+    ok(originalSourceRE.test(aEditor.sourceEditor.getText()),
+       "non-minified source has been left untouched");
+    editorTestedCount++;
+  }
+
+  if (editorTestedCount == 2) {
+    finish();
+  }
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_readonly.js
@@ -0,0 +1,74 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const TESTCASE_URI = TEST_BASE + "simple.html";
+
+
+let gEditorAddedCount = 0;
+let gEditorReadOnlyCount = 0;
+let gChromeListener = {
+  onEditorAdded: function (aChrome, aEditor) {
+    gEditorAddedCount++;
+    if (aEditor.readOnly) {
+      gEditorReadOnlyCount++;
+    }
+
+    if (gEditorAddedCount == aChrome.editors.length) {
+      // continue testing after all editors are ready
+
+      is(gEditorReadOnlyCount, 0,
+         "all editors are NOT read-only initially");
+
+      // all editors have been loaded, queue closing the content tab
+      executeSoon(function () {
+        gBrowser.removeCurrentTab();
+      });
+    }
+  },
+  onContentDetach: function (aChrome) {
+    // check that the UI has switched to read-only
+    run(aChrome);
+  }
+};
+
+function test()
+{
+  waitForExplicitFinish();
+
+  gBrowser.addTab(); // because we'll close the next one
+  addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
+    aChrome.addChromeListener(gChromeListener);
+  });
+
+  content.location = TESTCASE_URI;
+}
+
+function run(aChrome)
+{
+  let document = gChromeWindow.document;
+  let disabledCount;
+  let elements;
+
+  disabledCount = 0;
+  elements = document.querySelectorAll("button,input,select");
+  for (let i = 0; i < elements.length; ++i) {
+    if (elements[i].hasAttribute("disabled")) {
+      disabledCount++;
+    }
+  }
+  ok(elements.length && disabledCount == elements.length,
+     "all buttons, input and select elements are disabled");
+
+  disabledCount = 0;
+  aChrome.editors.forEach(function (aEditor) {
+    if (aEditor.readOnly) {
+      disabledCount++;
+    }
+  });
+  ok(aChrome.editors.length && disabledCount == aChrome.editors.length,
+     "all editors are read-only");
+
+  aChrome.removeChromeListener(gChromeListener);
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_reopen.js
@@ -0,0 +1,155 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// http rather than chrome to improve coverage
+const TESTCASE_URI = TEST_BASE_HTTP + "simple.gz.html";
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+Components.utils.import("resource://gre/modules/FileUtils.jsm");
+
+
+function test()
+{
+  waitForExplicitFinish();
+
+  addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
+    aChrome.addChromeListener({
+      onEditorAdded: function (aChrome, aEditor) {
+        if (aEditor.styleSheetIndex != 0) {
+          return; // we want to test against the first stylesheet
+        }
+
+        if (aEditor.sourceEditor) {
+          run(aEditor); // already attached to input element
+        } else {
+          aEditor.addActionListener({
+            onAttach: run
+          });
+        }
+      }
+    });
+
+    gChromeWindow.addEventListener("unload", function onClose() {
+      gChromeWindow.removeEventListener("unload", onClose, true);
+      gChromeWindow = null;
+      executeSoon(function () {
+        waitForFocus(function () {
+          // wait that browser has focus again
+          // open StyleEditorChrome again (a new one since we closed the previous one)
+          launchStyleEditorChrome(function (aChrome) {
+            is(gChromeWindow.document.documentElement.hasAttribute("data-marker"),
+               false,
+               "opened a completely new StyleEditorChrome window");
+
+            aChrome.addChromeListener({
+              onEditorAdded: function (aChrome, aEditor) {
+                if (aEditor.styleSheetIndex != 0) {
+                  return; // we want to test against the first stylesheet
+                }
+
+                if (aEditor.sourceEditor) {
+                  testNewChrome(aEditor); // already attached to input element
+                } else {
+                  aEditor.addActionListener({
+                    onAttach: testNewChrome
+                  });
+                }
+              }
+            });
+          });
+        });
+      });
+    }, true);
+  });
+
+  content.location = TESTCASE_URI;
+}
+
+let gFilename;
+
+function run(aEditor)
+{
+  gFilename = FileUtils.getFile("ProfD", ["styleeditor-test.css"])
+
+  aEditor.saveToFile(gFilename, function (aFile) {
+    ok(aFile, "file got saved successfully");
+
+    aEditor.addActionListener({
+      onFlagChange: function (aEditor, aFlag) {
+        if (aFlag != "unsaved") {
+          return;
+        }
+
+        ok(aEditor.hasFlag("unsaved"),
+           "first stylesheet has UNSAVED flag after making a change");
+
+        // marker used to check it does not exist when we reopen
+        // ie. the window we opened is indeed a new one
+        gChromeWindow.document.documentElement.setAttribute("data-marker", "true");
+        gChromeWindow.close();
+      }
+    });
+
+    waitForFocus(function () {
+      // insert char so that this stylesheet has the UNSAVED flag
+      EventUtils.synthesizeKey("x", {}, gChromeWindow);
+    }, gChromeWindow);
+  });
+}
+
+function testNewChrome(aEditor)
+{
+  ok(aEditor.savedFile,
+     "first stylesheet editor will save directly into the same file");
+
+  is(aEditor.getFriendlyName(), gFilename.leafName,
+     "first stylesheet still has the filename as it was saved");
+  gFilename = null;
+
+  ok(aEditor.hasFlag("unsaved"),
+     "first stylesheet still has UNSAVED flag at reopening");
+
+  ok(!aEditor.hasFlag("inline"),
+     "first stylesheet does not have INLINE flag");
+
+  ok(!aEditor.hasFlag("error"),
+     "editor does not have error flag initially");
+  let hadError = false;
+
+  let onSaveCallback = function (aFile) {
+    aEditor.addActionListener({
+      onFlagChange: function (aEditor, aFlag) {
+        if (!hadError && aFlag == "error") {
+          ok(aEditor.hasFlag("error"),
+             "editor has ERROR flag after attempting to save with invalid path");
+          hadError = true;
+
+          // save using source editor key binding (previous successful path)
+          waitForFocus(function () {
+            EventUtils.synthesizeKey("S", {accelKey: true}, gChromeWindow);
+          }, gChromeWindow);
+          return;
+        }
+
+        if (hadError && aFlag == "unsaved") {
+          executeSoon(function () {
+            ok(!aEditor.hasFlag("unsaved"),
+               "first stylesheet has no UNSAVED flag after successful save");
+            ok(!aEditor.hasFlag("error"),
+               "ERROR flag has been removed since last operation succeeded");
+            finish();
+          });
+        }
+      }
+    });
+  }
+
+  let os = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
+  if (os == "WINNT") {
+    aEditor.saveToFile("C:\\I_DO_NOT_EXIST_42\\bogus.css", onSaveCallback);
+  } else {
+    aEditor.saveToFile("/I_DO_NOT_EXIST_42/bogos.css", onSaveCallback);
+  }
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_sv_filter.js
@@ -0,0 +1,101 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const TESTCASE_URI = TEST_BASE + "simple.html";
+
+
+function test()
+{
+  waitForExplicitFinish();
+
+  addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
+    aChrome.addChromeListener({
+      onContentAttach: run
+    });
+    if (aChrome.isContentAttached) {
+      run(aChrome);
+    }
+  });
+
+  content.location = TESTCASE_URI;
+}
+
+function getFilteredItemsCount(nav)
+{
+  let matches = nav.querySelectorAll("*.splitview-filtered");
+  return matches ? matches.length : 0;
+}
+
+function run(aChrome)
+{
+  aChrome.editors[0].addActionListener({onAttach: onFirstEditorAttach});
+  aChrome.editors[1].addActionListener({onAttach: onSecondEditorAttach});
+}
+
+function onFirstEditorAttach(aEditor)
+{
+  let filter = gChromeWindow.document.querySelector(".splitview-filter");
+  // force the command event on input since it is not possible to disable
+  // the search textbox's timeout.
+  let forceCommandEvent = function forceCommandEvent() {
+    let evt = gChromeWindow.document.createEvent("XULCommandEvent");
+    evt.initCommandEvent("command", true, true, gChromeWindow, 0, false, false,
+                         false, false, null);
+    filter.dispatchEvent(evt);
+  }
+  filter.addEventListener("input", forceCommandEvent, false);
+
+  let nav = gChromeWindow.document.querySelector(".splitview-nav");
+  nav.focus();
+
+  is(getFilteredItemsCount(nav), 0,
+     "there is 0 filtered item initially");
+
+  waitForFocus(function () {
+    // Search [s] (type-on-search since we focused nav above - not filter directly)
+    EventUtils.synthesizeKey("s", {}, gChromeWindow);
+
+    // the search space is "simple.css" and "inline stylesheet #1" (2 sheets)
+    is(getFilteredItemsCount(nav), 0,
+       "there is 0 filtered item if searching for 's'");
+
+    EventUtils.synthesizeKey("i", {}, gChromeWindow); // Search [si]
+
+    is(getFilteredItemsCount(nav), 1, // inline stylesheet is filtered
+       "there is 1 filtered item if searching for 's'");
+
+    // use uppercase to check that filtering is case-insensitive
+    EventUtils.synthesizeKey("X", {}, gChromeWindow); // Search [siX]
+    is(getFilteredItemsCount(nav), 2,
+       "there is 2 filtered items if searching for 's'"); // no match
+
+    // clear the search
+    EventUtils.synthesizeKey("VK_ESCAPE", {}, gChromeWindow);
+
+    is(filter.value, "",
+       "filter is back to empty");
+    is(getFilteredItemsCount(nav), 0,
+       "there is 0 filtered item when filter is empty again");
+
+    for each (let c in "inline") {
+      EventUtils.synthesizeKey(c, {}, gChromeWindow);
+    }
+
+    is(getFilteredItemsCount(nav), 1, // simple.css is filtered
+       "there is 1 filtered item if searching for 'inline'");
+
+    // auto-select the only result (enter the editor)
+    EventUtils.synthesizeKey("VK_ENTER", {}, gChromeWindow);
+
+    filter.removeEventListener("input", forceCommandEvent, false);
+  }, gChromeWindow);
+}
+
+function onSecondEditorAttach(aEditor)
+{
+  ok(aEditor.sourceEditor.hasFocus(),
+     "second editor has been selected and focused automatically.");
+
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_sv_keynav.js
@@ -0,0 +1,80 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const TESTCASE_URI = TEST_BASE + "four.html";
+
+
+function test()
+{
+  waitForExplicitFinish();
+
+  addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
+    aChrome.addChromeListener({
+      onContentAttach: run
+    });
+    if (aChrome.isContentAttached) {
+      run(aChrome);
+    }
+  });
+
+  content.location = TESTCASE_URI;
+}
+
+let gChrome;
+
+function run(aChrome)
+{
+  gChrome = aChrome;
+  aChrome.editors[0].addActionListener({onAttach: onEditor0Attach});
+  aChrome.editors[2].addActionListener({onAttach: onEditor2Attach});
+}
+
+function getStylesheetNameLinkFor(aEditor)
+{
+  return gChrome.getSummaryElementForEditor(aEditor).querySelector(".stylesheet-name");
+}
+
+function onEditor0Attach(aEditor)
+{
+  waitForFocus(function () {
+    let summary = gChrome.getSummaryElementForEditor(aEditor);
+    EventUtils.synthesizeMouseAtCenter(summary, {}, gChromeWindow);
+
+    let item = getStylesheetNameLinkFor(gChrome.editors[0]);
+    is(gChromeWindow.document.activeElement, item,
+       "editor 0 item is the active element");
+
+    EventUtils.synthesizeKey("VK_DOWN", {}, gChromeWindow);
+    item = getStylesheetNameLinkFor(gChrome.editors[1]);
+    is(gChromeWindow.document.activeElement, item,
+       "editor 1 item is the active element");
+
+    EventUtils.synthesizeKey("VK_HOME", {}, gChromeWindow);
+    item = getStylesheetNameLinkFor(gChrome.editors[0]);
+    is(gChromeWindow.document.activeElement, item,
+       "fist editor item is the active element");
+
+    EventUtils.synthesizeKey("VK_END", {}, gChromeWindow);
+    item = getStylesheetNameLinkFor(gChrome.editors[3]);
+    is(gChromeWindow.document.activeElement, item,
+       "last editor item is the active element");
+
+    EventUtils.synthesizeKey("VK_UP", {}, gChromeWindow);
+    item = getStylesheetNameLinkFor(gChrome.editors[2]);
+    is(gChromeWindow.document.activeElement, item,
+       "editor 2 item is the active element");
+
+    EventUtils.synthesizeKey("VK_RETURN", {}, gChromeWindow);
+    // this will attach and give focus editor 2
+  }, gChromeWindow);
+}
+
+function onEditor2Attach(aEditor)
+{
+  ok(aEditor.sourceEditor.hasFocus(),
+     "editor 2 has focus");
+
+  gChrome = null;
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_sv_resize.js
@@ -0,0 +1,63 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const TESTCASE_URI = TEST_BASE + "simple.html";
+
+let gOriginalWidth; // these are set by run() when gChromeWindow is ready
+let gOriginalHeight;
+
+function test()
+{
+  waitForExplicitFinish();
+
+  addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
+    if (aChrome.isContentAttached) {
+      run(aChrome);
+    } else {
+      aChrome.addChromeListener({
+        onContentAttach: run
+      });
+    }
+  });
+
+  content.location = TESTCASE_URI;
+}
+
+function run(aChrome)
+{
+  is(aChrome.editors.length, 2,
+     "there is 2 stylesheets initially");
+
+  aChrome.editors[0].addActionListener({
+    onAttach: function onEditorAttached(aEditor) {
+      let originalSourceEditor = aEditor.sourceEditor;
+      aEditor.sourceEditor.setCaretOffset(4); // to check the caret is preserved
+
+      // queue a resize to inverse aspect ratio
+      // this will trigger a detach and reattach (to workaround bug 254144)
+      executeSoon(function () {
+        waitForFocus(function () {
+          gOriginalWidth = gChromeWindow.outerWidth;
+          gOriginalHeight = gChromeWindow.outerHeight;
+          gChromeWindow.resizeTo(120, 480);
+
+          executeSoon(function () {
+            is(aEditor.sourceEditor, originalSourceEditor,
+               "the editor still references the same SourceEditor instance");
+            is(aEditor.sourceEditor.getCaretOffset(), 4,
+               "the caret position has been preserved");
+
+            // queue a resize to original aspect ratio
+            waitForFocus(function () {
+              gChromeWindow.resizeTo(gOriginalWidth, gOriginalHeight);
+              executeSoon(function () {
+                finish();
+              });
+            }, gChromeWindow);
+          });
+        }, gChromeWindow);
+      });
+    }
+  });
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleeditor/test/four.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<html>
+<head>
+  <title>four stylesheets</title>
+  <link rel="stylesheet" type="text/css" media="scren" href="simple.css"/>
+  <style type="text/css">
+  div {
+    font-size: 2em;
+  }
+  </style>
+  <style type="text/css">
+  span {
+    font-size: 3em;
+  }
+  </style>
+  <style type="text/css">
+  p {
+    font-size: 4em;
+  }
+  </style>
+</head>
+<body>
+	<div>four <span>stylesheets</span></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleeditor/test/head.js
@@ -0,0 +1,46 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const TEST_BASE = "chrome://mochitests/content/browser/browser/devtools/styleeditor/test/";
+const TEST_BASE_HTTP = "http://example.com/browser/browser/devtools/styleeditor/test/";
+const TEST_BASE_HTTPS = "https://example.com/browser/browser/devtools/styleeditor/test/";
+
+let gChromeWindow;               //StyleEditorChrome window
+
+function cleanup()
+{
+  if (gChromeWindow) {
+    gChromeWindow.close();
+    gChromeWindow = null;
+  }
+  while (gBrowser.tabs.length > 1) {
+    gBrowser.removeCurrentTab();
+  }
+}
+
+function launchStyleEditorChrome(aCallback)
+{
+  gChromeWindow = StyleEditor.openChrome();
+  if (gChromeWindow.document.readyState != "complete") {
+    gChromeWindow.addEventListener("load", function onChromeLoad() {
+      gChromeWindow.removeEventListener("load", onChromeLoad, true);
+      gChromeWindow.styleEditorChrome._alwaysDisableAnimations = true;
+      aCallback(gChromeWindow.styleEditorChrome);
+    }, true);
+  } else {
+    gChromeWindow.styleEditorChrome._alwaysDisableAnimations = true;
+    aCallback(gChromeWindow.styleEditorChrome);
+  }
+}
+
+function addTabAndLaunchStyleEditorChromeWhenLoaded(aCallback)
+{
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
+    gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
+    launchStyleEditorChrome(aCallback);
+  }, true);
+}
+
+registerCleanupFunction(cleanup);
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleeditor/test/media-small.css
@@ -0,0 +1,5 @@
+/* this stylesheet applies when min-width<400px */
+body {
+	background: red;
+}
+
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleeditor/test/media.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<html>
+<head>
+  <link rel="stylesheet" type="text/css" href="simple.css" media="screen,print"/>
+  <link rel="stylesheet" type="text/css" href="media-small.css" media="screen and (min-width: 200px)"/>
+</head>
+<body>
+	<div>test for media labels</div>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleeditor/test/minified.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<html>
+<head>
+  <title>minified testcase</title>
+  <style type="text/css"><!--
+body{background:white;}div{font-size:4em;color:red}
+--></style>
+  <style type="text/css">body { background: red; }
+div {
+font-size: 5em;
+color: red
+}</style>
+</head>
+<body>
+	<div>minified <span>testcase</span></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleeditor/test/simple.css
@@ -0,0 +1,8 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+body {
+  margin: 0;
+}
+
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..ee3b9efbc1db99982c1bd4d723a4d33694170ab2
GIT binary patch
literal 166
zc$@*I09pSZiwFodqB2eZ19NF@aBO8RV{>x=9gV>bf-n#T@BNB*=D?ySQ^Uc;5AYXC
z+rTEaOWGBJ#(x(%&1EJ-4HixoH7d0BXY8!&PF?#;XVH+M2DiMy%e)mHCk0o87}z_F
z0V>cb;_(`u>~WXmIJXACq&iz7U!qf9qL-8;*H+3%^C5@BrFSesr?#X2%M21WeD*%M
UGIF-)uI6jZ4+5k&-lqTn09FA?{r~^~
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleeditor/test/simple.css.gz^headers^
@@ -0,0 +1,4 @@
+Vary: Accept-Encoding
+Content-Encoding: gzip
+Content-Type: text/css
+
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleeditor/test/simple.gz.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<html>
+<head>
+  <title>simple testcase</title>
+  <link rel="stylesheet" type="text/css" media="scren" href="simple.css.gz"/>
+  <style type="text/css">
+  body {
+    background: white;
+  }
+
+  div {
+    font-size: 4em;
+  }
+
+  div > span {
+     text-decoration: underline;
+  }
+  </style>
+</head>
+<body>
+	<div>simple <span>testcase</span></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleeditor/test/simple.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<html>
+<head>
+  <title>simple testcase</title>
+  <link rel="stylesheet" type="text/css" media="scren" href="simple.css"/>
+  <style type="text/css">
+  body {
+    background: white;
+  }
+
+  div {
+    font-size: 4em;
+  }
+
+  div > span {
+     text-decoration: underline;
+  }
+  </style>
+</head>
+<body>
+	<div>simple <span>testcase</span></div>
+</body>
+</html>