Backed out changeset 26caa9ab5c35 for XPCShell test failure
authorHeather Arthur <fayearthur@gmail.com>
Wed, 24 Apr 2013 17:17:39 -0700
changeset 129622 d25314aaf909cfa168f2f290ad8d3772e6762747
parent 129621 26caa9ab5c35ad5c927dfa145a21ed7a22d10353
child 129623 81309cf14f6116c22143282c1dbd684203b5df6b
push id1543
push userfayearthur@gmail.com
push dateThu, 25 Apr 2013 00:18:07 +0000
treeherderfx-team@d25314aaf909 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone23.0a1
backs out26caa9ab5c35ad5c927dfa145a21ed7a22d10353
Backed out changeset 26caa9ab5c35 for XPCShell test failure
browser/devtools/framework/ToolDefinitions.jsm
browser/devtools/styleeditor/StyleEditor.jsm
browser/devtools/styleeditor/StyleEditorChrome.jsm
browser/devtools/styleeditor/StyleEditorDebuggee.jsm
browser/devtools/styleeditor/StyleEditorPanel.jsm
browser/devtools/styleeditor/StyleEditorUI.jsm
browser/devtools/styleeditor/StyleEditorUtil.jsm
browser/devtools/styleeditor/StyleSheetEditor.jsm
browser/devtools/styleeditor/styleeditor.xul
browser/devtools/styleeditor/test/Makefile.in
browser/devtools/styleeditor/test/browser_styleeditor_bug_826982_location_changed.js
browser/devtools/styleeditor/test/browser_styleeditor_bug_851132_middle_click.js
browser/devtools/styleeditor/test/browser_styleeditor_enabled.js
browser/devtools/styleeditor/test/browser_styleeditor_filesave.js
browser/devtools/styleeditor/test/browser_styleeditor_import.js
browser/devtools/styleeditor/test/browser_styleeditor_import_rule.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_passedinsheet.js
browser/devtools/styleeditor/test/browser_styleeditor_pretty.js
browser/devtools/styleeditor/test/browser_styleeditor_private_perwindowpb.js
browser/devtools/styleeditor/test/browser_styleeditor_readonly.js
browser/devtools/styleeditor/test/browser_styleeditor_reopen.js
browser/devtools/styleeditor/test/browser_styleeditor_sv_keynav.js
browser/devtools/styleeditor/test/browser_styleeditor_sv_resize.js
browser/devtools/styleeditor/test/head.js
browser/devtools/styleeditor/test/longload.html
browser/devtools/styleinspector/test/browser_computedview_734259_style_editor_link.js
browser/devtools/styleinspector/test/browser_ruleview_734259_style_editor_link.js
browser/devtools/webconsole/HUDService.jsm
browser/devtools/webconsole/test/browser_webconsole_bug_782653_CSS_links_in_Style_Editor.js
toolkit/devtools/debugger/dbg-client.jsm
toolkit/devtools/debugger/server/dbg-server.js
toolkit/devtools/jar.mn
toolkit/devtools/moz.build
toolkit/devtools/styleeditor/Makefile.in
toolkit/devtools/styleeditor/dbg-styleeditor-actors.js
toolkit/devtools/styleeditor/moz.build
--- a/browser/devtools/framework/ToolDefinitions.jsm
+++ b/browser/devtools/framework/ToolDefinitions.jsm
@@ -138,17 +138,17 @@ let styleEditorDefinition = {
   accesskey: l10n("open.accesskey", styleEditorStrings),
   modifiers: "shift",
   icon: "chrome://browser/skin/devtools/tool-styleeditor.png",
   url: "chrome://browser/content/styleeditor.xul",
   label: l10n("ToolboxStyleEditor.label", styleEditorStrings),
   tooltip: l10n("ToolboxStyleEditor.tooltip", styleEditorStrings),
 
   isTargetSupported: function(target) {
-    return true;
+    return !target.isRemote;
   },
 
   build: function(iframeWindow, toolbox) {
     let panel = new StyleEditorPanel(iframeWindow, toolbox);
     return panel.open();
   }
 };
 
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleeditor/StyleEditor.jsm
@@ -0,0 +1,1329 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+this.EXPORTED_SYMBOLS = ["StyleEditor", "StyleEditorFlags"];
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+const DOMUtils = Cc["@mozilla.org/inspector/dom-utils;1"]
+                   .getService(Ci.inIDOMUtils);
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/NetUtil.jsm");
+Cu.import("resource://gre/modules/FileUtils.jsm");
+Cu.import("resource:///modules/devtools/StyleEditorUtil.jsm");
+Cu.import("resource:///modules/source-editor.jsm");
+
+const LOAD_ERROR = "error-load";
+const SAVE_ERROR = "error-save";
+
+// max update frequency in ms (avoid potential typing lag and/or flicker)
+// @see StyleEditor.updateStylesheet
+const UPDATE_STYLESHEET_THROTTLE_DELAY = 500;
+
+// @see StyleEditor._persistExpando
+const STYLESHEET_EXPANDO = "-moz-styleeditor-stylesheet-";
+
+const TRANSITIONS_PREF = "devtools.styleeditor.transitions";
+
+const TRANSITION_CLASS = "moz-styleeditor-transitioning";
+const TRANSITION_DURATION_MS = 500;
+const TRANSITION_RULE = "\
+:root.moz-styleeditor-transitioning, :root.moz-styleeditor-transitioning * {\
+transition-duration: " + TRANSITION_DURATION_MS + "ms !important; \
+transition-delay: 0ms !important;\
+transition-timing-function: ease-out !important;\
+transition-property: all !important;\
+}";
+
+/**
+ * Style Editor module-global preferences
+ */
+const TRANSITIONS_ENABLED = Services.prefs.getBoolPref(TRANSITIONS_PREF);
+
+
+/**
+ * StyleEditor constructor.
+ *
+ * The StyleEditor is initialized 'headless', it does not display source
+ * or receive input. Setting inputElement attaches a DOMElement to handle this.
+ *
+ * An editor can be created stand-alone or created by StyleEditorChrome to
+ * manage all the style sheets of a document, including @import'ed sheets.
+ *
+ * @param DOMDocument aDocument
+ *        The content document where changes will be applied to.
+ * @param DOMStyleSheet aStyleSheet
+ *        Optional. The DOMStyleSheet to edit.
+ *        If not set, a new empty style sheet will be appended to the document.
+ * @see inputElement
+ * @see StyleEditorChrome
+ */
+this.StyleEditor = function StyleEditor(aDocument, aStyleSheet)
+{
+  assert(aDocument, "Argument 'aDocument' is required.");
+
+  this._document = aDocument; // @see contentDocument
+  this._inputElement = null;  // @see inputElement
+  this._sourceEditor = null;  // @see sourceEditor
+
+  this._state = {             // state to handle inputElement attach/detach
+    text: "",                 // seamlessly
+    selection: {start: 0, end: 0},
+    readOnly: false,
+    topIndex: 0,              // the first visible line
+  };
+
+  this._styleSheet = aStyleSheet;
+  this._styleSheetIndex = -1; // unknown for now, will be set after load
+  this._styleSheetFilePath = null; // original file path for the style sheet
+
+  this._loaded = false;
+
+  this._flags = [];           // @see flags
+  this._savedFile = null;     // @see savedFile
+
+  this._errorMessage = null;  // @see errorMessage
+
+  // listeners for significant editor actions. @see addActionListener
+  this._actionListeners = [];
+
+  // this is to perform pending updates before editor closing
+  this._onWindowUnloadBinding = this._onWindowUnload.bind(this);
+
+  this._transitionRefCount = 0;
+
+  this._focusOnSourceEditorReady = false;
+}
+
+StyleEditor.prototype = {
+  /**
+   * Retrieve the content document this editor will apply changes to.
+   *
+   * @return DOMDocument
+   */
+  get contentDocument() this._document,
+
+  /**
+   * Retrieve the stylesheet this editor is attached to.
+   *
+   * @return DOMStyleSheet
+   */
+  get styleSheet()
+  {
+    assert(this._styleSheet, "StyleSheet must be loaded first.");
+    return this._styleSheet;
+  },
+
+  /**
+   * Recursively traverse imported stylesheets to find the index
+   *
+   * @param number aIndex
+   *        The index of the current sheet in the document.
+   * @param CSSStyleSheet aSheet
+   *        A stylesheet we're going to browse to look for all imported sheets.
+   */
+  _getImportedStyleSheetIndex: function SE__getImportedStyleSheetIndex(aIndex, aSheet)
+  {
+    let index = aIndex;
+    for (let j = 0; j < aSheet.cssRules.length; j++) {
+      let rule = aSheet.cssRules.item(j);
+      if (rule.type == Ci.nsIDOMCSSRule.IMPORT_RULE) {
+        // Associated styleSheet may be null if it has already been seen due to
+        // duplicate @imports for the same URL.
+        if (!rule.styleSheet) {
+          continue;
+        }
+
+        if (rule.styleSheet == this.styleSheet) {
+          this._styleSheetIndex = index;
+          return index;
+        }
+        index++;
+        index = this._getImportedStyleSheetIndex(index, rule.styleSheet);
+
+        if (this._styleSheetIndex != -1) {
+          return index;
+        }
+      } else if (rule.type != Ci.nsIDOMCSSRule.CHARSET_RULE) {
+        // @import rules must precede all others except @charset
+        return index;
+      }
+    }
+    return index;
+  },
+
+  /**
+   * Retrieve the index (order) of stylesheet in the document.
+   *
+   * @return number
+   */
+  get styleSheetIndex()
+  {
+    let document = this.contentDocument;
+    if (this._styleSheetIndex == -1) {
+      let index = 0;
+      let sheetIndex = 0;
+      while (sheetIndex <= document.styleSheets.length) {
+        let sheet = document.styleSheets[sheetIndex];
+        if (sheet == this.styleSheet) {
+          this._styleSheetIndex = index;
+          break;
+        }
+        index++;
+        index = this._getImportedStyleSheetIndex(index, sheet);
+        if (this._styleSheetIndex != -1) {
+          break;
+        }
+        sheetIndex++;
+      }
+    }
+    return this._styleSheetIndex;
+  },
+
+  /**
+   * Retrieve the input element that handles display and input for this editor.
+   * Can be null if the editor is detached/headless, which means that this
+   * StyleEditor is not attached to an input element.
+   *
+   * @return DOMElement
+   */
+  get inputElement() this._inputElement,
+
+  /**
+   * Set the input element that handles display and input for this editor.
+   * This detaches the previous input element if previously set.
+   *
+   * @param DOMElement aElement
+   */
+  set inputElement(aElement)
+  {
+    if (aElement == this._inputElement) {
+      return; // no change
+    }
+
+    if (this._inputElement) {
+      // detach from current input element
+      if (this._sourceEditor) {
+        // save existing state first (for seamless reattach)
+        this._state = {
+          text: this._sourceEditor.getText(),
+          selection: this._sourceEditor.getSelection(),
+          readOnly: this._sourceEditor.readOnly,
+          topIndex: this._sourceEditor.getTopIndex(),
+        };
+        this._sourceEditor.destroy();
+        this._sourceEditor = null;
+      }
+
+      this.window.removeEventListener("unload",
+                                      this._onWindowUnloadBinding, false);
+      this._triggerAction("Detach");
+    }
+
+    this._inputElement = aElement;
+    if (!aElement) {
+      return;
+    }
+
+    // attach to new input element
+    this.window.addEventListener("unload", this._onWindowUnloadBinding, false);
+    this._focusOnSourceEditorReady = false;
+
+    this._sourceEditor = null; // set it only when ready (safe to use)
+
+    let sourceEditor = new SourceEditor();
+    let config = {
+      initialText: this._state.text,
+      showLineNumbers: true,
+      mode: SourceEditor.MODES.CSS,
+      readOnly: this._state.readOnly,
+      keys: this._getKeyBindings()
+    };
+
+    sourceEditor.init(aElement, config, function onSourceEditorReady() {
+      setupBracketCompletion(sourceEditor);
+
+      sourceEditor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
+                                    function onTextChanged(aEvent) {
+        this.updateStyleSheet();
+      }.bind(this));
+
+      this._sourceEditor = sourceEditor;
+
+      if (this._focusOnSourceEditorReady) {
+        this._focusOnSourceEditorReady = false;
+        sourceEditor.focus();
+      }
+
+      sourceEditor.setTopIndex(this._state.topIndex);
+      sourceEditor.setSelection(this._state.selection.start,
+                                this._state.selection.end);
+
+      this._triggerAction("Attach");
+    }.bind(this));
+  },
+
+  /**
+   * Retrieve the underlying SourceEditor instance for this StyleEditor.
+   * Can be null if not ready or Style Editor is detached/headless.
+   *
+   * @return SourceEditor
+   */
+  get sourceEditor() this._sourceEditor,
+
+  /**
+   * Setter for the read-only state of the editor.
+   *
+   * @param boolean aValue
+   *        Tells if you want the editor to be read-only or not.
+   */
+  set readOnly(aValue)
+  {
+    this._state.readOnly = aValue;
+    if (this._sourceEditor) {
+      this._sourceEditor.readOnly = aValue;
+    }
+  },
+
+  /**
+   * Getter for the read-only state of the editor.
+   *
+   * @return boolean
+   */
+  get readOnly()
+  {
+    return this._state.readOnly;
+  },
+
+  /**
+   * Retrieve the window that contains the editor.
+   * Can be null if the editor is detached/headless.
+   *
+   * @return DOMWindow
+   */
+  get window()
+  {
+    if (!this.inputElement) {
+      return null;
+    }
+    return this.inputElement.ownerDocument.defaultView;
+  },
+
+  /**
+   * Retrieve the last file this editor has been saved to or null if none.
+   *
+   * @return nsIFile
+   */
+  get savedFile() this._savedFile,
+
+  /**
+   * Import style sheet from file and load it into the editor asynchronously.
+   * "Load" action triggers when complete.
+   *
+   * @param mixed aFile
+   *        Optional nsIFile or filename string.
+   *        If not set a file picker will be shown.
+   * @param nsIWindow aParentWindow
+   *        Optional parent window for the file picker.
+   */
+  importFromFile: function SE_importFromFile(aFile, aParentWindow)
+  {
+    let callback = function(aFile) {
+      if (aFile) {
+        this._savedFile = aFile; // remember filename for next save if any
+
+        NetUtil.asyncFetch(aFile, function onAsyncFetch(aStream, aStatus) {
+          if (!Components.isSuccessCode(aStatus)) {
+            return this._signalError(LOAD_ERROR);
+          }
+          let source = NetUtil.readInputStreamToString(aStream, aStream.available());
+          aStream.close();
+
+          this._appendNewStyleSheet(source);
+          this.clearFlag(StyleEditorFlags.ERROR);
+        }.bind(this));
+      }
+    }.bind(this);
+
+    this._showFilePicker(aFile, false, aParentWindow, callback);
+  },
+
+  /**
+    * Retrieve localized error message of last error condition, or null if none.
+    * This is set when the editor has flag StyleEditorFlags.ERROR.
+    *
+    * @see addActionListener
+    */
+  get errorMessage() this._errorMessage,
+
+  /**
+   * Tell whether the stylesheet has been loaded and ready for modifications.
+   *
+   * @return boolean
+   */
+  get isLoaded() this._loaded,
+
+  /**
+   * Load style sheet source into the editor, asynchronously.
+   * "Load" handler triggers when complete.
+   *
+   * @see addActionListener
+   */
+  load: function SE_load()
+  {
+    if (!this._styleSheet) {
+      this._appendNewStyleSheet();
+    }
+    this._loadSource();
+  },
+
+  /**
+   * Get a user-friendly name for the style sheet.
+   *
+   * @return string
+   */
+  getFriendlyName: function SE_getFriendlyName()
+  {
+    if (this.savedFile) { // reuse the saved filename if any
+      return this.savedFile.leafName;
+    }
+
+    if (this.hasFlag(StyleEditorFlags.NEW)) {
+      let index = this.styleSheetIndex + 1; // 0-indexing only works for devs
+      return _("newStyleSheet", index);
+    }
+
+    if (this.hasFlag(StyleEditorFlags.INLINE)) {
+      let index = this.styleSheetIndex + 1; // 0-indexing only works for devs
+      return _("inlineStyleSheet", index);
+    }
+
+    if (!this._friendlyName) {
+      let sheetURI = this.styleSheet.href;
+      let contentURI = this.contentDocument.baseURIObject;
+      let contentURIScheme = contentURI.scheme;
+      let contentURILeafIndex = contentURI.specIgnoringRef.lastIndexOf("/");
+      contentURI = contentURI.specIgnoringRef;
+
+      // get content base URI without leaf name (if any)
+      if (contentURILeafIndex > contentURIScheme.length) {
+        contentURI = contentURI.substring(0, contentURILeafIndex + 1);
+      }
+
+      // avoid verbose repetition of absolute URI when the style sheet URI
+      // is relative to the content URI
+      this._friendlyName = (sheetURI.indexOf(contentURI) == 0)
+                           ? sheetURI.substring(contentURI.length)
+                           : sheetURI;
+      try {
+        this._friendlyName = decodeURI(this._friendlyName);
+      } catch (ex) {
+      }
+    }
+    return this._friendlyName;
+  },
+
+  /**
+   * Add a listener for significant StyleEditor actions.
+   *
+   * The listener implements IStyleEditorActionListener := {
+   *   onLoad:                 Called when the style sheet has been loaded and
+   *                           parsed.
+   *                           Arguments: (StyleEditor editor)
+   *                           @see load
+   *
+   *   onFlagChange:           Called when a flag has been set or cleared.
+   *                           Arguments: (StyleEditor editor, string flagName)
+   *                           @see setFlag
+   *
+   *   onAttach:               Called when an input element has been attached.
+   *                           Arguments: (StyleEditor editor)
+   *                           @see inputElement
+   *
+   *   onDetach:               Called when input element has been detached.
+   *                           Arguments: (StyleEditor editor)
+   *                           @see inputElement
+   *
+   *   onUpdate:               Called when changes are being applied to the live
+   *                           DOM style sheet but might not be complete from
+   *                           a WYSIWYG perspective (eg. transitioned update).
+   *                           Arguments: (StyleEditor editor)
+   *
+   *   onCommit:               Called when changes have been completely committed
+   *                           /applied to the live DOM style sheet.
+   *                           Arguments: (StyleEditor editor)
+   * }
+   *
+   * All listener methods are optional.
+   *
+   * @param IStyleEditorActionListener aListener
+   * @see removeActionListener
+   */
+  addActionListener: function SE_addActionListener(aListener)
+  {
+    this._actionListeners.push(aListener);
+  },
+
+  /**
+   * Remove a listener for editor actions from the current list of listeners.
+   *
+   * @param IStyleEditorActionListener aListener
+   * @see addActionListener
+   */
+  removeActionListener: function SE_removeActionListener(aListener)
+  {
+    let index = this._actionListeners.indexOf(aListener);
+    if (index != -1) {
+      this._actionListeners.splice(index, 1);
+    }
+  },
+
+  /**
+   * Editor UI flags.
+   *
+   * These are 1-bit indicators that can be used for UI feedback/indicators or
+   * extensions to track the editor status.
+   * Since they are simple strings, they promote loose coupling and can simply
+   * map to CSS class names, which allows to 'expose' indicators declaratively
+   * via CSS (including possibly complex combinations).
+   *
+   * Flag changes can be tracked via onFlagChange (@see addActionListener).
+   *
+   * @see StyleEditorFlags
+   */
+
+  /**
+   * Retrieve a space-separated string of all UI flags set on this editor.
+   *
+   * @return string
+   * @see setFlag
+   * @see clearFlag
+   */
+  get flags() this._flags.join(" "),
+
+  /**
+   * Set a flag.
+   *
+   * @param string aName
+   *        Name of the flag to set. One of StyleEditorFlags members.
+   * @return boolean
+   *         True if the flag has been set, false if flag is already set.
+   * @see StyleEditorFlags
+   */
+  setFlag: function SE_setFlag(aName)
+  {
+    let prop = aName.toUpperCase();
+    assert(StyleEditorFlags[prop], "Unknown flag: " + prop);
+
+    if (this.hasFlag(aName)) {
+      return false;
+    }
+    this._flags.push(aName);
+    this._triggerAction("FlagChange", [aName]);
+    return true;
+  },
+
+  /**
+   * Clear a flag.
+   *
+   * @param string aName
+   *        Name of the flag to clear.
+   * @return boolean
+   *         True if the flag has been cleared, false if already clear.
+   */
+  clearFlag: function SE_clearFlag(aName)
+  {
+    let index = this._flags.indexOf(aName);
+    if (index == -1) {
+      return false;
+    }
+    this._flags.splice(index, 1);
+    this._triggerAction("FlagChange", [aName]);
+    return true;
+  },
+
+  /**
+   * Toggle a flag, according to a condition.
+   *
+   * @param aCondition
+   *        If true the flag is set, otherwise cleared.
+   * @param string aName
+   *        Name of the flag to toggle.
+   * @return boolean
+   *        True if the flag has been set or cleared, ie. the flag got switched.
+   */
+  toggleFlag: function SE_toggleFlag(aCondition, aName)
+  {
+    return (aCondition) ? this.setFlag(aName) : this.clearFlag(aName);
+  },
+
+  /**
+   * Check if given flag is set.
+   *
+   * @param string aName
+   *        Name of the flag to check presence for.
+   * @return boolean
+   *         True if the flag is set, false otherwise.
+   */
+  hasFlag: function SE_hasFlag(aName) (this._flags.indexOf(aName) != -1),
+
+  /**
+   * Enable or disable style sheet.
+   *
+   * @param boolean aEnabled
+   */
+  enableStyleSheet: function SE_enableStyleSheet(aEnabled)
+  {
+    this.styleSheet.disabled = !aEnabled;
+    this.toggleFlag(this.styleSheet.disabled, StyleEditorFlags.DISABLED);
+
+    if (this._updateTask) {
+      this._updateStyleSheet(); // perform cancelled update
+    }
+  },
+
+  /**
+   * Save the editor contents into a file and set savedFile property.
+   * A file picker UI will open if file is not set and editor is not headless.
+   *
+   * @param mixed aFile
+   *        Optional nsIFile or string representing the filename to save in the
+   *        background, no UI will be displayed.
+   *        If not specified, the original style sheet URI is used.
+   *        To implement 'Save' instead of 'Save as', you can pass savedFile here.
+   * @param function(nsIFile aFile) aCallback
+   *        Optional callback called when the operation has finished.
+   *        aFile has the nsIFile object for saved file or null if the operation
+   *        has failed or has been canceled by the user.
+   * @see savedFile
+   */
+  saveToFile: function SE_saveToFile(aFile, aCallback)
+  {
+    let callback = function(aReturnFile) {
+      if (!aReturnFile) {
+        if (aCallback) {
+          aCallback(null);
+        }
+        return;
+      }
+
+      if (this._sourceEditor) {
+        this._state.text = this._sourceEditor.getText();
+      }
+
+      let ostream = FileUtils.openSafeFileOutputStream(aReturnFile);
+      let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
+                        .createInstance(Ci.nsIScriptableUnicodeConverter);
+      converter.charset = "UTF-8";
+      let istream = converter.convertToInputStream(this._state.text);
+
+      NetUtil.asyncCopy(istream, ostream, function SE_onStreamCopied(status) {
+        if (!Components.isSuccessCode(status)) {
+          if (aCallback) {
+            aCallback(null);
+          }
+          this._signalError(SAVE_ERROR);
+          return;
+        }
+        FileUtils.closeSafeFileOutputStream(ostream);
+
+        // remember filename for next save if any
+        this._friendlyName = null;
+        this._savedFile = aReturnFile;
+        this._persistExpando();
+
+        if (aCallback) {
+          aCallback(aReturnFile);
+        }
+        this.clearFlag(StyleEditorFlags.UNSAVED);
+        this.clearFlag(StyleEditorFlags.ERROR);
+      }.bind(this));
+    }.bind(this);
+
+    this._showFilePicker(aFile || this._styleSheetFilePath, true, null, callback);
+  },
+
+  /**
+   * Queue a throttled task to update the live style sheet.
+   *
+   * @param boolean aImmediate
+   *        Optional. If true the update is performed immediately.
+   */
+  updateStyleSheet: function SE_updateStyleSheet(aImmediate)
+  {
+    let window = this.window;
+
+    if (this._updateTask) {
+      // cancel previous queued task not executed within throttle delay
+      window.clearTimeout(this._updateTask);
+    }
+
+    if (aImmediate) {
+      this._updateStyleSheet();
+    } else {
+      this._updateTask = window.setTimeout(this._updateStyleSheet.bind(this),
+                                           UPDATE_STYLESHEET_THROTTLE_DELAY);
+    }
+  },
+
+  /**
+   * Update live style sheet according to modifications.
+   */
+  _updateStyleSheet: function SE__updateStyleSheet()
+  {
+    this.setFlag(StyleEditorFlags.UNSAVED);
+
+    if (this.styleSheet.disabled) {
+      return;
+    }
+
+    this._updateTask = null; // reset only if we actually perform an update
+                             // (stylesheet is enabled) so that 'missed' updates
+                             // while the stylesheet is disabled can be performed
+                             // when it is enabled back. @see enableStylesheet
+
+    if (this.sourceEditor) {
+      this._state.text = this.sourceEditor.getText();
+    }
+    DOMUtils.parseStyleSheet(this.styleSheet, this._state.text);
+    this._persistExpando();
+
+    if (!TRANSITIONS_ENABLED) {
+      this._triggerAction("Update");
+      this._triggerAction("Commit");
+      return;
+    }
+
+    let content = this.contentDocument;
+
+    // Insert the global transition rule
+    // Use a ref count to make sure we do not add it multiple times.. and remove
+    // it only when all pending StyleEditor-generated transitions ended.
+    if (!this._transitionRefCount) {
+      this.styleSheet.insertRule(TRANSITION_RULE, this.styleSheet.cssRules.length);
+      content.documentElement.classList.add(TRANSITION_CLASS);
+    }
+
+    this._transitionRefCount++;
+
+    // Set up clean up and commit after transition duration (+10% buffer)
+    // @see _onTransitionEnd
+    content.defaultView.setTimeout(this._onTransitionEnd.bind(this),
+                                   Math.floor(TRANSITION_DURATION_MS * 1.1));
+
+    this._triggerAction("Update");
+  },
+
+  /**
+    * This cleans up class and rule added for transition effect and then trigger
+    * Commit as the changes have been completed.
+    */
+  _onTransitionEnd: function SE__onTransitionEnd()
+  {
+    if (--this._transitionRefCount == 0) {
+      this.contentDocument.documentElement.classList.remove(TRANSITION_CLASS);
+      this.styleSheet.deleteRule(this.styleSheet.cssRules.length - 1);
+    }
+
+    this._triggerAction("Commit");
+  },
+
+  /**
+   * Show file picker and return the file user selected.
+   *
+   * @param mixed aFile
+   *        Optional nsIFile or string representing the filename to auto-select.
+   * @param boolean aSave
+   *        If true, the user is selecting a filename to save.
+   * @param nsIWindow aParentWindow
+   *        Optional parent window. If null the parent window of the file picker
+   *        will be the window of the attached input element.
+   * @param aCallback
+   *        The callback method, which will be called passing in the selected
+   *        file or null if the user did not pick one.
+   */
+  _showFilePicker: function SE__showFilePicker(aFile, aSave, aParentWindow, aCallback)
+  {
+    if (typeof(aFile) == "string") {
+      try {
+        if (Services.io.extractScheme(aFile) == "file") {
+          let uri = Services.io.newURI(aFile, null, null);
+          let file = uri.QueryInterface(Ci.nsIFileURL).file;
+          aCallback(file);
+          return;
+        }
+      } catch (ex) {
+      }
+      try {
+        let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
+        file.initWithPath(aFile);
+        aCallback(file);
+        return;
+      } catch (ex) {
+        this._signalError(aSave ? SAVE_ERROR : LOAD_ERROR);
+        aCallback(null);
+        return;
+      }
+    }
+    if (aFile) {
+      aCallback(aFile);
+      return;
+    }
+
+    let window = aParentWindow
+                 ? aParentWindow
+                 : this.inputElement.ownerDocument.defaultView;
+    let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
+    let mode = aSave ? fp.modeSave : fp.modeOpen;
+    let key = aSave ? "saveStyleSheet" : "importStyleSheet";
+    let fpCallback = function fpCallback_done(aResult) {
+      if (aResult == Ci.nsIFilePicker.returnCancel) {
+        aCallback(null);
+      } else {
+        aCallback(fp.file);
+      }
+    };
+
+    fp.init(window, _(key + ".title"), mode);
+    fp.appendFilters(_(key + ".filter"), "*.css");
+    fp.appendFilters(fp.filterAll);
+    fp.open(fpCallback);
+    return;
+  },
+
+  /**
+   * Retrieve the style sheet source from the cache or from a local file.
+   */
+  _loadSource: function SE__loadSource()
+  {
+    if (!this.styleSheet.href) {
+      // this is an inline <style> sheet
+      this._flags.push(StyleEditorFlags.INLINE);
+      this._onSourceLoad(this.styleSheet.ownerNode.textContent);
+      return;
+    }
+
+    let scheme = Services.io.extractScheme(this.styleSheet.href);
+    switch (scheme) {
+      case "file":
+        this._styleSheetFilePath = this.styleSheet.href;
+      case "chrome":
+      case "resource":
+        this._loadSourceFromFile(this.styleSheet.href);
+        break;
+      default:
+        this._loadSourceFromCache(this.styleSheet.href);
+        break;
+    }
+  },
+
+  /**
+   * Decode a CSS source string to unicode according to the character set rules
+   * defined in <http://www.w3.org/TR/CSS2/syndata.html#charset>.
+   *
+   * @param string aString
+   *        Source of a CSS stylesheet, loaded from file or cache.
+   * @param string aChannelCharset
+   *        Charset of the source string if set by the HTTP channel.
+   * @return string
+   *         The CSS string, in unicode.
+   */
+  _decodeCSSCharset: function SE__decodeCSSCharset(aString, aChannelCharset)
+  {
+    // StyleSheet's charset can be specified from multiple sources
+
+    if (aChannelCharset.length > 0) {
+      // step 1 of syndata.html: charset given in HTTP header.
+      return this._convertToUnicode(aString, aChannelCharset);
+    }
+
+    let sheet = this.styleSheet;
+    if (sheet) {
+      // Do we have a @charset rule in the stylesheet?
+      // step 2 of syndata.html (without the BOM check).
+      if (sheet.cssRules) {
+        let rules = sheet.cssRules;
+        if (rules.length
+            && rules.item(0).type == Ci.nsIDOMCSSRule.CHARSET_RULE) {
+          return this._convertToUnicode(aString, rules.item(0).encoding);
+        }
+      }
+
+      // step 3: charset attribute of <link> or <style> element, if it exists
+      if (sheet.ownerNode && sheet.ownerNode.getAttribute) {
+        let linkCharset = sheet.ownerNode.getAttribute("charset");
+        if (linkCharset != null) {
+          return this._convertToUnicode(aString, linkCharset);
+        }
+      }
+
+      // step 4 (1 of 2): charset of referring stylesheet.
+      let parentSheet = sheet.parentStyleSheet;
+      if (parentSheet && parentSheet.cssRules &&
+          parentSheet.cssRules[0].type == Ci.nsIDOMCSSRule.CHARSET_RULE) {
+        return this._convertToUnicode(aString,
+            parentSheet.cssRules[0].encoding);
+      }
+
+      // step 4 (2 of 2): charset of referring document.
+      if (sheet.ownerNode && sheet.ownerNode.ownerDocument.characterSet) {
+        return this._convertToUnicode(aString,
+            sheet.ownerNode.ownerDocument.characterSet);
+      }
+    }
+
+    // step 5: default to utf-8.
+    return this._convertToUnicode(aString, "UTF-8");
+  },
+
+  /**
+   * Convert a given string, encoded in a given character set, to unicode.
+   * @param string aString
+   *        A string.
+   * @param string aCharset
+   *        A character set.
+   * @return string
+   *         A unicode string.
+   */
+  _convertToUnicode: function SE__convertToUnicode(aString, aCharset) {
+    // Decoding primitives.
+    let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
+        .createInstance(Ci.nsIScriptableUnicodeConverter);
+
+    try {
+      converter.charset = aCharset;
+      return converter.ConvertToUnicode(aString);
+    } catch(e) {
+      return aString;
+    }
+  },
+
+  /**
+   * Load source from a file or file-like resource.
+   *
+   * @param string aHref
+   *        URL for the stylesheet.
+   */
+  _loadSourceFromFile: function SE__loadSourceFromFile(aHref)
+  {
+    try {
+      NetUtil.asyncFetch(aHref, function onFetch(aStream, aStatus) {
+        if (!Components.isSuccessCode(aStatus)) {
+          return this._signalError(LOAD_ERROR);
+        }
+        let source = NetUtil.readInputStreamToString(aStream, aStream.available());
+        aStream.close();
+        this._onSourceLoad(source);
+      }.bind(this));
+    } catch (ex) {
+      this._signalError(LOAD_ERROR);
+    }
+  },
+
+  /**
+   * Load source from the HTTP cache.
+   *
+   * @param string aHref
+   *        URL for the stylesheet.
+   */
+  _loadSourceFromCache: function SE__loadSourceFromCache(aHref)
+  {
+    let channel = Services.io.newChannel(aHref, null, null);
+    let chunks = [];
+    let channelCharset = "";
+    let streamListener = { // nsIStreamListener inherits nsIRequestObserver
+      onStartRequest: function (aRequest, aContext, aStatusCode) {
+        if (!Components.isSuccessCode(aStatusCode)) {
+          return this._signalError(LOAD_ERROR);
+        }
+      }.bind(this),
+      onDataAvailable: function (aRequest, aContext, aStream, aOffset, aCount) {
+        let channel = aRequest.QueryInterface(Ci.nsIChannel);
+        if (!channelCharset) {
+          channelCharset = channel.contentCharset;
+        }
+        chunks.push(NetUtil.readInputStreamToString(aStream, aCount));
+      },
+      onStopRequest: function (aRequest, aContext, aStatusCode) {
+        if (!Components.isSuccessCode(aStatusCode)) {
+          return this._signalError(LOAD_ERROR);
+        }
+
+        this._onSourceLoad(chunks.join(""), channelCharset);
+      }.bind(this)
+    };
+
+    if (channel instanceof Ci.nsIPrivateBrowsingChannel) {
+      let contentWin = this.contentDocument.defaultView;
+      let loadContext = contentWin.QueryInterface(Ci.nsIInterfaceRequestor)
+                          .getInterface(Ci.nsIWebNavigation)
+                          .QueryInterface(Ci.nsILoadContext);
+      channel.setPrivate(loadContext.usePrivateBrowsing);
+    }
+    channel.loadFlags = channel.LOAD_FROM_CACHE;
+    channel.asyncOpen(streamListener, null);
+  },
+
+  /**
+   * Called when source has been loaded.
+   *
+   * @param string aSourceText
+   * @param string aCharset
+   *        Optional. The character set to use. The default is to detect the
+   *        character set following the standard (see
+   *        <http://www.w3.org/TR/CSS2/syndata.html#charset>).
+   */
+  _onSourceLoad: function SE__onSourceLoad(aSourceText, aCharset)
+  {
+    aSourceText = this._decodeCSSCharset(aSourceText, aCharset || "");
+    this._restoreExpando();
+    this._state.text = prettifyCSS(aSourceText);
+    this._loaded = true;
+    this._triggerAction("Load");
+  },
+
+  /**
+   * Create a new style sheet and append it to the content document.
+   *
+   * @param string aText
+   *        Optional CSS text.
+   */
+  _appendNewStyleSheet: function SE__appendNewStyleSheet(aText)
+  {
+    let document = this.contentDocument;
+    let parent = document.documentElement;
+    let style = document.createElementNS("http://www.w3.org/1999/xhtml", "style");
+    style.setAttribute("type", "text/css");
+    if (aText) {
+      style.appendChild(document.createTextNode(aText));
+    }
+    parent.appendChild(style);
+
+    this._styleSheet = document.styleSheets[document.styleSheets.length - 1];
+    if (aText) {
+      this._onSourceLoad(aText);
+      this._flags.push(StyleEditorFlags.IMPORTED);
+    } else {
+      this._flags.push(StyleEditorFlags.NEW);
+      this._flags.push(StyleEditorFlags.UNSAVED);
+    }
+  },
+
+  /**
+   * Signal an error to the user.
+   *
+   * @param string aErrorCode
+   *        String name for the localized error property in the string bundle.
+   * @param ...rest
+   *        Optional arguments to pass for message formatting.
+   * @see StyleEditorUtil._
+   */
+  _signalError: function SE__signalError(aErrorCode)
+  {
+    this._errorMessage = _.apply(null, arguments);
+    this.setFlag(StyleEditorFlags.ERROR);
+  },
+
+  /**
+   * Trigger named action handler in listeners.
+   *
+   * @param string aName
+   *        Name of the action to trigger.
+   * @param Array aArgs
+   *        Optional array of arguments to pass to the listener(s).
+   * @see addActionListener
+   */
+  _triggerAction: function SE__triggerAction(aName, aArgs)
+  {
+    // insert the origin editor instance as first argument
+    if (!aArgs) {
+      aArgs = [this];
+    } else {
+      aArgs.unshift(this);
+    }
+
+    // copy the list of listeners to allow adding/removing listeners in handlers
+    let listeners = this._actionListeners.concat();
+
+    // trigger all listeners that have this action handler
+    for (let i = 0; i < listeners.length; i++) {
+      let listener = listeners[i];
+      let actionHandler = listener["on" + aName];
+      if (actionHandler) {
+        actionHandler.apply(listener, aArgs);
+      }
+    }
+
+    // when a flag got changed, user-facing state need to be persisted
+    if (aName == "FlagChange") {
+      this._persistExpando();
+    }
+  },
+
+  /**
+    * Unload event handler to perform any pending update before closing
+    */
+  _onWindowUnload: function SE__onWindowUnload(aEvent)
+  {
+    if (this._updateTask) {
+      this.updateStyleSheet(true);
+    }
+  },
+
+  /**
+   * Focus the Style Editor input.
+   */
+  focus: function SE_focus()
+  {
+    if (this._sourceEditor) {
+      this._sourceEditor.focus();
+    } else {
+      this._focusOnSourceEditorReady = true;
+    }
+  },
+
+  /**
+   * Event handler for when the editor is shown. Call this after the editor is
+   * shown.
+   */
+  onShow: function SE_onShow()
+  {
+    if (this._sourceEditor) {
+      this._sourceEditor.setTopIndex(this._state.topIndex);
+    }
+    this.focus();
+  },
+
+  /**
+   * Event handler for when the editor is hidden. Call this before the editor is
+   * hidden.
+   */
+  onHide: function SE_onHide()
+  {
+    if (this._sourceEditor) {
+      this._state.topIndex = this._sourceEditor.getTopIndex();
+    }
+  },
+
+  /**
+    * Persist StyleEditor extra data to the attached DOM stylesheet expando.
+    * The expando on the DOM stylesheet is used to restore user-facing state
+    * when the StyleEditor is closed and then reopened again.
+    *
+    * @see styleSheet
+    */
+  _persistExpando: function SE__persistExpando()
+  {
+    if (!this._styleSheet) {
+      return; // not loaded
+    }
+    let name = STYLESHEET_EXPANDO + this.styleSheetIndex;
+    let expando = this.contentDocument.getUserData(name);
+    if (!expando) {
+      expando = {};
+      this.contentDocument.setUserData(name, expando, null);
+    }
+    expando._flags = this._flags;
+    expando._savedFile = this._savedFile;
+  },
+
+  /**
+    * Restore the attached DOM stylesheet expando into this editor state.
+    *
+    * @see styleSheet
+    */
+  _restoreExpando: function SE__restoreExpando()
+  {
+    if (!this._styleSheet) {
+      return; // not loaded
+    }
+    let name = STYLESHEET_EXPANDO + this.styleSheetIndex;
+    let expando = this.contentDocument.getUserData(name);
+    if (expando) {
+      this._flags = expando._flags;
+      this._savedFile = expando._savedFile;
+    }
+  },
+
+  /**
+    * Retrieve custom key bindings objects as expected by SourceEditor.
+    * SourceEditor action names are not displayed to the user.
+    *
+    * @return Array
+    */
+  _getKeyBindings: function SE__getKeyBindings()
+  {
+    let bindings = [];
+
+    bindings.push({
+      action: "StyleEditor.save",
+      code: _("saveStyleSheet.commandkey"),
+      accel: true,
+      callback: function save() {
+        this.saveToFile(this._savedFile);
+        return true;
+      }.bind(this)
+    });
+
+    bindings.push({
+      action: "StyleEditor.saveAs",
+      code: _("saveStyleSheet.commandkey"),
+      accel: true,
+      shift: true,
+      callback: function saveAs() {
+        this.saveToFile();
+        return true;
+      }.bind(this)
+    });
+
+    return bindings;
+  }
+};
+
+/**
+ * List of StyleEditor UI flags.
+ * A Style Editor add-on using its own flag needs to add it to this object.
+ *
+ * @see StyleEditor.setFlag
+ */
+this.StyleEditorFlags = {
+  DISABLED:      "disabled",
+  ERROR:         "error",
+  IMPORTED:      "imported",
+  INLINE:        "inline",
+  MODIFIED:      "modified",
+  NEW:           "new",
+  UNSAVED:       "unsaved"
+};
+
+
+const TAB_CHARS   = "\t";
+
+const OS = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
+const LINE_SEPARATOR = OS === "WINNT" ? "\r\n" : "\n";
+
+/**
+ * Prettify minified CSS text.
+ * This prettifies CSS code where there is no indentation in usual places while
+ * keeping original indentation as-is elsewhere.
+ *
+ * @param string aText
+ *        The CSS source to prettify.
+ * @return string
+ *         Prettified CSS source
+ */
+function prettifyCSS(aText)
+{
+  // remove initial and terminating HTML comments and surrounding whitespace
+  aText = aText.replace(/(?:^\s*<!--[\r\n]*)|(?:\s*-->\s*$)/g, "");
+
+  let parts = [];    // indented parts
+  let partStart = 0; // start offset of currently parsed part
+  let indent = "";
+  let indentLevel = 0;
+
+  for (let i = 0; i < aText.length; i++) {
+    let c = aText[i];
+    let shouldIndent = false;
+
+    switch (c) {
+      case "}":
+        if (i - partStart > 1) {
+          // there's more than just } on the line, add line
+          parts.push(indent + aText.substring(partStart, i));
+          partStart = i;
+        }
+        indent = repeat(TAB_CHARS, --indentLevel);
+        /* fallthrough */
+      case ";":
+      case "{":
+        shouldIndent = true;
+        break;
+    }
+
+    if (shouldIndent) {
+      let la = aText[i+1]; // one-character lookahead
+      if (!/\s/.test(la)) {
+        // following character should be a new line (or whitespace) but it isn't
+        // force indentation then
+        parts.push(indent + aText.substring(partStart, i + 1));
+        if (c == "}") {
+          parts.push(""); // for extra line separator
+        }
+        partStart = i + 1;
+      } else {
+        return aText; // assume it is not minified, early exit
+      }
+    }
+
+    if (c == "{") {
+      indent = repeat(TAB_CHARS, ++indentLevel);
+    }
+  }
+  return parts.join(LINE_SEPARATOR);
+}
+
+/**
+  * Return string that repeats aText for aCount times.
+  *
+  * @param string aText
+  * @param number aCount
+  * @return string
+  */
+function repeat(aText, aCount)
+{
+  return (new Array(aCount + 1)).join(aText);
+}
+
+/**
+ * Set up bracket completion on a given SourceEditor.
+ * This automatically closes the following CSS brackets: "{", "(", "["
+ *
+ * @param SourceEditor aSourceEditor
+ */
+function setupBracketCompletion(aSourceEditor)
+{
+  let editorElement = aSourceEditor.editorElement;
+  let pairs = {
+    123: { // {
+      closeString: "}",
+      closeKeyCode: Ci.nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET
+    },
+    40: { // (
+      closeString: ")",
+      closeKeyCode: Ci.nsIDOMKeyEvent.DOM_VK_0
+    },
+    91: { // [
+      closeString: "]",
+      closeKeyCode: Ci.nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET
+    },
+  };
+
+  editorElement.addEventListener("keypress", function onKeyPress(aEvent) {
+    let pair = pairs[aEvent.charCode];
+    if (!pair || aEvent.ctrlKey || aEvent.metaKey ||
+        aEvent.accelKey || aEvent.altKey) {
+      return true;
+    }
+
+    // We detected an open bracket, sending closing character
+    let keyCode = pair.closeKeyCode;
+    let charCode = pair.closeString.charCodeAt(0);
+    let modifiers = 0;
+    let utils = editorElement.ownerDocument.defaultView.
+                  QueryInterface(Ci.nsIInterfaceRequestor).
+                  getInterface(Ci.nsIDOMWindowUtils);
+    let handled = utils.sendKeyEvent("keydown", keyCode, 0, modifiers);
+    utils.sendKeyEvent("keypress", 0, charCode, modifiers, !handled);
+    utils.sendKeyEvent("keyup", keyCode, 0, modifiers);
+    // and rewind caret
+    aSourceEditor.setCaretOffset(aSourceEditor.getCaretOffset() - 1);
+  }, false);
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleeditor/StyleEditorChrome.jsm
@@ -0,0 +1,627 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+this.EXPORTED_SYMBOLS = ["StyleEditorChrome"];
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/PluralForm.jsm");
+Cu.import("resource:///modules/devtools/StyleEditor.jsm");
+Cu.import("resource:///modules/devtools/StyleEditorUtil.jsm");
+Cu.import("resource:///modules/devtools/SplitView.jsm");
+Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js");
+
+const STYLE_EDITOR_TEMPLATE = "stylesheet";
+
+
+/**
+ * StyleEditorChrome constructor.
+ *
+ * The 'chrome' of the Style Editor is all the around the actual editor (textbox).
+ * Manages the sheet selector, history, and opened editor(s) for the attached
+ * content window.
+ *
+ * @param DOMElement aRoot
+ *        Element that owns the chrome UI.
+ * @param DOMWindow aContentWindow
+ *        Content DOMWindow to attach to this chrome.
+ */
+this.StyleEditorChrome = function StyleEditorChrome(aRoot, aContentWindow)
+{
+  assert(aRoot, "Argument 'aRoot' is required to initialize StyleEditorChrome.");
+
+  this._root = aRoot;
+  this._document = this._root.ownerDocument;
+  this._window = this._document.defaultView;
+
+  this._editors = [];
+  this._listeners = []; // @see addChromeListener
+
+  // Store the content window so that we can call the real contentWindow setter
+  // in the open method.
+  this._contentWindowTemp = aContentWindow;
+
+  this._contentWindow = null;
+}
+
+StyleEditorChrome.prototype = {
+  _styleSheetToSelect: null,
+
+  open: function() {
+    let deferred = Promise.defer();
+    let initializeUI = function (aEvent) {
+      if (aEvent) {
+        this._window.removeEventListener("load", initializeUI, false);
+      }
+      let viewRoot = this._root.parentNode.querySelector(".splitview-root");
+      this._view = new SplitView(viewRoot);
+      this._setupChrome();
+
+      // We need to juggle arount the contentWindow items because we need to
+      // trigger the setter at the appropriate time.
+      this.contentWindow = this._contentWindowTemp; // calls setter
+      this._contentWindowTemp = null;
+
+      deferred.resolve();
+    }.bind(this);
+
+    if (this._document.readyState == "complete") {
+      initializeUI();
+    } else {
+      this._window.addEventListener("load", initializeUI, false);
+    }
+
+    return deferred.promise;
+  },
+
+  /**
+   * Retrieve the content window attached to this chrome.
+   *
+   * @return DOMWindow
+   *         Content window or null if no content window is attached.
+   */
+  get contentWindow() this._contentWindow,
+
+  /**
+   * Retrieve the ID of the content window attached to this chrome.
+   *
+   * @return number
+   *         Window ID or -1 if no content window is attached.
+   */
+  get contentWindowID()
+  {
+    try {
+      return this._contentWindow.QueryInterface(Ci.nsIInterfaceRequestor).
+        getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
+    } catch (ex) {
+      return -1;
+    }
+  },
+
+  /**
+   * Set the content window attached to this chrome.
+   * Content attach or detach events/notifications are triggered after the
+   * operation is complete (possibly asynchronous if the content is not fully
+   * loaded yet).
+   *
+   * @param DOMWindow aContentWindow
+   * @see addChromeListener
+   */
+  set contentWindow(aContentWindow)
+  {
+    if (this._contentWindow == aContentWindow) {
+      return; // no change
+    }
+
+    this._contentWindow = aContentWindow;
+
+    if (!aContentWindow) {
+      this._disableChrome();
+      return;
+    }
+
+    let onContentUnload = function () {
+      aContentWindow.removeEventListener("unload", onContentUnload, false);
+      if (this.contentWindow == aContentWindow) {
+        this.contentWindow = null; // detach
+      }
+    }.bind(this);
+    aContentWindow.addEventListener("unload", onContentUnload, false);
+
+    if (aContentWindow.document.readyState == "complete") {
+      this._root.classList.remove("loading");
+      this._populateChrome();
+      return;
+    } else {
+      this._root.classList.add("loading");
+      let onContentReady = function () {
+        aContentWindow.removeEventListener("load", onContentReady, false);
+        this._root.classList.remove("loading");
+        this._populateChrome();
+      }.bind(this);
+      aContentWindow.addEventListener("load", onContentReady, false);
+    }
+  },
+
+  /**
+   * Retrieve the content document attached to this chrome.
+   *
+   * @return DOMDocument
+   */
+  get contentDocument()
+  {
+    return this._contentWindow ? this._contentWindow.document : null;
+  },
+
+  /**
+   * Retrieve an array with the StyleEditor instance for each live style sheet,
+   * ordered by style sheet index.
+   *
+   * @return Array<StyleEditor>
+   */
+  get editors()
+  {
+    let editors = [];
+    this._editors.forEach(function (aEditor) {
+      if (aEditor.styleSheetIndex >= 0) {
+        editors[aEditor.styleSheetIndex] = aEditor;
+      }
+    });
+    return editors;
+  },
+
+  /**
+   * Get whether any of the editors have unsaved changes.
+   *
+   * @return boolean
+   */
+  get isDirty()
+  {
+    if (this._markedDirty === true) {
+      return true;
+    }
+    return this.editors.some(function(editor) {
+      return editor.sourceEditor && editor.sourceEditor.dirty;
+    });
+  },
+
+  /*
+   * Mark the style editor as having unsaved changes.
+   */
+  markDirty: function SEC_markDirty() {
+    this._markedDirty = true;
+  },
+
+  /**
+   * Add a listener for StyleEditorChrome events.
+   *
+   * The listener implements IStyleEditorChromeListener := {
+   *   onContentDetach:        Called when the content window has been detached.
+   *                           Arguments: (StyleEditorChrome aChrome)
+   *                           @see contentWindow
+   *
+   *   onEditorAdded:          Called when a stylesheet (therefore a StyleEditor
+   *                           instance) has been added to the UI.
+   *                           Arguments (StyleEditorChrome aChrome,
+   *                                      StyleEditor aEditor)
+   * }
+   *
+   * All listener methods are optional.
+   *
+   * @param IStyleEditorChromeListener aListener
+   * @see removeChromeListener
+   */
+  addChromeListener: function SEC_addChromeListener(aListener)
+  {
+    this._listeners.push(aListener);
+  },
+
+  /**
+   * Remove a listener for Chrome events from the current list of listeners.
+   *
+   * @param IStyleEditorChromeListener aListener
+   * @see addChromeListener
+   */
+  removeChromeListener: function SEC_removeChromeListener(aListener)
+  {
+    let index = this._listeners.indexOf(aListener);
+    if (index != -1) {
+      this._listeners.splice(index, 1);
+    }
+  },
+
+  /**
+   * Trigger named handlers in StyleEditorChrome listeners.
+   *
+   * @param string aName
+   *        Name of the event to trigger.
+   * @param Array aArgs
+   *        Optional array of arguments to pass to the listener(s).
+   * @see addActionListener
+   */
+  _triggerChromeListeners: function SE__triggerChromeListeners(aName, aArgs)
+  {
+    // insert the origin Chrome instance as first argument
+    if (!aArgs) {
+      aArgs = [this];
+    } else {
+      aArgs.unshift(this);
+    }
+
+    // copy the list of listeners to allow adding/removing listeners in handlers
+    let listeners = this._listeners.concat();
+    // trigger all listeners that have this named handler.
+    for (let i = 0; i < listeners.length; i++) {
+      let listener = listeners[i];
+      let handler = listener["on" + aName];
+      if (handler) {
+        handler.apply(listener, aArgs);
+      }
+    }
+  },
+
+  /**
+   * Create a new style editor, add to the list of editors, and bind this
+   * object as an action listener.
+   * @param DOMDocument aDocument
+   *        The document that the stylesheet is being referenced in.
+   * @param CSSStyleSheet aSheet
+   *        Optional stylesheet to edit from the document.
+   * @return StyleEditor
+   */
+  _createStyleEditor: function SEC__createStyleEditor(aDocument, aSheet) {
+    let editor = new StyleEditor(aDocument, aSheet);
+    this._editors.push(editor);
+    editor.addActionListener(this);
+    return editor;
+  },
+
+  /**
+   * Set up the chrome UI. Install event listeners and so on.
+   */
+  _setupChrome: function SEC__setupChrome()
+  {
+    // wire up UI elements
+    wire(this._view.rootElement, ".style-editor-newButton", function onNewButton() {
+      let editor = this._createStyleEditor(this.contentDocument);
+      editor.load();
+    }.bind(this));
+
+    wire(this._view.rootElement, ".style-editor-importButton", function onImportButton() {
+      let editor = this._createStyleEditor(this.contentDocument);
+      editor.importFromFile(this._mockImportFile || null, this._window);
+    }.bind(this));
+  },
+
+  /**
+   * Reset the chrome UI to an empty and ready state.
+   */
+  resetChrome: function SEC__resetChrome()
+  {
+    this._editors.forEach(function (aEditor) {
+      aEditor.removeActionListener(this);
+    }.bind(this));
+    this._editors = [];
+
+    this._view.removeAll();
+
+    // (re)enable UI
+    let matches = this._root.querySelectorAll("toolbarbutton,input,select");
+    for (let i = 0; i < matches.length; i++) {
+      matches[i].removeAttribute("disabled");
+    }
+  },
+
+  /**
+   * Add all imported stylesheets to chrome UI, recursively
+   *
+   * @param CSSStyleSheet aSheet
+   *        A stylesheet we're going to browse to look for all imported sheets.
+   */
+  _showImportedStyleSheets: function SEC__showImportedStyleSheets(aSheet)
+  {
+    let document = this.contentDocument;
+    for (let j = 0; j < aSheet.cssRules.length; j++) {
+      let rule = aSheet.cssRules.item(j);
+      if (rule.type == Ci.nsIDOMCSSRule.IMPORT_RULE) {
+        // Associated styleSheet may be null if it has already been seen due to
+        // duplicate @imports for the same URL.
+        if (!rule.styleSheet) {
+          continue;
+        }
+
+        this._createStyleEditor(document, rule.styleSheet);
+
+        this._showImportedStyleSheets(rule.styleSheet);
+      } else if (rule.type != Ci.nsIDOMCSSRule.CHARSET_RULE) {
+        // @import rules must precede all others except @charset
+        return;
+      }
+    }
+  },
+
+  /**
+   * Populate the chrome UI according to the content document.
+   *
+   * @see StyleEditor._setupShadowStyleSheet
+   */
+  _populateChrome: function SEC__populateChrome()
+  {
+    this.resetChrome();
+
+    let document = this.contentDocument;
+    this._document.title = _("chromeWindowTitle",
+      document.title || document.location.href);
+
+    for (let i = 0; i < document.styleSheets.length; i++) {
+      let styleSheet = document.styleSheets[i];
+
+      this._createStyleEditor(document, styleSheet);
+
+      this._showImportedStyleSheets(styleSheet);
+    }
+
+    // Queue editors loading so that ContentAttach is consistently triggered
+    // right after all editor instances are available (this.editors) but are
+    // NOT loaded/ready yet. This also helps responsivity during loading when
+    // there are many heavy stylesheets.
+    this._editors.forEach(function (aEditor) {
+      this._window.setTimeout(aEditor.load.bind(aEditor), 0);
+    }, this);
+  },
+
+  /**
+   * selects a stylesheet and optionally moves the cursor to a selected line
+   *
+   * @param {CSSStyleSheet} [aSheet]
+   *        Stylesheet that should be selected. If a stylesheet is not passed
+   *        and the editor is not initialized we focus the first stylesheet. If
+   *        a stylesheet is not passed and the editor is initialized we ignore
+   *        the call.
+   * @param {Number} [aLine]
+   *        Line to which the caret should be moved (one-indexed).
+   * @param {Number} [aCol]
+   *        Column to which the caret should be moved (one-indexed).
+   */
+  selectStyleSheet: function SEC_selectSheet(aSheet, aLine, aCol)
+  {
+    let alreadyCalled = !!this._styleSheetToSelect;
+
+    this._styleSheetToSelect = {
+      sheet: aSheet,
+      line: aLine,
+      col: aCol,
+    };
+
+    if (alreadyCalled) {
+      return;
+    }
+
+    let select = function DEC_select(aEditor) {
+      let sheet = this._styleSheetToSelect.sheet;
+      let line = this._styleSheetToSelect.line || 1;
+      let col = this._styleSheetToSelect.col || 1;
+
+      if (!aEditor.sourceEditor) {
+        let onAttach = function SEC_selectSheet_onAttach() {
+          aEditor.removeActionListener(this);
+          this.selectedStyleSheetIndex = aEditor.styleSheetIndex;
+          aEditor.sourceEditor.setCaretPosition(line - 1, col - 1);
+
+          let newSheet = this._styleSheetToSelect.sheet;
+          let newLine = this._styleSheetToSelect.line;
+          let newCol = this._styleSheetToSelect.col;
+          this._styleSheetToSelect = null;
+          if (newSheet != sheet) {
+              this.selectStyleSheet.bind(this, newSheet, newLine, newCol);
+          }
+        }.bind(this);
+
+        aEditor.addActionListener({
+          onAttach: onAttach
+        });
+      } else {
+        // If a line or column was specified we move the caret appropriately.
+        aEditor.sourceEditor.setCaretPosition(line - 1, col - 1);
+        this._styleSheetToSelect = null;
+      }
+
+        let summary = sheet ? this.getSummaryElementForEditor(aEditor)
+                            : this._view.getSummaryElementByOrdinal(0);
+        this._view.activeSummary = summary;
+      this.selectedStyleSheetIndex = aEditor.styleSheetIndex;
+    }.bind(this);
+
+    if (!this.editors.length) {
+      // We are in the main initialization phase so we wait for the editor
+      // containing the target stylesheet to be added and select the target
+      // stylesheet, optionally moving the cursor to a selected line.
+      let self = this;
+      this.addChromeListener({
+        onEditorAdded: function SEC_selectSheet_onEditorAdded(aChrome, aEditor) {
+          let sheet = self._styleSheetToSelect.sheet;
+          if ((sheet && aEditor.styleSheet == sheet) ||
+              (aEditor.styleSheetIndex == 0 && sheet == null)) {
+            aChrome.removeChromeListener(this);
+            aEditor.addActionListener(self);
+            select(aEditor);
+          }
+        }
+      });
+    } else if (aSheet) {
+      // We are already initialized and a stylesheet has been specified. Here
+      // we iterate through the editors and select the one containing the target
+      // stylesheet, optionally moving the cursor to a selected line.
+      for each (let editor in this.editors) {
+        if (editor.styleSheet == aSheet) {
+          select(editor);
+          break;
+        }
+      }
+    }
+  },
+
+  /**
+   * Disable all UI, effectively making editors read-only.
+   * This is automatically called when no content window is attached.
+   *
+   * @see contentWindow
+   */
+  _disableChrome: function SEC__disableChrome()
+  {
+    let matches = this._root.querySelectorAll("button,toolbarbutton,textbox");
+    for (let i = 0; i < matches.length; i++) {
+      matches[i].setAttribute("disabled", "disabled");
+    }
+
+    this.editors.forEach(function onEnterReadOnlyMode(aEditor) {
+      aEditor.readOnly = true;
+    });
+
+    this._view.rootElement.setAttribute("disabled", "disabled");
+
+    this._triggerChromeListeners("ContentDetach");
+  },
+
+  /**
+   * Retrieve the summary element for a given editor.
+   *
+   * @param StyleEditor aEditor
+   * @return DOMElement
+   *         Item's summary element or null if not found.
+   * @see SplitView
+   */
+  getSummaryElementForEditor: function SEC_getSummaryElementForEditor(aEditor)
+  {
+    return this._view.getSummaryElementByOrdinal(aEditor.styleSheetIndex);
+  },
+
+  /**
+   * Update split view summary of given StyleEditor instance.
+   *
+   * @param StyleEditor aEditor
+   * @param DOMElement aSummary
+   *        Optional item's summary element to update. If none, item corresponding
+   *        to passed aEditor is used.
+   */
+  _updateSummaryForEditor: function SEC__updateSummaryForEditor(aEditor, aSummary)
+  {
+    let summary = aSummary || this.getSummaryElementForEditor(aEditor);
+    let ruleCount = aEditor.styleSheet.cssRules.length;
+
+    this._view.setItemClassName(summary, aEditor.flags);
+
+    let label = summary.querySelector(".stylesheet-name > label");
+    label.setAttribute("value", aEditor.getFriendlyName());
+
+    text(summary, ".stylesheet-title", aEditor.styleSheet.title || "");
+    text(summary, ".stylesheet-rule-count",
+      PluralForm.get(ruleCount, _("ruleCount.label")).replace("#1", ruleCount));
+    text(summary, ".stylesheet-error-message", aEditor.errorMessage);
+  },
+
+  /**
+   * IStyleEditorActionListener implementation
+   * @See StyleEditor.addActionListener.
+   */
+
+  /**
+   * Called when source has been loaded and editor is ready for some action.
+   *
+   * @param StyleEditor aEditor
+   */
+  onLoad: function SEAL_onLoad(aEditor)
+  {
+    let item = this._view.appendTemplatedItem(STYLE_EDITOR_TEMPLATE, {
+      data: {
+        editor: aEditor
+      },
+      disableAnimations: this._alwaysDisableAnimations,
+      ordinal: aEditor.styleSheetIndex,
+      onCreate: function ASV_onItemCreate(aSummary, aDetails, aData) {
+        let editor = aData.editor;
+
+        wire(aSummary, ".stylesheet-enabled", function onToggleEnabled(aEvent) {
+          aEvent.stopPropagation();
+          aEvent.target.blur();
+
+          editor.enableStyleSheet(editor.styleSheet.disabled);
+        });
+
+        wire(aSummary, ".stylesheet-name", {
+          events: {
+            "keypress": function onStylesheetNameActivate(aEvent) {
+              if (aEvent.keyCode == aEvent.DOM_VK_RETURN) {
+                this._view.activeSummary = aSummary;
+              }
+            }.bind(this)
+          }
+        });
+
+        wire(aSummary, ".stylesheet-saveButton", function onSaveButton(aEvent) {
+          aEvent.stopPropagation();
+          aEvent.target.blur();
+
+          editor.saveToFile(editor.savedFile);
+        });
+
+        this._updateSummaryForEditor(editor, aSummary);
+
+        aSummary.addEventListener("focus", function onSummaryFocus(aEvent) {
+          if (aEvent.target == aSummary) {
+            // autofocus the stylesheet name
+            aSummary.querySelector(".stylesheet-name").focus();
+          }
+        }, false);
+
+        // autofocus new stylesheets
+        if (editor.hasFlag(StyleEditorFlags.NEW)) {
+          this._view.activeSummary = aSummary;
+        }
+
+        this._triggerChromeListeners("EditorAdded", [editor]);
+      }.bind(this),
+      onHide: function ASV_onItemShow(aSummary, aDetails, aData) {
+        aData.editor.onHide();
+      },
+      onShow: function ASV_onItemShow(aSummary, aDetails, aData) {
+        let editor = aData.editor;
+        if (!editor.inputElement) {
+          // attach editor to input element the first time it is shown
+          editor.inputElement = aDetails.querySelector(".stylesheet-editor-input");
+        }
+        editor.onShow();
+      }
+    });
+  },
+
+  /**
+   * Called when an editor flag changed.
+   *
+   * @param StyleEditor aEditor
+   * @param string aFlagName
+   * @see StyleEditor.flags
+   */
+  onFlagChange: function SEAL_onFlagChange(aEditor, aFlagName)
+  {
+    this._updateSummaryForEditor(aEditor);
+  },
+
+  /**
+   * Called when when changes have been committed/applied to the live DOM
+   * stylesheet.
+   *
+   * @param StyleEditor aEditor
+   */
+  onCommit: function SEAL_onCommit(aEditor)
+  {
+    this._updateSummaryForEditor(aEditor);
+  },
+};
deleted file mode 100644
--- a/browser/devtools/styleeditor/StyleEditorDebuggee.jsm
+++ /dev/null
@@ -1,332 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-this.EXPORTED_SYMBOLS = ["StyleEditorDebuggee", "StyleSheet"];
-
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-const Cu = Components.utils;
-
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource:///modules/devtools/EventEmitter.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "Promise",
-    "resource://gre/modules/commonjs/sdk/core/promise.js");
-
-/**
- * A StyleEditorDebuggee represents the document the style editor is debugging.
- * It maintains a list of StyleSheet objects that represent the stylesheets in
- * the target's document. It wraps remote debugging protocol comunications.
- *
- * It emits these events:
- *   'stylesheet-added': A stylesheet has been added to the debuggee's document
- *   'stylesheets-cleared': The debuggee's stylesheets have been reset (e.g. the
- *                          page navigated)
- *
- * @param {Target} target
- *         The target the debuggee is listening to
- */
-let StyleEditorDebuggee = function(target) {
-  EventEmitter.decorate(this);
-
-  this.styleSheets = [];
-
-  this.clear = this.clear.bind(this);
-  this._onNewDocument = this._onNewDocument.bind(this);
-  this._onStyleSheetsAdded = this._onStyleSheetsAdded.bind(this);
-
-  this._target = target;
-  this._actor = this.target.form.styleEditorActor;
-
-  this.client.addListener("styleSheetsAdded", this._onStyleSheetsAdded);
-  this._target.on("navigate", this._onNewDocument);
-
-  this._onNewDocument();
-}
-
-StyleEditorDebuggee.prototype = {
-  /**
-   * list of StyleSheet objects for this target
-   */
-  styleSheets: null,
-
-  /**
-   * baseURIObject for the current document
-   */
-  baseURI: null,
-
-  /**
-   * The target we're debugging
-   */
-  get target() {
-    return this._target;
-  },
-
-  /**
-   * Client for communicating with server with remote debug protocol.
-   */
-  get client() {
-    return this._target.client;
-  },
-
-  /**
-   * Get the StyleSheet object with the given href.
-   *
-   * @param  {string} href
-   *         Url of the stylesheet to find
-   * @return {StyleSheet}
-   *         StyleSheet with the matching href
-   */
-  styleSheetFromHref: function(href) {
-    for (let sheet of this.styleSheets) {
-      if (sheet.href == href) {
-        return sheet;
-      }
-    }
-    return null;
-  },
-
-  /**
-   * Clear stylesheets and state.
-   */
-  clear: function() {
-    this.baseURI = null;
-
-    for (let stylesheet of this.styleSheets) {
-      stylesheet.destroy();
-    }
-    this.styleSheets = [];
-
-    this.emit("stylesheets-cleared");
-  },
-
-  /**
-   * Called when target is created or has navigated.
-   * Clear previous sheets and request new document's
-   */
-  _onNewDocument: function() {
-    this.clear();
-
-    this._getBaseURI();
-
-    let message = { type: "newDocument" };
-    this._sendRequest(message);
-  },
-
-  /**
-   * request baseURIObject information from the document
-   */
-  _getBaseURI: function() {
-    let message = { type: "getBaseURI" };
-    this._sendRequest(message, (response) => {
-      this.baseURI = response.baseURI;
-    });
-  },
-
-  /**
-   * Handle stylesheet-added event from the target
-   *
-   * @param {string} type
-   *        Type of event
-   * @param {object} request
-   *        Event details
-   */
-  _onStyleSheetsAdded: function(type, request) {
-    for (let form of request.styleSheets) {
-      let sheet = this._addStyleSheet(form);
-      this.emit("stylesheet-added", sheet);
-    }
-  },
-
-  /**
-   * Create a new StyleSheet object from the form
-   * and add to our stylesheet list.
-   *
-   * @param {object} form
-   *        Initial properties of the stylesheet
-   */
-  _addStyleSheet: function(form) {
-    let sheet = new StyleSheet(form, this);
-    this.styleSheets.push(sheet);
-    return sheet;
-  },
-
-  /**
-   * Create a new stylesheet with the given text
-   * and attach it to the document.
-   *
-   * @param {string} text
-   *        Initial text of the stylesheet
-   * @param {function} callback
-   *        Function to call when the stylesheet has been added to the document
-   */
-  createStyleSheet: function(text, callback) {
-    let message = { type: "newStyleSheet", text: text };
-    this._sendRequest(message, (response) => {
-      let sheet = this._addStyleSheet(response.styleSheet);
-      callback(sheet);
-    });
-  },
-
-  /**
-   * Send a request to our actor on the server
-   *
-   * @param {object} message
-   *        Message to send to the actor
-   * @param {function} callback
-   *        Function to call with reponse from actor
-   */
-  _sendRequest: function(message, callback) {
-    message.to = this._actor;
-    this.client.request(message, callback);
-  },
-
-  /**
-   * Clean up and remove listeners
-   */
-  destroy: function() {
-    this.clear();
-
-    this._target.off("will-navigate", this.clear);
-    this._target.off("navigate", this._onNewDocument);
-  }
-}
-
-/**
- * A StyleSheet object represents a stylesheet on the debuggee. It wraps
- * communication with a complimentary StyleSheetActor on the server.
- *
- * It emits these events:
- *   'source-load' - The full text source of the stylesheet has been fetched
- *   'property-change' - Any property (e.g 'disabled') has changed
- *   'style-applied' - A change has been applied to the live stylesheet on the server
- *   'error' - An error occured when loading or saving stylesheet
- *
- * @param {object} form
- *        Initial properties of the stylesheet
- * @param {StyleEditorDebuggee} debuggee
- *        Owner of the stylesheet
- */
-let StyleSheet = function(form, debuggee) {
-  EventEmitter.decorate(this);
-
-  this.debuggee = debuggee;
-  this._client = debuggee.client;
-  this._actor = form.actor;
-
-  this._onSourceLoad = this._onSourceLoad.bind(this);
-  this._onPropertyChange = this._onPropertyChange.bind(this);
-  this._onError = this._onError.bind(this);
-  this._onStyleApplied = this._onStyleApplied.bind(this);
-
-  this._client.addListener("sourceLoad-" + this._actor, this._onSourceLoad);
-  this._client.addListener("propertyChange-" + this._actor, this._onPropertyChange);
-  this._client.addListener("error-" + this._actor, this._onError);
-  this._client.addListener("styleApplied-" + this._actor, this._onStyleApplied);
-
-  // set initial property values
-  for (let attr in form) {
-    this[attr] = form[attr];
-  }
-}
-
-StyleSheet.prototype = {
-  /**
-   * Toggle the disabled attribute of the stylesheet
-   */
-  toggleDisabled: function() {
-    let message = { type: "toggleDisabled" };
-    this._sendRequest(message);
-  },
-
-  /**
-   * Request that the source of the stylesheet be fetched.
-   * 'source-load' event will be fired when it's been fetched.
-   */
-  fetchSource: function() {
-    let message = { type: "fetchSource" };
-    this._sendRequest(message);
-  },
-
-  /**
-   * Update the stylesheet in place with the given full source.
-   *
-   * @param {string} sheetText
-   *        Full text to update the stylesheet with
-   */
-  update: function(sheetText) {
-    let message = { type: "update", text: sheetText, transition: true };
-    this._sendRequest(message);
-  },
-
-  /**
-   * Handle source load event from the client.
-   *
-   * @param {string} type
-   *        Event type
-   * @param {object} request
-   *        Event details
-   */
-  _onSourceLoad: function(type, request) {
-    this.emit("source-load", request.source);
-  },
-
-  /**
-   * Handle a property change on the stylesheet
-   *
-   * @param {string} type
-   *        Event type
-   * @param {object} request
-   *        Event details
-   */
-  _onPropertyChange: function(type, request) {
-    this[request.property] = request.value;
-    this.emit("property-change", request.property);
-  },
-
-  /**
-   * Propogate errors from the server that relate to this stylesheet.
-   *
-   * @param {string} type
-   *        Event type
-   * @param {object} request
-   *        Event details
-   */
-  _onError: function(type, request) {
-    this.emit("error", request.errorMessage);
-  },
-
-  /**
-   * Handle event when update has been successfully applied and propogate it.
-   */
-  _onStyleApplied: function() {
-    this.emit("style-applied");
-  },
-
-  /**
-   * Send a request to our actor on the server
-   *
-   * @param {object} message
-   *        Message to send to the actor
-   * @param {function} callback
-   *        Function to call with reponse from actor
-   */
-  _sendRequest: function(message, callback) {
-    message.to = this._actor;
-    this._client.request(message, callback);
-  },
-
-  /**
-   * Clean up and remove event listeners
-   */
-  destroy: function() {
-    this._client.removeListener("sourceLoad-" + this._actor, this._onSourceLoad);
-    this._client.removeListener("propertyChange-" + this._actor, this._onPropertyChange);
-    this._client.removeListener("error-" + this._actor, this._onError);
-    this._client.removeListener("styleApplied-" + this._actor, this._onStyleApplied);
-  }
-}
--- a/browser/devtools/styleeditor/StyleEditorPanel.jsm
+++ b/browser/devtools/styleeditor/StyleEditorPanel.jsm
@@ -7,122 +7,202 @@
 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 this.EXPORTED_SYMBOLS = ["StyleEditorPanel"];
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js");
 Cu.import("resource:///modules/devtools/EventEmitter.jsm");
-Cu.import("resource:///modules/devtools/StyleEditorDebuggee.jsm");
-Cu.import("resource:///modules/devtools/StyleEditorUI.jsm");
-Cu.import("resource:///modules/devtools/StyleEditorUtil.jsm");
-
 
 XPCOMUtils.defineLazyModuleGetter(this, "StyleEditorChrome",
                         "resource:///modules/devtools/StyleEditorChrome.jsm");
 
 this.StyleEditorPanel = function StyleEditorPanel(panelWin, toolbox) {
   EventEmitter.decorate(this);
 
   this._toolbox = toolbox;
   this._target = toolbox.target;
+
+  this.newPage = this.newPage.bind(this);
+  this.destroy = this.destroy.bind(this);
+  this.beforeNavigate = this.beforeNavigate.bind(this);
+
+  this._target.on("will-navigate", this.beforeNavigate);
+  this._target.on("navigate", this.newPage);
+  this._target.on("close", this.destroy);
+
   this._panelWin = panelWin;
   this._panelDoc = panelWin.document;
-
-  this.destroy = this.destroy.bind(this);
-  this._showError = this._showError.bind(this);
 }
 
 StyleEditorPanel.prototype = {
-  get target() this._toolbox.target,
-
-  get panelWindow() this._panelWin,
-
   /**
    * open is effectively an asynchronous constructor
    */
-  open: function() {
+  open: function StyleEditor_open() {
+    let contentWin = this._toolbox.target.window;
     let deferred = Promise.defer();
 
-    let promise;
-    // We always interact with the target as if it were remote
-    if (!this.target.isRemote) {
-      promise = this.target.makeRemote();
-    } else {
-      promise = Promise.resolve(this.target);
-    }
-
-    promise.then(() => {
-      this.target.on("close", this.destroy);
-
-      this._debuggee = new StyleEditorDebuggee(this.target);
-
-      this.UI = new StyleEditorUI(this._debuggee, this._panelDoc);
-      this.UI.on("error", this._showError);
-
+    this.setPage(contentWin).then(function() {
       this.isReady = true;
       deferred.resolve(this);
-    })
+    }.bind(this));
 
     return deferred.promise;
   },
 
   /**
-   * Show an error message from the style editor in the toolbox
-   * notification box.
-   *
-   * @param  {string} event
-   *         Type of event
-   * @param  {string} errorCode
-   *         Error code of error to report
+   * Target getter.
+   */
+  get target() this._target,
+
+  /**
+   * Panel window getter.
+   */
+  get panelWindow() this._panelWin,
+
+  /**
+   * StyleEditorChrome instance getter.
+   */
+  get styleEditorChrome() this._panelWin.styleEditorChrome,
+
+  /**
+   * Set the page to target.
+   */
+  setPage: function StyleEditor_setPage(contentWindow) {
+    if (this._panelWin.styleEditorChrome) {
+      this._panelWin.styleEditorChrome.contentWindow = contentWindow;
+      this.selectStyleSheet(null, null, null);
+    } else {
+      let chromeRoot = this._panelDoc.getElementById("style-editor-chrome");
+      let chrome = new StyleEditorChrome(chromeRoot, contentWindow);
+      let promise = chrome.open();
+
+      this._panelWin.styleEditorChrome = chrome;
+      this.selectStyleSheet(null, null, null);
+      return promise;
+    }
+  },
+
+  /**
+   * Navigated to a new page.
+   */
+  newPage: function StyleEditor_newPage(event, payload) {
+    let window = payload._navPayload || payload;
+    this.reset();
+    this.setPage(window);
+  },
+
+  /**
+   * Before navigating to a new page or reloading the page.
+   */
+  beforeNavigate: function StyleEditor_beforeNavigate(event, payload) {
+    let request = payload._navPayload || payload;
+    if (this.styleEditorChrome.isDirty) {
+      this.preventNavigate(request);
+    }
+  },
+
+  /**
+   * Show a notificiation about losing unsaved changes.
    */
-  _showError: function(event, errorCode) {
-    let message = _(errorCode);
-    let notificationBox = this._toolbox.getNotificationBox();
-    let notification = notificationBox.getNotificationWithValue("styleeditor-error");
-    if (!notification) {
-      notificationBox.appendNotification(message,
-        "styleeditor-error", "", notificationBox.PRIORITY_CRITICAL_LOW);
+  preventNavigate: function StyleEditor_preventNavigate(request) {
+    request.suspend();
+
+    let notificationBox = null;
+    if (this.target.isLocalTab) {
+      let gBrowser = this.target.tab.ownerDocument.defaultView.gBrowser;
+      notificationBox = gBrowser.getNotificationBox();
+    }
+    else {
+      notificationBox = this._toolbox.getNotificationBox();
+    }
+
+    let notification = notificationBox.
+      getNotificationWithValue("styleeditor-page-navigation");
+
+    if (notification) {
+      notificationBox.removeNotification(notification, true);
     }
+
+    let cancelRequest = function onCancelRequest() {
+      if (request) {
+        request.cancel(Cr.NS_BINDING_ABORTED);
+        request.resume(); // needed to allow the connection to be cancelled.
+        request = null;
+      }
+    };
+
+    let eventCallback = function onNotificationCallback(event) {
+      if (event == "removed") {
+        cancelRequest();
+      }
+    };
+
+    let buttons = [
+      {
+        id: "styleeditor.confirmNavigationAway.buttonLeave",
+        label: this.strings.GetStringFromName("confirmNavigationAway.buttonLeave"),
+        accessKey: this.strings.GetStringFromName("confirmNavigationAway.buttonLeaveAccesskey"),
+        callback: function onButtonLeave() {
+          if (request) {
+            request.resume();
+            request = null;
+          }
+        }.bind(this),
+      },
+      {
+        id: "styleeditor.confirmNavigationAway.buttonStay",
+        label: this.strings.GetStringFromName("confirmNavigationAway.buttonStay"),
+        accessKey: this.strings.GetStringFromName("confirmNavigationAway.buttonStayAccesskey"),
+        callback: cancelRequest
+      },
+    ];
+
+    let message = this.strings.GetStringFromName("confirmNavigationAway.message");
+
+    notification = notificationBox.appendNotification(message,
+      "styleeditor-page-navigation", "chrome://browser/skin/Info.png",
+      notificationBox.PRIORITY_WARNING_HIGH, buttons, eventCallback);
+
+    // Make sure this not a transient notification, to avoid the automatic
+    // transient notification removal.
+    notification.persistence = -1;
+  },
+
+
+  /**
+   * No window available anymore.
+   */
+  reset: function StyleEditor_reset() {
+    this._panelWin.styleEditorChrome.resetChrome();
   },
 
   /**
    * Select a stylesheet.
-   *
-   * @param {string} href
-   *        Url of stylesheet to find and select in editor
-   * @param {number} line
-   *        Line number to jump to after selecting
-   * @param {number} col
-   *        Column number to jump to after selecting
    */
-  selectStyleSheet: function(href, line, col) {
-    if (!this._debuggee || !this.UI) {
-      return;
-    }
-    let stylesheet = this._debuggee.styleSheetFromHref(href);
-    this.UI.selectStyleSheet(href, line, col);
+  selectStyleSheet: function StyleEditor_selectStyleSheet(stylesheet, line, col) {
+    this._panelWin.styleEditorChrome.selectStyleSheet(stylesheet, line, col);
   },
 
   /**
-   * Destroy the style editor.
+   * Destroy StyleEditor
    */
-  destroy: function() {
+  destroy: function StyleEditor_destroy() {
     if (!this._destroyed) {
       this._destroyed = true;
 
       this._target.off("will-navigate", this.beforeNavigate);
+      this._target.off("navigate", this.newPage);
       this._target.off("close", this.destroy);
       this._target = null;
       this._toolbox = null;
+      this._panelWin = null;
       this._panelDoc = null;
-
-      this._debuggee.destroy();
-      this.UI.destroy();
     }
 
     return Promise.resolve(null);
   },
 }
 
 XPCOMUtils.defineLazyGetter(StyleEditorPanel.prototype, "strings",
   function () {
deleted file mode 100644
--- a/browser/devtools/styleeditor/StyleEditorUI.jsm
+++ /dev/null
@@ -1,427 +0,0 @@
-/* vim:set ts=2 sw=2 sts=2 et: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-this.EXPORTED_SYMBOLS = ["StyleEditorUI"];
-
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-const Cu = Components.utils;
-
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/PluralForm.jsm");
-Cu.import("resource://gre/modules/NetUtil.jsm");
-Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js");
-Cu.import("resource:///modules/devtools/EventEmitter.jsm");
-Cu.import("resource:///modules/devtools/StyleEditorUtil.jsm");
-Cu.import("resource:///modules/devtools/SplitView.jsm");
-Cu.import("resource:///modules/devtools/StyleSheetEditor.jsm");
-
-
-const LOAD_ERROR = "error-load";
-
-const STYLE_EDITOR_TEMPLATE = "stylesheet";
-
-/**
- * StyleEditorUI is controls and builds the UI of the Style Editor, including
- * maintaining a list of editors for each stylesheet on a debuggee.
- *
- * Emits events:
- *   'editor-added': A new editor was added to the UI
- *   'error': An error occured
- *
- * @param {StyleEditorDebuggee} debuggee
- *        Debuggee of whose stylesheets should be shown in the UI
- * @param {Document} panelDoc
- *        Document of the toolbox panel to populate UI in.
- */
-function StyleEditorUI(debuggee, panelDoc) {
-  EventEmitter.decorate(this);
-
-  this._debuggee = debuggee;
-  this._panelDoc = panelDoc;
-  this._window = this._panelDoc.defaultView;
-  this._root = this._panelDoc.getElementById("style-editor-chrome");
-
-  this.editors = [];
-  this.selectedStyleSheetIndex = -1;
-
-  this._onStyleSheetAdded = this._onStyleSheetAdded.bind(this);
-  this._onStyleSheetCreated = this._onStyleSheetCreated.bind(this);
-  this._onStyleSheetsCleared = this._onStyleSheetsCleared.bind(this);
-  this._onError = this._onError.bind(this);
-
-  debuggee.on("stylesheet-added", this._onStyleSheetAdded);
-  debuggee.on("stylesheets-cleared", this._onStyleSheetsCleared);
-
-  this.createUI();
-}
-
-StyleEditorUI.prototype = {
-  /**
-   * Get whether any of the editors have unsaved changes.
-   *
-   * @return boolean
-   */
-  get isDirty()
-  {
-    if (this._markedDirty === true) {
-      return true;
-    }
-    return this.editors.some((editor) => {
-      return editor.sourceEditor && editor.sourceEditor.dirty;
-    });
-  },
-
-  /*
-   * Mark the style editor as having or not having unsaved changes.
-   */
-  set isDirty(value) {
-    this._markedDirty = value;
-  },
-
-  /**
-   * Build the initial UI and wire buttons with event handlers.
-   */
-  createUI: function() {
-    let viewRoot = this._root.parentNode.querySelector(".splitview-root");
-
-    this._view = new SplitView(viewRoot);
-
-    wire(this._view.rootElement, ".style-editor-newButton", function onNew() {
-      this._debuggee.createStyleSheet(null, this._onStyleSheetCreated);
-    }.bind(this));
-
-    wire(this._view.rootElement, ".style-editor-importButton", function onImport() {
-      this._importFromFile(this._mockImportFile || null, this._window);
-    }.bind(this));
-  },
-
-  /**
-   * Import a style sheet from file and asynchronously create a
-   * new stylesheet on the debuggee for it.
-   *
-   * @param {mixed} file
-   *        Optional nsIFile or filename string.
-   *        If not set a file picker will be shown.
-   * @param {nsIWindow} parentWindow
-   *        Optional parent window for the file picker.
-   */
-  _importFromFile: function(file, parentWindow)
-  {
-    let onFileSelected = function(file) {
-      if (!file) {
-        this.emit("error", LOAD_ERROR);
-        return;
-      }
-      NetUtil.asyncFetch(file, (stream, status) => {
-        if (!Components.isSuccessCode(status)) {
-          this.emit("error", LOAD_ERROR);
-          return;
-        }
-        let source = NetUtil.readInputStreamToString(stream, stream.available());
-        stream.close();
-
-        this._debuggee.createStyleSheet(source, (styleSheet) => {
-          this._onStyleSheetCreated(styleSheet, file);
-        });
-      });
-
-    }.bind(this);
-
-    showFilePicker(file, false, parentWindow, onFileSelected);
-  },
-
-  /**
-   * Handler for debuggee's 'stylesheets-cleared' event. Remove all editors.
-   */
-  _onStyleSheetsCleared: function() {
-    this._clearStyleSheetEditors();
-
-    this._view.removeAll();
-    this.selectedStyleSheetIndex = -1;
-
-    this._root.classList.add("loading");
-  },
-
-  /**
-   * When a new or imported stylesheet has been added to the document.
-   * Add an editor for it.
-   */
-  _onStyleSheetCreated: function(styleSheet, file) {
-    this._addStyleSheetEditor(styleSheet, file, true);
-  },
-
-  /**
-   * Handler for debuggee's 'stylesheet-added' event. Add an editor.
-   *
-   * @param {string} event
-   *        Event name
-   * @param {StyleSheet} styleSheet
-   *        StyleSheet object for new sheet
-   */
-  _onStyleSheetAdded: function(event, styleSheet) {
-    // this might be the first stylesheet, so remove loading indicator
-    this._root.classList.remove("loading");
-    this._addStyleSheetEditor(styleSheet);
-  },
-
-  /**
-   * Forward any error from a stylesheet.
-   *
-   * @param  {string} event
-   *         Event name
-   * @param  {string} errorCode
-   *         Code represeting type of error
-   */
-  _onError: function(event, errorCode) {
-    this.emit("error", errorCode);
-  },
-
-  /**
-   * Add a new editor to the UI for a stylesheet.
-   *
-   * @param {StyleSheet}  styleSheet
-   *        Object representing stylesheet
-   * @param {nsIfile}  file
-   *         Optional file object that sheet was imported from
-   * @param {Boolean} isNew
-   *         Optional if stylesheet is a new sheet created by user
-   */
-  _addStyleSheetEditor: function(styleSheet, file, isNew) {
-    let editor = new StyleSheetEditor(styleSheet, this._window, file, isNew);
-
-    editor.once("source-load", this._sourceLoaded.bind(this, editor));
-    editor.on("property-change", this._summaryChange.bind(this, editor));
-    editor.on("style-applied", this._summaryChange.bind(this, editor));
-    editor.on("error", this._onError);
-
-    this.editors.push(editor);
-
-    // Queue editor loading. This helps responsivity during loading when
-    // there are many heavy stylesheets.
-    this._window.setTimeout(editor.fetchSource.bind(editor), 0);
-  },
-
-  /**
-   * Clear all the editors from the UI.
-   */
-  _clearStyleSheetEditors: function() {
-    for (let editor of this.editors) {
-      editor.destroy();
-    }
-    this.editors = [];
-  },
-
-  /**
-   * Handler for an StyleSheetEditor's 'source-load' event.
-   * Create a summary UI for the editor.
-   *
-   * @param  {StyleSheetEditor} editor
-   *         Editor to create UI for.
-   */
-  _sourceLoaded: function(editor) {
-    // add new sidebar item and editor to the UI
-    this._view.appendTemplatedItem(STYLE_EDITOR_TEMPLATE, {
-      data: {
-        editor: editor
-      },
-      disableAnimations: this._alwaysDisableAnimations,
-      ordinal: editor.styleSheet.styleSheetIndex,
-      onCreate: function(summary, details, data) {
-        let editor = data.editor;
-        editor.summary = summary;
-
-        wire(summary, ".stylesheet-enabled", function onToggleDisabled(event) {
-          event.stopPropagation();
-          event.target.blur();
-
-          editor.toggleDisabled();
-        });
-
-        wire(summary, ".stylesheet-name", {
-          events: {
-            "keypress": function onStylesheetNameActivate(aEvent) {
-              if (aEvent.keyCode == aEvent.DOM_VK_RETURN) {
-                this._view.activeSummary = summary;
-              }
-            }.bind(this)
-          }
-        });
-
-        wire(summary, ".stylesheet-saveButton", function onSaveButton(event) {
-          event.stopPropagation();
-          event.target.blur();
-
-          editor.saveToFile(editor.savedFile);
-        });
-
-        this._updateSummaryForEditor(editor, summary);
-
-        summary.addEventListener("focus", function onSummaryFocus(event) {
-          if (event.target == summary) {
-            // autofocus the stylesheet name
-            summary.querySelector(".stylesheet-name").focus();
-          }
-        }, false);
-
-        // autofocus if it's a new user-created stylesheet
-        if (editor.isNew) {
-          this._selectEditor(editor);
-        }
-
-        if (this._styleSheetToSelect
-            && this._styleSheetToSelect.href == editor.styleSheet.href) {
-          this.switchToSelectedSheet();
-        }
-
-        // If this is the first stylesheet, select it
-        if (this.selectedStyleSheetIndex == -1
-            && !this._styleSheetToSelect
-            && editor.styleSheet.styleSheetIndex == 0) {
-          this._selectEditor(editor);
-        }
-
-        this.emit("editor-added", editor);
-      }.bind(this),
-
-      onShow: function(summary, details, data) {
-        let editor = data.editor;
-        if (!editor.sourceEditor) {
-          // only initialize source editor when we switch to this view
-          let inputElement = details.querySelector(".stylesheet-editor-input");
-          editor.load(inputElement);
-        }
-        editor.onShow();
-      }
-    });
-  },
-
-  /**
-   * Switch to the editor that has been marked to be selected.
-   */
-  switchToSelectedSheet: function() {
-    let sheet = this._styleSheetToSelect;
-
-    for each (let editor in this.editors) {
-      if (editor.styleSheet.href == sheet.href) {
-        this._selectEditor(editor, sheet.line, sheet.col);
-        this._styleSheetToSelect = null;
-        break;
-      }
-    }
-  },
-
-  /**
-   * Select an editor in the UI.
-   *
-   * @param  {StyleSheetEditor} editor
-   *         Editor to switch to.
-   * @param  {number} line
-   *         Line number to jump to
-   * @param  {number} col
-   *         Column number to jump to
-   */
-  _selectEditor: function(editor, line, col) {
-    line = line || 1;
-    col = col || 1;
-
-    this.selectedStyleSheetIndex = editor.styleSheet.styleSheetIndex;
-
-    editor.getSourceEditor().then(() => {
-      editor.sourceEditor.setCaretPosition(line - 1, col - 1);
-    });
-
-    this._view.activeSummary = editor.summary;
-  },
-
-  /**
-   * selects a stylesheet and optionally moves the cursor to a selected line
-   *
-   * @param {string} [href]
-   *        Href of stylesheet that should be selected. If a stylesheet is not passed
-   *        and the editor is not initialized we focus the first stylesheet. If
-   *        a stylesheet is not passed and the editor is initialized we ignore
-   *        the call.
-   * @param {Number} [line]
-   *        Line to which the caret should be moved (one-indexed).
-   * @param {Number} [col]
-   *        Column to which the caret should be moved (one-indexed).
-   */
-  selectStyleSheet: function(href, line, col)
-  {
-    let alreadyCalled = !!this._styleSheetToSelect;
-
-    this._styleSheetToSelect = {
-      href: href,
-      line: line,
-      col: col,
-    };
-
-    if (alreadyCalled) {
-      return;
-    }
-
-    /* Switch to the editor for this sheet, if it exists yet.
-       Otherwise each editor will be checked when it's created. */
-    this.switchToSelectedSheet();
-  },
-
-
-  /**
-   * Handler for an editor's 'property-changed' event.
-   * Update the summary in the UI.
-   *
-   * @param  {StyleSheetEditor} editor
-   *         Editor for which a property has changed
-   */
-  _summaryChange: function(editor) {
-    this._updateSummaryForEditor(editor);
-  },
-
-  /**
-   * Update split view summary of given StyleEditor instance.
-   *
-   * @param {StyleSheetEditor} editor
-   * @param {DOMElement} summary
-   *        Optional item's summary element to update. If none, item corresponding
-   *        to passed editor is used.
-   */
-  _updateSummaryForEditor: function(editor, summary) {
-    summary = summary || editor.summary;
-    if (!summary) {
-      return;
-    }
-    let ruleCount = "-";
-    if (editor.styleSheet.ruleCount !== undefined) {
-      ruleCount = editor.styleSheet.ruleCount;
-    }
-
-    var flags = [];
-    if (editor.styleSheet.disabled) {
-      flags.push("disabled");
-    }
-    if (editor.unsaved) {
-      flags.push("unsaved");
-    }
-    this._view.setItemClassName(summary, flags.join(" "));
-
-    let label = summary.querySelector(".stylesheet-name > label");
-    label.setAttribute("value", editor.friendlyName);
-
-    text(summary, ".stylesheet-title", editor.styleSheet.title || "");
-    text(summary, ".stylesheet-rule-count",
-      PluralForm.get(ruleCount, _("ruleCount.label")).replace("#1", ruleCount));
-    text(summary, ".stylesheet-error-message", editor.errorMessage);
-  },
-
-  destroy: function() {
-    this._clearStyleSheetEditors();
-
-    this._debuggee.off("stylesheet-added", this._onStyleSheetAdded);
-    this._debuggee.off("stylesheets-cleared", this._onStyleSheetsCleared);
-  }
-}
--- a/browser/devtools/styleeditor/StyleEditorUtil.jsm
+++ b/browser/devtools/styleeditor/StyleEditorUtil.jsm
@@ -3,20 +3,21 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 this.EXPORTED_SYMBOLS = [
   "_",
   "assert",
+  "attr", // XXXkhuey unused?
+  "getCurrentBrowserTabContentWindow", // XXXkhuey unused?
   "log",
   "text",
-  "wire",
-  "showFilePicker"
+  "wire"
 ];
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/Services.jsm");
 
@@ -102,17 +103,17 @@ function forEach(aObject, aCallback)
     if (aObject.hasOwnProperty(key)) {
       aCallback(key, aObject[key]);
     }
   }
 }
 
 /**
  * Log a message to the console.
- *
+ * 
  * @param ...rest
  *        One or multiple arguments to log.
  *        If multiple arguments are given, they will be joined by " " in the log.
  */
 this.log = function log()
 {
   console.logStringMessage(Array.prototype.slice.call(arguments).join(" "));
 }
@@ -156,68 +157,8 @@ this.wire = function wire(aRoot, aSelect
     forEach(aDescriptor.events, function (aName, aHandler) {
       element.addEventListener(aName, aHandler, false);
     });
     forEach(aDescriptor.attributes, element.setAttribute);
     forEach(aDescriptor.userData, element.setUserData);
   }
 }
 
-/**
- * Show file picker and return the file user selected.
- *
- * @param mixed file
- *        Optional nsIFile or string representing the filename to auto-select.
- * @param boolean toSave
- *        If true, the user is selecting a filename to save.
- * @param nsIWindow parentWindow
- *        Optional parent window. If null the parent window of the file picker
- *        will be the window of the attached input element.
- * @param callback
- *        The callback method, which will be called passing in the selected
- *        file or null if the user did not pick one.
- */
-this.showFilePicker = function showFilePicker(path, toSave, parentWindow, callback)
-{
-  if (typeof(path) == "string") {
-    try {
-      if (Services.io.extractScheme(path) == "file") {
-        let uri = Services.io.newURI(path, null, null);
-        let file = uri.QueryInterface(Ci.nsIFileURL).file;
-        callback(file);
-        return;
-      }
-    } catch (ex) {
-      callback(null);
-      return;
-    }
-    try {
-      let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
-      file.initWithPath(path);
-      callback(file);
-      return;
-    } catch (ex) {
-      callback(null);
-      return;
-    }
-  }
-  if (path) { // "path" is an nsIFile
-    callback(path);
-    return;
-  }
-
-  let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
-  let mode = toSave ? fp.modeSave : fp.modeOpen;
-  let key = toSave ? "saveStyleSheet" : "importStyleSheet";
-  let fpCallback = function(result) {
-    if (result == Ci.nsIFilePicker.returnCancel) {
-      callback(null);
-    } else {
-      callback(fp.file);
-    }
-  };
-
-  fp.init(parentWindow, _(key + ".title"), mode);
-  fp.appendFilters(_(key + ".filter"), "*.css");
-  fp.appendFilters(fp.filterAll);
-  fp.open(fpCallback);
-  return;
-}
deleted file mode 100644
--- a/browser/devtools/styleeditor/StyleSheetEditor.jsm
+++ /dev/null
@@ -1,548 +0,0 @@
-/* vim:set ts=2 sw=2 sts=2 et: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-this.EXPORTED_SYMBOLS = ["StyleSheetEditor"];
-
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-const Cu = Components.utils;
-
-Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js");
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/FileUtils.jsm");
-Cu.import("resource://gre/modules/NetUtil.jsm");
-Cu.import("resource:///modules/devtools/EventEmitter.jsm");
-Cu.import("resource:///modules/source-editor.jsm");
-Cu.import("resource:///modules/devtools/StyleEditorUtil.jsm");
-
-
-const SAVE_ERROR = "error-save";
-
-// max update frequency in ms (avoid potential typing lag and/or flicker)
-// @see StyleEditor.updateStylesheet
-const UPDATE_STYLESHEET_THROTTLE_DELAY = 500;
-
-/**
- * StyleSheetEditor controls the editor linked to a particular StyleSheet
- * object.
- *
- * Emits events:
- *   'source-load': The source of the stylesheet has been fetched
- *   'property-change': A property on the underlying stylesheet has changed
- *   'source-editor-load': The source editor for this editor has been loaded
- *   'error': An error has occured
- *
- * @param {StyleSheet}  styleSheet
- * @param {DOMWindow}  win
- *        panel window for style editor
- * @param {nsIFile}  file
- *        Optional file that the sheet was imported from
- * @param {boolean} isNew
- *        Optional whether the sheet was created by the user
- */
-function StyleSheetEditor(styleSheet, win, file, isNew) {
-  EventEmitter.decorate(this);
-
-  this.styleSheet = styleSheet;
-  this._inputElement = null;
-  this._sourceEditor = null;
-  this._window = win;
-  this._isNew = isNew;
-  this.savedFile = file;
-
-  this.errorMessage = null;
-
-  this._state = {   // state to use when inputElement attaches
-    text: "",
-    selection: {start: 0, end: 0},
-    readOnly: false,
-    topIndex: 0,              // the first visible line
-  };
-
-  this._styleSheetFilePath = null;
-  if (styleSheet.href &&
-      Services.io.extractScheme(this.styleSheet.href) == "file") {
-    this._styleSheetFilePath = this.styleSheet.href;
-  }
-
-  this._onSourceLoad = this._onSourceLoad.bind(this);
-  this._onPropertyChange = this._onPropertyChange.bind(this);
-  this._onError = this._onError.bind(this);
-
-  this._focusOnSourceEditorReady = false;
-
-  this.styleSheet.once("source-load", this._onSourceLoad);
-  this.styleSheet.on("property-change", this._onPropertyChange);
-  this.styleSheet.on("error", this._onError);
-}
-
-StyleSheetEditor.prototype = {
-  /**
-   * This editor's source editor
-   */
-  get sourceEditor() {
-    return this._sourceEditor;
-  },
-
-  /**
-   * Whether there are unsaved changes in the editor
-   */
-  get unsaved() {
-    return this._sourceEditor && this._sourceEditor.dirty;
-  },
-
-  /**
-   * Whether the editor is for a stylesheet created by the user
-   * through the style editor UI.
-   */
-  get isNew() {
-    return this._isNew;
-  },
-
-  /**
-   * Get a user-friendly name for the style sheet.
-   *
-   * @return string
-   */
-  get friendlyName() {
-    if (this.savedFile) { // reuse the saved filename if any
-      return this.savedFile.leafName;
-    }
-
-    if (this._isNew) {
-      let index = this.styleSheet.styleSheetIndex + 1; // 0-indexing only works for devs
-      return _("newStyleSheet", index);
-    }
-
-    if (!this.styleSheet.href) {
-      let index = this.styleSheet.styleSheetIndex + 1; // 0-indexing only works for devs
-      return _("inlineStyleSheet", index);
-    }
-
-    if (!this._friendlyName) {
-      let sheetURI = this.styleSheet.href;
-      let contentURI = this.styleSheet.debuggee.baseURI;
-      let contentURIScheme = contentURI.scheme;
-      let contentURILeafIndex = contentURI.specIgnoringRef.lastIndexOf("/");
-      contentURI = contentURI.specIgnoringRef;
-
-      // get content base URI without leaf name (if any)
-      if (contentURILeafIndex > contentURIScheme.length) {
-        contentURI = contentURI.substring(0, contentURILeafIndex + 1);
-      }
-
-      // avoid verbose repetition of absolute URI when the style sheet URI
-      // is relative to the content URI
-      this._friendlyName = (sheetURI.indexOf(contentURI) == 0)
-                           ? sheetURI.substring(contentURI.length)
-                           : sheetURI;
-      try {
-        this._friendlyName = decodeURI(this._friendlyName);
-      } catch (ex) {
-      }
-    }
-    return this._friendlyName;
-  },
-
-  /**
-   * Start fetching the full text source for this editor's sheet.
-   */
-  fetchSource: function() {
-    this.styleSheet.fetchSource();
-  },
-
-  /**
-   * Handle source fetched event. Forward source-load event.
-   *
-   * @param  {string} event
-   *         Event type
-   * @param  {string} source
-   *         Full-text source of the stylesheet
-   */
-  _onSourceLoad: function(event, source) {
-    this._state.text = prettifyCSS(source);
-    this.sourceLoaded = true;
-    this.emit("source-load");
-  },
-
-  /**
-   * Forward property-change event from stylesheet.
-   *
-   * @param  {string} event
-   *         Event type
-   * @param  {string} property
-   *         Property that has changed on sheet
-   */
-  _onPropertyChange: function(event, property) {
-    this.emit("property-change", property);
-  },
-
-  /**
-   * Forward error event from stylesheet.
-   *
-   * @param  {string} event
-   *         Event type
-   * @param  {string} errorCode
-   */
-  _onError: function(event, errorCode) {
-    this.emit("error", errorCode);
-  },
-
-  /**
-   * Create source editor and load state into it.
-   * @param  {DOMElement} inputElement
-   *         Element to load source editor in
-   */
-  load: function(inputElement) {
-    this._inputElement = inputElement;
-
-    let sourceEditor = new SourceEditor();
-    let config = {
-      initialText: this._state.text,
-      showLineNumbers: true,
-      mode: SourceEditor.MODES.CSS,
-      readOnly: this._state.readOnly,
-      keys: this._getKeyBindings()
-    };
-
-    sourceEditor.init(inputElement, config, function onSourceEditorReady() {
-      setupBracketCompletion(sourceEditor);
-      sourceEditor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
-                                    function onTextChanged(event) {
-        this.updateStyleSheet();
-      }.bind(this));
-
-      this._sourceEditor = sourceEditor;
-
-      if (this._focusOnSourceEditorReady) {
-        this._focusOnSourceEditorReady = false;
-        sourceEditor.focus();
-      }
-
-      sourceEditor.setTopIndex(this._state.topIndex);
-      sourceEditor.setSelection(this._state.selection.start,
-                                this._state.selection.end);
-
-      this.emit("source-editor-load");
-    }.bind(this));
-
-    sourceEditor.addEventListener(SourceEditor.EVENTS.DIRTY_CHANGED,
-                                  this._onPropertyChange);
-  },
-
-  /**
-   * Get the source editor for this editor.
-   *
-   * @return {Promise}
-   *         Promise that will resolve with the editor.
-   */
-  getSourceEditor: function() {
-    let deferred = Promise.defer();
-
-    if (this.sourceEditor) {
-      return Promise.resolve(this);
-    }
-    this.on("source-editor-load", (event) => {
-      deferred.resolve(this);
-    });
-    return deferred.promise;
-  },
-
-  /**
-   * Focus the Style Editor input.
-   */
-  focus: function() {
-    if (this._sourceEditor) {
-      this._sourceEditor.focus();
-    } else {
-      this._focusOnSourceEditorReady = true;
-    }
-  },
-
-  /**
-   * Event handler for when the editor is shown.
-   */
-  onShow: function() {
-    if (this._sourceEditor) {
-      this._sourceEditor.setTopIndex(this._state.topIndex);
-    }
-    this.focus();
-  },
-
-  /**
-   * Toggled the disabled state of the underlying stylesheet.
-   */
-  toggleDisabled: function() {
-    this.styleSheet.toggleDisabled();
-  },
-
-  /**
-   * Queue a throttled task to update the live style sheet.
-   *
-   * @param boolean immediate
-   *        Optional. If true the update is performed immediately.
-   */
-  updateStyleSheet: function(immediate) {
-    if (this._updateTask) {
-      // cancel previous queued task not executed within throttle delay
-      this._window.clearTimeout(this._updateTask);
-    }
-
-    if (immediate) {
-      this._updateStyleSheet();
-    } else {
-      this._updateTask = this._window.setTimeout(this._updateStyleSheet.bind(this),
-                                           UPDATE_STYLESHEET_THROTTLE_DELAY);
-    }
-  },
-
-  /**
-   * Update live style sheet according to modifications.
-   */
-  _updateStyleSheet: function() {
-    if (this.styleSheet.disabled) {
-      return;  // TODO: do we want to do this?
-    }
-
-    this._updateTask = null; // reset only if we actually perform an update
-                             // (stylesheet is enabled) so that 'missed' updates
-                             // while the stylesheet is disabled can be performed
-                             // when it is enabled back. @see enableStylesheet
-
-    if (this.sourceEditor) {
-      this._state.text = this.sourceEditor.getText();
-    }
-
-    this.styleSheet.update(this._state.text);
-  },
-
-  /**
-   * Save the editor contents into a file and set savedFile property.
-   * A file picker UI will open if file is not set and editor is not headless.
-   *
-   * @param mixed file
-   *        Optional nsIFile or string representing the filename to save in the
-   *        background, no UI will be displayed.
-   *        If not specified, the original style sheet URI is used.
-   *        To implement 'Save' instead of 'Save as', you can pass savedFile here.
-   * @param function(nsIFile aFile) callback
-   *        Optional callback called when the operation has finished.
-   *        aFile has the nsIFile object for saved file or null if the operation
-   *        has failed or has been canceled by the user.
-   * @see savedFile
-   */
-  saveToFile: function(file, callback) {
-    let onFile = (returnFile) => {
-      if (!returnFile) {
-        if (callback) {
-          callback(null);
-        }
-        return;
-      }
-
-      if (this._sourceEditor) {
-        this._state.text = this._sourceEditor.getText();
-      }
-
-      let ostream = FileUtils.openSafeFileOutputStream(returnFile);
-      let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
-                        .createInstance(Ci.nsIScriptableUnicodeConverter);
-      converter.charset = "UTF-8";
-      let istream = converter.convertToInputStream(this._state.text);
-
-      NetUtil.asyncCopy(istream, ostream, function onStreamCopied(status) {
-        if (!Components.isSuccessCode(status)) {
-          if (callback) {
-            callback(null);
-          }
-          this.emit("error", SAVE_ERROR);
-          return;
-        }
-        FileUtils.closeSafeFileOutputStream(ostream);
-        // remember filename for next save if any
-        this._friendlyName = null;
-        this.savedFile = returnFile;
-
-        if (callback) {
-          callback(returnFile);
-        }
-        this.sourceEditor.dirty = false;
-      }.bind(this));
-    };
-
-    showFilePicker(file || this._styleSheetFilePath, true, this._window, onFile);
-  },
-
-  /**
-    * Retrieve custom key bindings objects as expected by SourceEditor.
-    * SourceEditor action names are not displayed to the user.
-    *
-    * @return {array} key binding objects for the source editor
-    */
-  _getKeyBindings: function() {
-    let bindings = [];
-
-    bindings.push({
-      action: "StyleEditor.save",
-      code: _("saveStyleSheet.commandkey"),
-      accel: true,
-      callback: function save() {
-        this.saveToFile(this.savedFile);
-        return true;
-      }.bind(this)
-    });
-
-    bindings.push({
-      action: "StyleEditor.saveAs",
-      code: _("saveStyleSheet.commandkey"),
-      accel: true,
-      shift: true,
-      callback: function saveAs() {
-        this.saveToFile();
-        return true;
-      }.bind(this)
-    });
-
-    return bindings;
-  },
-
-  /**
-   * Clean up for this editor.
-   */
-  destroy: function() {
-    this.styleSheet.off("source-load", this._onSourceLoad);
-    this.styleSheet.off("property-change", this._onPropertyChange);
-    this.styleSheet.off("error", this._onError);
-  }
-}
-
-
-const TAB_CHARS = "\t";
-
-const OS = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
-const LINE_SEPARATOR = OS === "WINNT" ? "\r\n" : "\n";
-
-/**
-  * Return string that repeats text for aCount times.
-  *
-  * @param string text
-  * @param number aCount
-  * @return string
-  */
-function repeat(text, aCount)
-{
-  return (new Array(aCount + 1)).join(text);
-}
-
-/**
- * Prettify minified CSS text.
- * This prettifies CSS code where there is no indentation in usual places while
- * keeping original indentation as-is elsewhere.
- *
- * @param string text
- *        The CSS source to prettify.
- * @return string
- *         Prettified CSS source
- */
-function prettifyCSS(text)
-{
-  // remove initial and terminating HTML comments and surrounding whitespace
-  text = text.replace(/(?:^\s*<!--[\r\n]*)|(?:\s*-->\s*$)/g, "");
-
-  let parts = [];    // indented parts
-  let partStart = 0; // start offset of currently parsed part
-  let indent = "";
-  let indentLevel = 0;
-
-  for (let i = 0; i < text.length; i++) {
-    let c = text[i];
-    let shouldIndent = false;
-
-    switch (c) {
-      case "}":
-        if (i - partStart > 1) {
-          // there's more than just } on the line, add line
-          parts.push(indent + text.substring(partStart, i));
-          partStart = i;
-        }
-        indent = repeat(TAB_CHARS, --indentLevel);
-        /* fallthrough */
-      case ";":
-      case "{":
-        shouldIndent = true;
-        break;
-    }
-
-    if (shouldIndent) {
-      let la = text[i+1]; // one-character lookahead
-      if (!/\s/.test(la)) {
-        // following character should be a new line (or whitespace) but it isn't
-        // force indentation then
-        parts.push(indent + text.substring(partStart, i + 1));
-        if (c == "}") {
-          parts.push(""); // for extra line separator
-        }
-        partStart = i + 1;
-      } else {
-        return text; // assume it is not minified, early exit
-      }
-    }
-
-    if (c == "{") {
-      indent = repeat(TAB_CHARS, ++indentLevel);
-    }
-  }
-  return parts.join(LINE_SEPARATOR);
-}
-
-
-/**
- * Set up bracket completion on a given SourceEditor.
- * This automatically closes the following CSS brackets: "{", "(", "["
- *
- * @param SourceEditor sourceEditor
- */
-function setupBracketCompletion(sourceEditor)
-{
-  let editorElement = sourceEditor.editorElement;
-  let pairs = {
-    123: { // {
-      closeString: "}",
-      closeKeyCode: Ci.nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET
-    },
-    40: { // (
-      closeString: ")",
-      closeKeyCode: Ci.nsIDOMKeyEvent.DOM_VK_0
-    },
-    91: { // [
-      closeString: "]",
-      closeKeyCode: Ci.nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET
-    },
-  };
-
-  editorElement.addEventListener("keypress", function onKeyPress(event) {
-    let pair = pairs[event.charCode];
-    if (!pair || event.ctrlKey || event.metaKey ||
-        event.accelKey || event.altKey) {
-      return true;
-    }
-
-    // We detected an open bracket, sending closing character
-    let keyCode = pair.closeKeyCode;
-    let charCode = pair.closeString.charCodeAt(0);
-    let modifiers = 0;
-    let utils = editorElement.ownerDocument.defaultView.
-                  QueryInterface(Ci.nsIInterfaceRequestor).
-                  getInterface(Ci.nsIDOMWindowUtils);
-    let handled = utils.sendKeyEvent("keydown", keyCode, 0, modifiers);
-    utils.sendKeyEvent("keypress", 0, charCode, modifiers, !handled);
-    utils.sendKeyEvent("keyup", keyCode, 0, modifiers);
-    // and rewind caret
-    sourceEditor.setCaretOffset(sourceEditor.getCaretOffset() - 1);
-  }, false);
-}
-
--- a/browser/devtools/styleeditor/styleeditor.xul
+++ b/browser/devtools/styleeditor/styleeditor.xul
@@ -46,21 +46,23 @@
 
   <xul:box id="style-editor-chrome" class="splitview-root loading">
     <xul:box class="splitview-controller">
       <xul:box class="splitview-main">
         <xul:toolbar class="devtools-toolbar">
           <xul:toolbarbutton class="style-editor-newButton devtools-toolbarbutton"
                       accesskey="&newButton.accesskey;"
                       tooltiptext="&newButton.tooltip;"
-                      label="&newButton.label;"/>
+                      label="&newButton.label;"
+                      disabled="true"/>
           <xul:toolbarbutton class="style-editor-importButton devtools-toolbarbutton"
                       accesskey="&importButton.accesskey;"
                       tooltiptext="&importButton.tooltip;"
-                      label="&importButton.label;"/>
+                      label="&importButton.label;"
+                      disabled="true"/>
         </xul:toolbar>
       </xul:box>
       <xul:box id="splitview-resizer-target" class="splitview-nav-container"
               persist="width height">
         <ol class="splitview-nav" tabindex="0"></ol>
         <div class="splitview-nav placeholder empty">
           <p><strong>&noStyleSheet.label;</strong></p>
           <p>&noStyleSheet-tip-start.label;
--- a/browser/devtools/styleeditor/test/Makefile.in
+++ b/browser/devtools/styleeditor/test/Makefile.in
@@ -16,20 +16,24 @@ include $(topsrcdir)/config/rules.mk
                  browser_styleeditor_filesave.js \
                  browser_styleeditor_cmd_edit.js \
                  browser_styleeditor_cmd_edit.html \
                  browser_styleeditor_import.js \
                  browser_styleeditor_import_rule.js \
                  browser_styleeditor_init.js \
                  browser_styleeditor_loading.js \
                  browser_styleeditor_new.js \
+                 browser_styleeditor_passedinsheet.js \
                  browser_styleeditor_pretty.js \
                  browser_styleeditor_private_perwindowpb.js \
+                 browser_styleeditor_readonly.js \
+                 browser_styleeditor_reopen.js \
                  browser_styleeditor_sv_keynav.js \
                  browser_styleeditor_sv_resize.js \
+                 browser_styleeditor_bug_826982_location_changed.js \
                  browser_styleeditor_bug_851132_middle_click.js \
                  head.js \
                  helpers.js \
                  four.html \
                  head.js \
                  helpers.js \
                  import.css \
                  import.html \
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_bug_826982_location_changed.js
@@ -0,0 +1,123 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+let tempScope = {};
+Cu.import("resource:///modules/devtools/Target.jsm", tempScope);
+let TargetFactory = tempScope.TargetFactory;
+
+function test() {
+  let notificationBox, styleEditor;
+  let alertActive1_called = false;
+  let alertActive2_called = false;
+
+  function startLocationTests() {
+    let target = TargetFactory.forTab(gBrowser.selectedTab);
+
+    gDevTools.showToolbox(target, "styleeditor").then(function(toolbox) {
+      runTests(toolbox.getCurrentPanel(), toolbox);
+    }).then(null, console.error);
+  }
+
+  function runTests(aStyleEditor) {
+    styleEditor = aStyleEditor;
+    let para = content.document.querySelector("p");
+    ok(para, "found the paragraph element");
+    is(para.textContent, "init", "paragraph content is correct");
+
+    styleEditor.styleEditorChrome.markDirty();
+
+    notificationBox = gBrowser.getNotificationBox();
+    notificationBox.addEventListener("AlertActive", alertActive1, false);
+
+    gBrowser.selectedBrowser.addEventListener("load", onPageLoad, true);
+
+    content.location = "data:text/html,<div>location change test 1 for " +
+      "styleeditor</div><p>test1</p>";
+  }
+
+  function alertActive1() {
+    alertActive1_called = true;
+    notificationBox.removeEventListener("AlertActive", alertActive1, false);
+
+    let notification = notificationBox.
+      getNotificationWithValue("styleeditor-page-navigation");
+    ok(notification, "found the styleeditor-page-navigation notification");
+
+    // By closing the notification it is expected that page navigation is
+    // canceled.
+    executeSoon(function() {
+      notification.close();
+      locationTest2();
+    });
+  }
+
+  function locationTest2() {
+    // Location did not change.
+    let para = content.document.querySelector("p");
+    ok(para, "found the paragraph element, second time");
+    is(para.textContent, "init", "paragraph content is correct");
+
+    notificationBox.addEventListener("AlertActive", alertActive2, false);
+
+    content.location = "data:text/html,<div>location change test 2 for " +
+      "styleeditor</div><p>test2</p>";
+  }
+
+  function alertActive2() {
+    alertActive2_called = true;
+    notificationBox.removeEventListener("AlertActive", alertActive2, false);
+
+    let notification = notificationBox.
+      getNotificationWithValue("styleeditor-page-navigation");
+    ok(notification, "found the styleeditor-page-navigation notification");
+
+    let buttons = notification.querySelectorAll("button");
+    let buttonLeave = null;
+    for (let i = 0; i < buttons.length; i++) {
+      if (buttons[i].buttonInfo.id == "styleeditor.confirmNavigationAway.buttonLeave") {
+        buttonLeave = buttons[i];
+        break;
+      }
+    }
+
+    ok(buttonLeave, "the Leave page button was found");
+
+    // Accept page navigation.
+    executeSoon(function(){
+      buttonLeave.doCommand();
+    });
+  }
+
+  function onPageLoad() {
+    gBrowser.selectedBrowser.removeEventListener("load", onPageLoad, true);
+
+    isnot(content.location.href.indexOf("test2"), -1,
+          "page navigated to the correct location");
+
+    let para = content.document.querySelector("p");
+    ok(para, "found the paragraph element, third time");
+    is(para.textContent, "test2", "paragraph content is correct");
+
+    ok(alertActive1_called, "first notification box has been shown");
+    ok(alertActive2_called, "second notification box has been shown");
+    testEnd();
+  }
+
+
+  function testEnd() {
+    notificationBox = null;
+    gBrowser.removeCurrentTab();
+    executeSoon(finish);
+  }
+
+  waitForExplicitFinish();
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function onBrowserLoad() {
+    gBrowser.selectedBrowser.removeEventListener("load", onBrowserLoad, true);
+    waitForFocus(startLocationTests, content);
+  }, true);
+
+  content.location = "data:text/html,<div>location change tests for " +
+    "styleeditor.</div><p>init</p>";
+}
--- a/browser/devtools/styleeditor/test/browser_styleeditor_bug_851132_middle_click.js
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_bug_851132_middle_click.js
@@ -1,68 +1,60 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const TESTCASE_URI = TEST_BASE + "four.html";
 
-let gUI;
-
 function test() {
   waitForExplicitFinish();
 
-  let count = 0;
-  addTabAndOpenStyleEditor(function(panel) {
-    gUI = panel.UI;
-    gUI.on("editor-added", function(event, editor) {
-      count++;
-      if (count == 2) {
-        runTests();
-      }
-    })
+  addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
+    run(aChrome);
   });
 
   content.location = TESTCASE_URI;
 }
 
-let timeoutID;
+let gSEChrome, timeoutID;
 
-function runTests() {
+function run(aChrome) {
+  gSEChrome = aChrome;
   gBrowser.tabContainer.addEventListener("TabOpen", onTabAdded, false);
-  gUI.editors[0].getSourceEditor().then(onEditor0Attach);
-  gUI.editors[1].getSourceEditor().then(onEditor1Attach);
+  aChrome.editors[0].addActionListener({onAttach: onEditor0Attach});
+  aChrome.editors[1].addActionListener({onAttach: onEditor1Attach});
 }
 
 function getStylesheetNameLinkFor(aEditor) {
-  return aEditor.summary.querySelector(".stylesheet-name");
+  return gSEChrome.getSummaryElementForEditor(aEditor).querySelector(".stylesheet-name");
 }
 
 function onEditor0Attach(aEditor) {
   waitForFocus(function () {
     // left mouse click should focus editor 1
     EventUtils.synthesizeMouseAtCenter(
-      getStylesheetNameLinkFor(gUI.editors[1]),
+      getStylesheetNameLinkFor(gSEChrome.editors[1]),
       {button: 0},
-      gPanelWindow);
-  }, gPanelWindow);
+      gChromeWindow);
+  }, gChromeWindow);
 }
 
 function onEditor1Attach(aEditor) {
   ok(aEditor.sourceEditor.hasFocus(),
      "left mouse click has given editor 1 focus");
 
   // right mouse click should not open a new tab
   EventUtils.synthesizeMouseAtCenter(
-    getStylesheetNameLinkFor(gUI.editors[2]),
+    getStylesheetNameLinkFor(gSEChrome.editors[2]),
     {button: 1},
-    gPanelWindow);
+    gChromeWindow);
 
   setTimeout(finish, 0);
 }
 
 function onTabAdded() {
   ok(false, "middle mouse click has opened a new tab");
   finish();
 }
 
 registerCleanupFunction(function () {
   gBrowser.tabContainer.removeEventListener("TabOpen", onTabAdded, false);
-  gUI = null;
+  gSEChrome = null;
 });
--- a/browser/devtools/styleeditor/test/browser_styleeditor_enabled.js
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_enabled.js
@@ -1,75 +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;
-  addTabAndOpenStyleEditor(function(panel) {
-    let UI = panel.UI;
-    UI.on("editor-added", function(event, editor) {
-      count++;
-      if (count == 2) {
-        // we test against first stylesheet after all are ready
-        let editor = UI.editors[0];
-        editor.getSourceEditor().then(runTests.bind(this, UI, editor));
+  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 runTests(UI, editor)
+function run(aChrome, aEditor)
 {
-  testEnabledToggle(UI, editor);
+  testEnabledToggle(aChrome, aEditor);
 }
 
-function testEnabledToggle(UI, editor)
+function testEnabledToggle(aChrome, aEditor)
 {
-  let summary = editor.summary;
-  let enabledToggle = summary.querySelector(".stylesheet-enabled");
-  ok(enabledToggle, "enabled toggle button exists");
+  is(aEditor, aChrome.editors[0],
+     "stylesheet with index 0 is the first stylesheet listed in the UI");
 
-  is(editor.styleSheet.disabled, false,
+  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(summary.classList.contains("disabled"), false,
+  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;
-  editor.on("property-change", function(event, property) {
-    if (property != "disabled") {
-      return;
-    }
-    disabledToggleCount++;
+  firstStyleSheetEditor.addActionListener({
+    onFlagChange: function (aEditor, aFlagName) {
+      if (aFlagName != "disabled") {
+        return;
+      }
+      disabledToggleCount++;
 
-    if (disabledToggleCount == 1) {
-      is(editor.styleSheet.disabled, true, "first stylesheet is now disabled");
-      is(summary.classList.contains("disabled"), true,
-         "first stylesheet is now disabled, UI has DISABLED class");
+      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, {}, gPanelWindow);
-      }, gPanelWindow);
-      return;
-    }
+        // now toggle it back to enabled
+        waitForFocus(function () {
+          EventUtils.synthesizeMouseAtCenter(enabledToggle, {}, gChromeWindow);
+        }, gChromeWindow);
+        return;
+      }
 
-    // disabledToggleCount == 2
-    is(editor.styleSheet.disabled, false, "first stylesheet is now enabled again");
-    is(summary.classList.contains("disabled"), false,
-       "first stylesheet is now enabled again, UI does not have DISABLED class");
+      // 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();
+      finish();
+    }
   });
 
   waitForFocus(function () {
-    EventUtils.synthesizeMouseAtCenter(enabledToggle, {}, gPanelWindow);
-  }, gPanelWindow);
+    EventUtils.synthesizeMouseAtCenter(enabledToggle, {}, gChromeWindow);
+  }, gChromeWindow);
 }
--- a/browser/devtools/styleeditor/test/browser_styleeditor_filesave.js
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_filesave.js
@@ -16,38 +16,49 @@ let NetUtil = tempScope.NetUtil;
 
 
 function test()
 {
   waitForExplicitFinish();
 
   copy(TESTCASE_URI_HTML, "simple.html", function(htmlFile) {
     copy(TESTCASE_URI_CSS, "simple.css", function(cssFile) {
-      addTabAndOpenStyleEditor(function(panel) {
-        let UI = panel.UI;
-        UI.on("editor-added", function(event, editor) {
-          if (editor.styleSheet.styleSheetIndex != 0) {
-            return;  // we want to test against the first stylesheet
+
+      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
+              });
+            }
           }
-          let editor = UI.editors[0];
-          editor.getSourceEditor().then(runTests.bind(this, editor));
-        })
+        });
       });
 
       let uri = Services.io.newFileURI(htmlFile);
       let filePath = uri.resolve("");
+
       content.location = filePath;
     });
   });
 }
 
-function runTests(editor)
+function run(aEditor)
 {
-  editor.saveToFile(null, function (file) {
-    ok(file, "file should get saved directly when using a file:// URI");
+  aEditor.saveToFile(null, function (aFile) {
+    ok(aFile, "file should get saved directly when using a file:// URI");
+
+    gChromeWindow.close();
     finish();
   });
 }
 
 function copy(aSrcChromeURL, aDestFileName, aCallback)
 {
   let destFile = FileUtils.getFile("ProfD", [aDestFileName]);
   write(read(aSrcChromeURL), destFile, aCallback);
--- a/browser/devtools/styleeditor/test/browser_styleeditor_import.js
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_import.js
@@ -8,69 +8,80 @@ const TESTCASE_URI = TEST_BASE_HTTP + "s
 let tempScope = {};
 Components.utils.import("resource://gre/modules/FileUtils.jsm", tempScope);
 let FileUtils = tempScope.FileUtils;
 
 const FILENAME = "styleeditor-import-test.css";
 const SOURCE = "body{background:red;}";
 
 
-let gUI;
-
 function test()
 {
   waitForExplicitFinish();
 
-  addTabAndOpenStyleEditor(function(panel) {
-    gUI = panel.UI;
-    gUI.on("editor-added", testEditorAdded);
+  addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
+    aChrome.addChromeListener({
+      onEditorAdded: testEditorAdded
+    });
+    run(aChrome);
   });
 
   content.location = TESTCASE_URI;
 }
 
-function testImport()
+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
-    gUI._mockImportFile = file;
+    aChrome._mockImportFile = file;
 
     waitForFocus(function () {
-      let document = gPanelWindow.document
+      let document = gChromeWindow.document
       let importButton = document.querySelector(".style-editor-importButton");
-      ok(importButton, "import button exists");
-
-      EventUtils.synthesizeMouseAtCenter(importButton, {}, gPanelWindow);
-    }, gPanelWindow);
+      EventUtils.synthesizeMouseAtCenter(importButton, {}, gChromeWindow);
+    }, gChromeWindow);
   });
 }
 
 let gAddedCount = 0;
-function testEditorAdded(aEvent, aEditor)
+function testEditorAdded(aChrome, aEditor)
 {
   if (++gAddedCount == 2) {
     // test import after the 2 initial stylesheets have been loaded
-    gUI.editors[0].getSourceEditor().then(function() {
-      testImport();
-    });
+    if (!aChrome.editors[0].sourceEditor) {
+      aChrome.editors[0].addActionListener({
+        onAttach: function () {
+          testImport(aChrome);
+        }
+      });
+    } else {
+      testImport(aChrome);
+    }
   }
 
-  if (!aEditor.savedFile) {
+  if (!aEditor.hasFlag("imported")) {
     return;
   }
 
-  is(aEditor.savedFile.leafName, FILENAME,
+  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.friendlyName, FILENAME,
+  is(aEditor.getFriendlyName(), FILENAME,
      "imported stylesheet has the same name as the filename");
 
-  gUI = null;
   finish();
 }
--- a/browser/devtools/styleeditor/test/browser_styleeditor_import_rule.js
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_import_rule.js
@@ -1,43 +1,34 @@
 /* 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 + "import.html";
 
-let gUI;
-
 function test()
 {
   waitForExplicitFinish();
 
-  addTabAndOpenStyleEditor(function(panel) {
-    gUI = panel.UI;
-    gUI.on("editor-added", onEditorAdded);
+  addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
+    run(aChrome);
   });
 
   content.location = TESTCASE_URI;
 }
 
-let gAddedCount = 0;
-function onEditorAdded()
+function run(aChrome)
 {
-  if (++gAddedCount != 3) {
-    return;
-  }
-
-  is(gUI.editors.length, 3,
+  is(aChrome.editors.length, 3,
     "there are 3 stylesheets after loading @imports");
 
-  is(gUI.editors[0].styleSheet.href, TEST_BASE_HTTP + "simple.css",
+  is(aChrome.editors[0]._styleSheet.href, TEST_BASE_HTTP + "simple.css",
     "stylesheet 1 is simple.css");
 
-  is(gUI.editors[1].styleSheet.href, TEST_BASE_HTTP + "import.css",
+  is(aChrome.editors[1]._styleSheet.href, TEST_BASE_HTTP + "import.css",
     "stylesheet 2 is import.css");
 
-  is(gUI.editors[2].styleSheet.href, TEST_BASE_HTTP + "import2.css",
+  is(aChrome.editors[2]._styleSheet.href, TEST_BASE_HTTP + "import2.css",
     "stylesheet 3 is import2.css");
 
-  gUI = null;
   finish();
 }
--- a/browser/devtools/styleeditor/test/browser_styleeditor_init.js
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_init.js
@@ -1,83 +1,107 @@
 /* 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 gUI;
 
 function test()
 {
   waitForExplicitFinish();
 
-  addTabAndOpenStyleEditor(function(panel) {
-    gUI = panel.UI;
-    gUI.on("editor-added", testEditorAdded);
+  launchStyleEditorChrome(function(aChrome) {
+    aChrome.addChromeListener({
+      onEditorAdded: testEditorAdded
+    });
+    run(aChrome);
   });
+  content.location = TESTCASE_URI;
+}
 
-  content.location = TESTCASE_URI;
+function run(aChrome)
+{
+  is(aChrome.contentWindow.document.readyState, "complete",
+     "content document is complete");
+
+  let SEC = gChromeWindow.styleEditorChrome;
+  is(SEC, aChrome, "StyleEditorChrome object exists as new window property");
+
+  // 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");
 }
 
 let gEditorAddedCount = 0;
-function testEditorAdded(aEvent, aEditor)
+function testEditorAdded(aChrome, aEditor)
 {
-  if (aEditor.styleSheet.styleSheetIndex == 0) {
+  if (aEditor.styleSheetIndex == 0) {
     gEditorAddedCount++;
-    testFirstStyleSheetEditor(aEditor);
+    testFirstStyleSheetEditor(aChrome, aEditor);
   }
-  if (aEditor.styleSheet.styleSheetIndex == 1) {
+  if (aEditor.styleSheetIndex == 1) {
     gEditorAddedCount++;
-    testSecondStyleSheetEditor(aEditor);
+    testSecondStyleSheetEditor(aChrome, aEditor);
   }
 
   if (gEditorAddedCount == 2) {
-    gUI = null;
     finish();
   }
 }
 
-function testFirstStyleSheetEditor(aEditor)
+function testFirstStyleSheetEditor(aChrome, aEditor)
 {
   // Note: the html <link> contains charset="UTF-8".
   ok(aEditor._state.text.indexOf("\u263a") >= 0,
      "stylesheet is unicode-aware.");
 
   //testing TESTCASE's simple.css stylesheet
-  is(aEditor.styleSheet.styleSheetIndex, 0,
+  is(aEditor.styleSheetIndex, 0,
      "first stylesheet is at index 0");
 
-  is(aEditor, gUI.editors[0],
+  is(aEditor, aChrome.editors[0],
      "first stylesheet corresponds to StyleEditorChrome.editors[0]");
 
-  let summary = aEditor.summary;
+  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 > label").getAttribute("value");
   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(aEditor)
+function testSecondStyleSheetEditor(aChrome, aEditor)
 {
   //testing TESTCASE's inline stylesheet
-  is(aEditor.styleSheet.styleSheetIndex, 1,
+  is(aEditor.styleSheetIndex, 1,
      "second stylesheet is at index 1");
 
-  is(aEditor, gUI.editors[1],
+  is(aEditor, aChrome.editors[1],
      "second stylesheet corresponds to StyleEditorChrome.editors[1]");
 
-  let summary = aEditor.summary;
+  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 > label").getAttribute("value");
   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");
--- a/browser/devtools/styleeditor/test/browser_styleeditor_loading.js
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_loading.js
@@ -4,36 +4,34 @@
 
 const TESTCASE_URI = TEST_BASE + "longload.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. The Style Editor should not signal that
   // it is loaded until the accompanying content page is loaded.
+  launchStyleEditorChrome(function (aChrome) {
+    content.location = TESTCASE_URI;
+      is(aChrome.contentWindow.document.readyState, "complete",
+         "content document is complete");
 
-  addTabAndOpenStyleEditor(function(panel) {
-    panel.UI.on("editor-added", testEditorAdded);
+      let root = gChromeWindow.document.querySelector(".splitview-root");
+      ok(!root.classList.contains("loading"),
+         "style editor root element does not have 'loading' class name anymore");
 
-    content.location = TESTCASE_URI;
+      let button = gChromeWindow.document.querySelector(".style-editor-newButton");
+      ok(!button.hasAttribute("disabled"),
+         "new style sheet button is enabled");
+
+      button = gChromeWindow.document.querySelector(".style-editor-importButton");
+      ok(!button.hasAttribute("disabled"),
+         "import button is enabled");
+
+      finish();
   });
 }
-
-function testEditorAdded(event, editor)
-{
-  let root = gPanelWindow.document.querySelector(".splitview-root");
-  ok(!root.classList.contains("loading"),
-     "style editor root element does not have 'loading' class name anymore");
-
-  let button = gPanelWindow.document.querySelector(".style-editor-newButton");
-  ok(!button.hasAttribute("disabled"),
-     "new style sheet button is enabled");
-
-  button = gPanelWindow.document.querySelector(".style-editor-importButton");
-  ok(!button.hasAttribute("disabled"),
-     "import button is enabled");
-
-  finish();
-}
--- a/browser/devtools/styleeditor/test/browser_styleeditor_new.js
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_new.js
@@ -1,124 +1,169 @@
 /* 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 TRANSITION_CLASS = "moz-styleeditor-transitioning";
-let TESTCASE_CSS_SOURCE = "body{background-color:red;";
-
-let gUI;
+const TRANSITION_CLASS = "moz-styleeditor-transitioning";
+const TESTCASE_CSS_SOURCE = "body{background-color:red;";
 
 function test()
 {
   waitForExplicitFinish();
 
-  addTabAndOpenStyleEditor(function(panel) {
-    gUI = panel.UI;
-    gUI.on("editor-added", testEditorAdded);
+  addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
+    aChrome.addChromeListener({
+      onEditorAdded: testEditorAdded
+    });
+    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 gUpdateCount = 0; // to make sure only one Update event is triggered
+let gCommitCount = 0; // to make sure only one Commit event is triggered
+let gTransitionEndCount = 0;
+let gOriginalStyleSheet;
+let gOriginalOwnerNode;
 let gOriginalHref;
 
-function testEditorAdded(aEvent, aEditor)
+
+function finishOnTransitionEndAndCommit() {
+  if (gCommitCount && gTransitionEndCount) {
+    is(gUpdateCount, 1, "received one Update event");
+    is(gCommitCount, 1, "received one Commit event");
+    is(gTransitionEndCount, 1, "received one transitionend event");
+
+    if (gNewEditor) {
+      is(gNewEditor.styleSheet, gOriginalStyleSheet,
+         "style sheet object did not change");
+      is(gNewEditor.styleSheet.ownerNode, gOriginalOwnerNode,
+         "style sheet owner node did not change");
+      is(gNewEditor.styleSheet.href, gOriginalHref,
+         "style sheet href did not change");
+
+      gNewEditor = null;
+      finish();
+    }
+  }
+}
+
+function testEditorAdded(aChrome, aEditor)
 {
   gAddedCount++;
   if (gAddedCount == 2) {
-    waitForFocus(function () {// create a new style sheet
-      let newButton = gPanelWindow.document.querySelector(".style-editor-newButton");
-      ok(newButton, "'new' button exists");
-
-      EventUtils.synthesizeMouseAtCenter(newButton, {}, gPanelWindow);
-    }, gPanelWindow);
+    waitForFocus(function () { // create a new style sheet
+      let newButton = gChromeWindow.document.querySelector(".style-editor-newButton");
+      EventUtils.synthesizeMouseAtCenter(newButton, {}, gChromeWindow);
+    }, gChromeWindow);
   }
-  if (gAddedCount < 3) {
+  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(gUI.editors.length, 3,
+  is(aChrome.editors.length, 3,
      "creating a new stylesheet added a new StyleEditor instance");
 
-  aEditor.getSourceEditor().then(testEditor);
-
-  aEditor.styleSheet.once("style-applied", function() {
-    // when changes have been completely applied to live stylesheet after transisiton
-    let summary = aEditor.summary;
-    let ruleCount = summary.querySelector(".stylesheet-rule-count").textContent;
-    is(parseInt(ruleCount), 1,
-       "new editor shows 1 rule after modification");
+  let listener = {
+    onAttach: function (aEditor) {
+      waitForFocus(function () {
+        gOriginalStyleSheet = aEditor.styleSheet;
+        gOriginalOwnerNode = aEditor.styleSheet.ownerNode;
+        gOriginalHref = aEditor.styleSheet.href;
 
-    ok(!content.document.documentElement.classList.contains(TRANSITION_CLASS),
-       "StyleEditor's transition class has been removed from content");
-  });
-}
+        ok(aEditor.isLoaded,
+           "new editor is loaded when attached");
+        ok(aEditor.hasFlag("new"),
+           "new editor has NEW flag");
+        ok(aEditor.hasFlag("unsaved"),
+           "new editor has UNSAVED flag");
 
-function testEditor(aEditor) {
-  waitForFocus(function () {
-  gOriginalHref = aEditor.styleSheet.href;
+        ok(aEditor.inputElement,
+           "new editor has an input element attached");
+
+        ok(aEditor.sourceEditor.hasFocus(),
+           "new editor has focus");
 
-  let summary = aEditor.summary;
+        let summary = aChrome.getSummaryElementForEditor(aEditor);
+        let ruleCount = summary.querySelector(".stylesheet-rule-count").textContent;
+        is(parseInt(ruleCount), 0,
+           "new editor initially shows 0 rules");
 
-  ok(aEditor.sourceLoaded,
-     "new editor is loaded when attached");
-  ok(aEditor.isNew,
-     "new editor has isNew flag");
+        let computedStyle = content.getComputedStyle(content.document.body, null);
+        is(computedStyle.backgroundColor, "rgb(255, 255, 255)",
+           "content's background color is initially white");
 
-  ok(aEditor.sourceEditor.hasFocus(),
-     "new editor has focus");
+        EventUtils.synthesizeKey("[", {accelKey: true}, gChromeWindow);
+        is(aEditor.sourceEditor.getText(), "",
+           "Nothing happened as it is a known shortcut in source editor");
 
-  let summary = aEditor.summary;
-  let ruleCount = summary.querySelector(".stylesheet-rule-count").textContent;
-  is(parseInt(ruleCount), 0,
-     "new editor initially shows 0 rules");
+        EventUtils.synthesizeKey("]", {accelKey: true}, gChromeWindow);
+        is(aEditor.sourceEditor.getText(), "",
+           "Nothing happened as it is a known shortcut in source editor");
 
-  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 TESTCASE_CSS_SOURCE) {
+          EventUtils.synthesizeKey(c, {}, gChromeWindow);
+        }
+
+        is(aEditor.sourceEditor.getText(), TESTCASE_CSS_SOURCE + "}",
+           "rule bracket has been auto-closed");
 
-  EventUtils.synthesizeKey("[", {accelKey: true}, gPanelWindow);
-  is(aEditor.sourceEditor.getText(), "",
-     "Nothing happened as it is a known shortcut in source editor");
+        // we know that the testcase above will start a CSS transition
+        content.addEventListener("transitionend", function () {
+          gTransitionEndCount++;
 
-  EventUtils.synthesizeKey("]", {accelKey: true}, gPanelWindow);
-  is(aEditor.sourceEditor.getText(), "",
-     "Nothing happened as it is a known shortcut in source editor");
+          let computedStyle = content.getComputedStyle(content.document.body, null);
+          is(computedStyle.backgroundColor, "rgb(255, 0, 0)",
+             "content's background color has been updated to red");
 
-  for each (let c in TESTCASE_CSS_SOURCE) {
-    EventUtils.synthesizeKey(c, {}, gPanelWindow);
-  }
+          executeSoon(finishOnTransitionEndAndCommit);
+        }, false);
+      }, gChromeWindow) ;
+    },
 
-  is(aEditor.sourceEditor.getText(), TESTCASE_CSS_SOURCE + "}",
-     "rule bracket has been auto-closed");
+    onUpdate: function (aEditor) {
+      gUpdateCount++;
 
-  ok(aEditor.unsaved,
-     "new editor has unsaved flag");
+      ok(content.document.documentElement.classList.contains(TRANSITION_CLASS),
+         "StyleEditor's transition class has been added to content");
+    },
+
+    onCommit: function (aEditor) {
+      gCommitCount++;
 
-  // we know that the testcase above will start a CSS transition
-  content.addEventListener("transitionend", onTransitionEnd, false);
-}, gPanelWindow) ;
-}
+      ok(aEditor.hasFlag("new"),
+         "new editor still has NEW flag");
+      ok(aEditor.hasFlag("unsaved"),
+         "new editor has UNSAVED flag after modification");
 
-function onTransitionEnd() {
-  content.removeEventListener("transitionend", onTransitionEnd, false);
+      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");
+      ok(!content.document.documentElement.classList.contains(TRANSITION_CLASS),
+         "StyleEditor's transition class has been removed from content");
+
+      aEditor.removeActionListener(listener);
 
-  if (gNewEditor) {
-    is(gNewEditor.styleSheet.href, gOriginalHref,
-       "style sheet href did not change");
+      executeSoon(finishOnTransitionEndAndCommit);
+    }
+  };
 
-    gNewEditor = null;
-    gUI = null;
-    finish();
+  aEditor.addActionListener(listener);
+  if (aEditor.sourceEditor) {
+    listener.onAttach(aEditor);
   }
 }
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_passedinsheet.js
@@ -0,0 +1,58 @@
+/* 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";
+const LINE = 6;
+const COL = 2;
+
+function test()
+{
+  let editor = null;
+  let sheet = null;
+
+  waitForExplicitFinish();
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
+    gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
+    run();
+  }, true);
+  content.location = TESTCASE_URI;
+
+  function run()
+  {
+    sheet = content.document.styleSheets[1];
+    launchStyleEditorChrome(function attachListeners(aChrome) {
+      aChrome.addChromeListener({
+        onEditorAdded: checkSourceEditor
+      });
+    }, sheet, LINE, COL);
+  }
+
+  function checkSourceEditor(aChrome, aEditor)
+  {
+    aChrome.removeChromeListener(this);
+    if (!aEditor.sourceEditor) {
+      aEditor.addActionListener({
+        onAttach: function (aEditor) {
+          aEditor.removeActionListener(this);
+          validate(aEditor);
+        }
+      });
+    } else {
+      validate(aEditor);
+    }
+  }
+
+  function validate(aEditor)
+  {
+    info("validating style editor");
+    let sourceEditor = aEditor.sourceEditor;
+    let caretPosition = sourceEditor.getCaretPosition();
+    is(caretPosition.line, LINE - 1, "caret row is correct"); // index based
+    is(caretPosition.col, COL - 1, "caret column is correct");
+    is(aEditor.styleSheet, sheet, "loaded stylesheet matches document stylesheet");
+    finish();
+  }
+}
--- a/browser/devtools/styleeditor/test/browser_styleeditor_pretty.js
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_pretty.js
@@ -1,52 +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";
 
-let gUI;
 
 function test()
 {
   waitForExplicitFinish();
 
-  addTabAndOpenStyleEditor(function(panel) {
-    gUI = panel.UI;
-    gUI.on("editor-added", function(event, editor) {
-      editor.getSourceEditor().then(function() {
-        testEditor(editor);
-      });
+  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 testEditor(aEditor)
+function run(aEditor)
 {
-  if (aEditor.styleSheet.styleSheetIndex == 0) {
+  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 summary = gUI.editors[1].summary;
-    EventUtils.synthesizeMouseAtCenter(summary, {}, gPanelWindow);
+    let chrome = gChromeWindow.styleEditorChrome;
+    let summary = chrome.getSummaryElementForEditor(chrome.editors[1]);
+    EventUtils.synthesizeMouseAtCenter(summary, {}, gChromeWindow);
   }
 
-  if (aEditor.styleSheet.styleSheetIndex == 1) {
+  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) {
-    gUI = null;
     finish();
   }
 }
--- a/browser/devtools/styleeditor/test/browser_styleeditor_private_perwindowpb.js
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_private_perwindowpb.js
@@ -1,48 +1,45 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // This test makes sure that the style editor does not store any
 // content CSS files in the permanent cache when opened from PB mode.
-
-let gUI;
-
 function test() {
   waitForExplicitFinish();
   let windowsToClose = [];
   let testURI = 'http://' + TEST_HOST + '/browser/browser/devtools/styleeditor/test/test_private.html';
 
   function checkCache() {
     checkDiskCacheFor(TEST_HOST);
-
-    gUI = null;
     finish();
   }
 
   function doTest(aWindow) {
     aWindow.gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
       aWindow.gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
       cache.evictEntries(Ci.nsICache.STORE_ANYWHERE);
-      openStyleEditorInWindow(aWindow, function(panel) {
-        gUI = panel.UI;
-        gUI.on("editor-added", onEditorAdded);
+      launchStyleEditorChromeFromWindow(aWindow, function(aChrome) {
+        onEditorAdded(aChrome, aChrome.editors[0]);
       });
     }, true);
 
     aWindow.gBrowser.selectedBrowser.loadURI(testURI);
   }
 
-  function onEditorAdded(aEvent, aEditor) {
-    if (aEditor.sourceLoaded) {
+  function onEditorAdded(aChrome, aEditor) {
+    aChrome.removeChromeListener(this);
+
+    if (aEditor.isLoaded) {
       checkCache();
-    }
-    else {
-      aEditor.on("source-load", checkCache);
+    } else {
+      aEditor.addActionListener({
+        onLoad: checkCache
+      });
     }
   }
 
   function testOnWindow(options, callback) {
     let win = OpenBrowserWindow(options);
     win.addEventListener("load", function onLoad() {
       win.removeEventListener("load", onLoad, false);
       windowsToClose.push(win);
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,toolbarbutton,textbox");
+  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,165 @@
+/* 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:///modules/devtools/Target.jsm", tempScope);
+let TargetFactory = tempScope.TargetFactory;
+
+function test() {
+
+  // http rather than chrome to improve coverage
+  const TESTCASE_URI = TEST_BASE_HTTP + "simple.gz.html";
+
+  const Cc = Components.classes;
+  const Ci = Components.interfaces;
+
+  let toolbox;
+  let tempScope = {};
+  Components.utils.import("resource://gre/modules/FileUtils.jsm", tempScope);
+  let FileUtils = tempScope.FileUtils;
+
+
+  waitForExplicitFinish();
+
+  addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
+    let target = TargetFactory.forTab(gBrowser.selectedTab);
+    toolbox = gDevTools.getToolbox(target);
+
+    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
+          });
+        }
+      }
+    });
+
+    toolbox.once("destroyed", function onClose() {
+      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");
+          toolbox.destroy();
+        }
+      });
+
+      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);
+    }
+  }
+}
--- a/browser/devtools/styleeditor/test/browser_styleeditor_sv_keynav.js
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_sv_keynav.js
@@ -1,78 +1,75 @@
 /* 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";
 
-let gUI;
 
 function test()
 {
   waitForExplicitFinish();
 
-  addTabAndOpenStyleEditor(function(panel) {
-    gUI = panel.UI;
-    gUI.on("editor-added", function(event, editor) {
-      if (editor == gUI.editors[3]) {
-        runTests();
-      }
-    });
+  addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
+    run(aChrome);
   });
 
   content.location = TESTCASE_URI;
 }
 
-function runTests()
+let gChrome;
+
+function run(aChrome)
 {
-  gUI.editors[0].getSourceEditor().then(onEditor0Attach);
-  gUI.editors[2].getSourceEditor().then(onEditor2Attach);
+  gChrome = aChrome;
+  aChrome.editors[0].addActionListener({onAttach: onEditor0Attach});
+  aChrome.editors[2].addActionListener({onAttach: onEditor2Attach});
 }
 
 function getStylesheetNameLinkFor(aEditor)
 {
-  return aEditor.summary.querySelector(".stylesheet-name");
+  return gChrome.getSummaryElementForEditor(aEditor).querySelector(".stylesheet-name");
 }
 
 function onEditor0Attach(aEditor)
 {
   waitForFocus(function () {
-    let summary = aEditor.summary;
-    EventUtils.synthesizeMouseAtCenter(summary, {}, gPanelWindow);
+    let summary = gChrome.getSummaryElementForEditor(aEditor);
+    EventUtils.synthesizeMouseAtCenter(summary, {}, gChromeWindow);
 
-    let item = getStylesheetNameLinkFor(gUI.editors[0]);
-    is(gPanelWindow.document.activeElement, item,
+    let item = getStylesheetNameLinkFor(gChrome.editors[0]);
+    is(gChromeWindow.document.activeElement, item,
        "editor 0 item is the active element");
 
-    EventUtils.synthesizeKey("VK_DOWN", {}, gPanelWindow);
-    item = getStylesheetNameLinkFor(gUI.editors[1]);
-    is(gPanelWindow.document.activeElement, item,
+    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", {}, gPanelWindow);
-    item = getStylesheetNameLinkFor(gUI.editors[0]);
-    is(gPanelWindow.document.activeElement, item,
+    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", {}, gPanelWindow);
-    item = getStylesheetNameLinkFor(gUI.editors[3]);
-    is(gPanelWindow.document.activeElement, item,
+    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", {}, gPanelWindow);
-    item = getStylesheetNameLinkFor(gUI.editors[2]);
-    is(gPanelWindow.document.activeElement, item,
+    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", {}, gPanelWindow);
+    EventUtils.synthesizeKey("VK_RETURN", {}, gChromeWindow);
     // this will attach and give focus editor 2
-  }, gPanelWindow);
+  }, gChromeWindow);
 }
 
 function onEditor2Attach(aEditor)
 {
   ok(aEditor.sourceEditor.hasFocus(),
      "editor 2 has focus");
 
-  gUI = null;
+  gChrome = null;
   finish();
 }
--- a/browser/devtools/styleeditor/test/browser_styleeditor_sv_resize.js
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_sv_resize.js
@@ -1,61 +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 + "simple.html";
 
-let gOriginalWidth; // these are set by runTests()
+let gOriginalWidth; // these are set by run() when gChromeWindow is ready
 let gOriginalHeight;
 
 function test()
 {
   waitForExplicitFinish();
 
-  addTabAndOpenStyleEditor(function(panel) {
-    let UI = panel.UI;
-    UI.on("editor-added", function(event, editor) {
-      if (editor == UI.editors[1]) {
-        // wait until both editors are added
-        runTests(UI);
-      }
-    });
+  addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
+    run(aChrome);
   });
-
   content.location = TESTCASE_URI;
 }
 
-function runTests(aUI)
+function run(aChrome)
 {
-  is(aUI.editors.length, 2,
+  is(aChrome.editors.length, 2,
      "there is 2 stylesheets initially");
 
-  aUI.editors[0].getSourceEditor().then(function onEditorAttached(aEditor) {
-    executeSoon(function () {
-      waitForFocus(function () {
-        // queue a resize to inverse aspect ratio
-        // this will trigger a detach and reattach (to workaround bug 254144)
-        let originalSourceEditor = aEditor.sourceEditor;
-        aEditor.sourceEditor.setCaretOffset(4); // to check the caret is preserved
+  aChrome.editors[0].addActionListener({
+    onAttach: function onEditorAttached(aEditor) {
+      executeSoon(function () {
+        waitForFocus(function () {
+          // queue a resize to inverse aspect ratio
+          // this will trigger a detach and reattach (to workaround bug 254144)
+          let originalSourceEditor = aEditor.sourceEditor;
+          aEditor.sourceEditor.setCaretOffset(4); // to check the caret is preserved
 
-        gOriginalWidth = gPanelWindow.outerWidth;
-        gOriginalHeight = gPanelWindow.outerHeight;
-        gPanelWindow.resizeTo(120, 480);
+          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");
+          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 () {
-            gPanelWindow.resizeTo(gOriginalWidth, gOriginalHeight);
-            executeSoon(function () {
-              finish();
-            });
-          }, gPanelWindow);
-        });
-      }, gPanelWindow);
-    });
+            // queue a resize to original aspect ratio
+            waitForFocus(function () {
+              gChromeWindow.resizeTo(gOriginalWidth, gOriginalHeight);
+              executeSoon(function () {
+                finish();
+              });
+            }, gChromeWindow);
+          });
+        }, gChromeWindow);
+      });
+    }
   });
 }
--- a/browser/devtools/styleeditor/test/head.js
+++ b/browser/devtools/styleeditor/test/head.js
@@ -7,87 +7,60 @@ const TEST_BASE_HTTPS = "https://example
 const TEST_HOST = 'mochi.test:8888';
 
 let tempScope = {};
 Cu.import("resource:///modules/devtools/Target.jsm", tempScope);
 let TargetFactory = tempScope.TargetFactory;
 Components.utils.import("resource://gre/modules/devtools/Console.jsm", tempScope);
 let console = tempScope.console;
 
-let gPanelWindow;
+let gChromeWindow;               //StyleEditorChrome window
 let cache = Cc["@mozilla.org/network/cache-service;1"]
               .getService(Ci.nsICacheService);
 
 
 // Import the GCLI test helper
 let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
 Services.scriptloader.loadSubScript(testDir + "/helpers.js", this);
 
 function cleanup()
 {
-  gPanelWindow = null;
+  gChromeWindow = null;
   while (gBrowser.tabs.length > 1) {
     gBrowser.removeCurrentTab();
   }
 }
 
-function addTabAndOpenStyleEditor(callback) {
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
-    gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
-    openStyleEditorInWindow(window, callback);
-  }, true);
-}
-
-function openStyleEditorInWindow(win, callback) {
-  let target = TargetFactory.forTab(win.gBrowser.selectedTab);
-  win.gDevTools.showToolbox(target, "styleeditor").then(function(toolbox) {
-    let panel = toolbox.getCurrentPanel();
-    gPanelWindow = panel._panelWin;
-
-    panel.UI._alwaysDisableAnimations = true;
-
-    /*
-    if (aSheet) {
-      panel.selectStyleSheet(aSheet, aLine, aCol);
-    } */
-
-    callback(panel);
-  });
-}
-
-/*
 function launchStyleEditorChrome(aCallback, aSheet, aLine, aCol)
 {
   launchStyleEditorChromeFromWindow(window, aCallback, aSheet, aLine, aCol);
 }
 
 function launchStyleEditorChromeFromWindow(aWindow, aCallback, aSheet, aLine, aCol)
 {
   let target = TargetFactory.forTab(aWindow.gBrowser.selectedTab);
   gDevTools.showToolbox(target, "styleeditor").then(function(toolbox) {
     let panel = toolbox.getCurrentPanel();
-    gPanelWindow = panel._panelWin;
-    gPanelWindow.styleEditorChrome._alwaysDisableAnimations = true;
+    gChromeWindow = panel._panelWin;
+    gChromeWindow.styleEditorChrome._alwaysDisableAnimations = true;
     if (aSheet) {
       panel.selectStyleSheet(aSheet, aLine, aCol);
     }
-    aCallback(gPanelWindow.styleEditorChrome);
+    aCallback(gChromeWindow.styleEditorChrome);
   });
 }
 
 function addTabAndLaunchStyleEditorChromeWhenLoaded(aCallback, aSheet, aLine, aCol)
 {
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
     gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
     launchStyleEditorChrome(aCallback, aSheet, aLine, aCol);
   }, true);
 }
-*/
 
 function checkDiskCacheFor(host)
 {
   let foundPrivateData = false;
 
   let visitor = {
     visitDevice: function(deviceID, deviceInfo) {
       if (deviceID == "disk")
--- a/browser/devtools/styleeditor/test/longload.html
+++ b/browser/devtools/styleeditor/test/longload.html
@@ -15,14 +15,14 @@
   div > span {
      text-decoration: underline;
   }
   </style>
 </head>
 <body>
   Time passes:
   <script>
-    for (i = 0; i < 5000; i++) {
+    for (i = 0; i < 30000; i++) {
       document.write("<br>...");
     }
   </script>
 </body>
 </html>
--- a/browser/devtools/styleinspector/test/browser_computedview_734259_style_editor_link.js
+++ b/browser/devtools/styleinspector/test/browser_computedview_734259_style_editor_link.js
@@ -79,32 +79,44 @@ function testInlineStyle()
 
 function testInlineStyleSheet()
 {
   info("clicking an inline stylesheet");
 
   let target = TargetFactory.forTab(gBrowser.selectedTab);
   gDevTools.showToolbox(target, "styleeditor").then(function(toolbox) {
     let panel = toolbox.getCurrentPanel();
+    let win = panel._panelWin;
 
-    panel.UI.on("editor-added", (event, editor) => {
-      validateStyleEditorSheet(editor);
-    })
+    win.styleEditorChrome.addChromeListener({
+      onEditorAdded: function checkEditor(aChrome, aEditor) {
+        if (!aEditor.sourceEditor) {
+          aEditor.addActionListener({
+            onAttach: function (aEditor) {
+              aEditor.removeActionListener(this);
+              validateStyleEditorSheet(aEditor);
+            }
+          });
+        } else {
+          validateStyleEditorSheet(aEditor);
+        }
+      }
+    });
   });
 
   let link = getLinkByIndex(1);
   link.click();
 }
 
 function validateStyleEditorSheet(aEditor)
 {
   info("validating style editor stylesheet");
 
   let sheet = doc.styleSheets[0];
-  is(aEditor.styleSheet.href, sheet.href, "loaded stylesheet matches document stylesheet");
+  is(aEditor.styleSheet, sheet, "loaded stylesheet matches document stylesheet");
 
   finishUp();
 }
 
 function expandProperty(aIndex, aCallback)
 {
   let contentDoc = computedView.styleDocument;
   let expando = contentDoc.querySelectorAll(".expandable")[aIndex];
--- a/browser/devtools/styleinspector/test/browser_ruleview_734259_style_editor_link.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_734259_style_editor_link.js
@@ -83,35 +83,33 @@ function testInlineStyle()
   });
 }
 
 function testInlineStyleSheet()
 {
   info("clicking an inline stylesheet");
 
   toolbox.once("styleeditor-ready", function(id, aToolbox) {
-    let panel = toolbox.getCurrentPanel();
-
-    panel.UI.on("editor-added", (event, editor) => {
-     validateStyleEditorSheet(editor);
+    aToolbox.panelWindow.styleEditorChrome.addChromeListener({
+      onEditorAdded: validateStyleEditorSheet
     });
   });
 
   let link = getLinkByIndex(1);
   link.scrollIntoView();
   link.click();
 }
 
-function validateStyleEditorSheet(aEditor)
+function validateStyleEditorSheet(aChrome, aEditor)
 {
   info("validating style editor stylesheet");
 
   let sheet = doc.styleSheets[0];
 
-  is(aEditor.styleSheet.href, sheet.href, "loaded stylesheet matches document stylesheet");
+  is(aEditor.styleSheet, sheet, "loaded stylesheet matches document stylesheet");
   win.close();
 
   finishup();
 }
 
 function getLinkByIndex(aIndex)
 {
   let contentDoc = ruleView().doc;
--- a/browser/devtools/webconsole/HUDService.jsm
+++ b/browser/devtools/webconsole/HUDService.jsm
@@ -335,24 +335,30 @@ WebConsole.prototype = {
    * @param integer aSourceLine
    *        The line number which you want to place the caret.
    * TODO: This function breaks the client-server boundaries.
    *       To be fixed in bug 793259.
    */
   viewSourceInStyleEditor:
   function WC_viewSourceInStyleEditor(aSourceURL, aSourceLine)
   {
-    gDevTools.showToolbox(this.target, "styleeditor").then(function(toolbox) {
-      try {
-        toolbox.getCurrentPanel().selectStyleSheet(aSourceURL, aSourceLine);
-      } catch(e) {
-        // Open view source if style editor fails.
-        this.viewSource(aSourceURL, aSourceLine);
+    let styleSheets = {};
+    if (this.target.isLocalTab) {
+      styleSheets = this.target.window.document.styleSheets;
+    }
+    for each (let style in styleSheets) {
+      if (style.href == aSourceURL) {
+        gDevTools.showToolbox(this.target, "styleeditor").then(function(toolbox) {
+          toolbox.getCurrentPanel().selectStyleSheet(style, aSourceLine);
+        });
+        return;
       }
-    });
+    }
+    // Open view source if style editor fails.
+    this.viewSource(aSourceURL, aSourceLine);
   },
 
   /**
    * Tries to open a JavaScript file related to the web page for the web console
    * instance in the Script Debugger. If the file is not found, it is opened in
    * source view instead.
    *
    * @param string aSourceURL
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_782653_CSS_links_in_Style_Editor.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_782653_CSS_links_in_Style_Editor.js
@@ -2,33 +2,33 @@
 /* ***** BEGIN LICENSE BLOCK *****
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  * ***** END LICENSE BLOCK ***** */
 
 const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test" +
                  "/test-bug-782653-css-errors.html";
 
-let nodes, hud, StyleEditorUI;
+let nodes, hud, SEC;
 
 function test()
 {
   addTab(TEST_URI);
   browser.addEventListener("load", function onLoad() {
     browser.removeEventListener("load", onLoad, true);
     openConsole(null, testViewSource);
   }, true);
 }
 
 function testViewSource(aHud)
 {
   hud = aHud;
 
   registerCleanupFunction(function() {
-    nodes = hud = StyleEditorUI = null;
+    nodes = hud = SEC = null;
   });
 
   let selector = ".webconsole-msg-cssparser .webconsole-location";
 
   waitForSuccess({
     name: "find the location node",
     validatorFn: function()
     {
@@ -36,85 +36,91 @@ function testViewSource(aHud)
     },
     successFn: function()
     {
       nodes = hud.outputNode.querySelectorAll(selector);
       is(nodes.length, 2, "correct number of css messages");
 
       let target = TargetFactory.forTab(gBrowser.selectedTab);
       let toolbox = gDevTools.getToolbox(target);
-      toolbox.once("styleeditor-selected", (event, panel) => {
-        StyleEditorUI = panel.UI;
-
-        let count = 0;
-        StyleEditorUI.on("editor-added", function() {
-          if (++count == 2) {
-            onStyleEditorReady(panel);
-          }
-        });
-      });
+      toolbox.once("styleeditor-selected", onStyleEditorReady);
 
       EventUtils.sendMouseEvent({ type: "click" }, nodes[0]);
     },
     failureFn: finishTest,
   });
 }
 
-function onStyleEditorReady(aPanel)
+function onStyleEditorReady(aEvent, aPanel)
 {
+  info(aEvent + " event fired");
+
+  SEC = aPanel.styleEditorChrome;
   let win = aPanel.panelWindow;
   ok(win, "Style Editor Window is defined");
-  ok(StyleEditorUI, "Style Editor UI is defined");
+  ok(SEC, "Style Editor Chrome is defined");
+
+  function sheetForNode(aNode)
+  {
+    let href = aNode.getAttribute("title");
+    let sheet, i = 0;
+    while((sheet = content.document.styleSheets[i++])) {
+      if (sheet.href == href) {
+        return sheet;
+      }
+    }
+    return null;
+  }
 
   waitForFocus(function() {
     info("style editor window focused");
 
-    let href = nodes[0].getAttribute("title");
-    ok(href.contains("test-bug-782653-css-errors-1.css"), "got first stylesheet href")
+    let sheet = sheetForNode(nodes[0]);
+    ok(sheet, "sheet found");
     let line = nodes[0].sourceLine;
-    is(line, 8, "found source line");
+    ok(line, "found source line");
 
-    checkStyleEditorForSheetAndLine(href, line - 1, function() {
+    checkStyleEditorForSheetAndLine(sheet, line - 1, function() {
       info("first check done");
 
       let target = TargetFactory.forTab(gBrowser.selectedTab);
       let toolbox = gDevTools.getToolbox(target);
 
-      let href = nodes[1].getAttribute("title");
-      ok(href.contains("test-bug-782653-css-errors-2.css"), "got second stylesheet href")
+      let sheet = sheetForNode(nodes[1]);
+      ok(sheet, "sheet found");
       let line = nodes[1].sourceLine;
-      is(line, 7, "found source line");
+      ok(line, "found source line");
 
       toolbox.selectTool("webconsole").then(function() {
         info("webconsole selected");
 
         toolbox.once("styleeditor-selected", function(aEvent) {
           info(aEvent + " event fired");
 
-          checkStyleEditorForSheetAndLine(href, line - 1, function() {
+          checkStyleEditorForSheetAndLine(sheet, line - 1, function() {
             info("second check done");
             finishTest();
           });
         });
 
         EventUtils.sendMouseEvent({ type: "click" }, nodes[1]);
       });
     });
   }, win);
 }
 
-function checkStyleEditorForSheetAndLine(aHref, aLine, aCallback)
+function checkStyleEditorForSheetAndLine(aStyleSheet, aLine, aCallback)
 {
   let foundEditor = null;
   waitForSuccess({
     name: "style editor for stylesheet",
     validatorFn: function()
     {
-      for (let editor of StyleEditorUI.editors) {
-        if (editor.styleSheet.href == aHref) {
+      for (let editor of SEC.editors) {
+        if (editor.styleSheet == aStyleSheet) {
           foundEditor = editor;
           return true;
         }
       }
       return false;
     },
     successFn: function()
     {
@@ -125,28 +131,27 @@ function checkStyleEditorForSheetAndLine
 }
 
 function performLineCheck(aEditor, aLine, aCallback)
 {
   function checkForCorrectState()
   {
     is(aEditor.sourceEditor.getCaretPosition().line, aLine,
        "correct line is selected");
-    is(StyleEditorUI.selectedStyleSheetIndex, aEditor.styleSheet.styleSheetIndex,
+    is(SEC.selectedStyleSheetIndex, aEditor.styleSheetIndex,
        "correct stylesheet is selected in the editor");
 
     aCallback && executeSoon(aCallback);
   }
 
   waitForSuccess({
     name: "source editor load",
     validatorFn: function()
     {
       return aEditor.sourceEditor;
     },
     successFn: checkForCorrectState,
     failureFn: function() {
-      info("selectedStyleSheetIndex " + StyleEditorUI.selectedStyleSheetIndex
-           + " expected " + aEditor.styleSheet.styleSheetIndex);
+      info("selectedStyleSheetIndex " + SEC.selectedStyleSheetIndex + " expected " + aEditor.styleSheetIndex);
       finishTest();
     },
   });
 }
--- a/toolkit/devtools/debugger/dbg-client.jsm
+++ b/toolkit/devtools/debugger/dbg-client.jsm
@@ -177,18 +177,17 @@ const UnsolicitedNotifications = {
   "networkEvent": "networkEvent",
   "networkEventUpdate": "networkEventUpdate",
   "newGlobal": "newGlobal",
   "newScript": "newScript",
   "newSource": "newSource",
   "tabDetached": "tabDetached",
   "tabNavigated": "tabNavigated",
   "pageError": "pageError",
-  "webappsEvent": "webappsEvent",
-  "styleSheetsAdded": "styleSheetsAdded"
+  "webappsEvent": "webappsEvent"
 };
 
 /**
  * Set of pause types that are sent by the server and not as an immediate
  * response to a client request.
  */
 const UnsolicitedPauses = {
   "resumeLimit": "resumeLimit",
--- a/toolkit/devtools/debugger/server/dbg-server.js
+++ b/toolkit/devtools/debugger/server/dbg-server.js
@@ -190,19 +190,16 @@ var DebuggerServer = {
     this.addActors("chrome://global/content/devtools/dbg-browser-actors.js");
 #ifndef MOZ_WIDGET_GONK
     this.addActors("chrome://global/content/devtools/dbg-webconsole-actors.js");
     this.addTabActor(this.WebConsoleActor, "consoleActor");
     this.addGlobalActor(this.WebConsoleActor, "consoleActor");
 #endif
     if ("nsIProfiler" in Ci)
       this.addActors("chrome://global/content/devtools/dbg-profiler-actors.js");
-
-    this.addActors("chrome://global/content/devtools/dbg-styleeditor-actors.js");
-    this.addTabActor(this.StyleEditorActor, "styleEditorActor");
   },
 
   /**
    * Listens on the given port for remote debugger connections.
    *
    * @param aPort int
    *        The port to listen on.
    */
--- a/toolkit/devtools/jar.mn
+++ b/toolkit/devtools/jar.mn
@@ -4,9 +4,8 @@
 
 toolkit.jar:
   content/global/devtools/dbg-transport.js        (debugger/dbg-transport.js)
 * content/global/devtools/dbg-server.js           (debugger/server/dbg-server.js)
   content/global/devtools/dbg-script-actors.js    (debugger/server/dbg-script-actors.js)
   content/global/devtools/dbg-browser-actors.js   (debugger/server/dbg-browser-actors.js)
   content/global/devtools/dbg-webconsole-actors.js (webconsole/dbg-webconsole-actors.js)
   content/global/devtools/dbg-profiler-actors.js  (debugger/server/dbg-profiler-actors.js)
-  content/global/devtools/dbg-styleeditor-actors.js  (styleeditor/dbg-styleeditor-actors.js)
--- a/toolkit/devtools/moz.build
+++ b/toolkit/devtools/moz.build
@@ -3,10 +3,9 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 PARALLEL_DIRS += [
     'debugger',
     'sourcemap',
     'webconsole',
-    'styleeditor'
 ]
deleted file mode 100644
--- a/toolkit/devtools/styleeditor/Makefile.in
+++ /dev/null
@@ -1,19 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-DEPTH = ../../..
-topsrcdir = @top_srcdir@
-srcdir = @srcdir@
-VPATH = @srcdir@
-
-include $(DEPTH)/config/autoconf.mk
-
-#ifneq (Android,$(OS_TARGET))
-#  TEST_DIRS += test
-#endif
-
-include $(topsrcdir)/config/rules.mk
-
-#libs::
-#  $(INSTALL) $(IFLAGS1) $(srcdir)/*.jsm $(FINAL_TARGET)/modules/devtools
deleted file mode 100644
--- a/toolkit/devtools/styleeditor/dbg-styleeditor-actors.js
+++ /dev/null
@@ -1,744 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-let Cc = Components.classes;
-let Ci = Components.interfaces;
-let Cu = Components.utils;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/NetUtil.jsm");
-Cu.import("resource://gre/modules/FileUtils.jsm");
-Cu.import("resource:///modules/devtools/StyleEditorUtil.jsm");
-
-let TRANSITION_CLASS = "moz-styleeditor-transitioning";
-let TRANSITION_DURATION_MS = 500;
-let TRANSITION_RULE = "\
-:root.moz-styleeditor-transitioning, :root.moz-styleeditor-transitioning * {\
-transition-duration: " + TRANSITION_DURATION_MS + "ms !important; \
-transition-delay: 0ms !important;\
-transition-timing-function: ease-out !important;\
-transition-property: all !important;\
-}";
-
-let LOAD_ERROR = "error-load";
-
-/**
- * Creates a StyleEditorActor. StyleEditorActor provides remote access to the
- * built-in style editor module.
- */
-function StyleEditorActor(aConnection, aParentActor)
-{
-  this.conn = aConnection;
-  this._onDocumentLoaded = this._onDocumentLoaded.bind(this);
-  this._onSheetLoaded = this._onSheetLoaded.bind(this);
-
-  if (aParentActor instanceof BrowserTabActor &&
-      aParentActor.browser instanceof Ci.nsIDOMWindow) {
-    this._window = aParentActor.browser;
-  }
-  else if (aParentActor instanceof BrowserTabActor &&
-           aParentActor.browser instanceof Ci.nsIDOMElement) {
-    this._window = aParentActor.browser.contentWindow;
-  }
-  else {
-    this._window = Services.wm.getMostRecentWindow("navigator:browser");
-  }
-
-  // keep a map of sheets-to-actors so we don't create two actors for one sheet
-  this._sheets = new Map();
-
-  this._actorPool = new ActorPool(this.conn);
-  this.conn.addActorPool(this._actorPool);
-}
-
-StyleEditorActor.prototype = {
-  /**
-   * Actor pool for all of the actors we send to the client.
-   */
-  _actorPool: null,
-
-  /**
-   * The debugger server connection instance.
-   */
-  conn: null,
-
-  /**
-   * The content window we work with.
-   */
-  get win() this._window,
-
-  /**
-   * The current content document of the window we work with.
-   */
-  get doc() this._window.document,
-
-  /**
-   * A window object, usually the browser window
-   */
-  _window: null,
-
-  actorPrefix: "styleEditor",
-
-  form: function()
-  {
-    return { actor: this.actorID };
-  },
-
-  /**
-   * Destroy the current StyleEditorActor instance.
-   */
-  disconnect: function()
-  {
-    if (this._observer) {
-      this._observer.disconnect();
-      delete this._observer;
-    }
-
-    this._sheets.clear();
-
-    this.conn.removeActorPool(this._actorPool);
-    this._actorPool = null;
-    this.conn = this._window = null;
-  },
-
-  /**
-   * Release an actor from our actor pool.
-   */
-  releaseActor: function(actor)
-  {
-    if (this._actorPool) {
-      this._actorPool.removeActor(actor.actorID);
-    }
-  },
-
-  /**
-   * Get the BaseURI for the document.
-   *
-   * @return {object} JSON message to with BaseURI
-   */
-  onGetBaseURI: function() {
-    return { baseURI: this.doc.baseURIObject };
-  },
-
-  /**
-   * Called when target navigates to a new document.
-   * Adds load listeners to document.
-   */
-  onNewDocument: function() {
-    // delete previous document's actors
-    this._clearStyleSheetActors();
-
-    // Note: listening for load won't be necessary once
-    // https://bugzilla.mozilla.org/show_bug.cgi?id=839103 is fixed
-    if (this.doc.readyState == "complete") {
-      this._onDocumentLoaded();
-    }
-    else {
-      this.win.addEventListener("load", this._onDocumentLoaded, false);
-    }
-    return {};
-  },
-
-  /**
-   * Event handler for document loaded event.
-   */
-  _onDocumentLoaded: function(event) {
-    if (event) {
-      this.win.removeEventListener("load", this._onDocumentLoaded, false);
-    }
-    let styleSheets = [];
-
-    if (this.doc.styleSheets.length) {
-      this._addStyleSheets(this.doc.styleSheets);
-    }
-  },
-
-  /**
-   * Clear all the current stylesheet actors in map.
-   */
-  _clearStyleSheetActors: function() {
-    for (let actor in this._sheets) {
-      this.releaseActor(this._sheets[actor]);
-    }
-    this._sheets.clear();
-  },
-
-  /**
-   * Get the actors of all the stylesheets in the current document.
-   *
-   * @return {object} JSON message with the stylesheet actors' forms
-   */
-  onGetStyleSheets: function() {
-    let styleSheets = [];
-
-    for (let i = 0; i < this.doc.styleSheets.length; ++i) {
-      let styleSheet = this.doc.styleSheets[i];
-      let actor = this._createStyleSheetActor(styleSheet);
-      styleSheets.push(actor.form());
-    }
-
-    return { "styleSheets": styleSheets };
-  },
-
-  /**
-   * Add all the stylesheets to the map and create an actor
-   * for each one if not already created. Send event that there
-   * are new stylesheets.
-   *
-   * @param {[DOMStyleSheet]} styleSheets
-   *        Stylesheets to add
-   */
-  _addStyleSheets: function(styleSheets)
-  {
-    let sheets = [];
-    for (let i = 0; i < styleSheets.length; i++) {
-      let styleSheet = styleSheets[i];
-      sheets.push(styleSheet);
-
-      // Get all sheets, including imported ones
-      let imports = this._getImported(styleSheet);
-      sheets = sheets.concat(imports);
-    }
-
-    let actors = sheets.map((sheet) => {
-      let actor = this._createStyleSheetActor(sheet);
-      return actor.form();
-    });
-
-    this._notifyStyleSheetsAdded(actors);
-  },
-
-  /**
-   * Send an event notifying that there are new style sheets
-   *
-   * @param  {[object]} actors
-   *         Forms of the new style sheet actors
-   */
-  _notifyStyleSheetsAdded: function(actors)
-  {
-    this.conn.send({
-      from: this.actorID,
-      type: "styleSheetsAdded",
-      styleSheets: actors
-    });
-  },
-
-  /**
-   * Get all the stylesheets @imported from a stylesheet.
-   *
-   * @param  {DOMStyleSheet} styleSheet
-   *         Style sheet to search
-   * @return {array}
-   *         All the imported stylesheets
-   */
-  _getImported: function(styleSheet) {
-   let imported = [];
-
-   for (let i = 0; i < styleSheet.cssRules.length; i++) {
-      let rule = styleSheet.cssRules[i];
-      if (rule.type == Ci.nsIDOMCSSRule.IMPORT_RULE) {
-        // Associated styleSheet may be null if it has already been seen due to
-        // duplicate @imports for the same URL.
-        if (!rule.styleSheet) {
-          continue;
-        }
-        imported.push(rule.styleSheet);
-
-        // recurse imports in this stylesheet as well
-        imported = imported.concat(this._getImported(rule.styleSheet));
-      }
-      else if (rule.type != Ci.nsIDOMCSSRule.CHARSET_RULE) {
-        // @import rules must precede all others except @charset
-        break;
-      }
-    }
-    return imported;
-  },
-
-  /**
-   * Create a new actor for a style sheet, if it hasn't
-   * already been created, and return it.
-   *
-   * @param  {DOMStyleSheet} aStyleSheet
-   *         The style sheet to create an actor for.
-   * @return {StyleSheetActor}
-   *         The actor for this style sheet
-   */
-  _createStyleSheetActor: function(aStyleSheet)
-  {
-    if (this._sheets.has(aStyleSheet)) {
-      return this._sheets.get(aStyleSheet);
-    }
-    let actor = new StyleSheetActor(aStyleSheet, this);
-    this._actorPool.addActor(actor);
-    this._sheets.set(aStyleSheet, actor);
-    return actor;
-  },
-
-  /**
-   * Handler for style sheet loading event. Add
-   * a new actor for the sheet and notify.
-   *
-   * @param  {Event} event
-   */
-  _onSheetLoaded: function(event) {
-    let style = event.target;
-    style.removeEventListener("load", this._onSheetLoaded, false);
-
-    let actor = this._createStyleSheetActor(style.sheet);
-    this._notifyStyleSheetsAdded([actor.form()]);
-  },
-
-  /**
-   * Create a new style sheet in the document with the given text.
-   * Return an actor for it.
-   *
-   * @param  {object} request
-   *         Debugging protocol request object, with 'text property'
-   * @return {object}
-   *         Object with 'styelSheet' property for form on new actor.
-   */
-  onNewStyleSheet: function(request) {
-    let parent = this.doc.documentElement;
-    let style = this.doc.createElementNS("http://www.w3.org/1999/xhtml", "style");
-    style.setAttribute("type", "text/css");
-
-    if (request.text) {
-      style.appendChild(this.doc.createTextNode(request.text));
-    }
-    parent.appendChild(style);
-
-    let actor = this._createStyleSheetActor(style.sheet);
-    return { styleSheet: actor.form() };
-  }
-};
-
-/**
- * The request types this actor can handle.
- */
-StyleEditorActor.prototype.requestTypes = {
-  "getStyleSheets": StyleEditorActor.prototype.onGetStyleSheets,
-  "newStyleSheet": StyleEditorActor.prototype.onNewStyleSheet,
-  "getBaseURI": StyleEditorActor.prototype.onGetBaseURI,
-  "newDocument": StyleEditorActor.prototype.onNewDocument
-};
-
-
-function StyleSheetActor(aStyleSheet, aParentActor) {
-  this.styleSheet = aStyleSheet;
-  this.parentActor = aParentActor;
-
-  // text and index are unknown until source load
-  this.text = null;
-  this._styleSheetIndex = -1;
-
-  this._transitionRefCount = 0;
-
-  this._onSourceLoad = this._onSourceLoad.bind(this);
-  this._notifyError = this._notifyError.bind(this);
-
-  // if this sheet has an @import, then it's rules are loaded async
-  let ownerNode = this.styleSheet.ownerNode;
-  if (ownerNode) {
-    let onSheetLoaded = function(event) {
-      ownerNode.removeEventListener("load", onSheetLoaded, false);
-      this._notifyPropertyChanged("ruleCount");
-    }.bind(this);
-
-    ownerNode.addEventListener("load", onSheetLoaded, false);
-  }
-}
-
-StyleSheetActor.prototype = {
-  actorPrefix: "stylesheet",
-
-  toString: function() {
-    return "[StyleSheetActor " + this.actorID + "]";
-  },
-
-  disconnect: function() {
-    this.parentActor.releaseActor(this);
-  },
-
-  /**
-   * Window of target
-   */
-  get win() {
-    return this.parentActor._window;
-  },
-
-  /**
-   * Document of target.
-   */
-  get doc() {
-    return this.win.document;
-  },
-
-  /**
-   * Retrieve the index (order) of stylesheet in the document.
-   *
-   * @return number
-   */
-  get styleSheetIndex()
-  {
-    if (this._styleSheetIndex == -1) {
-      for (let i = 0; i < this.doc.styleSheets.length; i++) {
-        if (this.doc.styleSheets[i] == this.styleSheet) {
-          this._styleSheetIndex = i;
-          break;
-        }
-      }
-    }
-    return this._styleSheetIndex;
-  },
-
-  /**
-   * Get the current state of the actor
-   *
-   * @return {object}
-   *         With properties of the underlying stylesheet, plus 'text',
-   *        'styleSheetIndex' and 'parentActor' if it's @imported
-   */
-  form: function() {
-    let form = {
-      actor: this.actorID,  // actorID is set when this actor is added to a pool
-      href: this.styleSheet.href,
-      disabled: this.styleSheet.disabled,
-      title: this.styleSheet.title,
-      styleSheetIndex: this.styleSheetIndex,
-      text: this.text
-    }
-
-    // get parent actor if this sheet was @imported
-    let parent = this.styleSheet.parentStyleSheet;
-    if (parent) {
-      form.parentActor = this.parentActor._sheets.get(parent);
-    }
-
-    try {
-      form.ruleCount = this.styleSheet.cssRules.length;
-    }
-    catch(e) {
-      // stylesheet had an @import rule that wasn't loaded yet
-    }
-
-    return form;
-  },
-
-  /**
-   * Toggle the disabled property of the style sheet
-   *
-   * @return {object}
-   *         'disabled' - the disabled state after toggling.
-   */
-  onToggleDisabled: function() {
-    this.styleSheet.disabled = !this.styleSheet.disabled;
-    this._notifyPropertyChanged("disabled");
-
-    return { disabled: this.styleSheet.disabled };
-  },
-
-  /**
-   * Send an event notifying that a property of the stylesheet
-   * has changed.
-   *
-   * @param  {string} property
-   *         Name of the changed property
-   */
-  _notifyPropertyChanged: function(property) {
-    this.conn.send({
-      from: this.actorID,
-      type: "propertyChange-" + this.actorID,
-      property: property,
-      value: this.form()[property]
-    })
-  },
-
-  /**
-   * Send an event notifying that an error has occured
-   *
-   * @param  {string} message
-   *         Error message
-   */
-  _notifyError: function(message) {
-    this.conn.send({
-      from: this.actorID,
-      type: "error-" + this.actorID,
-      errorMessage: message
-    });
-  },
-
-  /**
-   * Handler for event when the style sheet's full text has been
-   * loaded from its source.
-   *
-   * @param  {string} source
-   *         Text of the style sheet
-   * @param  {[type]} charset
-   *         Optional charset of the source
-   */
-  _onSourceLoad: function(source, charset) {
-    this.text = this._decodeCSSCharset(source, charset || "");
-
-    this.conn.send({
-      from: this.actorID,
-      type: "sourceLoad-" + this.actorID,
-      source: this.text
-    });
-  },
-
-  /**
-   * Fetch the source of the style sheet from its URL
-   */
-  onFetchSource: function() {
-    if (!this.styleSheet.href) {
-      // this is an inline <style> sheet
-      let source = this.styleSheet.ownerNode.textContent;
-      this._onSourceLoad(source);
-      return {};
-    }
-
-    let scheme = Services.io.extractScheme(this.styleSheet.href);
-    switch (scheme) {
-      case "file":
-        this._styleSheetFilePath = this.styleSheet.href;
-      case "chrome":
-      case "resource":
-        this._loadSourceFromFile(this.styleSheet.href);
-        break;
-      default:
-        this._loadSourceFromCache(this.styleSheet.href);
-        break;
-    }
-    return {};
-  },
-
-  /**
-   * Decode a CSS source string to unicode according to the character set rules
-   * defined in <http://www.w3.org/TR/CSS2/syndata.html#charset>.
-   *
-   * @param string string
-   *        Source of a CSS stylesheet, loaded from file or cache.
-   * @param string channelCharset
-   *        Charset of the source string if set by the HTTP channel.
-   * @return string
-   *         The CSS string, in unicode.
-   */
-  _decodeCSSCharset: function(string, channelCharset)
-  {
-    // StyleSheet's charset can be specified from multiple sources
-
-    if (channelCharset.length > 0) {
-      // step 1 of syndata.html: charset given in HTTP header.
-      return this._convertToUnicode(string, channelCharset);
-    }
-
-    let sheet = this.styleSheet;
-    if (sheet) {
-      // Do we have a @charset rule in the stylesheet?
-      // step 2 of syndata.html (without the BOM check).
-      if (sheet.cssRules) {
-        let rules = sheet.cssRules;
-        if (rules.length
-            && rules.item(0).type == Ci.nsIDOMCSSRule.CHARSET_RULE) {
-          return this._convertToUnicode(string, rules.item(0).encoding);
-        }
-      }
-
-      // step 3: charset attribute of <link> or <style> element, if it exists
-      if (sheet.ownerNode && sheet.ownerNode.getAttribute) {
-        let linkCharset = sheet.ownerNode.getAttribute("charset");
-        if (linkCharset != null) {
-          return this._convertToUnicode(string, linkCharset);
-        }
-      }
-
-      // step 4 (1 of 2): charset of referring stylesheet.
-      let parentSheet = sheet.parentStyleSheet;
-      if (parentSheet && parentSheet.cssRules &&
-          parentSheet.cssRules[0].type == Ci.nsIDOMCSSRule.CHARSET_RULE) {
-        return this._convertToUnicode(string,
-            parentSheet.cssRules[0].encoding);
-      }
-
-      // step 4 (2 of 2): charset of referring document.
-      if (sheet.ownerNode && sheet.ownerNode.ownerDocument.characterSet) {
-        return this._convertToUnicode(string,
-            sheet.ownerNode.ownerDocument.characterSet);
-      }
-    }
-
-    // step 5: default to utf-8.
-    return this._convertToUnicode(string, "UTF-8");
-  },
-
-  /**
-   * Convert a given string, encoded in a given character set, to unicode.
-   *
-   * @param string string
-   *        A string.
-   * @param string charset
-   *        A character set.
-   * @return string
-   *         A unicode string.
-   */
-  _convertToUnicode: function(string, charset) {
-    // Decoding primitives.
-    let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
-        .createInstance(Ci.nsIScriptableUnicodeConverter);
-
-    try {
-      converter.charset = charset;
-      return converter.ConvertToUnicode(string);
-    } catch(e) {
-      return string;
-    }
-  },
-
-  /**
-   * Load source from a file or file-like resource.
-   *
-   * @param string href
-   *        URL for the stylesheet.
-   */
-  _loadSourceFromFile: function(href)
-  {
-    try {
-      NetUtil.asyncFetch(href, function onFetch(stream, status) {
-        if (!Components.isSuccessCode(status)) {
-          return this._notifyError(LOAD_ERROR);
-        }
-        let source = NetUtil.readInputStreamToString(stream, stream.available());
-        stream.close();
-        this._onSourceLoad(source);
-      }.bind(this));
-    } catch (ex) {
-      this._notifyError(LOAD_ERROR);
-    }
-  },
-
-  /**
-   * Load source from the HTTP cache.
-   *
-   * @param string href
-   *        URL for the stylesheet.
-   */
-  _loadSourceFromCache: function(href)
-  {
-    let channel = Services.io.newChannel(href, null, null);
-    let chunks = [];
-    let channelCharset = "";
-    let streamListener = { // nsIStreamListener inherits nsIRequestObserver
-      onStartRequest: function (aRequest, aContext, aStatusCode) {
-        if (!Components.isSuccessCode(aStatusCode)) {
-          return this._notifyError(LOAD_ERROR);
-        }
-      }.bind(this),
-      onDataAvailable: function (aRequest, aContext, aStream, aOffset, aCount) {
-        let channel = aRequest.QueryInterface(Ci.nsIChannel);
-        if (!channelCharset) {
-          channelCharset = channel.contentCharset;
-        }
-        chunks.push(NetUtil.readInputStreamToString(aStream, aCount));
-      },
-      onStopRequest: function SEA_onStopRequest(aRequest, aContext, aStatusCode) {
-        if (!Components.isSuccessCode(aStatusCode)) {
-          return this._notifyError(LOAD_ERROR);
-        }
-        let source = chunks.join("");
-        this._onSourceLoad(source, channelCharset);
-      }.bind(this)
-    };
-
-    if (channel instanceof Ci.nsIPrivateBrowsingChannel) {
-      let loadContext = this.win.QueryInterface(Ci.nsIInterfaceRequestor)
-                          .getInterface(Ci.nsIWebNavigation)
-                          .QueryInterface(Ci.nsILoadContext);
-      channel.setPrivate(loadContext.usePrivateBrowsing);
-    }
-    channel.loadFlags = channel.LOAD_FROM_CACHE;
-    channel.asyncOpen(streamListener, null);
-  },
-
-  /**
-   * Update the style sheet in place with new text
-   *
-   * @param  {object} request
-   *         'text' - new text
-   *         'transition' - whether to do CSS transition for change.
-   */
-  onUpdate: function(request) {
-    DOMUtils.parseStyleSheet(this.styleSheet, request.text);
-
-    if (request.transition) {
-      this._insertTransistionRule();
-    }
-    else {
-      this._notifyStyleApplied();
-    }
-    this._notifyPropertyChanged("ruleCount");
-
-    return {};
-  },
-
-  /**
-   * Insert a catch-all transition rule into the document. Set a timeout
-   * to remove the rule after a certain time.
-   */
-  _insertTransistionRule: function() {
-    // Insert the global transition rule
-    // Use a ref count to make sure we do not add it multiple times.. and remove
-    // it only when all pending StyleEditor-generated transitions ended.
-    if (this._transitionRefCount == 0) {
-      this.styleSheet.insertRule(TRANSITION_RULE, this.styleSheet.cssRules.length);
-      this.doc.documentElement.classList.add(TRANSITION_CLASS);
-    }
-
-    this._transitionRefCount++;
-
-    // Set up clean up and commit after transition duration (+10% buffer)
-    // @see _onTransitionEnd
-    this.win.setTimeout(this._onTransitionEnd.bind(this),
-                           Math.floor(TRANSITION_DURATION_MS * 1.1));
-  },
-
-  /**
-    * This cleans up class and rule added for transition effect and then
-    * notifies that the style has been applied.
-    */
-  _onTransitionEnd: function()
-  {
-    if (--this._transitionRefCount == 0) {
-      this.doc.documentElement.classList.remove(TRANSITION_CLASS);
-      this.styleSheet.deleteRule(this.styleSheet.cssRules.length - 1);
-    }
-
-    this._notifyStyleApplied();
-  },
-
-  /**
-   * Send and event notifying that the new style has been applied fully.
-   */
-  _notifyStyleApplied: function()
-  {
-    this.conn.send({
-      from: this.actorID,
-      type: "styleApplied-" + this.actorID
-    })
-  }
-}
-
-StyleSheetActor.prototype.requestTypes = {
-  "toggleDisabled": StyleSheetActor.prototype.onToggleDisabled,
-  "fetchSource": StyleSheetActor.prototype.onFetchSource,
-  "update": StyleSheetActor.prototype.onUpdate
-};
-
-
-XPCOMUtils.defineLazyGetter(this, "DOMUtils", function () {
-  return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
-});
deleted file mode 100644
--- a/toolkit/devtools/styleeditor/moz.build
+++ /dev/null
@@ -1,6 +0,0 @@
-# vim: set filetype=python:
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-#TEST_DIRS += ['tests']