Merge fx-team to m-c.
authorRyan VanderMeulen <ryanvm@gmail.com>
Wed, 27 Mar 2013 22:22:20 -0400
changeset 126510 962f5293f87f652fe3e891a971c4ae0e4177a717
parent 126467 123017ccf036c2d0bc6f459481420640e8456718 (current diff)
parent 126509 36332ce24dd1fc30911232d52b569e11e38d3117 (diff)
child 126511 da133fd0dbb72e9c57cd3e04a6f09761808e8283
child 126852 d68b52ce9322e8d38f8127f3ed209c416d3c8ccd
child 127475 1aaed48de5e5a970b2ee9efc89391b270df4aa09
push id25496
push userryanvm@gmail.com
push dateThu, 28 Mar 2013 02:23:47 +0000
treeherdermozilla-inbound@da133fd0dbb7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone22.0a1
first release with
nightly linux32
962f5293f87f / 22.0a1 / 20130328031013 / files
nightly linux64
962f5293f87f / 22.0a1 / 20130328031013 / files
nightly mac
962f5293f87f / 22.0a1 / 20130328031013 / files
nightly win32
962f5293f87f / 22.0a1 / 20130328031013 / files
nightly win64
962f5293f87f / 22.0a1 / 20130328031013 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge fx-team to m-c.
browser/app/profile/firefox.js
browser/devtools/styleinspector/csshtmltree.xul
browser/devtools/styleinspector/cssruleview.xul
browser/devtools/styleinspector/styleinspector.css
browser/devtools/styleinspector/test/browser_computedview_bug_703643_context_menu_copy.js
browser/devtools/styleinspector/test/browser_ruleview_bug_703643_context_menu_copy.js
browser/themes/linux/devtools/csshtmltree.css
browser/themes/osx/devtools/csshtmltree.css
browser/themes/windows/devtools/csshtmltree.css
configure.in
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1051,16 +1051,18 @@ pref("devtools.layoutview.enabled", true
 pref("devtools.layoutview.open", false);
 
 // Enable the Responsive UI tool
 pref("devtools.responsiveUI.enabled", true);
 
 // Enable the Debugger
 pref("devtools.debugger.enabled", true);
 pref("devtools.debugger.chrome-enabled", true);
+pref("devtools.debugger.chrome-debugging-host", "localhost");
+pref("devtools.debugger.chrome-debugging-port", 6080);
 pref("devtools.debugger.remote-host", "localhost");
 pref("devtools.debugger.remote-autoconnect", false);
 pref("devtools.debugger.remote-connection-retries", 3);
 pref("devtools.debugger.remote-timeout", 20000);
 
 // The default Debugger UI settings
 pref("devtools.debugger.ui.win-x", 0);
 pref("devtools.debugger.ui.win-y", 0);
@@ -1092,16 +1094,19 @@ pref("devtools.scratchpad.recentFilesMax
 
 // Enable the Style Editor.
 pref("devtools.styleeditor.enabled", true);
 pref("devtools.styleeditor.transitions", true);
 
 // Enable tools for Chrome development.
 pref("devtools.chrome.enabled", false);
 
+// Default theme ("dark" or "light")
+pref("devtools.theme", "light");
+
 // Display the introductory text
 pref("devtools.gcli.hideIntro", false);
 
 // How eager are we to show help: never=1, sometimes=2, always=3
 pref("devtools.gcli.eagerHelper", 2);
 
 // Do we allow the 'pref set' command
 pref("devtools.gcli.allowSet", false);
--- a/browser/devtools/debugger/DebuggerUI.jsm
+++ b/browser/devtools/debugger/DebuggerUI.jsm
@@ -6,17 +6,17 @@
 "use strict";
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 const DBG_XUL = "chrome://browser/content/debugger.xul";
 const DBG_STRINGS_URI = "chrome://browser/locale/devtools/debugger.properties";
-const CHROME_DEBUGGER_PROFILE_NAME = "_chrome-debugger-profile";
+const CHROME_DEBUGGER_PROFILE_NAME = "-chrome-debugger";
 const TAB_SWITCH_NOTIFICATION = "debugger-tab-switch";
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this,
   "DebuggerServer", "resource://gre/modules/devtools/dbg-server.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this,
@@ -470,17 +470,17 @@ ChromeDebuggerProcess.prototype = {
   /**
    * Initializes the debugger server.
    */
   _initServer: function RDP__initServer() {
     if (!DebuggerServer.initialized) {
       DebuggerServer.init();
       DebuggerServer.addBrowserActors();
     }
-    DebuggerServer.openListener(Prefs.remotePort);
+    DebuggerServer.openListener(Prefs.chromeDebuggingPort);
   },
 
   /**
    * Initializes a profile for the remote debugger process.
    */
   _initProfile: function RDP__initProfile() {
     let profileService = Cc["@mozilla.org/toolkit/profile-service;1"]
       .createInstance(Ci.nsIToolkitProfileService);
@@ -592,40 +592,26 @@ XPCOMUtils.defineLazyGetter(L10N, "strin
 });
 
 /**
  * Shortcuts for accessing various debugger preferences.
  */
 let Prefs = {};
 
 /**
- * Gets the preferred default remote debugging host.
- * @return string
- */
-XPCOMUtils.defineLazyGetter(Prefs, "remoteHost", function() {
-  return Services.prefs.getCharPref("devtools.debugger.remote-host");
-});
-
-/**
- * Gets the preferred default remote debugging port.
+ * Gets the preferred default remote browser debugging port.
  * @return number
  */
-XPCOMUtils.defineLazyGetter(Prefs, "remotePort", function() {
-  return Services.prefs.getIntPref("devtools.debugger.remote-port");
-});
-
-/**
- * Gets the preferred default remote debugging port.
- * @return number
- */
-XPCOMUtils.defineLazyGetter(Prefs, "wantLogging", function() {
-  return Services.prefs.getBoolPref("devtools.debugger.log");
+XPCOMUtils.defineLazyGetter(Prefs, "chromeDebuggingPort", function() {
+  return Services.prefs.getIntPref("devtools.debugger.chrome-debugging-port");
 });
 
 /**
  * Helper method for debugging.
  * @param string
  */
 function dumpn(str) {
-  if (Prefs.wantLogging) {
+  if (wantLogging) {
     dump("DBG-FRONTEND: " + str + "\n");
   }
 }
+
+let wantLogging = Services.prefs.getBoolPref("devtools.debugger.log");
--- a/browser/devtools/debugger/debugger-controller.js
+++ b/browser/devtools/debugger/debugger-controller.js
@@ -180,17 +180,18 @@ let DebuggerController = {
         this._startChromeDebugging(client, dbg, callback);
       } else {
         this._startDebuggingTab(client, this._target.form, callback);
       }
       return;
     }
 
     // Chrome debugging needs to make the connection to the debuggee.
-    let transport = debuggerSocketConnect(Prefs.remoteHost, Prefs.remotePort);
+    let transport = debuggerSocketConnect(Prefs.chromeDebuggingHost,
+                                          Prefs.chromeDebuggingPort);
 
     let client = this.client = new DebuggerClient(transport);
     client.addListener("tabNavigated", this._onTabNavigated);
     client.addListener("tabDetached", this._onTabDetached);
 
     client.connect(function(aType, aTraits) {
       client.listTabs(function(aResponse) {
         this._startChromeDebugging(client, aResponse.chromeDebugger, callback);
@@ -1726,16 +1727,18 @@ let Prefs = {
   map: function P_map(aType, aPropertyName, aPrefName) {
     Object.defineProperty(this, aPropertyName, {
       get: function() this._get(aType, aPrefName),
       set: function(aValue) this._set(aType, aPrefName, aValue)
     });
   }
 };
 
+Prefs.map("Char", "chromeDebuggingHost", "devtools.debugger.chrome-debugging-host");
+Prefs.map("Int", "chromeDebuggingPort", "devtools.debugger.chrome-debugging-port");
 Prefs.map("Int", "windowX", "devtools.debugger.ui.win-x");
 Prefs.map("Int", "windowY", "devtools.debugger.ui.win-y");
 Prefs.map("Int", "windowWidth", "devtools.debugger.ui.win-width");
 Prefs.map("Int", "windowHeight", "devtools.debugger.ui.win-height");
 Prefs.map("Int", "sourcesWidth", "devtools.debugger.ui.panes-sources-width");
 Prefs.map("Int", "instrumentsWidth", "devtools.debugger.ui.panes-instruments-width");
 Prefs.map("Bool", "pauseOnExceptions", "devtools.debugger.ui.pause-on-exceptions");
 Prefs.map("Bool", "panesVisibleOnStartup", "devtools.debugger.ui.panes-visible-on-startup");
--- a/browser/devtools/debugger/debugger-panes.js
+++ b/browser/devtools/debugger/debugger-panes.js
@@ -972,35 +972,28 @@ let SourceUtils = {
     this._groupsCache = new Map();
   },
 
   /**
    * Gets a unique, simplified label from a source url.
    *
    * @param string aUrl
    *        The source url.
-   * @param number aLength [optional]
-   *        The expected source url length.
-   * @param number aSection [optional]
-   *        The section to trim. Supported values: "start", "center", "end"
    * @return string
    *         The simplified label.
    */
-  getSourceLabel: function SU_getSourceLabel(aUrl, aLength, aSection) {
-    aLength = aLength || SOURCE_URL_DEFAULT_MAX_LENGTH;
-    aSection = aSection || "end";
-    let id = [aUrl, aLength, aSection].join();
-    let cachedLabel = this._labelsCache.get(id);
+  getSourceLabel: function SU_getSourceLabel(aUrl) {
+    let cachedLabel = this._labelsCache.get(aUrl);
     if (cachedLabel) {
       return cachedLabel;
     }
 
-    let sourceLabel = this.trimUrlLength(this.trimUrl(aUrl), aLength, aSection);
+    let sourceLabel = this.trimUrl(aUrl);
     let unicodeLabel = this.convertToUnicode(window.unescape(sourceLabel));
-    this._labelsCache.set(id, unicodeLabel);
+    this._labelsCache.set(aUrl, unicodeLabel);
     return unicodeLabel;
   },
 
   /**
    * Gets as much information as possible about the hostname and directory paths
    * of an url to create a short url group identifier.
    *
    * @param string aUrl
--- a/browser/devtools/debugger/debugger-toolbar.js
+++ b/browser/devtools/debugger/debugger-toolbar.js
@@ -427,20 +427,21 @@ create({ constructor: StackFramesView, p
    *        The line number to be displayed in the list.
    * @param number aDepth
    *        The frame depth specified by the debugger.
    * @return nsIDOMNode
    *         The stack frame view.
    */
   _createFrameView:
   function DVSF__createFrameView(aFrameTitle, aSourceLocation, aLineNumber, aDepth) {
-    let frameDetails = SourceUtils.getSourceLabel(aSourceLocation,
-      STACK_FRAMES_SOURCE_URL_MAX_LENGTH,
-      STACK_FRAMES_SOURCE_URL_TRIM_SECTION) +
-      SEARCH_LINE_FLAG + aLineNumber;
+    let frameDetails =
+      SourceUtils.trimUrlLength(
+        SourceUtils.getSourceLabel(aSourceLocation),
+        STACK_FRAMES_SOURCE_URL_MAX_LENGTH,
+        STACK_FRAMES_SOURCE_URL_TRIM_SECTION) + SEARCH_LINE_FLAG + aLineNumber;
 
     let frameTitleNode = document.createElement("label");
     frameTitleNode.className = "plain dbg-stackframe-title breadcrumbs-widget-item-tag";
     frameTitleNode.setAttribute("value", aFrameTitle);
 
     let frameDetailsNode = document.createElement("label");
     frameDetailsNode.className = "plain dbg-stackframe-details breadcrumbs-widget-item-id";
     frameDetailsNode.setAttribute("value", frameDetails);
@@ -466,20 +467,21 @@ create({ constructor: StackFramesView, p
    *        The line number to be displayed in the list.
    * @param number aDepth
    *        The frame depth specified by the debugger.
    * @return object
    *         An object containing the stack frame command and menu item.
    */
   _createMenuEntry:
   function DVSF__createMenuEntry(aFrameTitle, aSourceLocation, aLineNumber, aDepth) {
-    let frameDescription = SourceUtils.getSourceLabel(aSourceLocation,
-      STACK_FRAMES_POPUP_SOURCE_URL_MAX_LENGTH,
-      STACK_FRAMES_POPUP_SOURCE_URL_TRIM_SECTION) +
-      SEARCH_LINE_FLAG + aLineNumber;
+    let frameDescription =
+      SourceUtils.trimUrlLength(
+        SourceUtils.getSourceLabel(aSourceLocation),
+        STACK_FRAMES_POPUP_SOURCE_URL_MAX_LENGTH,
+        STACK_FRAMES_POPUP_SOURCE_URL_TRIM_SECTION) + SEARCH_LINE_FLAG + aLineNumber;
 
     let prefix = "sf-cMenu-"; // "stackframes context menu"
     let commandId = prefix + aDepth + "-" + "-command";
     let menuitemId = prefix + aDepth + "-" + "-menuitem";
 
     let command = document.createElement("command");
     command.id = commandId;
     command.addEventListener("command", this._selectFrame.bind(this, aDepth), false);
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-01.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-01.js
@@ -76,17 +76,17 @@ function testlabelshortening() {
       { href: "resource://random/", leaf: "script_t3_3.js&a=1&b=2#id" },
 
       { href: nanana, leaf: "Batman!" + "{trim me, now and forevermore}" }
     ];
 
     urls.forEach(function(url) {
       executeSoon(function() {
         let loc = url.href + url.leaf;
-        vs.push([sv.getSourceLabel(loc), loc], { forced: true });
+        vs.push([sv.trimUrlLength(sv.getSourceLabel(loc)), loc], { forced: true });
       });
     });
 
     executeSoon(function() {
       info("Script labels:");
       info(vs.labels.toSource());
 
       info("Script locations:");
--- a/browser/devtools/fontinspector/font-inspector.js
+++ b/browser/devtools/fontinspector/font-inspector.js
@@ -177,27 +177,30 @@ FontInspector.prototype = {
      *   </head>
      *   <style>
      *   p {font-family: {name};}
      *   * {font-size: 40px;line-height:60px;padding:0 10px;margin:0};
      *   </style>
      *   <p contenteditable>Abc</p>
      */
     let extraCSS = "* {padding:0;margin:0}";
+    extraCSS += ".theme-dark {color: white}";
     extraCSS += "p {font-family: '" + name + "';}";
     extraCSS += "p {font-size: 40px;line-height:60px;padding:0 10px;margin:0;}";
     cssCode += extraCSS;
     let src = "data:text/html;charset=utf-8,<!DOCTYPE HTML><head><base></base></head><style></style><p contenteditable>Abc</p>";
     iframe.addEventListener("load", function onload() {
       iframe.removeEventListener("load", onload, true);
       let doc = iframe.contentWindow.document;
       // We could have done that earlier, but we want to avoid any URL-encoding
       // nightmare.
       doc.querySelector("base").href = base;
       doc.querySelector("style").textContent = cssCode;
+      // Forward theme
+      doc.documentElement.className = document.documentElement.className;
     }, true);
     iframe.src = src;
   },
 
   /**
    * Select the <body> to show all the fonts included in the document.
    */
   showAll: function FI_showAll() {
--- a/browser/devtools/fontinspector/font-inspector.xhtml
+++ b/browser/devtools/fontinspector/font-inspector.xhtml
@@ -9,18 +9,19 @@
 
 <html xmlns="http://www.w3.org/1999/xhtml">
   <head>
     <title>&title;</title>
     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
     <link rel="stylesheet" href="font-inspector.css" type="text/css"/>
     <link rel="stylesheet" href="chrome://browser/skin/devtools/common.css" type="text/css"/>
     <link rel="stylesheet" href="chrome://browser/skin/devtools/font-inspector.css" type="text/css"/>
+    <script type="application/javascript;version=1.8" src="chrome://browser/content/devtools/theme-switching.js"/>
   </head>
-  <body class="devtools-monospace" role="application">
+  <body class="theme-body devtools-monospace" role="application">
     <script type="application/javascript;version=1.8" src="font-inspector.js"></script>
     <div id="root">
       <ul id="all-fonts"></ul>
       <button id="showall" onclick="fontInspector.showAll()">&showAllFonts;</button>
     </div>
     <div id="template" style="display:none">
       <section class="font">
         <iframe sandbox="allow-same-origin" class="font-preview"></iframe>
--- a/browser/devtools/framework/Toolbox.jsm
+++ b/browser/devtools/framework/Toolbox.jsm
@@ -709,26 +709,34 @@ Toolbox.prototype = {
     gDevTools.off("tool-unregistered", this._toolUnregistered);
 
     let outstanding = [];
 
     for (let [id, panel] of this._toolPanels) {
       outstanding.push(panel.destroy());
     }
 
+    let container = this.doc.getElementById("toolbox-buttons");
+    while(container.firstChild) {
+      container.removeChild(container.firstChild);
+    }
+
     outstanding.push(this._host.destroy());
 
     // Targets need to be notified that the toolbox is being torn down, so that
     // remote protocol connections can be gracefully terminated.
     if (this._target) {
       this._target.off("close", this.destroy);
       outstanding.push(this._target.destroy());
     }
     this._target = null;
 
     Promise.all(outstanding).then(function() {
       this.emit("destroyed");
+      // Free _host after the call to destroyed in order to let a chance
+      // to destroyed listeners to still query toolbox attributes
+      this._host = null;
       deferred.resolve();
     }.bind(this));
 
     return this._destroyer;
   }
 };
--- a/browser/devtools/framework/gDevTools.jsm
+++ b/browser/devtools/framework/gDevTools.jsm
@@ -602,17 +602,17 @@ let gDevToolsBrowser = {
    * @param  {XULWindow} win
    *         The window containing the menu entry
    */
   forgetBrowserWindow: function DT_forgetBrowserWindow(win) {
     gDevToolsBrowser._trackedBrowserWindows.delete(win);
 
     // Destroy toolboxes for closed window
     for (let [target, toolbox] of gDevTools._toolboxes) {
-      if (toolbox.frame.ownerDocument.defaultView == win) {
+      if (toolbox.frame && toolbox.frame.ownerDocument.defaultView == win) {
         toolbox.destroy();
       }
     }
 
     let tabContainer = win.document.getElementById("tabbrowser-tabs")
     tabContainer.removeEventListener("TabSelect",
                                      gDevToolsBrowser._updateMenuCheckbox, false);
   },
--- a/browser/devtools/inspector/InspectorPanel.jsm
+++ b/browser/devtools/inspector/InspectorPanel.jsm
@@ -216,21 +216,21 @@ InspectorPanel.prototype = {
     this._setDefaultSidebar = function(event, toolId) {
       Services.prefs.setCharPref("devtools.inspector.activeSidebar", toolId);
     }.bind(this);
 
     this.sidebar.on("select", this._setDefaultSidebar);
     this.toggleHighlighter = this.toggleHighlighter.bind(this);
 
     this.sidebar.addTab("ruleview",
-                        "chrome://browser/content/devtools/cssruleview.xul",
+                        "chrome://browser/content/devtools/cssruleview.xhtml",
                         "ruleview" == defaultTab);
 
     this.sidebar.addTab("computedview",
-                        "chrome://browser/content/devtools/csshtmltree.xul",
+                        "chrome://browser/content/devtools/computedview.xhtml",
                         "computedview" == defaultTab);
 
     if (Services.prefs.getBoolPref("devtools.fontinspector.enabled")) {
       this.sidebar.addTab("fontinspector",
                           "chrome://browser/content/devtools/fontinspector/font-inspector.xhtml",
                           "fontinspector" == defaultTab);
     }
 
--- a/browser/devtools/inspector/inspector.xul
+++ b/browser/devtools/inspector/inspector.xul
@@ -82,14 +82,14 @@
           timeout="50"
           class="devtools-searchinput"
           placeholder="&inspectorSearchHTML.label;"/>
       </toolbar>
       <vbox flex="1" id="markup-box">
       </vbox>
     </vbox>
     <splitter class="devtools-side-splitter"/>
-    <tabbox id="inspector-sidebar" class="devtools-sidebar-tabs" hidden="true">
+    <tabbox id="inspector-sidebar" handleCtrlTab="false" class="devtools-sidebar-tabs" hidden="true">
       <tabs/>
       <tabpanels flex="1"/>
     </tabbox>
   </hbox>
 </window>
--- a/browser/devtools/jar.mn
+++ b/browser/devtools/jar.mn
@@ -7,21 +7,22 @@ browser.jar:
     content/browser/devtools/markup-view.xhtml    (markupview/markup-view.xhtml)
     content/browser/devtools/markup-view.css      (markupview/markup-view.css)
     content/browser/NetworkPanel.xhtml            (webconsole/NetworkPanel.xhtml)
     content/browser/devtools/webconsole.js        (webconsole/webconsole.js)
     content/browser/devtools/webconsole.xul       (webconsole/webconsole.xul)
 *   content/browser/scratchpad.xul                (scratchpad/scratchpad.xul)
     content/browser/scratchpad.js                 (scratchpad/scratchpad.js)
     content/browser/splitview.css                 (shared/splitview.css)
+    content/browser/devtools/theme-switching.js   (shared/theme-switching.js)
     content/browser/styleeditor.xul               (styleeditor/styleeditor.xul)
     content/browser/styleeditor.css               (styleeditor/styleeditor.css)
-    content/browser/devtools/csshtmltree.xul      (styleinspector/csshtmltree.xul)
-    content/browser/devtools/cssruleview.xul      (styleinspector/cssruleview.xul)
-    content/browser/devtools/styleinspector.css   (styleinspector/styleinspector.css)
+    content/browser/devtools/computedview.xhtml   (styleinspector/computedview.xhtml)
+    content/browser/devtools/cssruleview.xhtml    (styleinspector/cssruleview.xhtml)
+    content/browser/devtools/ruleview.css         (styleinspector/ruleview.css)
     content/browser/devtools/layoutview/view.js   (layoutview/view.js)
     content/browser/devtools/layoutview/view.xhtml  (layoutview/view.xhtml)
     content/browser/devtools/layoutview/view.css  (layoutview/view.css)
     content/browser/devtools/fontinspector/font-inspector.js    (fontinspector/font-inspector.js)
     content/browser/devtools/fontinspector/font-inspector.xhtml (fontinspector/font-inspector.xhtml)
     content/browser/devtools/fontinspector/font-inspector.css   (fontinspector/font-inspector.css)
     content/browser/orion.js                      (sourceeditor/orion/orion.js)
 *   content/browser/source-editor-overlay.xul     (sourceeditor/source-editor-overlay.xul)
--- a/browser/devtools/layoutview/view.css
+++ b/browser/devtools/layoutview/view.css
@@ -42,16 +42,26 @@ body {
   margin: 0 10px 10px 10px;
   -moz-box-sizing: border-box;
   width: calc(100% - 2 * 10px);
   position: absolute;
   border-width: 1px;
   font: 10px/12px monospace;
 }
 
+@media (min-width: 320px) {
+  body {
+    position: absolute;
+    width: 320px;
+    left: -160px;
+    margin-left: 50%;
+  }
+}
+
+
 #margins {
   padding: 28px;
 }
 
 #content {
   height: 20px;
   border-width: 1px;
 }
--- a/browser/devtools/layoutview/view.xhtml
+++ b/browser/devtools/layoutview/view.xhtml
@@ -7,16 +7,18 @@
  %layoutviewDTD;
 ]>
 
 <html xmlns="http://www.w3.org/1999/xhtml"
       xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
   <head>
     <title>&title;</title>
 
+    <script id="script-theme" type="application/javascript;version=1.8" src="chrome://browser/content/devtools/theme-switching.js"/>
+
     <script type="application/javascript;version=1.8" src="view.js"></script>
     <script type="application/javascript;version=1.8">
     <![CDATA[
       let elts;
       let tooltip;
 
       const Ci = Components.interfaces;
       const Cc = Components.classes;
@@ -62,17 +64,17 @@
       }
     ]]>
     </script>
 
     <link rel="stylesheet" href="chrome://browser/skin/devtools/layoutview.css" type="text/css"/>
     <link rel="stylesheet" href="view.css" type="text/css"/>
 
   </head>
-  <body>
+  <body class="theme-body">
 
     <p id="header">
         <span id="element-size"></span>
     </p>
 
     <div id="main">
 
       <div id="margins" tooltip="&margins.tooltip;">
--- a/browser/devtools/markupview/MarkupView.jsm
+++ b/browser/devtools/markupview/MarkupView.jsm
@@ -838,23 +838,23 @@ MarkupContainer.prototype = {
    * True if the node has been visually expanded in the tree.
    */
   get expanded() {
     return this.children.hasAttribute("expanded");
   },
 
   set expanded(aValue) {
     if (aValue) {
-      this.expander.setAttribute("expanded", "");
+      this.expander.setAttribute("open", "");
       this.children.setAttribute("expanded", "");
       if (this.editor.summaryElt) {
         this.editor.summaryElt.setAttribute("expanded", "");
       }
     } else {
-      this.expander.removeAttribute("expanded");
+      this.expander.removeAttribute("open");
       this.children.removeAttribute("expanded");
       if (this.editor.summaryElt) {
         this.editor.summaryElt.removeAttribute("expanded");
       }
     }
   },
 
   /**
@@ -872,24 +872,24 @@ MarkupContainer.prototype = {
 
   get selected() {
     return this._selected;
   },
 
   set selected(aValue) {
     this._selected = aValue;
     if (this._selected) {
-      this.editor.elt.classList.add("selected");
+      this.editor.elt.classList.add("theme-selected");
       if (this.editor.closeElt) {
-        this.editor.closeElt.classList.add("selected");
+        this.editor.closeElt.classList.add("theme-selected");
       }
     } else {
-      this.editor.elt.classList.remove("selected");
+      this.editor.elt.classList.remove("theme-selected");
       if (this.editor.closeElt) {
-        this.editor.closeElt.classList.remove("selected");
+        this.editor.closeElt.classList.remove("theme-selected");
       }
     }
   },
 
   /**
    * Update the container's editor to the current state of the
    * viewed node.
    */
@@ -1173,38 +1173,31 @@ ElementEditor.prototype = {
   /**
    * Parse a user-entered attribute string and apply the resulting
    * attributes to the node.  This operation is undoable.
    *
    * @param string aValue the user-entered value.
    * @param Element aAttrNode the attribute editor that created this
    *        set of attributes, used to place new attributes where the
    *        user put them.
-   * @throws SYNTAX_ERR if aValue is not well-formed.
    */
   _applyAttributes: function EE__applyAttributes(aValue, aAttrNode)
   {
-    // Create a dummy node for parsing the attribute list.
-    let dummyNode = this.doc.createElement("div");
-
-    let parseTag = (this.node.namespaceURI.match(/svg/i) ? "svg" :
-                   (this.node.namespaceURI.match(/mathml/i) ? "math" : "div"));
-    let parseText = "<" + parseTag + " " + aValue + "/>";
-    // Throws exception if parseText is not well-formed.
-    dummyNode.innerHTML = parseText;
-    let parsedNode = dummyNode.firstChild;
-
-    let attrs = parsedNode.attributes;
+    let attrs = escapeAttributeValues(aValue);
 
     this.undo.startBatch();
 
-    for (let i = 0; i < attrs.length; i++) {
+    for (let attr of attrs) {
+      let attribute = {
+        name: attr.name,
+        value: attr.value
+      };
       // Create an attribute editor next to the current attribute if needed.
-      this._createAttribute(attrs[i], aAttrNode ? aAttrNode.nextSibling : null);
-      this._setAttribute(this.node, attrs[i].name, attrs[i].value);
+      this._createAttribute(attribute, aAttrNode ? aAttrNode.nextSibling : null);
+      this._setAttribute(this.node, attr.name, attr.value);
     }
 
     this.undo.endBatch();
   },
 
   /**
    * Helper function for _setAttribute and _removeAttribute,
    * returns a function that puts an attribute back the way it was.
@@ -1419,16 +1412,110 @@ DocumentWalker.prototype = {
     }
     return this.walker.lastChild();
   },
 
   previousSibling: function DW_previousSibling() this.walker.previousSibling(),
   nextSibling: function DW_nextSibling() this.walker.nextSibling(),
 
   // XXX bug 785143: not doing previousNode or nextNode, which would sure be useful.
+};
+
+/**
+ * Properly escape attribute values.
+ *
+ * @param  {String} attr
+ *         The attributes for which the values are to be escaped.
+ * @return {Array}
+ *         An array of attribute names and their escaped values.
+ */
+function escapeAttributeValues(attr) {
+  let name = null;
+  let value = null;
+  let result = "";
+  let attributes = [];
+
+  while(attr.length > 0) {
+    let match;
+    let dirty = false;
+
+    // Trim quotes and spaces from attr start
+    match = attr.match(/^["\s]+/);
+    if (match && match.length == 1) {
+      attr = attr.substr(match[0].length);
+    }
+
+    // Name
+    if (!dirty) {
+      match = attr.match(/^([\w-]+)="/);
+      if (match && match.length == 2) {
+        if (name) {
+          // We had a name without a value e.g. disabled. Let's set the value to "";
+          value = "";
+        } else {
+          name = match[1];
+          attr = attr.substr(match[0].length);
+        }
+        dirty = true;
+      }
+    }
+
+    // Value (in the case of multiple attributes)
+    if (!dirty) {
+      match = attr.match(/^(.+?)"\s+[\w-]+="/);
+      if (match && match.length > 1) {
+        value = typeof match[1] == "undefined" ? match[2] : match[1];
+        attr = attr.substr(value.length);
+        value = simpleEscape(value);
+        dirty = true;
+      }
+    }
+
+    // Final value
+    if (!dirty && attr.indexOf("=\"") == -1) {
+      // No more attributes, get the remaining value minus it's ending quote.
+      if (attr.charAt(attr.length - 1) == '"') {
+        attr = attr.substr(0, attr.length - 1);
+      }
+
+      if (!name) {
+        name = attr;
+        value = "";
+      } else {
+        value = simpleEscape(attr);
+      }
+      attr = "";
+      dirty = true;
+    }
+
+    if (name !== null && value !== null) {
+      attributes.push({name: name, value: value});
+      name = value = null;
+    }
+
+    if (!dirty) {
+      // This should never happen but we exit here if it does.
+      return attributes;
+    }
+  }
+  return attributes;
+}
+
+/**
+ * Escape basic html entities <, >, " and '.
+ * @param  {String} value
+ *         Value to escape.
+ * @return {String}
+ *         Escaped value.
+ */
+function simpleEscape(value) {
+  return value.replace(/</g, "&lt;")
+              .replace(/>/g, "&gt;")
+              .replace(/"/g, "&quot;")
+              .replace(/'/g, "&apos;");
 }
 
 /**
  * A tree walker filter for avoiding empty whitespace text nodes.
  */
 function whitespaceTextFilter(aNode)
 {
     if (aNode.nodeType == Ci.nsIDOMNode.TEXT_NODE &&
--- a/browser/devtools/markupview/markup-view.xhtml
+++ b/browser/devtools/markupview/markup-view.xhtml
@@ -5,40 +5,43 @@
 <!DOCTYPE html>
 
 <html xmlns="http://www.w3.org/1999/xhtml">
 <head>
   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
   <link rel="stylesheet" href="chrome://browser/content/devtools/markup-view.css" type="text/css"/>
   <link rel="stylesheet" href="chrome://browser/skin/devtools/markup-view.css" type="text/css"/>
   <link rel="stylesheet" href="chrome://browser/skin/devtools/common.css" type="text/css"/>
+
+  <script type="application/javascript;version=1.8" src="theme-switching.js"/>
+
 </head>
-<body class="devtools-theme-background devtools-monospace" role="application">
+<body class="theme-body devtools-monospace" role="application">
   <div id="root"></div>
   <div id="templates" style="display:none">
     <ul>
-      <li id="template-container" save="${elt}" class="container"><span save="${expander}" class="expander"></span><span save="${codeBox}" class="codebox"><ul save="${children}" class="children"></ul></span></li>
+      <li id="template-container" save="${elt}" class="container"><span save="${expander}" class="theme-twisty expander"></span><span save="${codeBox}" class="codebox"><ul save="${children}" class="children"></ul></span></li>
 
       <li id="template-more-nodes" class="more-nodes devtools-class-comment" save="${elt}"><span>${showing}</span> <button href="#" onclick="${allButtonClick}">${showAll}</button></li>
     </ul>
 
-    <span id="template-element" save="${elt}" class="editor"><span>&lt;</span><span save="${tag}" class="tagname devtools-theme-tagname"></span><span save="${attrList}"></span><span save="${newAttr}" class="newattr" tabindex="0"></span>&gt;</span>
+    <span id="template-element" save="${elt}" class="editor"><span>&lt;</span><span save="${tag}" class="tagname theme-fg-color3"></span><span save="${attrList}"></span><span save="${newAttr}" class="newattr" tabindex="0"></span>&gt;</span>
 
-    <span id="template-attribute" save="${attr}" data-attr="${attrName}" class="attreditor" style="display:none"> <span class="editable" save="${inner}" tabindex="0"><span save="${name}" class="attrname devtools-theme-attrname"></span>=&quot;<span save="${val}" class="attrvalue devtools-theme-attrvalue"></span>&quot;</span></span>
+    <span id="template-attribute" save="${attr}" data-attr="${attrName}" class="attreditor" style="display:none"> <span class="editable" save="${inner}" tabindex="0"><span save="${name}" class="attrname theme-fg-color2"></span>=&quot;<span save="${val}" class="attrvalue theme-fg-color6"></span>&quot;</span></span>
 
     <span id="template-text" save="${elt}" class="editor text">
       <pre save="${value}" style="display:inline-block;" tabindex="0"></pre>
     </span>
 
-    <span id="template-comment" save="${elt}" class="editor comment devtools-theme-comment">
+    <span id="template-comment" save="${elt}" class="editor comment theme-comment">
       <span>&lt;!--</span><pre save="${value}" style="display:inline-block;" tabindex="0"></pre><span>--&gt;</span>
     </span>
 
     <span id="template-elementContentSummary" save="${summaryElt}" class="summary"> … </span>
 
-    <span id="template-elementClose" save="${closeElt}">&lt;/<span save="${closeTag}" class="tagname devtools-theme-tagname"></span>&gt;</span>
+    <span id="template-elementClose" save="${closeElt}">&lt;/<span save="${closeTag}" class="tagname theme-fg-color3"></span>&gt;</span>
    </div>
    <div id="previewbar" class="disabled">
      <div id="preview"/>
      <div id="viewbox"/>
    </div>
 </body>
 </html>
--- a/browser/devtools/markupview/test/browser_inspector_markup_edit.html
+++ b/browser/devtools/markupview/test/browser_inspector_markup_edit.html
@@ -34,10 +34,11 @@
       </div>
     </div>
     <div id="node22" class="unchanged"></div>
     <div id="node23"></div>
     <div id="node24"></div>
     <div id="retag-me">
       <div id="retag-me-2"></div>
     </div>
+    <div id="node25"></div>
   </body>
 </html>
--- a/browser/devtools/markupview/test/browser_inspector_markup_edit.js
+++ b/browser/devtools/markupview/test/browser_inspector_markup_edit.js
@@ -37,22 +37,37 @@ function test() {
   {
     aField.focus();
     EventUtils.sendKey("return", inspector.panelWin);
     let input = inplaceEditor(aField).input;
     input.value = aValue;
     EventUtils.sendKey("return", inspector.panelWin);
   }
 
+  /**
+   * Check that the appropriate attributes are assigned to a node.
+   *
+   * @param  {HTMLNode} aElement
+   *         The node to check.
+   * @param  {Object} aAttributes
+   *         An object containing the arguments to check.
+   *         e.g. {id="id1",class="someclass"}
+   *
+   * NOTE: When checking attribute values bare in mind that node.getAttribute()
+   *       returns attribute values provided by the HTML parser. The parser only
+   *       provides unescaped entities so &amp; will return &.
+   */
   function assertAttributes(aElement, aAttributes)
   {
     let attrs = Object.getOwnPropertyNames(aAttributes);
-    is(aElement.attributes.length, attrs.length, "Node has the correct number of attributes");
+    is(aElement.attributes.length, attrs.length,
+      "Node has the correct number of attributes");
     for (let attr of attrs) {
-      is(aElement.getAttribute(attr), aAttributes[attr], "Node has the correct " + attr + " attribute.");
+      is(aElement.getAttribute(attr), aAttributes[attr],
+        "Node has the correct " + attr + " attribute.");
     }
   }
 
   // All the mutation types we want to test.
   let edits = [
     {
       desc: "Change an attribute",
       before: function() {
@@ -71,33 +86,34 @@ function test() {
         assertAttributes(doc.querySelector("#node1"), {
           id: "node1",
           class: "changednode1"
         });
       }
     },
 
     {
-      desc: "Try change an attribute to a badly formed string",
+      desc: 'Try changing an attribute to a quote (") - this should result ' +
+            'in it being set to an empty string',
       before: function() {
         assertAttributes(doc.querySelector("#node22"), {
           id: "node22",
           class: "unchanged"
         });
       },
       execute: function(after) {
         let editor = markup.getContainer(doc.querySelector("#node22")).editor;
         let attr = editor.attrs["class"].querySelector(".editable");
         editField(attr, 'class="""');
         executeSoon(after);
       },
       after: function() {
         assertAttributes(doc.querySelector("#node22"), {
           id: "node22",
-          class: "unchanged"
+          class: ""
         });
       }
     },
 
     {
       desc: "Remove an attribute",
       before: function() {
         assertAttributes(doc.querySelector("#node4"), {
@@ -136,31 +152,35 @@ function test() {
           id: "node14",
           class: "newclass",
           style: "color:green"
         });
       }
     },
 
     {
-      desc: "Try add a badly formed attribute by clicking the empty space after a node",
+      desc: 'Try add an attribute containing a quote (") attribute by ' +
+            'clicking the empty space after a node - this should result ' +
+            'in it being set to an empty string',
       before: function() {
         assertAttributes(doc.querySelector("#node23"), {
           id: "node23",
         });
       },
       execute: function(after) {
         let editor = markup.getContainer(doc.querySelector("#node23")).editor;
         let attr = editor.newAttr;
         editField(attr, 'class="newclass" style="""');
         executeSoon(after);
       },
       after: function() {
         assertAttributes(doc.querySelector("#node23"), {
           id: "node23",
+          class: "newclass",
+          style: ""
         });
       }
     },
 
     {
       desc: "Try add attributes by adding to an existing attribute's entry",
       before: function() {
         assertAttributes(doc.querySelector("#node24"), {
@@ -171,16 +191,17 @@ function test() {
         let editor = markup.getContainer(doc.querySelector("#node24")).editor;
         let attr = editor.attrs["id"].querySelector(".editable");
         editField(attr, attr.textContent + ' class="""');
         executeSoon(after);
       },
       after: function() {
         assertAttributes(doc.querySelector("#node24"), {
           id: "node24",
+          class: ""
         });
       }
     },
 
     {
       desc: "Edit text",
       before: function() {
         let node = doc.querySelector('.node6').firstChild;
@@ -193,16 +214,37 @@ function test() {
         let field = editor.elt.querySelector("pre");
         editField(field, "New text");
       },
       after: function() {
         let node = doc.querySelector('.node6').firstChild;
         is(node.nodeValue, "New text", "Text should be changed.");
       },
     },
+
+    {
+      desc: "Add an attribute value containing < > &uuml; \" & '",
+      before: function() {
+        assertAttributes(doc.querySelector("#node25"), {
+          id: "node25",
+        });
+      },
+      execute: function(after) {
+        inspector.once("markupmutation", after);
+        let editor = markup.getContainer(doc.querySelector("#node25")).editor;
+        let attr = editor.newAttr;
+        editField(attr, 'src="somefile.html?param1=<a>&param2=&uuml;"bl\'ah"');
+      },
+      after: function() {
+        assertAttributes(doc.querySelector("#node25"), {
+          id: "node25",
+          src: "somefile.html?param1=&lt;a&gt;&param2=&uuml;&quot;bl&apos;ah"
+        });
+      }
+    },
   ];
 
   // Create the helper tab for parsing...
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.selectedBrowser.addEventListener("load", function onload() {
     gBrowser.selectedBrowser.removeEventListener("load", onload, true);
     doc = content.document;
     waitForFocus(setupTest, content);
--- a/browser/devtools/responsivedesign/test/browser_responsiveuiaddcustompreset.js
+++ b/browser/devtools/responsivedesign/test/browser_responsiveuiaddcustompreset.js
@@ -1,13 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 function test() {
   let instance, deletedPresetA, deletedPresetB, oldPrompt;
+  let mgr = ResponsiveUI.ResponsiveUIManager;
 
   waitForExplicitFinish();
 
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.selectedBrowser.addEventListener("load", function onload() {
     gBrowser.selectedBrowser.removeEventListener("load", onload, true);
     waitForFocus(startTest, content);
   }, true);
@@ -22,18 +23,18 @@ function test() {
       returnBool: true,
       prompt: function(aParent, aDialogTitle, aText, aValue, aCheckMsg, aCheckState) {
         aValue.value = this.value;
         return this.returnBool;
       }
     };
 
     document.getElementById("Tools:ResponsiveUI").removeAttribute("disabled");
+    mgr.once("on", onUIOpen);
     synthesizeKeyFromKeyTag("key_responsiveUI");
-    executeSoon(onUIOpen);
   }
 
   function onUIOpen() {
     // Is it open?
     let container = gBrowser.getBrowserContainer();
     is(container.getAttribute("responsivemode"), "true", "In responsive mode.");
 
     instance = gBrowser.selectedTab.__responsiveUI;
@@ -61,29 +62,41 @@ function test() {
 
     // Adds the custom preset with "Testing preset"
     Services.prompt.value = "Testing preset";
     Services.prompt.returnBool = true;
     instance.addbutton.doCommand();
 
     instance.menulist.selectedIndex = 1;
 
-    EventUtils.synthesizeKey("VK_ESCAPE", {});
-    executeSoon(restart);
+    mgr.once("off", restart);
+
+    // We're still in the loop of initializing the responsive mode.
+    // Let's wait next loop to stop it.
+    executeSoon(function() {
+      EventUtils.synthesizeKey("VK_ESCAPE", {});
+    });
   }
 
   function restart() {
-    synthesizeKeyFromKeyTag("key_responsiveUI");
+    info("Restarting Responsive Mode");
+    mgr.once("on", function() {
+      let container = gBrowser.getBrowserContainer();
+      is(container.getAttribute("responsivemode"), "true", "In responsive mode.");
+
+      instance = gBrowser.selectedTab.__responsiveUI;
 
-    let container = gBrowser.getBrowserContainer();
-    is(container.getAttribute("responsivemode"), "true", "In responsive mode.");
+      testCustomPresetInList();
+    });
 
-    instance = gBrowser.selectedTab.__responsiveUI;
-
-    testCustomPresetInList();
+    // We're still in the loop of destroying the responsive mode.
+    // Let's wait next loop to start it.
+    executeSoon(function() {
+      synthesizeKeyFromKeyTag("key_responsiveUI");
+    });
   }
 
   function testCustomPresetInList() {
     let customPresetIndex = getPresetIndex("456x123 (Testing preset)");
     ok(customPresetIndex >= 0, "is the previously added preset (idx = " + customPresetIndex + ") in the list of items");
 
     instance.menulist.selectedIndex = customPresetIndex;
 
new file mode 100644
--- /dev/null
+++ b/browser/devtools/shared/theme-switching.js
@@ -0,0 +1,17 @@
+/* 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/. */
+
+(function() {
+  const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+  Cu.import("resource://gre/modules/Services.jsm");
+  let theme = Services.prefs.getCharPref("devtools.theme");
+  let theme_url = Services.io.newURI("chrome://browser/skin/devtools/" + theme + "-theme.css", null, null);
+  let winUtils = window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
+  winUtils.loadSheet(theme_url, window.AUTHOR_SHEET);
+  if (theme == "dark") {
+    let scrollbar_url = Services.io.newURI("chrome://browser/skin/devtools/floating-scrollbars-light.css", null, null);
+    winUtils.loadSheet(scrollbar_url, window.AGENT_SHEET);
+  }
+  document.documentElement.classList.add("theme-" + theme);
+})()
--- a/browser/devtools/styleinspector/CssHtmlTree.jsm
+++ b/browser/devtools/styleinspector/CssHtmlTree.jsm
@@ -129,37 +129,32 @@ this.CssHtmlTree = function CssHtmlTree(
   this.propertyViews = [];
 
   let chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"].
     getService(Ci.nsIXULChromeRegistry);
   this.getRTLAttr = chromeReg.isLocaleRTL("global") ? "rtl" : "ltr";
 
   // Create bound methods.
   this.siFocusWindow = this.focusWindow.bind(this);
-  this.siBoundMenuUpdate = this.computedViewMenuUpdate.bind(this);
   this.siBoundCopy = this.computedViewCopy.bind(this);
-  this.siBoundCopyDeclaration = this.computedViewCopyDeclaration.bind(this);
-  this.siBoundCopyProperty = this.computedViewCopyProperty.bind(this);
-  this.siBoundCopyPropertyValue = this.computedViewCopyPropertyValue.bind(this);
 
   this.styleDocument.addEventListener("copy", this.siBoundCopy);
   this.styleDocument.addEventListener("mousedown", this.siFocusWindow);
 
   // Nodes used in templating
   this.root = this.styleDocument.getElementById("root");
   this.templateRoot = this.styleDocument.getElementById("templateRoot");
   this.propertyContainer = this.styleDocument.getElementById("propertyContainer");
 
   // No results text.
   this.noResults = this.styleDocument.getElementById("noResults");
 
   // The element that we're inspecting, and the document that it comes from.
   this.viewedElement = null;
   this.createStyleViews();
-  this.createContextMenu();
 }
 
 /**
  * Memoized lookup of a l10n string from a string bundle.
  * @param {string} aName The key to lookup.
  * @returns A localized version of the given key.
  */
 CssHtmlTree.l10n = function CssHtmlTree_l10n(aName)
@@ -201,20 +196,16 @@ CssHtmlTree.processTemplate = function C
   while (duplicated.firstChild) {
     aDestination.appendChild(duplicated.firstChild);
   }
 };
 
 XPCOMUtils.defineLazyGetter(CssHtmlTree, "_strings", function() Services.strings
         .createBundle("chrome://browser/locale/devtools/styleinspector.properties"));
 
-XPCOMUtils.defineLazyGetter(CssHtmlTree, "HELP_LINK_TITLE", function() {
-  return CssHtmlTree.HELP_LINK_TITLE = CssHtmlTree.l10n("helpLinkTitle");
-});
-
 XPCOMUtils.defineLazyGetter(this, "clipboardHelper", function() {
   return Cc["@mozilla.org/widget/clipboardhelper;1"].
     getService(Ci.nsIClipboardHelper);
 });
 
 CssHtmlTree.prototype = {
   // Cache the list of properties that match the selected element.
   _matchedProperties: null,
@@ -434,107 +425,16 @@ CssHtmlTree.prototype = {
     if (!this._matchedProperties) {
       this._matchedProperties =
         this.cssLogic.hasMatchedSelectors(CssHtmlTree.propertyNames);
     }
     return this._matchedProperties;
   },
 
   /**
-   * Create a context menu.
-   */
-  createContextMenu: function SI_createContextMenu()
-  {
-    let iframe = this.styleInspector.outerIFrame;
-    let outerDoc = iframe.ownerDocument;
-
-    let popupSet = outerDoc.getElementById("inspectorPopupSet");
-
-    let menu = outerDoc.createElement("menupopup");
-    menu.addEventListener("popupshowing", this.siBoundMenuUpdate);
-    menu.id = "computed-view-context-menu";
-    popupSet.appendChild(menu);
-
-    // Copy selection
-    let label = CssHtmlTree.l10n("style.contextmenu.copyselection");
-    let accessKey = CssHtmlTree.l10n("style.contextmenu.copyselection.accesskey");
-    let item = outerDoc.createElement("menuitem");
-    item.id = "computed-view-copy";
-    item.setAttribute("label", label);
-    item.setAttribute("accesskey", accessKey);
-    item.addEventListener("command", this.siBoundCopy);
-    menu.appendChild(item);
-
-    // Copy declaration
-    label = CssHtmlTree.l10n("style.contextmenu.copydeclaration");
-    accessKey = CssHtmlTree.l10n("style.contextmenu.copydeclaration.accesskey");
-    item = outerDoc.createElement("menuitem");
-    item.id = "computed-view-copy-declaration";
-    item.setAttribute("label", label);
-    item.setAttribute("accesskey", accessKey);
-    item.addEventListener("command", this.siBoundCopyDeclaration);
-    menu.appendChild(item);
-
-    // Copy property name
-    label = CssHtmlTree.l10n("style.contextmenu.copyproperty");
-    accessKey = CssHtmlTree.l10n("style.contextmenu.copyproperty.accesskey");
-    item = outerDoc.createElement("menuitem");
-    item.id = "computed-view-copy-property";
-    item.setAttribute("label", label);
-    item.setAttribute("accesskey", accessKey);
-    item.addEventListener("command", this.siBoundCopyProperty);
-    menu.appendChild(item);
-
-    // Copy property value
-    label = CssHtmlTree.l10n("style.contextmenu.copypropertyvalue");
-    accessKey = CssHtmlTree.l10n("style.contextmenu.copypropertyvalue.accesskey");
-    item = outerDoc.createElement("menuitem");
-    item.id = "computed-view-copy-property-value";
-    item.setAttribute("label", label);
-    item.setAttribute("accesskey", accessKey);
-    item.addEventListener("command", this.siBoundCopyPropertyValue);
-    menu.appendChild(item);
-
-    iframe.setAttribute("context", menu.id);
-  },
-
-  /**
-   * Update the context menu by disabling irrelevant menuitems and enabling
-   * relevant ones.
-   */
-  computedViewMenuUpdate: function si_computedViewMenuUpdate()
-  {
-    let disable = this.styleWindow.getSelection().isCollapsed;
-
-    let outerDoc = this.styleInspector.outerIFrame.ownerDocument;
-    let menuitem = outerDoc.querySelector("#computed-view-copy");
-    menuitem.disabled = disable;
-
-    let node = outerDoc.popupNode;
-    if (!node) {
-      return;
-    }
-
-    if (!node.classList.contains("property-view")) {
-      while (node = node.parentElement) {
-        if (node.classList.contains("property-view")) {
-          break;
-        }
-      }
-    }
-    let disablePropertyItems = !node;
-    menuitem = outerDoc.querySelector("#computed-view-copy-declaration");
-    menuitem.disabled = disablePropertyItems;
-    menuitem = outerDoc.querySelector("#computed-view-copy-property");
-    menuitem.disabled = disablePropertyItems;
-    menuitem = outerDoc.querySelector("#computed-view-copy-property-value");
-    menuitem.disabled = disablePropertyItems;
-  },
-
-  /**
    * Focus the window on mousedown.
    *
    * @param aEvent The event object
    */
   focusWindow: function si_focusWindow(aEvent)
   {
     let win = this.styleDocument.defaultView;
     win.focus();
@@ -547,109 +447,28 @@ CssHtmlTree.prototype = {
    */
   computedViewCopy: function si_computedViewCopy(aEvent)
   {
     let win = this.styleDocument.defaultView;
     let text = win.getSelection().toString();
 
     // Tidy up block headings by moving CSS property names and their values onto
     // the same line and inserting a colon between them.
-    text = text.replace(/\t(.+)\t\t(.+)/g, "$1: $2");
+    text = text.replace(/(.+)\r\n(.+)/g, "$1: $2;");
+    text = text.replace(/(.+)\n(.+)/g, "$1: $2;");
 
-    // Remove any MDN link titles
-    text = text.replace(CssHtmlTree.HELP_LINK_TITLE, "");
     let outerDoc = this.styleInspector.outerIFrame.ownerDocument;
     clipboardHelper.copyString(text, outerDoc);
 
     if (aEvent) {
       aEvent.preventDefault();
     }
   },
 
   /**
-   * Copy declaration.
-   *
-   * @param aEvent The event object
-   */
-  computedViewCopyDeclaration: function si_computedViewCopyDeclaration(aEvent)
-  {
-    let outerDoc = this.styleInspector.outerIFrame.ownerDocument;
-    let node = outerDoc.popupNode;
-    if (!node) {
-      return;
-    }
-
-    if (!node.classList.contains("property-view")) {
-      while (node = node.parentElement) {
-        if (node.classList.contains("property-view")) {
-          break;
-        }
-      }
-    }
-    if (node) {
-      let name = node.querySelector(".property-name").textContent;
-      let value = node.querySelector(".property-value").textContent;
-
-      clipboardHelper.copyString(name + ": " + value + ";", outerDoc);
-    }
-  },
-
-  /**
-   * Copy property name.
-   *
-   * @param aEvent The event object
-   */
-  computedViewCopyProperty: function si_computedViewCopyProperty(aEvent)
-  {
-    let outerDoc = this.styleInspector.outerIFrame.ownerDocument;
-    let node = outerDoc.popupNode;
-    if (!node) {
-      return;
-    }
-
-    if (!node.classList.contains("property-view")) {
-      while (node = node.parentElement) {
-        if (node.classList.contains("property-view")) {
-          break;
-        }
-      }
-    }
-    if (node) {
-      node = node.querySelector(".property-name");
-      clipboardHelper.copyString(node.textContent, outerDoc);
-    }
-  },
-
-  /**
-   * Copy property value.
-   *
-   * @param aEvent The event object
-   */
-  computedViewCopyPropertyValue: function si_computedViewCopyPropertyValue(aEvent)
-  {
-    let outerDoc = this.styleInspector.outerIFrame.ownerDocument;
-    let node = outerDoc.popupNode;
-    if (!node) {
-      return;
-    }
-
-    if (!node.classList.contains("property-view")) {
-      while (node = node.parentElement) {
-        if (node.classList.contains("property-view")) {
-          break;
-        }
-      }
-    }
-    if (node) {
-      node = node.querySelector(".property-value");
-      clipboardHelper.copyString(node.textContent, outerDoc);
-    }
-  },
-
-  /**
    * Destructor for CssHtmlTree.
    */
   destroy: function CssHtmlTree_destroy()
   {
     delete this.viewedElement;
 
     // Remove event listeners
     this.includeBrowserStylesCheckbox.removeEventListener("command",
@@ -803,100 +622,84 @@ PropertyView.prototype = {
    *
    * @return string
    */
   get propertyHeaderClassName()
   {
     if (this.visible) {
       this.tree._darkStripe = !this.tree._darkStripe;
       let darkValue = this.tree._darkStripe ?
-                      "property-view darkrow" : "property-view";
+                      "property-view theme-bg-darker" : "property-view";
       return darkValue;
     }
     return "property-view-hidden";
   },
 
   /**
    * Returns the className that should be assigned to the propertyView content
    * container.
    * @return string
    */
   get propertyContentClassName()
   {
     if (this.visible) {
       let darkValue = this.tree._darkStripe ?
-                      "property-content darkrow" : "property-content";
+                      "property-content theme-bg-darker" : "property-content";
       return darkValue;
     }
     return "property-content-hidden";
   },
 
   buildMain: function PropertyView_buildMain()
   {
     let doc = this.tree.styleDocument;
-    this.element = doc.createElementNS(HTML_NS, "tr");
+    this.element = doc.createElementNS(HTML_NS, "div");
     this.element.setAttribute("class", this.propertyHeaderClassName);
 
-    this.expanderContainer = doc.createElementNS(HTML_NS, "td");
-    this.element.appendChild(this.expanderContainer);
-    this.expanderContainer.setAttribute("class", "expander-container");
-
     this.matchedExpander = doc.createElementNS(HTML_NS, "div");
-    this.matchedExpander.setAttribute("class", "match expander");
+    this.matchedExpander.className = "expander theme-twisty";
     this.matchedExpander.setAttribute("tabindex", "0");
     this.matchedExpander.addEventListener("click",
       this.matchedExpanderClick.bind(this), false);
     this.matchedExpander.addEventListener("keydown", function(aEvent) {
       let keyEvent = Ci.nsIDOMKeyEvent;
       if (aEvent.keyCode == keyEvent.DOM_VK_F1) {
         this.mdnLinkClick();
       }
       if (aEvent.keyCode == keyEvent.DOM_VK_RETURN ||
         aEvent.keyCode == keyEvent.DOM_VK_SPACE) {
         this.matchedExpanderClick(aEvent);
       }
     }.bind(this), false);
-    this.expanderContainer.appendChild(this.matchedExpander);
+    this.element.appendChild(this.matchedExpander);
 
-    this.nameNode = doc.createElementNS(HTML_NS, "td");
+    this.nameNode = doc.createElementNS(HTML_NS, "div");
     this.element.appendChild(this.nameNode);
-    this.nameNode.setAttribute("class", "property-name");
-    this.nameNode.textContent = this.name;
+    this.nameNode.setAttribute("class", "property-name theme-fg-color5");
+    this.nameNode.textContent = this.nameNode.title = this.name;
     this.nameNode.addEventListener("click", function(aEvent) {
       this.matchedExpander.focus();
     }.bind(this), false);
 
-    let helpcontainer = doc.createElementNS(HTML_NS, "td");
-    this.element.appendChild(helpcontainer);
-    helpcontainer.setAttribute("class", "helplink-container");
-
-    let helplink = doc.createElementNS(HTML_NS, "a");
-    helpcontainer.appendChild(helplink);
-    helplink.setAttribute("class", "helplink");
-    helplink.setAttribute("title", CssHtmlTree.HELP_LINK_TITLE);
-    helplink.textContent = CssHtmlTree.HELP_LINK_TITLE;
-    helplink.addEventListener("click", this.mdnLinkClick.bind(this), false);
-
-    this.valueNode = doc.createElementNS(HTML_NS, "td");
+    this.valueNode = doc.createElementNS(HTML_NS, "div");
     this.element.appendChild(this.valueNode);
-    this.valueNode.setAttribute("class", "property-value");
+    this.valueNode.setAttribute("class", "property-value theme-fg-color1");
     this.valueNode.setAttribute("dir", "ltr");
-    this.valueNode.textContent = this.value;
+    this.valueNode.textContent = this.valueNode.title = this.value;
 
     return this.element;
   },
 
   buildSelectorContainer: function PropertyView_buildSelectorContainer()
   {
     let doc = this.tree.styleDocument;
-    let element = doc.createElementNS(HTML_NS, "tr");
+    let element = doc.createElementNS(HTML_NS, "div");
     element.setAttribute("class", this.propertyContentClassName);
-    this.matchedSelectorsContainer = doc.createElementNS(HTML_NS, "td");
-    this.matchedSelectorsContainer.setAttribute("colspan", "0");
-    this.matchedSelectorsContainer.setAttribute("class", "rulelink");
+    this.matchedSelectorsContainer = doc.createElementNS(HTML_NS, "div");
+    this.matchedSelectorsContainer.setAttribute("class", "matchedselectors");
     element.appendChild(this.matchedSelectorsContainer);
 
     return element;
   },
 
   /**
    * Refresh the panel's CSS property value.
    */
@@ -906,25 +709,25 @@ PropertyView.prototype = {
     this.element.nextElementSibling.className = this.propertyContentClassName;
 
     if (this.prevViewedElement != this.tree.viewedElement) {
       this._matchedSelectorViews = null;
       this.prevViewedElement = this.tree.viewedElement;
     }
 
     if (!this.tree.viewedElement || !this.visible) {
-      this.valueNode.textContent = "";
+      this.valueNode.textContent = this.valueNode.title = "";
       this.matchedSelectorsContainer.parentNode.hidden = true;
       this.matchedSelectorsContainer.textContent = "";
       this.matchedExpander.removeAttribute("open");
       return;
     }
 
     this.tree.numVisibleProperties++;
-    this.valueNode.textContent = this.propertyInfo.value;
+    this.valueNode.textContent = this.valueNode.title = this.propertyInfo.value;
     this.refreshMatchedSelectors();
   },
 
   /**
    * Refresh the panel matched rules.
    */
   refreshMatchedSelectors: function PropertyView_refreshMatchedSelectors()
   {
@@ -1051,31 +854,22 @@ SelectorView.prototype = {
     return SelectorView.STATUS_NAMES[this.selectorInfo.status];
   },
 
   /**
    * Get class name for selector depending on status
    */
   get statusClass()
   {
-    return SelectorView.CLASS_NAMES[this.selectorInfo.status];
+    return SelectorView.CLASS_NAMES[this.selectorInfo.status - 1];
   },
 
   /**
    * A localized Get localized human readable info
    */
-  humanReadableText: function SelectorView_humanReadableText(aElement)
-  {
-    if (this.tree.getRTLAttr == "rtl") {
-      return this.selectorInfo.value + " \u2190 " + this.text(aElement);
-    } else {
-      return this.text(aElement) + " \u2192 " + this.selectorInfo.value;
-    }
-  },
-
   text: function SelectorView_text(aElement) {
     let result = this.selectorInfo.selector.text;
     if (this.selectorInfo.elementStyle) {
       let source = this.selectorInfo.sourceElement;
       let inspector = this.tree.styleInspector.inspector;
 
       if (inspector.selection.node == source) {
         result = "this";
--- a/browser/devtools/styleinspector/CssRuleView.jsm
+++ b/browser/devtools/styleinspector/CssRuleView.jsm
@@ -858,31 +858,31 @@ TextProperty.prototype = {
  * property will be available with the user interface.
  *
  * @param {Document} aDoc
  *        The document that will contain the rule view.
  * @param {object} aStore
  *        The CSS rule view can use this object to store metadata
  *        that might outlast the rule view, particularly the current
  *        set of disabled properties.
+ * @param {<iframe>} aOuterIFrame
+ *        The iframe containing the ruleview.
  * @constructor
  */
 this.CssRuleView = function CssRuleView(aDoc, aStore)
 {
   this.doc = aDoc;
   this.store = aStore;
-  this.element = this.doc.createElementNS(XUL_NS, "vbox");
-  this.element.setAttribute("tabindex", "0");
+  this.element = this.doc.createElementNS(HTML_NS, "div");
   this.element.className = "ruleview devtools-monospace";
   this.element.flex = 1;
 
   this._boundCopy = this._onCopy.bind(this);
   this.element.addEventListener("copy", this._boundCopy);
 
-  this._createContextMenu();
   this._showEmpty();
 }
 
 CssRuleView.prototype = {
   // The element that we're inspecting.
   _viewedElement: null,
 
   /**
@@ -892,35 +892,18 @@ CssRuleView.prototype = {
     return this.element.querySelectorAll(".styleinspector-propertyeditor").length > 0;
   },
 
   destroy: function CssRuleView_destroy()
   {
     this.clear();
 
     this.element.removeEventListener("copy", this._boundCopy);
-    this._copyItem.removeEventListener("command", this._boundCopy);
     delete this._boundCopy;
 
-    this._ruleItem.removeEventListener("command", this._boundCopyRule);
-    delete this._boundCopyRule;
-
-    this._declarationItem.removeEventListener("command", this._boundCopyDeclaration);
-    delete this._boundCopyDeclaration;
-
-    this._propertyItem.removeEventListener("command", this._boundCopyProperty);
-    delete this._boundCopyProperty;
-
-    this._propertyValueItem.removeEventListener("command", this._boundCopyPropertyValue);
-    delete this._boundCopyPropertyValue;
-
-    this._contextMenu.removeEventListener("popupshowing", this._boundMenuUpdate);
-    delete this._boundMenuUpdate;
-    delete this._contextMenu;
-
     if (this.element.parentNode) {
       this.element.parentNode.removeChild(this.element);
     }
   },
 
   /**
    * Update the highlighted element.
    *
@@ -1031,137 +1014,40 @@ CssRuleView.prototype = {
     // Run through the current list of rules, attaching
     // their editors in order.  Create editors if needed.
     let lastInheritedSource = "";
     for each (let rule in this._elementStyle.rules) {
 
       let inheritedSource = rule.inheritedSource;
       if (inheritedSource != lastInheritedSource) {
         let h2 = this.doc.createElementNS(HTML_NS, "div");
-        h2.className = "ruleview-rule-inheritance";
+        h2.className = "ruleview-rule-inheritance theme-gutter";
         h2.textContent = inheritedSource;
         lastInheritedSource = inheritedSource;
         this.element.appendChild(h2);
       }
 
       if (!rule.editor) {
         new RuleEditor(this, rule);
       }
 
       this.element.appendChild(rule.editor.element);
     }
   },
 
   /**
-   * Add a context menu to the rule view.
-   */
-  _createContextMenu: function CssRuleView_createContextMenu()
-  {
-    let popupSet = this.doc.createElement("popupset");
-    this.doc.documentElement.appendChild(popupSet);
-
-    let menu = this.doc.createElement("menupopup");
-    menu.id = "rule-view-context-menu";
-
-    this._boundMenuUpdate = this._onMenuUpdate.bind(this);
-    menu.addEventListener("popupshowing", this._boundMenuUpdate);
-
-    // Copy selection
-    this._copyItem = createMenuItem(menu, {
-      label: "rule.contextmenu.copyselection",
-      accesskey: "rule.contextmenu.copyselection.accesskey",
-      command: this._boundCopy
-    });
-
-    // Copy rule
-    this._boundCopyRule = this._onCopyRule.bind(this);
-    this._ruleItem = createMenuItem(menu, {
-      label: "rule.contextmenu.copyrule",
-      accesskey: "rule.contextmenu.copyrule.accesskey",
-      command: this._boundCopyRule
-    });
-
-    // Copy declaration
-    this._boundCopyDeclaration = this._onCopyDeclaration.bind(this);
-    this._declarationItem = createMenuItem(menu, {
-      label: "rule.contextmenu.copydeclaration",
-      accesskey: "rule.contextmenu.copydeclaration.accesskey",
-      command: this._boundCopyDeclaration
-    });
-
-    this._boundCopyProperty = this._onCopyProperty.bind(this);
-    this._propertyItem = createMenuItem(menu, {
-      label: "rule.contextmenu.copyproperty",
-      accesskey: "rule.contextmenu.copyproperty.accesskey",
-      command: this._boundCopyProperty
-    });
-
-    this._boundCopyPropertyValue = this._onCopyPropertyValue.bind(this);
-    this._propertyValueItem = createMenuItem(menu,{
-      label: "rule.contextmenu.copypropertyvalue",
-      accesskey: "rule.contextmenu.copypropertyvalue.accesskey",
-      command: this._boundCopyPropertyValue
-    });
-
-    popupSet.appendChild(menu);
-    this.element.setAttribute("context", menu.id);
-
-    this._contextMenu = menu;
-  },
-
-  /**
-   * Update the rule view's context menu by disabling irrelevant menuitems and
-   * enabling relevant ones.
-   *
-   * @param {Event} aEvent
-   *        The event object.
-   */
-  _onMenuUpdate: function CssRuleView_onMenuUpdate(aEvent)
-  {
-    let node = this.doc.popupNode;
-
-    // Copy selection.
-    let editorSelection = node.className == "styleinspector-propertyeditor" &&
-                          node.selectionEnd - node.selectionStart != 0;
-    let disable = this.doc.defaultView.getSelection().isCollapsed &&
-                  !editorSelection;
-    this._copyItem.disabled = disable;
-
-    // Copy property, copy property name & copy property value.
-    if (!node) {
-      return;
-    }
-
-    if (!node.classList.contains("ruleview-property") &&
-        !node.classList.contains("ruleview-computed")) {
-      while (node = node.parentElement) {
-        if (node.classList.contains("ruleview-property") ||
-          node.classList.contains("ruleview-computed")) {
-          break;
-        }
-      }
-    }
-    let disablePropertyItems = !node || (node &&
-      !node.classList.contains("ruleview-property") &&
-      !node.classList.contains("ruleview-computed"));
-
-    this._declarationItem.disabled = disablePropertyItems;
-    this._propertyItem.disabled = disablePropertyItems;
-    this._propertyValueItem.disabled = disablePropertyItems;
-  },
-
-  /**
    * Copy selected text from the rule view.
    *
    * @param {Event} aEvent
    *        The event object.
    */
   _onCopy: function CssRuleView_onCopy(aEvent)
   {
-    let target = this.doc.popupNode || aEvent.target;
+    let target = aEvent.target;
+
     let text;
 
     if (target.nodeName == "input") {
       let start = Math.min(target.selectionStart, target.selectionEnd);
       let end = Math.max(target.selectionStart, target.selectionEnd);
       let count = end - start;
       text = target.value.substr(start, count);
     } else {
@@ -1174,159 +1060,19 @@ CssRuleView.prototype = {
       // Remove "inline"
       let inline = _strings.GetStringFromName("rule.sourceInline");
       let rx = new RegExp("^" + inline + "\\r?\\n?", "g");
       text = text.replace(rx, "");
     }
 
     clipboardHelper.copyString(text, this.doc);
 
-    if (aEvent) {
-      aEvent.preventDefault();
-    }
-  },
-
-  /**
-   * Copy a rule from the rule view.
-   *
-   * @param {Event} aEvent
-   *        The event object.
-   */
-  _onCopyRule: function CssRuleView_onCopyRule(aEvent)
-  {
-    let terminator;
-    let node = this.doc.popupNode;
-    if (!node) {
-      return;
-    }
-
-    if (node.className != "ruleview-rule") {
-      while (node = node.parentElement) {
-        if (node.className == "ruleview-rule") {
-          break;
-        }
-      }
-    }
-    node = node.cloneNode();
-
-    let computedLists = node.querySelectorAll(".ruleview-computedlist");
-    for (let computedList of computedLists) {
-      computedList.parentNode.removeChild(computedList);
-    }
-
-    let autosizers = node.querySelectorAll(".autosizer");
-    for (let autosizer of autosizers) {
-      autosizer.parentNode.removeChild(autosizer);
-    }
-    let selector = node.querySelector(".ruleview-selector").textContent;
-    let propertyNames = node.querySelectorAll(".ruleview-propertyname");
-    let propertyValues = node.querySelectorAll(".ruleview-propertyvalue");
-
-    // Format the rule
-    if (osString == "WINNT") {
-      terminator = "\r\n";
-    } else {
-      terminator = "\n";
-    }
-
-    let out = selector + " {" + terminator;
-    for (let i = 0; i < propertyNames.length; i++) {
-      let name = propertyNames[i].textContent;
-      let value = propertyValues[i].textContent;
-      out += "    " + name + ": " + value + ";" + terminator;
-    }
-    out += "}" + terminator;
-
-    clipboardHelper.copyString(out, this.doc);
+    aEvent.preventDefault();
   },
 
-  /**
-   * Copy a declaration from the rule view.
-   *
-   * @param {Event} aEvent
-   *        The event object.
-   */
-  _onCopyDeclaration: function CssRuleView_onCopyDeclaration(aEvent)
-  {
-    let node = this.doc.popupNode;
-    if (!node) {
-      return;
-    }
-
-    if (!node.classList.contains("ruleview-property") &&
-        !node.classList.contains("ruleview-computed")) {
-      while (node = node.parentElement) {
-        if (node.classList.contains("ruleview-property") ||
-            node.classList.contains("ruleview-computed")) {
-          break;
-        }
-      }
-    }
-
-    // We need to strip expanded properties from the node because we use
-    // node.textContent below, which also gets text from hidden nodes. The
-    // simplest way to do this is to clone the node and remove them from the
-    // clone.
-    node = node.cloneNode();
-    let computedLists = node.querySelectorAll(".ruleview-computedlist");
-    for (let computedList of computedLists) {
-      computedList.parentNode.removeChild(computedList);
-    }
-
-    let propertyName = node.querySelector(".ruleview-propertyname").textContent;
-    let propertyValue = node.querySelector(".ruleview-propertyvalue").textContent;
-    let out = propertyName + ": " + propertyValue + ";";
-
-    clipboardHelper.copyString(out, this.doc);
-  },
-
-  /**
-   * Copy a property name from the rule view.
-   *
-   * @param {Event} aEvent
-   *        The event object.
-   */
-  _onCopyProperty: function CssRuleView_onCopyProperty(aEvent)
-  {
-    let node = this.doc.popupNode;
-
-    if (!node) {
-      return;
-    }
-
-    if (!node.classList.contains("ruleview-propertyname")) {
-      node = node.parentNode.parentNode.querySelector(".ruleview-propertyname");
-    }
-
-    if (node) {
-      clipboardHelper.copyString(node.textContent, this.doc);
-    }
-  },
-
- /**
-   * Copy a property value from the rule view.
-   *
-   * @param {Event} aEvent
-   *        The event object.
-   */
-  _onCopyPropertyValue: function CssRuleView_onCopyPropertyValue(aEvent)
-  {
-    let node = this.doc.popupNode;
-    if (!node) {
-      return;
-    }
-
-    if (!node.classList.contains("ruleview-propertyvalue")) {
-      node = node.parentNode.parentNode.querySelector(".ruleview-propertyvalue");
-    }
-
-    if (node) {
-      clipboardHelper.copyString(node.textContent, this.doc);
-    }
-  }
 };
 
 /**
  * Create a RuleEditor.
  *
  * @param {CssRuleView} aRuleView
  *        The CssRuleView containg the document holding this rule editor.
  * @param {Rule} aRule
@@ -1345,26 +1091,26 @@ function RuleEditor(aRuleView, aRule)
 
   this._create();
 }
 
 RuleEditor.prototype = {
   _create: function RuleEditor_create()
   {
     this.element = this.doc.createElementNS(HTML_NS, "div");
-    this.element.className = "ruleview-rule";
+    this.element.className = "ruleview-rule theme-separator";
     this.element._ruleEditor = this;
 
     // Give a relative position for the inplace editor's measurement
     // span to be placed absolutely against.
     this.element.style.position = "relative";
 
     // Add the source link.
     let source = createChild(this.element, "div", {
-      class: "ruleview-rule-source",
+      class: "ruleview-rule-source theme-link",
       textContent: this.rule.title
     });
     source.addEventListener("click", function() {
       let rule = this.rule;
       let evt = this.doc.createEvent("CustomEvent");
       evt.initCustomEvent("CssRuleViewCSSLinkClicked", true, false, {
         rule: rule,
       });
@@ -1373,17 +1119,17 @@ RuleEditor.prototype = {
 
     let code = createChild(this.element, "div", {
       class: "ruleview-code"
     });
 
     let header = createChild(code, "div", {});
 
     this.selectorText = createChild(header, "span", {
-      class: "ruleview-selector"
+      class: "ruleview-selector theme-fg-color3"
     });
 
     this.openBrace = createChild(header, "span", {
       class: "ruleview-ruleopen",
       textContent: " {"
     });
 
     code.addEventListener("click", function() {
@@ -1594,26 +1340,25 @@ TextPropertyEditor.prototype = {
    * Create the property editor's DOM.
    */
   _create: function TextPropertyEditor_create()
   {
     this.element = this.doc.createElementNS(HTML_NS, "li");
     this.element.classList.add("ruleview-property");
 
     // The enable checkbox will disable or enable the rule.
-    this.enable = createChild(this.element, "input", {
-      class: "ruleview-enableproperty",
-      type: "checkbox",
+    this.enable = createChild(this.element, "div", {
+      class: "ruleview-enableproperty theme-checkbox",
       tabindex: "-1"
     });
     this.enable.addEventListener("click", this._onEnableClicked, true);
 
     // Click to expand the computed properties of the text property.
     this.expander = createChild(this.element, "span", {
-      class: "ruleview-expander"
+      class: "ruleview-expander theme-twisty"
     });
     this.expander.addEventListener("click", this._onExpandClicked, true);
 
     this.nameContainer = createChild(this.element, "span", {
       class: "ruleview-namecontainer"
     });
     this.nameContainer.addEventListener("click", function(aEvent) {
       // Clicks within the name shouldn't propagate any further.
@@ -1621,17 +1366,17 @@ TextPropertyEditor.prototype = {
       if (aEvent.target === propertyContainer) {
         this.nameSpan.click();
       }
     }.bind(this), false);
 
     // Property name, editable when focused.  Property name
     // is committed when the editor is unfocused.
     this.nameSpan = createChild(this.nameContainer, "span", {
-      class: "ruleview-propertyname",
+      class: "ruleview-propertyname theme-fg-color5",
       tabindex: "0",
     });
 
     editableField({
       start: this._onStartEditing,
       element: this.nameSpan,
       done: this._onNameDone,
       advanceChars: ':'
@@ -1652,17 +1397,17 @@ TextPropertyEditor.prototype = {
         this.valueSpan.click();
       }
     }.bind(this), false);
 
     // Property value, editable when focused.  Changes to the
     // property value are applied as they are typed, and reverted
     // if the user presses escape.
     this.valueSpan = createChild(propertyContainer, "span", {
-      class: "ruleview-propertyvalue",
+      class: "ruleview-propertyvalue theme-fg-color1",
       tabindex: "0",
     });
 
     // Save the initial value as the last committed value,
     // for restoring after pressing escape.
     this.committed = { name: this.prop.name,
                        value: this.prop.value,
                        priority: this.prop.priority };
@@ -1763,23 +1508,23 @@ TextPropertyEditor.prototype = {
         class: "ruleview-computed"
       });
 
       if (computed.overridden) {
         li.classList.add("ruleview-overridden");
       }
 
       createChild(li, "span", {
-        class: "ruleview-propertyname",
+        class: "ruleview-propertyname theme-fg-color5",
         textContent: computed.name
       });
       appendText(li, ": ");
 
       createChild(li, "span", {
-        class: "ruleview-propertyvalue",
+        class: "ruleview-propertyvalue theme-fg-color1",
         textContent: computed.value
       });
       appendText(li, ";");
     }
 
     // Show or hide the expander as needed.
     if (showExpander) {
       this.expander.style.visibility = "visible";
@@ -1788,27 +1533,37 @@ TextPropertyEditor.prototype = {
     }
   },
 
   /**
    * Handles clicks on the disabled property.
    */
   _onEnableClicked: function TextPropertyEditor_onEnableClicked(aEvent)
   {
-    this.prop.setEnabled(this.enable.checked);
+    let checked = this.enable.hasAttribute("checked");
+    if (checked) {
+      this.enable.removeAttribute("checked");
+    } else {
+      this.enable.setAttribute("checked", "");
+    }
+    this.prop.setEnabled(!checked);
     aEvent.stopPropagation();
   },
 
   /**
    * Handles clicks on the computed property expander.
    */
   _onExpandClicked: function TextPropertyEditor_onExpandClicked(aEvent)
   {
-    this.expander.classList.toggle("styleinspector-open");
     this.computed.classList.toggle("styleinspector-open");
+    if (this.computed.classList.contains("styleinspector-open")) {
+      this.expander.setAttribute("open", "true");
+    } else {
+      this.expander.removeAttribute("open");
+    }
     aEvent.stopPropagation();
   },
 
   /**
    * Called when the property name's inplace editor is closed.
    * Ignores the change if the user pressed escape, otherwise
    * commits it.
    *
@@ -2041,15 +1796,11 @@ XPCOMUtils.defineLazyGetter(this, "clipb
     getService(Ci.nsIClipboardHelper);
 });
 
 XPCOMUtils.defineLazyGetter(this, "_strings", function() {
   return Services.strings.createBundle(
     "chrome://browser/locale/devtools/styleinspector.properties");
 });
 
-XPCOMUtils.defineLazyGetter(this, "osString", function() {
-  return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
-});
-
 XPCOMUtils.defineLazyGetter(this, "domUtils", function() {
   return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
 });
--- a/browser/devtools/styleinspector/StyleInspector.jsm
+++ b/browser/devtools/styleinspector/StyleInspector.jsm
@@ -21,17 +21,17 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 this.EXPORTED_SYMBOLS = ["RuleViewTool", "ComputedViewTool"];
 
 this.RuleViewTool = function RVT_RuleViewTool(aInspector, aWindow, aIFrame)
 {
   this.inspector = aInspector;
   this.doc = aWindow.document;
   this.outerIFrame = aIFrame;
 
-  this.view = new CssRuleView(this.doc);
+  this.view = new CssRuleView(this.doc, null);
   this.doc.documentElement.appendChild(this.view.element);
 
   this._changeHandler = function() {
     this.inspector.markDirty();
   }.bind(this);
 
   this.view.element.addEventListener("CssRuleViewChanged", this._changeHandler)
 
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/computedview.xhtml
@@ -0,0 +1,113 @@
+<?xml version="1.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/. -->
+
+<!DOCTYPE window [
+  <!ENTITY % inspectorDTD SYSTEM "chrome://browser/locale/devtools/styleinspector.dtd">
+  %inspectorDTD;
+  <!ELEMENT loop ANY>
+  <!ATTLIST li foreach CDATA #IMPLIED>
+  <!ATTLIST div foreach CDATA #IMPLIED>
+  <!ATTLIST loop foreach CDATA #IMPLIED>
+  <!ATTLIST a target CDATA #IMPLIED>
+  <!ATTLIST a __pathElement CDATA #IMPLIED>
+  <!ATTLIST div _id CDATA #IMPLIED>
+  <!ATTLIST div save CDATA #IMPLIED>
+  <!ATTLIST table save CDATA #IMPLIED>
+  <!ATTLIST loop if CDATA #IMPLIED>
+  <!ATTLIST tr if CDATA #IMPLIED>
+]>
+
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+      class="theme-body">
+
+  <head>
+
+    <title>&computedViewTitle;</title>
+
+    <link rel="stylesheet" href="chrome://global/skin/global.css"  type="text/css"/>
+    <link rel="stylesheet" href="chrome://browser/skin/devtools/common.css"  type="text/css"/>
+    <link rel="stylesheet" href="chrome://browser/skin/devtools/computedview.css"  type="text/css"/>
+
+    <script type="application/javascript;version=1.8" src="theme-switching.js"/>
+
+    <script type="application/javascript;version=1.8">
+      window.setPanel = function(panel, iframe) {
+        Components.utils.import("resource:///modules/devtools/StyleInspector.jsm");
+        this.computedview = new ComputedViewTool(panel, window, iframe);
+      }
+      window.onunload = function() {
+        if (this.computedview) {
+          this.computedview.destroy();
+        }
+      }
+    </script>
+  </head>
+
+  <body>
+
+    <!-- The output from #templateProperty (below) is appended here. -->
+    <div id="propertyContainer" class="devtools-monospace">
+    </div>
+
+    <!-- When no properties are found the following block is displayed. -->
+    <div id="noResults" hidden="">
+      &noPropertiesFound;
+    </div>
+
+    <!-- The output from #templateRoot (below) is inserted here. -->
+    <div id="root" class="devtools-monospace"></div>
+
+    <!--
+    To visually debug the templates without running firefox, alter the display:none
+    -->
+    <div style="display:none;">
+      <!--
+      templateRoot sits at the top of the window and contains the "include default
+      styles" checkbox. For data it needs an instance of CssHtmlTree.
+      -->
+      <div id="templateRoot">
+        <xul:hbox class="devtools-toolbar" flex="1" align="center">
+          <xul:checkbox class="includebrowserstyles"
+                        save="${includeBrowserStylesCheckbox}"
+                        oncommand="${includeBrowserStylesChanged}" checked="false"
+                        label="&browserStylesLabel;"/>
+          <xul:textbox class="devtools-searchinput" type="search" save="${searchField}"
+                      placeholder="&userStylesSearch;" flex="1"
+                      oncommand="${filterChanged}"/>
+        </xul:hbox>
+      </div>
+
+
+      <!--
+      A templateMatchedSelectors sits inside each templateProperties showing the
+      list of selectors that affect that property. Each needs data like this:
+      {
+        matchedSelectorViews: ..., // from cssHtmlTree.propertyViews[name].matchedSelectorViews
+      }
+      This is a template so the parent does not need to be a table, except that
+      using a div as the parent causes the DOM to muck with the tr elements
+      -->
+      <div id="templateMatchedSelectors">
+        <loop foreach="selector in ${matchedSelectorViews}">
+          <p>
+            <span class="rule-link">
+              <a target="_blank" class="link theme-link"
+                  onclick="${selector.openStyleEditor}"
+                  onkeydown="${selector.maybeOpenStyleEditor}"
+                  title="${selector.selectorInfo.href}"
+                  tabindex="0">${selector.selectorInfo.source}</a>
+            </span>
+            <span dir="ltr" class="rule-text ${selector.statusClass} theme-fg-color3" title="${selector.statusText}">
+              ${selector.text(__element)}
+              <span class="other-property-value theme-fg-color1">${selector.selectorInfo.value}</span>
+            </span>
+          </p>
+        </loop>
+      </div>
+    </div>
+
+  </body>
+</html>
deleted file mode 100644
--- a/browser/devtools/styleinspector/csshtmltree.xul
+++ /dev/null
@@ -1,110 +0,0 @@
-<?xml version="1.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/. -->
-
-<?xml-stylesheet href="chrome://global/skin/global.css"?>
-<?xml-stylesheet href="chrome://browser/content/devtools/styleinspector.css" type="text/css"?>
-<?xml-stylesheet href="chrome://browser/skin/devtools/csshtmltree.css" type="text/css"?>
-<?xml-stylesheet href="chrome://browser/skin/devtools/common.css" type="text/css"?>
-
-<!DOCTYPE window [
-  <!ENTITY % inspectorDTD SYSTEM "chrome://browser/locale/devtools/styleinspector.dtd">
-  %inspectorDTD;
-  <!ELEMENT loop ANY>
-  <!ATTLIST li foreach CDATA #IMPLIED>
-  <!ATTLIST div foreach CDATA #IMPLIED>
-  <!ATTLIST loop foreach CDATA #IMPLIED>
-  <!ATTLIST a target CDATA #IMPLIED>
-  <!ATTLIST a __pathElement CDATA #IMPLIED>
-  <!ATTLIST div _id CDATA #IMPLIED>
-  <!ATTLIST div save CDATA #IMPLIED>
-  <!ATTLIST table save CDATA #IMPLIED>
-  <!ATTLIST loop if CDATA #IMPLIED>
-  <!ATTLIST tr if CDATA #IMPLIED>
-]>
-
-<xul:window xmlns="http://www.w3.org/1999/xhtml"
-            xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
-            title="&computedViewTitle;">
-
-<script type="application/javascript;version=1.8">
-  window.setPanel = function(panel, iframe) {
-    Components.utils.import("resource:///modules/devtools/StyleInspector.jsm");
-    this.computedview = new ComputedViewTool(panel, window, iframe);
-  }
-  window.onunload = function() {
-    if (this.computedview) {
-      this.computedview.destroy();
-    }
-  }
-</script>
-
-<!-- The output from #templateRoot (below) is inserted here. -->
-<div id="root" class="devtools-monospace"></div>
-
-<!-- When no properties are found the following block is displayed. -->
-<div id="noResults" hidden="">
-  &noPropertiesFound;
-</div>
-
-<!-- The output from #templateProperty (below) is appended here. -->
-<table id="propertyContainer" class="devtools-monospace">
-</table>
-
-<xul:hbox id="footer">
-  <xul:label class="legendKey bestmatch">&bestMatch;</xul:label>
-  <xul:label class="legendKey matched">&matched;</xul:label>
-  <xul:label class="legendKey parentmatch">&parentMatch;</xul:label>
-</xul:hbox>
-<!--
-To visually debug the templates without running firefox, alter the display:none
--->
-<div style="display:none;">
-  <!--
-  templateRoot sits at the top of the window and contains the "include default
-  styles" checkbox. For data it needs an instance of CssHtmlTree.
-  -->
-  <div id="templateRoot">
-    <xul:hbox class="headerControls" flex="1" align="center">
-      <xul:checkbox class="includebrowserstyles"
-                    save="${includeBrowserStylesCheckbox}"
-                    oncommand="${includeBrowserStylesChanged}" checked="false"
-                    label="&browserStylesLabel;"/>
-      <xul:textbox class="searchfield" type="search" save="${searchField}"
-                   placeholder="&userStylesSearch;" flex="1"
-                   oncommand="${filterChanged}"/>
-    </xul:hbox>
-  </div>
-
-
-  <!--
-  A templateMatchedSelectors sits inside each templateProperties showing the
-  list of selectors that affect that property. Each needs data like this:
-  {
-    matchedSelectorViews: ..., // from cssHtmlTree.propertyViews[name].matchedSelectorViews
-  }
-  This is a template so the parent does not need to be a table, except that
-  using a div as the parent causes the DOM to muck with the tr elements
-  -->
-  <div id="templateMatchedSelectors">
-    <table>
-      <loop foreach="selector in ${matchedSelectorViews}">
-        <tr>
-          <td dir="ltr" class="rule-text ${selector.statusClass}">
-            ${selector.humanReadableText(__element)}
-          </td>
-          <td class="rule-link">
-            <a target="_blank" class="link"
-               onclick="${selector.openStyleEditor}"
-               onkeydown="${selector.maybeOpenStyleEditor}"
-               title="${selector.selectorInfo.href}"
-               tabindex="0">${selector.selectorInfo.source}</a>
-          </td>
-        </tr>
-      </loop>
-    </table>
-  </div>
-</div>
-
-</xul:window>
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/cssruleview.xhtml
@@ -0,0 +1,37 @@
+<?xml version="1.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/. -->
+
+<!DOCTYPE window [
+  <!ENTITY % inspectorDTD SYSTEM "chrome://browser/locale/devtools/styleinspector.dtd">
+  %inspectorDTD;
+]>
+
+
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+      class="theme-body">
+
+  <head>
+    <title>&ruleViewTitle;</title>
+    <link rel="stylesheet" href="chrome://global/skin/global.css"  type="text/css"/>
+    <link rel="stylesheet" href="chrome://browser/skin/devtools/common.css"  type="text/css"/>
+    <link rel="stylesheet" href="chrome://browser/content/devtools/ruleview.css"  type="text/css"/>
+    <link rel="stylesheet" href="chrome://browser/skin/devtools/ruleview.css"  type="text/css"/>
+
+    <script type="application/javascript;version=1.8" src="theme-switching.js"/>
+
+    <script type="application/javascript;version=1.8">
+      window.setPanel = function(panel, iframe) {
+        Components.utils.import("resource:///modules/devtools/StyleInspector.jsm");
+        this.ruleview = new RuleViewTool(panel, window, iframe);
+      }
+      window.onunload = function() {
+        if (this.ruleview) {
+          this.ruleview.destroy();
+        }
+      }
+    </script>
+  </head>
+</html>
deleted file mode 100644
--- a/browser/devtools/styleinspector/cssruleview.xul
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.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/. -->
-<?xml-stylesheet href="chrome://global/skin/global.css"?>
-<?xml-stylesheet href="chrome://browser/content/devtools/styleinspector.css" type="text/css"?>
-<?xml-stylesheet href="chrome://browser/skin/devtools/csshtmltree.css" type="text/css"?>
-<?xml-stylesheet href="chrome://browser/skin/devtools/common.css" type="text/css"?>
-
-<!DOCTYPE window [
-  <!ENTITY % inspectorDTD SYSTEM "chrome://browser/locale/devtools/styleinspector.dtd">
-  %inspectorDTD;
-]>
-<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" title="&ruleViewTitle;">
-  <script type="application/javascript;version=1.8">
-    window.setPanel = function(panel, iframe) {
-      Components.utils.import("resource:///modules/devtools/StyleInspector.jsm");
-      this.ruleview = new RuleViewTool(panel, window, iframe);
-    }
-    window.onunload = function() {
-      if (this.ruleview) {
-        this.ruleview.destroy();
-      }
-    }
-  </script>
-</window>
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/ruleview.css
@@ -0,0 +1,34 @@
+/* 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/. */
+
+#root {
+  display: -moz-box;
+}
+
+.ruleview {
+  overflow: auto;
+  -moz-user-select: text;
+}
+
+.ruleview-code {
+  direction: ltr;
+}
+
+.ruleview-property:not(:hover) > .ruleview-enableproperty {
+  pointer-events: none;
+}
+
+.ruleview-namecontainer {
+  cursor: text;
+}
+
+.ruleview-propertycontainer {
+  cursor: text;
+  padding-right: 15px;
+}
+
+.ruleview-computedlist:not(.styleinspector-open),
+.ruleview-warning[hidden] {
+  display: none;
+}
deleted file mode 100644
--- a/browser/devtools/styleinspector/styleinspector.css
+++ /dev/null
@@ -1,56 +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/. */
-
-#root {
-  display: -moz-box;
-}
-
-.helplink {
-  display: block;
-}
-
-.expander,
-.property-name,
-.ruleview-propertyname,
-.ruleview-warning,
-.ruleview-expander {
-  display: inline-block;
-}
-
-#propertyContainer {
-  display: -moz-box;
-  -moz-box-orient: vertical;
-  -moz-box-flex: 1;
-  overflow-y: auto;
-  -moz-user-select: text;
-}
-
-.ruleview {
-  overflow: auto;
-  -moz-user-select: text;
-}
-
-.property-view-hidden,
-.property-content-hidden,
-.ruleview-computedlist:not(.styleinspector-open),
-.ruleview-warning[hidden] {
-  display: none;
-}
-
-.ruleview-code {
-  direction: ltr;
-}
-
-.ruleview-property:not(:hover) > .ruleview-enableproperty {
-  pointer-events: none;
-}
-
-.ruleview-namecontainer {
-  cursor: text;
-}
-
-.ruleview-propertycontainer {
-  cursor: text;
-  padding-right: 15px;
-}
--- a/browser/devtools/styleinspector/test/Makefile.in
+++ b/browser/devtools/styleinspector/test/Makefile.in
@@ -18,30 +18,30 @@ MOCHITEST_BROWSER_FILES = \
   browser_styleinspector_bug_672744_search_filter.js \
   $(filter awaiting-promise-based-init, browser_bug589375_keybindings.js) \
   browser_styleinspector_bug_689759_no_results_placeholder.js \
   browser_bug_692400_element_style.js \
   browser_csslogic_inherited.js \
   browser_ruleview_734259_style_editor_link.js \
   browser_ruleview_editor.js \
   browser_ruleview_editor_changedvalues.js \
-  browser_ruleview_bug_703643_context_menu_copy.js \
+  browser_ruleview_copy.js \
   browser_ruleview_focus.js \
   browser_ruleview_inherit.js \
   browser_ruleview_manipulation.js \
   browser_ruleview_override.js \
   browser_ruleview_ui.js \
   browser_ruleview_update.js \
   browser_bug705707_is_content_stylesheet.js \
   browser_bug722196_property_view_media_queries.js \
   browser_bug722196_rule_view_media_queries.js \
   browser_bug_592743_specificity.js \
   browser_bug722691_rule_view_increment.js \
   browser_computedview_734259_style_editor_link.js \
-  browser_computedview_bug_703643_context_menu_copy.js\
+  browser_computedview_copy.js\
   head.js \
   $(NULL)
 
 MOCHITEST_BROWSER_FILES += \
   browser_bug683672.html \
   browser_bug705707_is_content_stylesheet.html \
   browser_bug705707_is_content_stylesheet_imported.css \
   browser_bug705707_is_content_stylesheet_imported2.css \
--- a/browser/devtools/styleinspector/test/browser_bug722691_rule_view_increment.js
+++ b/browser/devtools/styleinspector/test/browser_bug722691_rule_view_increment.js
@@ -18,17 +18,17 @@ function setUpTests()
 {
   doc.body.innerHTML = '<div id="test" style="' +
                            'margin-top:0px;' +
                            'padding-top: 0px;' +
                            'color:#000000;' +
                            'background-color: #000000; >"'+
                        '</div>';
   let testElement = doc.getElementById("test");
-  ruleDialog = openDialog("chrome://browser/content/devtools/cssruleview.xul",
+  ruleDialog = openDialog("chrome://browser/content/devtools/cssruleview.xhtml",
                           "cssruleviewtest",
                           "width=350,height=350");
   ruleDialog.addEventListener("load", function onLoad(evt) {
     ruleDialog.removeEventListener("load", onLoad, true);
     let doc = ruleDialog.document;
     ruleView = new CssRuleView(doc);
     doc.documentElement.appendChild(ruleView.element);
     ruleView.highlight(testElement);
--- a/browser/devtools/styleinspector/test/browser_bug_692400_element_style.js
+++ b/browser/devtools/styleinspector/test/browser_bug_692400_element_style.js
@@ -47,30 +47,22 @@ function SI_checkText()
 
   ok(propertyView, "found PropertyView for color");
 
   is(propertyView.hasMatchedSelectors, true, "hasMatchedSelectors is true");
 
   propertyView.matchedExpanded = true;
   propertyView.refreshMatchedSelectors();
 
-  let td = propertyView.matchedSelectorsContainer.querySelector("td.rule-text");
-  ok(td, "found the first table row");
+  let span = propertyView.matchedSelectorsContainer.querySelector("span.rule-text");
+  ok(span, "found the first table row");
 
   let selector = propertyView.matchedSelectorViews[0];
   ok(selector, "found the first matched selector view");
 
-  try {
-    is(td.textContent.trim(), selector.humanReadableText(td).trim(),
-      "selector text is correct");
-  } catch (ex) {
-    info("EXCEPTION: " + ex);
-    ok(false, "getting the selector text should not raise an exception");
-  }
-
   finishUp();
 }
 
 function finishUp()
 {
   doc = computedView = null;
   gBrowser.removeCurrentTab();
   finish();
deleted file mode 100644
--- a/browser/devtools/styleinspector/test/browser_computedview_bug_703643_context_menu_copy.js
+++ /dev/null
@@ -1,197 +0,0 @@
-/* vim: set ts=2 et sw=2 tw=80: */
-/* Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ */
-
-// Tests that the style inspector works properly
-
-let doc;
-let win;
-let computedView;
-
-XPCOMUtils.defineLazyGetter(this, "osString", function() {
-  return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
-});
-
-function createDocument()
-{
-  doc.body.innerHTML = '<style type="text/css"> ' +
-    'span { font-variant: small-caps; color: #000000; } ' +
-    '.nomatches {color: #ff0000;}</style> <div id="first" style="margin: 10em; ' +
-    'font-size: 14pt; font-family: helvetica, sans-serif; color: #AAA">\n' +
-    '<h1>Some header text</h1>\n' +
-    '<p id="salutation" style="font-size: 12pt">hi.</p>\n' +
-    '<p id="body" style="font-size: 12pt">I am a test-case. This text exists ' +
-    'solely to provide some things to <span style="color: yellow">' +
-    'highlight</span> and <span style="font-weight: bold">count</span> ' +
-    'style list-items in the box at right. If you are reading this, ' +
-    'you should go do something else instead. Maybe read a book. Or better ' +
-    'yet, write some test-cases for another bit of code. ' +
-    '<span style="font-style: italic">some text</span></p>\n' +
-    '<p id="closing">more text</p>\n' +
-    '<p>even more text</p>' +
-    '</div>';
-  doc.title = "Computed view context menu test";
-
-  openInspector(selectNode)
-}
-
-function selectNode(aInspector)
-{
-  let span = doc.querySelector("span");
-  ok(span, "captain, we have the span");
-
-  aInspector.selection.setNode(span);
-
-  aInspector.sidebar.once("computedview-ready", function() {
-    aInspector.sidebar.select("computedview");
-
-    computedView = getComputedView(aInspector);
-    win = aInspector.sidebar.getWindowForTab("computedview");
-
-    Services.obs.addObserver(runStyleInspectorTests,
-      "StyleInspector-populated", false);
-  });
-}
-
-
-function runStyleInspectorTests()
-{
-  Services.obs.removeObserver(runStyleInspectorTests,
-    "StyleInspector-populated", false);
-
-  let contentDocument = computedView.styleDocument;
-  let prop = contentDocument.querySelector(".property-view");
-  ok(prop, "captain, we have the property-view node");
-
-  // We need the context menu to open in the correct place in order for
-  // popupNode to be propertly set.
-  contextMenuClick(prop);
-
-  checkCopyProperty();
-}
-
-function checkCopyProperty()
-{
-  info("Checking that cssHtmlTree.siBoundCopyDeclaration() returns the " +
-       "correct clipboard value");
-  let expectedPattern = "color: rgb\\(255, 255, 0\\);";
-
-  SimpleTest.waitForClipboard(function CS_boundCopyPropCheck() {
-      return checkClipboardData(expectedPattern);
-    },
-    computedView.siBoundCopyDeclaration,
-    checkCopyPropertyName, function() {
-      failedClipboard(expectedPattern, checkCopyPropertyName);
-    });
-}
-
-function checkCopyPropertyName()
-{
-  info("Checking that cssHtmlTree.siBoundCopyProperty() returns the " +
-       "correct clipboard value");
-  let expectedPattern = "color";
-
-  SimpleTest.waitForClipboard(function CS_boundCopyPropNameCheck() {
-      return checkClipboardData(expectedPattern);
-    },
-    computedView.siBoundCopyProperty,
-    checkCopyPropertyValue, function() {
-      failedClipboard(expectedPattern, checkCopyPropertyValue);
-    });
-}
-
-function checkCopyPropertyValue()
-{
-  info("Checking that cssHtmlTree.siBoundCopyPropertyValue() returns the " +
-       "correct clipboard value");
-  let expectedPattern = "rgb\\(255, 255, 0\\)";
-
-  SimpleTest.waitForClipboard(function CS_boundCopyPropValueCheck() {
-      return checkClipboardData(expectedPattern);
-    },
-    computedView.siBoundCopyPropertyValue,
-    checkCopySelection, function() {
-      failedClipboard(expectedPattern, checkCopySelection);
-    });
-}
-
-function checkCopySelection()
-{
-  let contentDocument = computedView.styleDocument;
-  let props = contentDocument.querySelectorAll(".property-view");
-  ok(props, "captain, we have the property-view nodes");
-
-  let range = document.createRange();
-  range.setStart(props[0], 0);
-  range.setEnd(props[3], 4);
-  win.getSelection().addRange(range);
-
-  info("Checking that cssHtmlTree.siBoundCopy() " +
-       " returns the correct clipboard value");
-
-  let expectedPattern = "color: rgb\\(255, 255, 0\\)[\\r\\n]+" +
-                 "font-family: helvetica,sans-serif[\\r\\n]+" +
-                 "font-size: 16px[\\r\\n]+" +
-                 "font-variant: small-caps[\\r\\n]*";
-
-  SimpleTest.waitForClipboard(function CS_boundCopyCheck() {
-      return checkClipboardData(expectedPattern);
-    },
-    computedView.siBoundCopy, closeStyleInspector, function() {
-      failedClipboard(expectedPattern, closeStyleInspector);
-    });
-}
-
-function checkClipboardData(aExpectedPattern)
-{
-  let actual = SpecialPowers.getClipboardData("text/unicode");
-  let expectedRegExp = new RegExp(aExpectedPattern, "g");
-  return expectedRegExp.test(actual);
-}
-
-function failedClipboard(aExpectedPattern, aCallback)
-{
-  // Format expected text for comparison
-  let terminator = osString == "WINNT" ? "\r\n" : "\n";
-  aExpectedPattern = aExpectedPattern.replace(/\[\\r\\n\][+*]/g, terminator);
-  aExpectedPattern = aExpectedPattern.replace(/\\\(/g, "(");
-  aExpectedPattern = aExpectedPattern.replace(/\\\)/g, ")");
-
-  let actual = SpecialPowers.getClipboardData("text/unicode");
-
-  // Trim the right hand side of our strings. This is because expectedPattern
-  // accounts for windows sometimes adding a newline to our copied data.
-  aExpectedPattern = aExpectedPattern.trimRight();
-  actual = actual.trimRight();
-
-  dump("TEST-UNEXPECTED-FAIL | Clipboard text does not match expected ... " +
-    "results (escaped for accurate comparison):\n");
-  info("Actual: " + escape(actual));
-  info("Expected: " + escape(aExpectedPattern));
-  aCallback();
-}
-
-function closeStyleInspector()
-{
-  finishUp();
-}
-
-function finishUp()
-{
-  computedView = doc = win = null;
-  gBrowser.removeCurrentTab();
-  finish();
-}
-
-function test()
-{
-  waitForExplicitFinish();
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.selectedBrowser.addEventListener("load", function(evt) {
-    gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee, true);
-    doc = content.document;
-    waitForFocus(createDocument, content);
-  }, true);
-
-  content.location = "data:text/html,computed view context menu test";
-}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_computedview_copy.js
@@ -0,0 +1,148 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that the style inspector works properly
+
+let doc;
+let win;
+let computedView;
+
+XPCOMUtils.defineLazyGetter(this, "osString", function() {
+  return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
+});
+
+function createDocument()
+{
+  doc.body.innerHTML = '<style type="text/css"> ' +
+    'span { font-variant: small-caps; color: #000000; } ' +
+    '.nomatches {color: #ff0000;}</style> <div id="first" style="margin: 10em; ' +
+    'font-size: 14pt; font-family: helvetica, sans-serif; color: #AAA">\n' +
+    '<h1>Some header text</h1>\n' +
+    '<p id="salutation" style="font-size: 12pt">hi.</p>\n' +
+    '<p id="body" style="font-size: 12pt">I am a test-case. This text exists ' +
+    'solely to provide some things to <span style="color: yellow">' +
+    'highlight</span> and <span style="font-weight: bold">count</span> ' +
+    'style list-items in the box at right. If you are reading this, ' +
+    'you should go do something else instead. Maybe read a book. Or better ' +
+    'yet, write some test-cases for another bit of code. ' +
+    '<span style="font-style: italic">some text</span></p>\n' +
+    '<p id="closing">more text</p>\n' +
+    '<p>even more text</p>' +
+    '</div>';
+  doc.title = "Computed view context menu test";
+
+  openInspector(selectNode)
+}
+
+function selectNode(aInspector)
+{
+  let span = doc.querySelector("span");
+  ok(span, "captain, we have the span");
+
+  aInspector.selection.setNode(span);
+
+  aInspector.sidebar.once("computedview-ready", function() {
+    aInspector.sidebar.select("computedview");
+
+    computedView = getComputedView(aInspector);
+    win = aInspector.sidebar.getWindowForTab("computedview");
+
+    Services.obs.addObserver(runStyleInspectorTests,
+      "StyleInspector-populated", false);
+  });
+}
+
+
+function runStyleInspectorTests()
+{
+  Services.obs.removeObserver(runStyleInspectorTests,
+    "StyleInspector-populated", false);
+
+  let contentDocument = computedView.styleDocument;
+  let prop = contentDocument.querySelector(".property-view");
+  ok(prop, "captain, we have the property-view node");
+
+  checkCopySelection();
+}
+
+function checkCopySelection()
+{
+  let contentDocument = computedView.styleDocument;
+  let props = contentDocument.querySelectorAll(".property-view");
+  ok(props, "captain, we have the property-view nodes");
+
+  let range = document.createRange();
+  range.setStart(props[0], 0);
+  range.setEnd(props[3], 3);
+  win.getSelection().addRange(range);
+
+  info("Checking that cssHtmlTree.siBoundCopy() " +
+       " returns the correct clipboard value");
+
+  let expectedPattern = "color: rgb\\(255, 255, 0\\);[\\r\\n]+" +
+                 "font-family: helvetica,sans-serif;[\\r\\n]+" +
+                 "font-size: 16px;[\\r\\n]+" +
+                 "font-variant: small-caps;[\\r\\n]*";
+
+  SimpleTest.waitForClipboard(function CS_boundCopyCheck() {
+      return checkClipboardData(expectedPattern);
+    },
+    function() {fireCopyEvent(props[0])}, closeStyleInspector, function() {
+      failedClipboard(expectedPattern, closeStyleInspector);
+    });
+}
+
+function checkClipboardData(aExpectedPattern)
+{
+  let actual = SpecialPowers.getClipboardData("text/unicode");
+  let expectedRegExp = new RegExp(aExpectedPattern, "g");
+  return expectedRegExp.test(actual);
+}
+
+function failedClipboard(aExpectedPattern, aCallback)
+{
+  // Format expected text for comparison
+  let terminator = osString == "WINNT" ? "\r\n" : "\n";
+  aExpectedPattern = aExpectedPattern.replace(/\[\\r\\n\][+*]/g, terminator);
+  aExpectedPattern = aExpectedPattern.replace(/\\\(/g, "(");
+  aExpectedPattern = aExpectedPattern.replace(/\\\)/g, ")");
+
+  let actual = SpecialPowers.getClipboardData("text/unicode");
+
+  // Trim the right hand side of our strings. This is because expectedPattern
+  // accounts for windows sometimes adding a newline to our copied data.
+  aExpectedPattern = aExpectedPattern.trimRight();
+  actual = actual.trimRight();
+
+  dump("TEST-UNEXPECTED-FAIL | Clipboard text does not match expected ... " +
+    "results (escaped for accurate comparison):\n");
+  info("Actual: " + escape(actual));
+  info("Expected: " + escape(aExpectedPattern));
+  aCallback();
+}
+
+function closeStyleInspector()
+{
+  finishUp();
+}
+
+function finishUp()
+{
+  computedView = doc = win = null;
+  gBrowser.removeCurrentTab();
+  finish();
+}
+
+function test()
+{
+  waitForExplicitFinish();
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function(evt) {
+    gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee, true);
+    doc = content.document;
+    waitForFocus(createDocument, content);
+  }, true);
+
+  content.location = "data:text/html,computed view context menu test";
+}
deleted file mode 100644
--- a/browser/devtools/styleinspector/test/browser_ruleview_bug_703643_context_menu_copy.js
+++ /dev/null
@@ -1,282 +0,0 @@
-/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
-/* Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ */
-
-let doc;
-let inspector;
-let win;
-
-XPCOMUtils.defineLazyGetter(this, "osString", function() {
-  return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
-});
-
-function createDocument()
-{
-  doc.body.innerHTML = '<style type="text/css"> ' +
-    'html { color: #000000; } ' +
-    'span { font-variant: small-caps; color: #000000; } ' +
-    '.nomatches {color: #ff0000;}</style> <div id="first" style="margin: 10em; ' +
-    'font-size: 14pt; font-family: helvetica, sans-serif; color: #AAA">\n' +
-    '<h1>Some header text</h1>\n' +
-    '<p id="salutation" style="font-size: 12pt">hi.</p>\n' +
-    '<p id="body" style="font-size: 12pt">I am a test-case. This text exists ' +
-    'solely to provide some things to <span style="color: yellow">' +
-    'highlight</span> and <span style="font-weight: bold">count</span> ' +
-    'style list-items in the box at right. If you are reading this, ' +
-    'you should go do something else instead. Maybe read a book. Or better ' +
-    'yet, write some test-cases for another bit of code. ' +
-    '<span style="font-style: italic">some text</span></p>\n' +
-    '<p id="closing">more text</p>\n' +
-    '<p>even more text</p>' +
-    '</div>';
-  doc.title = "Rule view context menu test";
-
-  let target = TargetFactory.forTab(gBrowser.selectedTab);
-  gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
-    inspector = toolbox.getCurrentPanel();
-    inspector.sidebar.select("ruleview");
-    win = inspector.sidebar.getWindowForTab("ruleview");
-    highlightNode();
-  });
-}
-
-function highlightNode()
-{
-  // Highlight a node.
-  let div = content.document.getElementsByTagName("div")[0];
-
-  inspector.selection.once("new-node", function() {
-    is(inspector.selection.node, div, "selection matches the div element");
-    testClip();
-  });
-  executeSoon(function() {
-    inspector.selection.setNode(div);
-  });
-}
-
-function testClip()
-{
-  executeSoon(function() {
-    info("Checking that _onCopyRule() returns " +
-         "the correct clipboard value");
-    let expectedPattern = "element {[\\r\\n]+" +
-      "    margin: 10em;[\\r\\n]+" +
-      "    font-size: 14pt;[\\r\\n]+" +
-      "    font-family: helvetica,sans-serif;[\\r\\n]+" +
-      "    color: rgb\\(170, 170, 170\\);[\\r\\n]+" +
-      "}[\\r\\n]*";
-
-    SimpleTest.waitForClipboard(function IUI_boundCopyPropCheck() {
-        return checkClipboardData(expectedPattern);
-      },
-      checkCopyRule, checkCopyProperty, function() {
-        failedClipboard(expectedPattern, checkCopyProperty);
-      });
-  });
-}
-
-function checkCopyRule() {
-  let contentDoc = win.document;
-  let props = contentDoc.querySelectorAll(".ruleview-property");
-
-  is(props.length, 5, "checking property length");
-
-  let prop = props[2];
-  let propName = prop.querySelector(".ruleview-propertyname").textContent;
-  let propValue = prop.querySelector(".ruleview-propertyvalue").textContent;
-
-  is(propName, "font-family", "checking property name");
-  is(propValue, "helvetica,sans-serif", "checking property value");
-
-  // We need the context menu to open in the correct place in order for
-  // popupNode to be propertly set.
-  contextMenuClick(prop);
-
-  ruleView()._boundCopyRule();
-  let menu = contentDoc.querySelector("#rule-view-context-menu");
-  ok(menu, "we have the context menu");
-  menu.hidePopup();
-}
-
-function checkCopyProperty()
-{
-  let contentDoc = win.document;
-  let props = contentDoc.querySelectorAll(".ruleview-property");
-  let prop = props[2];
-
-  info("Checking that _onCopyDeclaration() returns " +
-       "the correct clipboard value");
-  let expectedPattern = "font-family: helvetica,sans-serif;";
-
-  // We need the context menu to open in the correct place in order for
-  // popupNode to be propertly set.
-  contextMenuClick(prop);
-
-  SimpleTest.waitForClipboard(function IUI_boundCopyPropCheck() {
-    return checkClipboardData(expectedPattern);
-  },
-  ruleView()._boundCopyDeclaration,
-  checkCopyPropertyName, function() {
-    failedClipboard(expectedPattern, checkCopyPropertyName);
-  });
-}
-
-function checkCopyPropertyName()
-{
-  info("Checking that _onCopyProperty() returns " +
-       "the correct clipboard value");
-  let expectedPattern = "margin";
-
-  SimpleTest.waitForClipboard(function IUI_boundCopyPropNameCheck() {
-    return checkClipboardData(expectedPattern);
-  },
-  ruleView()._boundCopyProperty,
-  checkCopyPropertyValue, function() {
-    failedClipboard(expectedPattern, checkCopyPropertyValue);
-  });
-}
-
-function checkCopyPropertyValue()
-{
-  info("Checking that _onCopyPropertyValue() " +
-       " returns the correct clipboard value");
-  let expectedPattern = "10em";
-
-  SimpleTest.waitForClipboard(function IUI_boundCopyPropValueCheck() {
-    return checkClipboardData(expectedPattern);
-  },
-  ruleView()._boundCopyPropertyValue,
-  checkCopySelection, function() {
-    failedClipboard(expectedPattern, checkCopySelection);
-  });
-}
-
-function checkCopySelection()
-{
-  let contentDoc = win.document;
-  let props = contentDoc.querySelectorAll(".ruleview-property");
-  let values = contentDoc.querySelectorAll(".ruleview-propertycontainer");
-
-  let range = document.createRange();
-  range.setStart(props[0], 0);
-  range.setEnd(values[4], 2);
-
-  let selection = win.getSelection();
-  selection.addRange(range);
-
-  info("Checking that _boundCopy() returns the correct " +
-    "clipboard value");
-  let expectedPattern = "    margin: 10em;[\\r\\n]+" +
-                        "    font-size: 14pt;[\\r\\n]+" +
-                        "    font-family: helvetica,sans-serif;[\\r\\n]+" +
-                        "    color: rgb\\(170, 170, 170\\);[\\r\\n]+" +
-                        "}[\\r\\n]+" +
-                        "html {[\\r\\n]+" +
-                        "    color: rgb\\(0, 0, 0\\);[\\r\\n]*";
-
-  SimpleTest.waitForClipboard(function IUI_boundCopyCheck() {
-    return checkClipboardData(expectedPattern);
-  },ruleView()._boundCopy, testSimpleCopy, function() {
-    failedClipboard(expectedPattern, testSimpleCopy);
-  });
-}
-
-function testSimpleCopy()
-{
-  executeSoon(function() {
-    info("Checking that _onCopy() returns the correct clipboard value");
-    let expectedPattern = "element {[\\r\\n]+" +
-      "    margin: 10em;[\\r\\n]+" +
-      "    font-size: 14pt;[\\r\\n]+" +
-      "    font-family: helvetica,sans-serif;[\\r\\n]+" +
-      "    color: rgb\\(170, 170, 170\\);[\\r\\n]+" +
-      "}[\\r\\n]*";
-
-    SimpleTest.waitForClipboard(function IUI_testSimpleCopy() {
-        return checkClipboardData(expectedPattern);
-      },
-      checkSimpleCopy, finishup, function() {
-        failedClipboard(expectedPattern, finishup);
-      });
-  });
-}
-
-function checkSimpleCopy() {
-  let contentDoc = win.document;
-  let props = contentDoc.querySelectorAll(".ruleview-code");
-
-  is(props.length, 2, "checking property length");
-
-  let prop = props[0];
-
-  selectNode(prop);
-
-  // We need the context menu to open in the correct place in order for
-  // popupNode to be propertly set.
-  contextMenuClick(prop);
-
-  ruleView()._boundCopy();
-  let menu = contentDoc.querySelector("#rule-view-context-menu");
-  ok(menu, "we have the context menu");
-  menu.hidePopup();
-}
-
-function selectNode(aNode) {
-  let doc = aNode.ownerDocument;
-  let win = doc.defaultView;
-  let range = doc.createRange();
-
-  range.selectNode(aNode);
-  win.getSelection().addRange(range);
-}
-
-function checkClipboardData(aExpectedPattern)
-{
-  let actual = SpecialPowers.getClipboardData("text/unicode");
-  let expectedRegExp = new RegExp(aExpectedPattern, "g");
-  return expectedRegExp.test(actual);
-}
-
-function failedClipboard(aExpectedPattern, aCallback)
-{
-  // Format expected text for comparison
-  let terminator = osString == "WINNT" ? "\r\n" : "\n";
-  aExpectedPattern = aExpectedPattern.replace(/\[\\r\\n\][+*]/g, terminator);
-  aExpectedPattern = aExpectedPattern.replace(/\\\(/g, "(");
-  aExpectedPattern = aExpectedPattern.replace(/\\\)/g, ")");
-
-  let actual = SpecialPowers.getClipboardData("text/unicode");
-
-  // Trim the right hand side of our strings. This is because expectedPattern
-  // accounts for windows sometimes adding a newline to our copied data.
-  aExpectedPattern = aExpectedPattern.trimRight();
-  actual = actual.trimRight();
-
-  dump("TEST-UNEXPECTED-FAIL | Clipboard text does not match expected ... " +
-    "results (escaped for accurate comparison):\n");
-  info("Actual: " + escape(actual));
-  info("Expected: " + escape(aExpectedPattern));
-  aCallback();
-}
-
-function finishup()
-{
-  gBrowser.removeCurrentTab();
-  doc = inspector = null;
-  finish();
-}
-
-function test()
-{
-  waitForExplicitFinish();
-
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.selectedBrowser.addEventListener("load", function(evt) {
-    gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee,
-      true);
-    doc = content.document;
-    waitForFocus(createDocument, content);
-  }, true);
-
-  content.location = "data:text/html,<p>rule view context menu test</p>";
-}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_ruleview_copy.js
@@ -0,0 +1,145 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+let doc;
+let inspector;
+let win;
+
+XPCOMUtils.defineLazyGetter(this, "osString", function() {
+  return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
+});
+
+function createDocument()
+{
+  doc.body.innerHTML = '<style type="text/css"> ' +
+    'html { color: #000000; } ' +
+    'span { font-variant: small-caps; color: #000000; } ' +
+    '.nomatches {color: #ff0000;}</style> <div id="first" style="margin: 10em; ' +
+    'font-size: 14pt; font-family: helvetica, sans-serif; color: #AAA">\n' +
+    '<h1>Some header text</h1>\n' +
+    '<p id="salutation" style="font-size: 12pt">hi.</p>\n' +
+    '<p id="body" style="font-size: 12pt">I am a test-case. This text exists ' +
+    'solely to provide some things to <span style="color: yellow">' +
+    'highlight</span> and <span style="font-weight: bold">count</span> ' +
+    'style list-items in the box at right. If you are reading this, ' +
+    'you should go do something else instead. Maybe read a book. Or better ' +
+    'yet, write some test-cases for another bit of code. ' +
+    '<span style="font-style: italic">some text</span></p>\n' +
+    '<p id="closing">more text</p>\n' +
+    '<p>even more text</p>' +
+    '</div>';
+  doc.title = "Rule view context menu test";
+
+  let target = TargetFactory.forTab(gBrowser.selectedTab);
+  gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
+    inspector = toolbox.getCurrentPanel();
+    inspector.sidebar.select("ruleview");
+    win = inspector.sidebar.getWindowForTab("ruleview");
+    highlightNode();
+  });
+}
+
+function highlightNode()
+{
+  // Highlight a node.
+  let div = content.document.getElementsByTagName("div")[0];
+
+  inspector.selection.once("new-node", function() {
+    is(inspector.selection.node, div, "selection matches the div element");
+    executeSoon(checkCopySelection);
+  });
+  executeSoon(function() {
+    inspector.selection.setNode(div);
+  });
+}
+
+function checkCopySelection()
+{
+  let contentDoc = win.document;
+  let props = contentDoc.querySelectorAll(".ruleview-property");
+  let values = contentDoc.querySelectorAll(".ruleview-propertycontainer");
+
+  let range = document.createRange();
+  range.setStart(props[0], 0);
+  range.setEnd(values[4], 2);
+
+  let selection = win.getSelection();
+  selection.addRange(range);
+
+  info("Checking that _boundCopy() returns the correct " +
+    "clipboard value");
+  let expectedPattern = "    margin: 10em;[\\r\\n]+" +
+                        "    font-size: 14pt;[\\r\\n]+" +
+                        "    font-family: helvetica,sans-serif;[\\r\\n]+" +
+                        "    color: rgb\\(170, 170, 170\\);[\\r\\n]+" +
+                        "}[\\r\\n]+" +
+                        "html {[\\r\\n]+" +
+                        "    color: rgb\\(0, 0, 0\\);[\\r\\n]*";
+
+  SimpleTest.waitForClipboard(function IUI_boundCopyCheck() {
+    return checkClipboardData(expectedPattern);
+  },function() {fireCopyEvent(props[0])}, finishup, function() {
+    failedClipboard(expectedPattern, finishup);
+  });
+}
+
+function selectNode(aNode) {
+  let doc = aNode.ownerDocument;
+  let win = doc.defaultView;
+  let range = doc.createRange();
+
+  range.selectNode(aNode);
+  win.getSelection().addRange(range);
+}
+
+function checkClipboardData(aExpectedPattern)
+{
+  let actual = SpecialPowers.getClipboardData("text/unicode");
+  let expectedRegExp = new RegExp(aExpectedPattern, "g");
+  return expectedRegExp.test(actual);
+}
+
+function failedClipboard(aExpectedPattern, aCallback)
+{
+  // Format expected text for comparison
+  let terminator = osString == "WINNT" ? "\r\n" : "\n";
+  aExpectedPattern = aExpectedPattern.replace(/\[\\r\\n\][+*]/g, terminator);
+  aExpectedPattern = aExpectedPattern.replace(/\\\(/g, "(");
+  aExpectedPattern = aExpectedPattern.replace(/\\\)/g, ")");
+
+  let actual = SpecialPowers.getClipboardData("text/unicode");
+
+  // Trim the right hand side of our strings. This is because expectedPattern
+  // accounts for windows sometimes adding a newline to our copied data.
+  aExpectedPattern = aExpectedPattern.trimRight();
+  actual = actual.trimRight();
+
+  dump("TEST-UNEXPECTED-FAIL | Clipboard text does not match expected ... " +
+    "results (escaped for accurate comparison):\n");
+  info("Actual: " + escape(actual));
+  info("Expected: " + escape(aExpectedPattern));
+  aCallback();
+}
+
+function finishup()
+{
+  gBrowser.removeCurrentTab();
+  doc = inspector = null;
+  finish();
+}
+
+function test()
+{
+  waitForExplicitFinish();
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function(evt) {
+    gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee,
+      true);
+    doc = content.document;
+    waitForFocus(createDocument, content);
+  }, true);
+
+  content.location = "data:text/html,<p>rule view context menu test</p>";
+}
--- a/browser/devtools/styleinspector/test/browser_ruleview_editor_changedvalues.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_editor_changedvalues.js
@@ -32,17 +32,17 @@ function startTest()
     '.testclass {' +
     '  background-color: green;' +
     '}';
 
   let styleNode = addStyle(doc, style);
   doc.body.innerHTML = '<div id="testid" class="testclass">Styled Node</div>';
   let testElement = doc.getElementById("testid");
 
-  ruleDialog = openDialog("chrome://browser/content/devtools/cssruleview.xul",
+  ruleDialog = openDialog("chrome://browser/content/devtools/cssruleview.xhtml",
                           "cssruleviewtest",
                           "width=200,height=350");
   ruleDialog.addEventListener("load", function onLoad(evt) {
     ruleDialog.removeEventListener("load", onLoad, true);
     let doc = ruleDialog.document;
     ruleView = new CssRuleView(doc);
     doc.documentElement.appendChild(ruleView.element);
     ruleView.element.addEventListener("CssRuleViewChanged", ruleViewChanged, false);
--- a/browser/devtools/styleinspector/test/browser_ruleview_ui.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_ui.js
@@ -32,17 +32,17 @@ function startTest()
     '.testclass, .unmatched {' +
     '  background-color: green;' +
     '}';
 
   let styleNode = addStyle(doc, style);
   doc.body.innerHTML = '<div id="testid" class="testclass">Styled Node</div>';
   let testElement = doc.getElementById("testid");
 
-  ruleDialog = openDialog("chrome://browser/content/devtools/cssruleview.xul",
+  ruleDialog = openDialog("chrome://browser/content/devtools/cssruleview.xhtml",
                           "cssruleviewtest",
                           "width=200,height=350");
   ruleDialog.addEventListener("load", function onLoad(evt) {
     ruleDialog.removeEventListener("load", onLoad);
     let doc = ruleDialog.document;
     ruleView = new CssRuleView(doc);
     doc.documentElement.appendChild(ruleView.element);
     ruleView.element.addEventListener("CssRuleViewChanged", ruleViewChanged, false);
--- a/browser/devtools/styleinspector/test/browser_ruleview_update.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_update.js
@@ -24,17 +24,17 @@ function startTest()
 
   let styleNode = addStyle(doc, style);
   doc.body.innerHTML = '<div id="testid" class="testclass">Styled Node</div>';
   testElement = doc.getElementById("testid");
 
   let elementStyle = 'margin-top: 1px; padding-top: 5px;'
   testElement.setAttribute("style", elementStyle);
 
-  ruleDialog = openDialog("chrome://browser/content/devtools/cssruleview.xul",
+  ruleDialog = openDialog("chrome://browser/content/devtools/cssruleview.xhtml",
                           "cssruleviewtest",
                           "width=200,height=350");
   ruleDialog.addEventListener("load", function onLoad(evt) {
     ruleDialog.removeEventListener("load", onLoad);
     let doc = ruleDialog.document;
     ruleView = new CssRuleView(doc);
     doc.documentElement.appendChild(ruleView.element);
     ruleView.highlight(testElement);
--- a/browser/devtools/styleinspector/test/browser_styleinspector_bug_672744_search_filter.js
+++ b/browser/devtools/styleinspector/test/browser_styleinspector_bug_672744_search_filter.js
@@ -62,17 +62,17 @@ function SI_toggleDefaultStyles()
   checkbox.click();
 }
 
 function SI_AddFilterText()
 {
   Services.obs.removeObserver(SI_AddFilterText, "StyleInspector-populated", false);
 
   let doc = computedView.styleDocument;
-  let searchbar = doc.querySelector(".searchfield");
+  let searchbar = doc.querySelector(".devtools-searchinput");
   Services.obs.addObserver(SI_checkFilter, "StyleInspector-populated", false);
   info("setting filter text to \"color\"");
   searchbar.focus();
 
   let win =computedView.styleWindow;
   EventUtils.synthesizeKey("c", {}, win);
   EventUtils.synthesizeKey("o", {}, win);
   EventUtils.synthesizeKey("l", {}, win);
--- a/browser/devtools/styleinspector/test/head.js
+++ b/browser/devtools/styleinspector/test/head.js
@@ -94,16 +94,22 @@ function waitForEditorBlur(aEditor, aCal
   input.addEventListener("blur", function onBlur() {
     input.removeEventListener("blur", onBlur, false);
     executeSoon(function() {
       aCallback();
     });
   }, false);
 }
 
+function fireCopyEvent(element) {
+  let evt = element.ownerDocument.createEvent("Event");
+  evt.initEvent("copy", true, true);
+  element.dispatchEvent(evt);
+}
+
 function contextMenuClick(element) {
   var evt = element.ownerDocument.createEvent('MouseEvents');
 
   var button = 2;  // right click
 
   evt.initMouseEvent('contextmenu', true, true,
        element.ownerDocument.defaultView, 1, 0, 0, 0, 0, false,
        false, false, false, button, null);
--- a/browser/locales/en-US/chrome/browser/devtools/styleinspector.dtd
+++ b/browser/locales/en-US/chrome/browser/devtools/styleinspector.dtd
@@ -22,19 +22,11 @@
   -  tree. -->
 <!ENTITY selectedElementLabel  "Selected element:">
 
 <!-- LOCALIZATION NOTE (noPropertiesFound): In the case where there are no CSS
   -  properties to display e.g. due to search criteria this message is
   -  displayed. -->
 <!ENTITY noPropertiesFound     "No CSS properties found.">
 
-<!-- LOCALIZATION NOTE (bestMatch, matched, parentMatch): For each style
-  -  property the panel shows the rules containing that property. For every
-  -  rule, the rule status is also displayed: a rule can be the best match, a
-  -  match or a parent match. -->
-<!ENTITY bestMatch             "Best Match">
-<!ENTITY matched               "Matched">
-<!ENTITY parentMatch           "Parent Match">
-
 <!-- FIXME: notes -->
 <!ENTITY computedViewTitle     "Computed">
 <!ENTITY ruleViewTitle         "Rules">
--- a/browser/locales/en-US/chrome/browser/devtools/styleinspector.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/styleinspector.properties
@@ -41,83 +41,11 @@ rule.inheritedFrom=Inherited from %S
 # This is the link title shown in the hover tooltip.
 helpLinkTitle=Read the documentation for this property
 
 # LOCALIZATION NOTE (rule.warning.title): When an invalid property value is
 # entered into the rule view a warning icon is displayed. This text is used for
 # the title attribute of the warning icon.
 rule.warning.title=Invalid property value
 
-# LOCALIZATION NOTE (style.contextmenu.copyselection): The computed view's
-# context menu copy entry.
-style.contextmenu.copyselection=Copy selection
-
-# LOCALIZATION NOTE (style.contextmenu.copyselection.accesskey): The computed
-# view's context menu copy entry access key.
-style.contextmenu.copyselection.accesskey=C
-
-# LOCALIZATION NOTE (style.contextmenu.copydeclaration): The style inspector's
-# context menu copy property entry allows a complete CSS property to be copied.
-style.contextmenu.copydeclaration=Copy declaration line
-
-# LOCALIZATION NOTE (style.contextmenu.copydeclaration.accesskey): The style
-# inspector's context menu copy property access key.
-style.contextmenu.copydeclaration.accesskey=D
-
-# LOCALIZATION NOTE (style.contextmenu.copyproperty): The style inspector's
-# context menu copy property name entry allows a CSS property name to be copied.
-style.contextmenu.copyproperty=Copy property
-
-# LOCALIZATION NOTE (style.contextmenu.copyproperty.accesskey): The style
-# inspector's context menu copy property name access key.
-style.contextmenu.copyproperty.accesskey=P
-
-# LOCALIZATION NOTE (style.contextmenu.copypropertyvalue): The style inspector's
-# context menu copy property value entry allows a CSS property name to be copied.
-style.contextmenu.copypropertyvalue=Copy property value
-
-# LOCALIZATION NOTE (style.contextmenu.copypropertyvalue.accesskey): The style
-# inspector's context menu copy property value access key.
-style.contextmenu.copypropertyvalue.accesskey=U
-
-# LOCALIZATION NOTE (rule.contextmenu.copyselection): The rule view's context
-# menu copy entry.
-rule.contextmenu.copyselection=Copy selection
-
-# LOCALIZATION NOTE (rule.contextmenu.copyselection.accesskey): The rule view's
-# context menu copy entry access key.
-rule.contextmenu.copyselection.accesskey=C
-
-# LOCALIZATION NOTE (rule.contextmenu.copyrule): The rule view's context menu
-# copy rule entry allows a complete CSS rule to be copied.
-rule.contextmenu.copyrule=Copy rule
-
-# LOCALIZATION NOTE (rule.contextmenu.copyrule.accesskey): The rule view's
-# context menu copy rule access key.
-rule.contextmenu.copyrule.accesskey=R
-
-# LOCALIZATION NOTE (rule.contextmenu.copydeclaration): The rule view's context
-# menu copy property entry allows a complete CSS property to be copied.
-rule.contextmenu.copydeclaration=Copy declaration line
-
-# LOCALIZATION NOTE (rule.contextmenu.copydeclaration.accesskey): The rule view's
-# context menu copy property access key.
-rule.contextmenu.copydeclaration.accesskey=D
-
-# LOCALIZATION NOTE (rule.contextmenu.copyproperty): The rule view's context
-# menu copy property entry allows a CSS property name to be copied.
-rule.contextmenu.copyproperty=Copy property
-
-# LOCALIZATION NOTE (rule.contextmenu.copyproperty.accesskey): The rule
-# view's context menu copy property name access key.
-rule.contextmenu.copyproperty.accesskey=P
-
-# LOCALIZATION NOTE (rule.contextmenu.copypropertyvalue): The rule view's
-# context menu copy property entry allows a CSS property value to be copied.
-rule.contextmenu.copypropertyvalue=Copy property value
-
-# LOCALIZATION NOTE (rule.contextmenu.copypropertyvalue.accesskey): The rule
-# view's context menu copy property value access key.
-rule.contextmenu.copypropertyvalue.accesskey=U
-
 # LOCALIZATION NOTE (ruleView.empty): Text displayed when the highlighter is
 # first opened and there's no node selected in the rule view.
 rule.empty=No element selected.
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..09691bc5e540079d8b83efbd4aa78f2b71e03d95
GIT binary patch
literal 177
zc%17D@N?(olHy`uVBq!ia0vp^tU%1c!3HD^Kbl$tsR~aQ#}JO_<QXREoZJG>84N%F
z|NmdoVMgMiHL8}{+htBLUKCE#OJm{IySrmLYe%uej`UxLWHu;tL~JM%2Wn_2IR2#4
zGU4Df&f<vlwRh$*ALjXLyE#JoSK4!**j=JM2XAHXN_*lHu|u#;QS9rP=<_^BEES}l
bsW2G6i@Q8)Zo@918yGxY{an^LB{Ts5rvyQk
--- a/browser/themes/linux/devtools/common.css
+++ b/browser/themes/linux/devtools/common.css
@@ -134,16 +134,17 @@
 }
 
 /* Text input */
 
 .devtools-textinput,
 .devtools-searchinput {
   -moz-appearance: none;
   margin: 0 3px;
+  min-height: 22px;
   border: 1px solid hsla(210,8%,5%,.6);
   border-radius: 2px;
   background-color: transparent;
   background-image: -moz-linear-gradient(hsla(210,16%,76%,.15), hsla(210,16%,76%,.35));
   padding: 3px;
   box-shadow: 0 1px 1px hsla(210,8%,5%,.3) inset,
               0 0 0 1px hsla(210,16%,76%,.1) inset,
               0 1px 0 hsla(210,16%,76%,.15);
@@ -330,39 +331,9 @@
   box-shadow: 0 1px 0 hsla(0,0%,100%,.05) inset, 0 -2px 0 hsla(206,37%,4%,.05) inset, 0 -1px 1px hsla(206,37%,4%,.1) inset;
 }
 
 .devtools-sidebar-tabs > tabs > tab[selected=true]:hover:active {
   background-image: linear-gradient(#1f3e4f, #1b3243), @solidSeparator@;
   box-shadow: 0 1px 0 hsla(0,0%,100%,.05) inset, 0 -2px 0 hsla(206,37%,4%,.05) inset, 0 -1px 1px hsla(206,37%,4%,.1) inset;
 }
 
-/* Theme */
-
-.devtools-theme-background {
-  background-color: white;
-}
-
-.devtools-theme-comment {
-  color: hsl(90,2%,46%); /* grey */
-}
-
-.devtools-theme-keyword {
-  color: hsl(276,44%,45%); /* purple */
-}
-
-.devtools-theme-string {
-  color: hsl(72,100%,27%); /* green */
-}
-
-.devtools-theme-tagname {
-  color: hsl(208,81%,21%); /* dark blue */
-}
-
-.devtools-theme-attrname {
-  color: hsl(208,56%,40%); /* blue */
-}
-
-.devtools-theme-attrvalue {
-  color: hsl(24,85%,39%); /* orange */
-}
-
 %include ../../shared/devtools/common.inc.css
new file mode 100644
--- /dev/null
+++ b/browser/themes/linux/devtools/computedview.css
@@ -0,0 +1,157 @@
+/* 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/. */
+
+* {
+  -moz-box-sizing: border-box;
+}
+
+:root {
+  height: 100%;
+}
+
+body {
+  margin: 0;
+  display : flex;
+  flex-direction: column;
+  height: 100%;
+}
+
+#propertyContainer {
+  -moz-user-select: text;
+  overflow: auto;
+  min-height: 0;
+  flex: 1;
+}
+
+.property-view-hidden,
+.property-content-hidden {
+  display: none;
+}
+
+.property-view {
+  clear: both;
+  padding: 2px 0 2px 17px;
+}
+
+.property-view > * {
+  display: inline-block;
+  vertical-align: middle;
+}
+
+.property-name {
+  width: 50%;
+  overflow-x: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.property-value {
+  width: 50%;
+  max-width: 100%;
+  overflow-x: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  background-image: url(arrow-e.png);
+  background-repeat: no-repeat;
+  background-size: 5px 8px;
+  background-position: 2px center;
+  padding-left: 10px;
+}
+
+.other-property-value {
+  background-image: url(arrow-e.png);
+  background-repeat: no-repeat;
+  background-size: 5px 8px;
+  background-position: left center;
+  padding-left: 8px;
+}
+
+@media (min-width: 400px) {
+  .property-name {
+    width: 200px;
+  }
+  .property-value {
+    width: auto;
+  }
+}
+
+.property-content {
+  padding-left: 17px;
+}
+
+/* From skin */
+.helplink {
+  /* FIXME: remove this image 
+  background-image: url("chrome://browser/skin/devtools/goto-mdn.png");
+  */
+}
+
+.expander {
+  visibility: hidden;
+  margin-left: -12px!important;
+}
+
+.expander[open] {
+  margin-left: -17px!important;
+}
+
+.expandable {
+  visibility: visible;
+}
+
+.match {
+  visibility: hidden;
+}
+
+.matchedselectors > p {
+  clear: both;
+  margin: 0 2px 0 0;
+  padding: 2px;
+  overflow-x: hidden;
+  border-style: dotted;
+  border-color: rgba(128,128,128,0.4);
+  border-width: 1px 1px 0 1px;
+}
+
+.matchedselectors > p:last-of-type {
+  border-bottom-width: 1px;
+}
+
+/* This rule is necessary because Templater.jsm breaks LTR TDs in RTL docs */
+.rule-text {
+  direction: ltr;
+}
+
+.matched {
+  text-decoration: line-through;
+}
+
+.parentmatch {
+  opacity: 0.5;
+}
+
+#noResults {
+  font-size: 110%;
+  margin: 5px;
+  text-align: center;
+}
+
+.onlyuserstyles {
+  cursor: pointer;
+}
+
+.legendKey {
+  margin: 0 5px;
+}
+
+.devtools-toolbar {
+  width: 100%;
+}
+
+.link {
+  padding: 0 3px;
+  cursor: pointer;
+  float: right;
+}
deleted file mode 100644
--- a/browser/themes/linux/devtools/csshtmltree.css
+++ /dev/null
@@ -1,293 +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/. */
-
-:root {
-  -moz-appearance: none;
-  background: -moz-Field;
-  color: -moz-FieldText;
-  font: message-box;
-  font-family: monospace;
-}
-
-/* Take away these two :visited rules to get a core dumper     */
-/* See https://bugzilla.mozilla.org/show_bug.cgi?id=575675#c30 */
-.link,
-.link:visited {
-  color: #0091ff;
-}
-.link,
-.helplink,
-.link:visited,
-.helplink:visited {
-  text-decoration: none;
-}
-.link:hover {
-  text-decoration: underline;
-}
-
-.helplink {
-  height: 14px;
-  width: 0;
-  overflow: hidden;
-  -moz-padding-start: 14px;
-  background-image: url("chrome://browser/skin/devtools/goto-mdn.png");
-  -moz-margin-end: 2px;
-  cursor: pointer;
-}
-
-.property-view:not(:hover) > .helplink-container {
-  visibility: hidden;
-}
-
-.rulelink {
-  color: -moz-dialogtext;
-  padding: 0;
-}
-
-.expander {
-  -moz-appearance: treetwisty;
-  padding-top: 12px;
-  -moz-margin-start: 5px;
-  vertical-align: middle;
-}
-
-.expander[open] {
-  -moz-appearance: treetwistyopen;
-}
-
-.match {
-  visibility: hidden;
-}
-
-.expandable {
-  cursor: pointer;
-  visibility: visible;
-}
-
-.expander-container {
-  vertical-align: text-top;
-}
-
-.property-name {
-  padding: 2px 0;
-  color: -moz-FieldText;
-}
-
-.property-value {
-  padding: 0;
-  -moz-padding-end: 6px;
-  color: grey;
-  width: 100%;
-}
-
-.rule-link {
-  text-align: end;
-  -moz-padding-start: 10px;
-  cursor: pointer;
-}
-
-/* This rule is necessary because Templater.jsm breaks LTR TDs in RTL docs */
-.rule-text {
-  direction: ltr;
-  padding: 0;
-  -moz-padding-start: 20px;
-  vertical-align: text-bottom;
-}
-
-.bestmatch {
-  color: black;
-}
-.matched {
-  text-decoration: line-through;
-}
-.parentmatch {
-  color: #666;
-}
-
-#propertyContainer {
-  border-collapse: collapse;
-}
-
-.darkrow {
-  background-color: rgba(0,0,0,.022);
-}
-
-#noResults {
-  font-size: 18px;
-  margin-top: 5px;
-  text-align: center;
-}
-
-.headerControls {
-  color: -moz-dialogtext;
-  background-color: -moz-dialog;
-}
-
-.includebrowserstyles {
-  cursor: pointer;
-  font-size: 11px;
-}
-
-#footer {
-  border-top: 1px solid -moz-dialog;
-}
-
-.legendKey {
-  margin: 0 5px;
-}
-
-/**
- * CSS Rule View
- */
-
-.ruleview {
-  background-color: white;
-}
-
-.ruleview-rule-source {
-  color: hsl(90,2%,46%); /* grey */
-  -moz-padding-start: 5px;
-  cursor: pointer;
-  text-align: right;
-  float: right;
-  -moz-user-select: none;
-}
-
-.ruleview-rule-inheritance {
-  background-color: hsl(0,0%,90%);
-  color: hsl(0,0%,50%);
-  border-top: 1px solid hsl(0,0%,65%);
-  border-bottom: 1px solid hsl(0,0%,65%);
-  padding: 1px 4px;
-  margin-top: 4px;
-  -moz-user-select: none;
-}
-
-.ruleview-rule-source:hover {
-  text-decoration: underline;
-}
-
-.ruleview-rule {
-  padding: 2px 4px;
-}
-
-.ruleview-rule + .ruleview-rule {
-  border-top: 1px dotted #cddae5;
-}
-
-.ruleview-warning {
-  background: url("chrome://browser/skin/devtools/alerticon-warning.png");
-  -moz-margin-start: 5px;
-  vertical-align: middle;
-  width: 13px;
-  height: 12px;
-}
-
-.ruleview-ruleopen {
-  -moz-padding-end: 5px;
-}
-
-.ruleview-ruleclose {
-  cursor: text;
-  padding-right: 20px;
-}
-
-.ruleview-propertylist {
-  list-style: none;
-  padding: 0;
-  margin: 0;
-}
-
-.ruleview-enableproperty {
-  height: 10px;
-  width: 10px;
-  -moz-margin-start: 1px;
-  -moz-margin-end: 0;
-  transition: opacity 100ms;
-  transition-delay: 200ms;
-}
-
-.ruleview-property:not(:hover) > .ruleview-enableproperty[checked] {
-  opacity: 0;
-  transition: none;
-}
-
-.ruleview-expander {
-  width: 8px;
-  height: 8px;
-  background: url("chrome://browser/skin/devtools/arrows.png") 24px 0;
-  cursor: pointer;
-  -moz-margin-start: 2px;
-  -moz-margin-end: 5px;
-}
-
-.ruleview-expander.styleinspector-open {
-  background-position: 8px 0;
-}
-
-.ruleview-newproperty {
-  /* (enable checkbox width: 12px) + (expander width: 15px) */
-  -moz-margin-start: 27px;
-}
-
-.ruleview-propertyname {
-  padding: 1px 0;
-  color: hsl(276,44%,45%); /* purple */
-}
-
-.ruleview-propertyvalue {
-  padding: 1px 0;
-}
-
-.ruleview-namecontainer,
-.ruleview-propertycontainer,
-.ruleview-propertyname,
-.ruleview-propertyvalue {
-  text-decoration: inherit;
-}
-
-.ruleview-computedlist {
-  list-style: none;
-  padding: 0;
-}
-
-.ruleview-computed {
-  -moz-margin-start: 4em;
-}
-
-.ruleview-overridden {
-  text-decoration: line-through;
-}
-
-.styleinspector-propertyeditor {
-  border: 1px solid #CCC;
-  padding: 0;
-  box-shadow: 2px 2px 2px #CCC;
-}
-
-.ruleview-property {
-  border-left: 2px solid transparent;
-}
-
-.ruleview-property  > * {
-  vertical-align: middle;
-}
-
-.ruleview-property[dirty] {
-  border-left-color: #68E268;
-}
-
-.ruleview-namecontainer > .ruleview-propertyname,
-.ruleview-propertycontainer > .ruleview-propertyvalue {
-  border-bottom: 1px dashed transparent;
-}
-
-.ruleview-namecontainer:hover > .ruleview-propertyname,
-.ruleview-propertycontainer:hover > .ruleview-propertyvalue {
-  border-bottom-color: hsl(0,0%,50%);
-}
-
-.ruleview-selector-separator, .ruleview-selector-unmatched {
-  color: #888;
-}
new file mode 100644
--- /dev/null
+++ b/browser/themes/linux/devtools/dark-theme.css
@@ -0,0 +1,107 @@
+/* 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/. */
+
+/* According to:
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=715472#c17
+ */
+.theme-body {
+  background: #131c26;
+  color: #8fa1b2
+}
+
+.theme-twisty {
+  cursor: pointer;
+  margin-right: 5px;
+}
+
+.theme-twisty:-moz-focusring {
+  outline-style: none;
+}
+
+.theme-twisty:not([open]) {
+  width: 0;
+  height: 0;
+  border-top: 5px solid transparent;
+  border-bottom: 5px solid transparent;
+  border-left: 5px solid rgba(255,255,255,0.5);
+  margin-left: 5px;
+}
+
+.theme-twisty[open] {
+  width: 10px;
+  height: 10px;
+  background-image: linear-gradient(to bottom right, transparent 68%,  rgba(255,255,255,0.5) 68%);
+}
+
+.theme-checkbox {
+  display: inline-block;
+  border: 1px solid rgba(160,160,160,0.4);
+  width: 6px;
+  height: 6px;
+  padding: 2px;
+  background-color: transparent;
+  background-repeat: no-repeat;
+  outline: none;
+}
+
+.theme-checkbox[checked] {
+  background-clip: content-box;
+  background-image: linear-gradient(to bottom right, transparent 48%, rgba(160,160,160,1) 48%, rgba(160,160,160,1) 52%, transparent 52%),
+                    linear-gradient(to bottom left, transparent 48%, rgba(160,160,160,1) 48%, rgba(160,160,160,1) 52%, transparent 52%);
+}
+
+.theme-selected {
+  background: #26384E;
+}
+
+.theme-bg-darker {
+  background-color: rgba(0,0,0,0.1);
+}
+
+.theme-link { /* blue */
+  color: #3689b2;
+}
+
+.theme-comment { /* grey */
+  color: #5c6773;
+}
+
+.theme-gutter {
+  background-color: #0f171f;
+  color: #667380;
+  border-color: #303b47;
+}
+
+.theme-separator { /* grey */
+  border-color: #303b47;
+}
+
+.theme-fg-color1 { /* green */
+  color: #5c9966;
+}
+
+.theme-fg-color2 { /* blue */
+  color: #3689b2;
+}
+
+.theme-fg-color3 { /* pink/lavender */
+  color: #a673bf;
+}
+
+.theme-fg-color4 { /* purple/violet */
+  color: #6270b2;
+}
+
+.theme-fg-color5 { /* Yellow */
+  color: #a18650;
+}
+
+.theme-fg-color6 { /* Orange */
+  color: #b26b47;
+}
+
+.theme-fg-color7 { /* Red */
+  color: #bf5656;
+}
new file mode 100644
--- /dev/null
+++ b/browser/themes/linux/devtools/floating-scrollbars-light.css
@@ -0,0 +1,10 @@
+/* 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/. */
+
+@import url("floating-scrollbars.css");
+
+scrollbar thumb {
+  background-color: rgba(170,170,170,0.2) !important;
+}
--- a/browser/themes/linux/devtools/floating-scrollbars.css
+++ b/browser/themes/linux/devtools/floating-scrollbars.css
@@ -5,29 +5,29 @@ scrollbar {
   position: relative;
   background-color: transparent;
   background-image: none;
   z-index: 2147483647;
   padding: 2px;
 }
 
 scrollbar[orient="vertical"] {
-  -moz-margin-start: -8px;
-  min-width: 8px;
-  max-width: 8px;
+  -moz-margin-start: -10px;
+  min-width: 10px;
+  max-width: 10px;
 }
 
 scrollbar[orient="horizontal"] {
-  margin-top: -8px;
-  min-height: 8px;
-  max-height: 8px;
+  margin-top: -10px;
+  min-height: 10px;
+  max-height: 10px;
 }
 
 scrollbar thumb {
   -moz-appearance: none !important;
   border-width: 0px !important;
-  background-color: rgba(0,0,0,0.2) !important;
+  background-color: rgba(170,170,170,0.2) !important;
   border-radius: 3px !important;
 }
 
 scrollbar scrollbarbutton, scrollbar gripper {
   display: none;
 }
--- a/browser/themes/linux/devtools/font-inspector.css
+++ b/browser/themes/linux/devtools/font-inspector.css
@@ -1,14 +1,13 @@
 * {
   -moz-box-sizing: border-box;
 }
 
 body {
-  background: #F9F9F9;
   margin: 0;
   padding-bottom: 20px;
 }
 
 #all-fonts {
   padding: 0 5px;
   margin: 0;
 }
@@ -19,26 +18,33 @@ body {
   margin: 3px;
   cursor: pointer;
   position: fixed;
   bottom: 0;
   right: 0;
 }
 
 .font {
-  border-bottom: 1px solid #DDD;
   padding: 10px 5px;
   font-size: 0;
 }
 
+.theme-dark .font {
+  border-bottom: 1px solid #444;
+}
+
+.theme-light .font {
+  border-bottom: 1px solid #DDD;
+}
+
 .font:last-of-type {
   border-bottom: 0;
 }
 
-.font:nth-child(even) {
+.theme-light .font:nth-child(even) {
   background: #F4F4F4;
 }
 
 .font-preview {
   height: 60px;
   width: 100%;
   border: 0;
   display: block;
@@ -52,28 +58,23 @@ body {
 .font-name {
   display: inline;
 }
 
 .font-css-code {
   max-width: 100%;
   overflow: hidden;
   text-overflow: ellipsis;
-  background: white;
   padding: 5px;
-  border: 1px dotted #CCC;
 }
 
-.font-is-local,
-.font-is-remote,
-.font-format-url,
-.font-css {
-  color: #999
+.theme-light .font-css-code,
+.theme-light .font-url {
+  border: 1px solid #CCC;
+  background: white;
 }
 
-.font-url {
-  border: 1px solid #CCC;
-  color: #888;
+.theme-dark .font-css-code,
+.theme-dark .font-url {
+  border: 1px solid #333;
+  background: black;
+  color: white;
 }
-
-.font-url:focus {
-  color: black;
-}
--- a/browser/themes/linux/devtools/layoutview.css
+++ b/browser/themes/linux/devtools/layoutview.css
@@ -1,22 +1,25 @@
 /* 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/. */
 
-body {
-  background: url(layout-background-grid.png), -moz-radial-gradient(50% 70%, circle cover, hsl(210,53%,45%) 0%, hsl(210,54%,33%) 100%);
+.theme-dark .theme-body {
+  background-image: url(layout-background-grid.png);
+}
+
+.theme-light .theme-body {
+  background-image: url(layout-background-grid.png), -moz-radial-gradient(50% 70%, circle cover, hsl(210,53%,45%) 0%, hsl(210,54%,33%) 100%);
+}
+
+.theme-body {
   color: hsl(210,100%,85%);
   -moz-box-sizing: border-box;
 }
 
-#element-size {
-  color: hsl(210,100%,95%);
-}
-
 #main {
   border-color: hsla(210,100%,85%,0.7);
   border-style: dotted;
 }
 
 #main > .border {
   color: hsl(210,53%,45%);
 }
new file mode 100644
--- /dev/null
+++ b/browser/themes/linux/devtools/light-theme.css
@@ -0,0 +1,107 @@
+/* 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/. */
+
+/* According to:
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=715472#c17
+ */
+.theme-body {
+  background: white;
+  color: black;
+}
+
+.theme-twisty {
+  cursor: pointer;
+  margin-right: 5px;
+}
+
+.theme-twisty:-moz-focusring {
+  outline-style: none;
+}
+
+.theme-twisty:not([open]) {
+  width: 0;
+  height: 0;
+  border-top: 5px solid transparent;
+  border-bottom: 5px solid transparent;
+  border-left: 5px solid rgba(0,0,0,0.5);
+  margin-left: 5px;
+}
+
+.theme-twisty[open] {
+  width: 10px;
+  height: 10px;
+  background-image: linear-gradient(to bottom right, transparent 68%,  rgba(0,0,0,0.5) 68%);
+}
+
+.theme-checkbox {
+  display: inline-block;
+  border: 1px solid rgba(160,160,160,0.4);
+  width: 6px;
+  height: 6px;
+  padding: 2px;
+  background-color: transparent;
+  background-repeat: no-repeat;
+  outline: none;
+}
+
+.theme-checkbox[checked] {
+  background-clip: content-box;
+  background-image: linear-gradient(to bottom right, transparent 48%, rgba(160,160,160,1) 48%, rgba(160,160,160,1) 52%, transparent 52%),
+                    linear-gradient(to bottom left, transparent 48%, rgba(160,160,160,1) 48%, rgba(160,160,160,1) 52%, transparent 52%);
+}
+
+.theme-selected {
+  background-color: hsl(0,0%,90%);
+}
+
+.theme-bg-darker {
+  background: #F9F9F9;
+}
+
+.theme-link { /* blue */
+  color: hsl(208,56%,40%);
+}
+
+.theme-comment { /* grey */
+  color: hsl(90,2%,46%);
+}
+
+.theme-gutter {
+  background-color: hsl(0,0%,90%);
+  color: #667380;
+  border-color: hsl(0,0%,65%);
+}
+
+.theme-separator { /* grey */
+  border-color: #cddae5;
+}
+
+.theme-fg-color1 { /* green */
+  color: hsl(72,100%,27%);
+}
+
+.theme-fg-color2 { /* blue */
+  color: hsl(208,56%,40%);
+}
+
+.theme-fg-color3 { /* dark blue */
+  color: hsl(208,81%,21%)
+}
+
+.theme-fg-color4 { /* Orange */
+  color: hsl(24,85%,39%);
+}
+
+.theme-fg-color5 { /* Yellow */
+  color: #a18650;
+}
+
+.theme-fg-color6 { /* Orange */
+  color: hsl(24,85%,39%);
+}
+
+.theme-fg-color7 { /* Red */
+  color: #bf5656;
+}
--- a/browser/themes/linux/devtools/markup-view.css
+++ b/browser/themes/linux/devtools/markup-view.css
@@ -2,24 +2,16 @@
  * 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/. */
 
 * {
   padding: 0;
   margin: 0;
 }
 
-body {
-  color: hsl(0,0%,50%);
-}
-
-.text {
-  color: black;
-}
-
 .newattr {
   cursor: pointer;
 }
 
 .selected {
   background-color: hsl(0,0%,90%);
 }
 
@@ -36,22 +28,16 @@ li.container {
 }
 
 .codebox {
   padding-left: 14px;
 }
 
 .expander {
   position: absolute;
-  -moz-appearance: treetwisty;
-  padding: 11px 0;
-}
-
-.expander[expanded] {
-  -moz-appearance: treetwistyopen;
 }
 
 .more-nodes {
   padding-left: 16px;
 }
 
 .styleinspector-propertyeditor {
   border: 1px solid #CCC;
new file mode 100644
--- /dev/null
+++ b/browser/themes/linux/devtools/ruleview.css
@@ -0,0 +1,125 @@
+/* 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/. */
+
+.ruleview {
+  height: 100%;
+}
+
+.ruleview-rule-source {
+  -moz-padding-start: 5px;
+  cursor: pointer;
+  text-align: right;
+  float: right;
+  -moz-user-select: none;
+}
+
+.ruleview-rule-inheritance {
+  border-top-width: 1px;
+  border-bottom-width: 1px;
+  border-top-style: solid;
+  border-bottom-style: solid;
+  padding: 1px 4px;
+  margin-top: 4px;
+  -moz-user-select: none;
+}
+
+.ruleview-rule-source:hover {
+  text-decoration: underline;
+}
+
+.ruleview-rule {
+  padding: 2px 4px;
+}
+
+.ruleview-rule + .ruleview-rule {
+  border-top-width: 1px;
+  border-top-style: dotted;
+}
+
+.ruleview-warning {
+  background: url("chrome://browser/skin/devtools/alerticon-warning.png");
+  -moz-margin-start: 5px;
+  vertical-align: middle;
+  width: 13px;
+  height: 12px;
+}
+
+.ruleview-ruleopen {
+  -moz-padding-end: 5px;
+}
+
+.ruleview-ruleclose {
+  cursor: text;
+  padding-right: 20px;
+}
+
+.ruleview-propertylist {
+  list-style: none;
+  padding: 0;
+  margin: 0;
+}
+
+.ruleview-rule:not(:hover) .ruleview-enableproperty {
+  visibility: hidden;
+}
+
+.ruleview-expander {
+  display: inline-block;
+}
+
+.ruleview-newproperty {
+  /* (enable checkbox width: 12px) + (expander width: 15px) */
+  -moz-margin-start: 27px;
+}
+
+.ruleview-namecontainer,
+.ruleview-propertycontainer,
+.ruleview-propertyname,
+.ruleview-propertyvalue {
+  text-decoration: inherit;
+}
+
+.ruleview-computedlist {
+  list-style: none;
+  padding: 0;
+}
+
+.ruleview-computed {
+  -moz-margin-start: 35px;
+}
+
+.ruleview-overridden {
+  text-decoration: line-through;
+}
+
+.styleinspector-propertyeditor {
+  border: 1px solid #CCC;
+  padding: 0;
+}
+
+.ruleview-property {
+  border-left: 2px solid transparent;
+}
+
+.ruleview-property  > * {
+  vertical-align: middle;
+}
+
+.ruleview-property[dirty] {
+  border-left-color: #68E268;
+}
+
+.ruleview-namecontainer > .ruleview-propertyname,
+.ruleview-propertycontainer > .ruleview-propertyvalue {
+  border-bottom: 1px dashed transparent;
+}
+
+.ruleview-namecontainer:hover > .ruleview-propertyname,
+.ruleview-propertycontainer:hover > .ruleview-propertyvalue {
+  border-bottom-color: hsl(0,0%,50%);
+}
+
+.ruleview-selector-separator, .ruleview-selector-unmatched {
+  color: #888;
+}
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -110,26 +110,28 @@ browser.jar:
   skin/classic/browser/tabbrowser/tab-overflow-border.png (tabbrowser/tab-overflow-border.png)
   skin/classic/browser/tabbrowser/tabDragIndicator.png (tabbrowser/tabDragIndicator.png)
   skin/classic/browser/tabview/edit-light.png         (tabview/edit-light.png)
   skin/classic/browser/tabview/search.png             (tabview/search.png)
   skin/classic/browser/tabview/stack-expander.png     (tabview/stack-expander.png)
   skin/classic/browser/tabview/tabview.png            (tabview/tabview.png)
   skin/classic/browser/tabview/tabview.css            (tabview/tabview.css)
 * skin/classic/browser/devtools/common.css            (devtools/common.css)
+  skin/classic/browser/devtools/dark-theme.css        (devtools/dark-theme.css)
+  skin/classic/browser/devtools/light-theme.css       (devtools/light-theme.css)
   skin/classic/browser/devtools/arrows.png            (devtools/arrows.png)
   skin/classic/browser/devtools/widgets.css           (devtools/widgets.css)
   skin/classic/browser/devtools/commandline.png       (devtools/commandline.png)
   skin/classic/browser/devtools/command-paintflashing.png  (devtools/command-paintflashing.png)
   skin/classic/browser/devtools/command-responsivemode.png (devtools/command-responsivemode.png)
   skin/classic/browser/devtools/command-scratchpad.png (devtools/command-scratchpad.png)
   skin/classic/browser/devtools/command-tilt.png      (devtools/command-tilt.png)
   skin/classic/browser/devtools/alerticon-warning.png (devtools/alerticon-warning.png)
   skin/classic/browser/devtools/goto-mdn.png          (devtools/goto-mdn.png)
-  skin/classic/browser/devtools/csshtmltree.css       (devtools/csshtmltree.css)
+  skin/classic/browser/devtools/ruleview.css          (devtools/ruleview.css)
   skin/classic/browser/devtools/webconsole.css                  (devtools/webconsole.css)
   skin/classic/browser/devtools/webconsole_networkpanel.css     (devtools/webconsole_networkpanel.css)
   skin/classic/browser/devtools/webconsole.png                  (devtools/webconsole.png)
   skin/classic/browser/devtools/commandline.css              (devtools/commandline.css)
   skin/classic/browser/devtools/markup-view.css      (devtools/markup-view.css)
   skin/classic/browser/devtools/orion.css             (devtools/orion.css)
   skin/classic/browser/devtools/orion-container.css   (devtools/orion-container.css)
   skin/classic/browser/devtools/orion-task.png        (devtools/orion-task.png)
@@ -182,26 +184,29 @@ browser.jar:
   skin/classic/browser/devtools/debugger-step-over.png (devtools/debugger-step-over.png)
   skin/classic/browser/devtools/responsive-se-resizer.png (devtools/responsive-se-resizer.png)
   skin/classic/browser/devtools/responsive-vertical-resizer.png (devtools/responsive-vertical-resizer.png)
   skin/classic/browser/devtools/responsive-background.png (devtools/responsive-background.png)
   skin/classic/browser/devtools/tools-icons-small.png     (devtools/tools-icons-small.png)
   skin/classic/browser/devtools/dock-bottom.png           (devtools/dock-bottom.png)
   skin/classic/browser/devtools/dock-side.png             (devtools/dock-side.png)
   skin/classic/browser/devtools/floating-scrollbars.css   (devtools/floating-scrollbars.css)
+  skin/classic/browser/devtools/floating-scrollbars-light.css (devtools/floating-scrollbars-light.css)
   skin/classic/browser/devtools/inspector.css             (devtools/inspector.css)
   skin/classic/browser/devtools/toolbox.css               (devtools/toolbox.css)
   skin/classic/browser/devtools/tool-webconsole.png       (devtools/tool-webconsole.png)
   skin/classic/browser/devtools/tool-debugger.png         (devtools/tool-debugger.png)
   skin/classic/browser/devtools/tool-inspector.png        (devtools/tool-inspector.png)
   skin/classic/browser/devtools/tool-styleeditor.png      (devtools/tool-styleeditor.png)
   skin/classic/browser/devtools/tool-profiler.png         (devtools/tool-profiler.png)
   skin/classic/browser/devtools/close.png                 (devtools/close.png)
   skin/classic/browser/devtools/undock.png                (devtools/undock.png)
   skin/classic/browser/devtools/font-inspector.css        (devtools/font-inspector.css)
+  skin/classic/browser/devtools/computedview.css          (devtools/computedview.css)
+  skin/classic/browser/devtools/arrow-e.png               (devtools/arrow-e.png)
 #ifdef MOZ_SERVICES_SYNC
   skin/classic/browser/sync-16-throbber.png
   skin/classic/browser/sync-16.png
   skin/classic/browser/sync-24-throbber.png
   skin/classic/browser/sync-32.png
   skin/classic/browser/sync-bg.png
   skin/classic/browser/sync-128.png
   skin/classic/browser/sync-desktopIcon.png
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..09691bc5e540079d8b83efbd4aa78f2b71e03d95
GIT binary patch
literal 177
zc%17D@N?(olHy`uVBq!ia0vp^tU%1c!3HD^Kbl$tsR~aQ#}JO_<QXREoZJG>84N%F
z|NmdoVMgMiHL8}{+htBLUKCE#OJm{IySrmLYe%uej`UxLWHu;tL~JM%2Wn_2IR2#4
zGU4Df&f<vlwRh$*ALjXLyE#JoSK4!**j=JM2XAHXN_*lHu|u#;QS9rP=<_^BEES}l
bsW2G6i@Q8)Zo@918yGxY{an^LB{Ts5rvyQk
--- a/browser/themes/osx/devtools/common.css
+++ b/browser/themes/osx/devtools/common.css
@@ -139,16 +139,17 @@
 }
 
 /* Text input */
 
 .devtools-textinput,
 .devtools-searchinput {
   -moz-appearance: none;
   margin: 0 3px;
+  min-height: 22px;
   background-color: transparent;
   border: 1px solid hsla(210,8%,5%,.6);
   border-radius: 20px;
   background-image: -moz-linear-gradient(hsla(210,16%,76%,.15), hsla(210,16%,76%,.35));
   padding: 3px;
   box-shadow: 0 1px 1px hsla(210,8%,5%,.3) inset,
               0 0 0 1px hsla(210,16%,76%,.1) inset,
               0 1px 0 hsla(210,16%,76%,.15);
@@ -349,39 +350,9 @@
   box-shadow: 0 1px 0 hsla(0,0%,100%,.05) inset, 0 -2px 0 hsla(206,37%,4%,.05) inset, 0 -1px 1px hsla(206,37%,4%,.1) inset;
 }
 
 .devtools-sidebar-tabs > tabs > tab[selected=true]:hover:active {
   background-image: linear-gradient(#1f3e4f, #1b3243), @solidSeparator@;
   box-shadow: 0 1px 0 hsla(0,0%,100%,.05) inset, 0 -2px 0 hsla(206,37%,4%,.05) inset, 0 -1px 1px hsla(206,37%,4%,.1) inset;
 }
 
-/* Theme */
-
-.devtools-theme-background {
-  background-color: white;
-}
-
-.devtools-theme-comment {
-  color: hsl(90,2%,46%); /* grey */
-}
-
-.devtools-theme-keyword {
-  color: hsl(276,44%,45%); /* purple */
-}
-
-.devtools-theme-string {
-  color: hsl(72,100%,27%); /* green */
-}
-
-.devtools-theme-tagname {
-  color: hsl(208,81%,21%); /* dark blue */
-}
-
-.devtools-theme-attrname {
-  color: hsl(208,56%,40%); /* blue */
-}
-
-.devtools-theme-attrvalue {
-  color: hsl(24,85%,39%); /* orange */
-}
-
 %include ../../shared/devtools/common.inc.css
new file mode 100644
--- /dev/null
+++ b/browser/themes/osx/devtools/computedview.css
@@ -0,0 +1,175 @@
+/* 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/. */
+
+/* Take away these two :visited rules to get a core dumper     */
+/* See https://bugzilla.mozilla.org/show_bug.cgi?id=575675#c30 */
+.link,
+.link:visited {
+  color: #0091ff;
+}
+.link,
+.helplink,
+.link:visited,
+.helplink:visited {
+  text-decoration: none;
+}
+.link:hover {
+  text-decoration: underline;
+}
+
+/* From content */
+
+* {
+  -moz-box-sizing: border-box;
+}
+
+:root {
+  height: 100%;
+}
+
+body {
+  margin: 0;
+  display : flex;
+  flex-direction: column;
+  height: 100%;
+}
+
+#propertyContainer {
+  -moz-user-select: text;
+  overflow: auto;
+  min-height: 0;
+  flex: 1;
+}
+
+.property-view-hidden,
+.property-content-hidden {
+  display: none;
+}
+
+.property-view {
+  clear: both;
+  padding: 2px 0 2px 17px;
+}
+
+.property-view > * {
+  display: inline-block;
+  vertical-align: middle;
+}
+
+.property-name {
+  width: 50%;
+  overflow-x: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.property-value {
+  width: 50%;
+  max-width: 100%;
+  overflow-x: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  background-image: url(arrow-e.png);
+  background-repeat: no-repeat;
+  background-size: 5px 8px;
+  background-position: 2px center;
+  padding-left: 10px;
+}
+
+.other-property-value {
+  background-image: url(arrow-e.png);
+  background-repeat: no-repeat;
+  background-size: 5px 8px;
+  background-position: left center;
+  padding-left: 8px;
+}
+
+@media (min-width: 400px) {
+  .property-name {
+    width: 200px;
+  }
+  .property-value {
+    width: auto;
+  }
+}
+
+.property-content {
+  padding-left: 17px;
+}
+
+/* From skin */
+.helplink {
+  /* FIXME: remove this image 
+  background-image: url("chrome://browser/skin/devtools/goto-mdn.png");
+  */
+}
+
+.expander {
+  visibility: hidden;
+  margin-left: -12px!important;
+}
+
+.expander[open] {
+  margin-left: -17px!important;
+}
+
+.expandable {
+  visibility: visible;
+}
+
+.match {
+  visibility: hidden;
+}
+
+.matchedselectors > p {
+  clear: both;
+  margin: 0 2px 0 0;
+  padding: 2px;
+  overflow-x: hidden;
+  border-style: dotted;
+  border-color: rgba(128,128,128,0.4);
+  border-width: 1px 1px 0 1px;
+}
+
+.matchedselectors > p:last-of-type {
+  border-bottom-width: 1px;
+}
+
+/* This rule is necessary because Templater.jsm breaks LTR TDs in RTL docs */
+.rule-text {
+  direction: ltr;
+}
+
+.matched {
+  text-decoration: line-through;
+}
+
+.parentmatch {
+  opacity: 0.5;
+}
+
+#noResults {
+  font-size: 110%;
+  margin: 5px;
+  text-align: center;
+}
+
+.onlyuserstyles {
+  cursor: pointer;
+}
+
+.legendKey {
+  margin: 0 5px;
+}
+
+.devtools-toolbar {
+  width: 100%;
+}
+
+.link {
+  padding: 0 3px;
+  cursor: pointer;
+  float: right;
+}
deleted file mode 100644
--- a/browser/themes/osx/devtools/csshtmltree.css
+++ /dev/null
@@ -1,296 +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/. */
-
-:root {
-  -moz-appearance: none;
-  background: -moz-Field;
-  color: -moz-FieldText;
-  font: message-box;
-  font-family: monospace;
-}
-
-/* Take away these two :visited rules to get a core dumper     */
-/* See https://bugzilla.mozilla.org/show_bug.cgi?id=575675#c30 */
-.link,
-.link:visited {
-  color: #0091ff;
-}
-.link,
-.helplink,
-.link:visited,
-.helplink:visited {
-  text-decoration: none;
-}
-.link:hover {
-  text-decoration: underline;
-}
-
-.helplink {
-  height: 14px;
-  width: 0;
-  overflow: hidden;
-  -moz-padding-start: 14px;
-  background-image: url("chrome://browser/skin/devtools/goto-mdn.png");
-  -moz-margin-end: 2px;
-  cursor: pointer;
-}
-
-.property-view:not(:hover) > .helplink-container {
-  visibility: hidden;
-}
-
-.rulelink {
-  color: -moz-dialogtext;
-  padding: 0;
-}
-
-.expander {
-  -moz-appearance: treetwisty;
-  width: 12px;
-  height: 12px;
-  padding-top: 12px;
-  -moz-margin-start: 5px;
-  vertical-align: middle;
-}
-
-.expander[open] {
-  -moz-appearance: treetwistyopen;
-}
-
-.match {
-  visibility: hidden;
-}
-
-.expandable {
-  cursor: pointer;
-  visibility: visible;
-}
-
-.expander-container {
-  vertical-align: text-top;
-}
-
-.property-name {
-  padding: 2px 0;
-  color: -moz-FieldText;
-}
-
-.property-value {
-  padding: 0;
-  -moz-padding-end: 6px;
-  color: grey;
-  width: 100%;
-}
-
-.rule-link {
-  text-align: end;
-  -moz-padding-start: 10px;
-  cursor: pointer;
-}
-
-/* This rule is necessary because Templater.jsm breaks LTR TDs in RTL docs */
-.rule-text {
-  direction: ltr;
-  padding: 0;
-  -moz-padding-start: 20px;
-  vertical-align: text-bottom;
-}
-
-.bestmatch {
-  color: black;
-}
-.matched {
-  text-decoration: line-through;
-}
-.parentmatch {
-  color: #666;
-}
-
-#propertyContainer {
-  border-collapse: collapse;
-}
-
-.darkrow {
-  background-color: rgba(0,0,0,.022);
-}
-
-#noResults {
-  font-size: 18px;
-  margin-top: 5px;
-  text-align: center;
-}
-
-.headerControls {
-  color: -moz-dialogtext;
-  background-color: -moz-dialog;
-}
-
-.includebrowserstyles {
-  cursor: pointer;
-  font-size: 11px;
-}
-
-#footer {
-  border-top: 1px solid -moz-dialog;
-}
-
-.legendKey {
-  margin: 0 5px;
-}
-
-/**
- * CSS Rule View
- */
-
-.ruleview {
-  background-color: white;
-}
-
-.ruleview-rule-source {
-  color: hsl(90,2%,46%); /* grey */
-  -moz-padding-start: 5px;
-  cursor: pointer;
-  text-align: right;
-  float: right;
-  -moz-user-select: none;
-}
-
-.ruleview-rule-inheritance {
-  background-color: hsl(0,0%,90%);
-  color: hsl(0,0%,50%);
-  border-top: 1px solid hsl(0,0%,65%);
-  border-bottom: 1px solid hsl(0,0%,65%);
-  padding: 1px 4px;
-  margin-top: 4px;
-  -moz-user-select: none;
-}
-
-.ruleview-rule-source:hover {
-  text-decoration: underline;
-}
-
-.ruleview-rule {
-  padding: 2px 4px;
-}
-
-.ruleview-rule + .ruleview-rule {
-  border-top: 1px dotted #cddae5;
-}
-
-.ruleview-warning {
-  background: url("chrome://browser/skin/devtools/alerticon-warning.png");
-  -moz-margin-start: 5px;
-  vertical-align: middle;
-  width: 13px;
-  height: 12px;
-}
-
-.ruleview-ruleopen {
-  -moz-padding-end: 5px;
-}
-
-.ruleview-ruleclose {
-  cursor: text;
-  padding-right: 20px;
-}
-
-.ruleview-propertylist {
-  list-style: none;
-  padding: 0;
-  margin: 0;
-}
-
-.ruleview-enableproperty {
-  height: 10px;
-  width: 10px;
-  -moz-margin-start: 1px;
-  -moz-margin-end: 0;
-  transition: opacity 100ms;
-  transition-delay: 200ms;
-}
-
-.ruleview-property:not(:hover) > .ruleview-enableproperty[checked] {
-  opacity: 0;
-  transition: none;
-}
-
-.ruleview-expander {
-  width: 8px;
-  height: 8px;
-  background: url("chrome://browser/skin/devtools/arrows.png") 24px 0;
-  cursor: pointer;
-  -moz-margin-start: 2px;
-  -moz-margin-end: 5px;
-}
-
-.ruleview-expander.styleinspector-open {
-  background-position: 8px 0;
-}
-
-.ruleview-newproperty {
-  /* (enable checkbox width: 12px) + (expander width: 15px) */
-  -moz-margin-start: 27px;
-}
-
-.ruleview-propertyname {
-  padding: 1px 0;
-  color: hsl(276,44%,45%); /* purple */
-}
-
-.ruleview-propertyvalue {
-  padding: 1px 0;
-}
-
-.ruleview-namecontainer,
-.ruleview-propertycontainer,
-.ruleview-propertyname,
-.ruleview-propertyvalue {
-  text-decoration: inherit;
-}
-
-.ruleview-computedlist {
-  list-style: none;
-  padding: 0;
-}
-
-.ruleview-computed {
-  -moz-margin-start: 4em;
-}
-
-.ruleview-overridden {
-  text-decoration: line-through;
-}
-
-.styleinspector-propertyeditor {
-  border: 1px solid #CCC;
-  padding: 0;
-  box-shadow: 2px 2px 2px #CCC;
-}
-
-.ruleview-property {
-  border-left: 2px solid transparent;
-}
-
-.ruleview-property  > * {
-  vertical-align: middle;
-}
-
-.ruleview-property[dirty] {
-  border-left-color: #68E268;
-}
-
-.ruleview-namecontainer > .ruleview-propertyname,
-.ruleview-propertycontainer > .ruleview-propertyvalue {
-  border-bottom: 1px dashed transparent;
-}
-
-.ruleview-namecontainer:hover > .ruleview-propertyname,
-.ruleview-propertycontainer:hover > .ruleview-propertyvalue {
-  border-bottom-color: hsl(0,0%,50%);
-}
-
-.ruleview-selector-separator, .ruleview-selector-unmatched {
-  color: #888;
-}
-
new file mode 100644
--- /dev/null
+++ b/browser/themes/osx/devtools/dark-theme.css
@@ -0,0 +1,107 @@
+/* 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/. */
+
+/* According to:
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=715472#c17
+ */
+.theme-body {
+  background: #131c26;
+  color: #8fa1b2
+}
+
+.theme-twisty {
+  cursor: pointer;
+  margin-right: 5px;
+}
+
+.theme-twisty:-moz-focusring {
+  outline-style: none;
+}
+
+.theme-twisty:not([open]) {
+  width: 0;
+  height: 0;
+  border-top: 5px solid transparent;
+  border-bottom: 5px solid transparent;
+  border-left: 5px solid rgba(255,255,255,0.5);
+  margin-left: 5px;
+}
+
+.theme-twisty[open] {
+  width: 10px;
+  height: 10px;
+  background-image: linear-gradient(to bottom right, transparent 68%,  rgba(255,255,255,0.5) 68%);
+}
+
+.theme-checkbox {
+  display: inline-block;
+  border: 1px solid rgba(160,160,160,0.4);
+  width: 6px;
+  height: 6px;
+  padding: 2px;
+  background-color: transparent;
+  background-repeat: no-repeat;
+  outline: none;
+}
+
+.theme-checkbox[checked] {
+  background-clip: content-box;
+  background-image: linear-gradient(to bottom right, transparent 48%, rgba(160,160,160,1) 48%, rgba(160,160,160,1) 52%, transparent 52%),
+                    linear-gradient(to bottom left, transparent 48%, rgba(160,160,160,1) 48%, rgba(160,160,160,1) 52%, transparent 52%);
+}
+
+.theme-selected {
+  background: #26384E;
+}
+
+.theme-bg-darker {
+  background-color: rgba(0,0,0,0.1);
+}
+
+.theme-link { /* blue */
+  color: #3689b2;
+}
+
+.theme-comment { /* grey */
+  color: #5c6773;
+}
+
+.theme-gutter {
+  background-color: #0f171f;
+  color: #667380;
+  border-color: #303b47;
+}
+
+.theme-separator { /* grey */
+  border-color: #303b47;
+}
+
+.theme-fg-color1 { /* green */
+  color: #5c9966;
+}
+
+.theme-fg-color2 { /* blue */
+  color: #3689b2;
+}
+
+.theme-fg-color3 { /* pink/lavender */
+  color: #a673bf;
+}
+
+.theme-fg-color4 { /* purple/violet */
+  color: #6270b2;
+}
+
+.theme-fg-color5 { /* Yellow */
+  color: #a18650;
+}
+
+.theme-fg-color6 { /* Orange */
+  color: #b26b47;
+}
+
+.theme-fg-color7 { /* Red */
+  color: #bf5656;
+}
new file mode 100644
--- /dev/null
+++ b/browser/themes/osx/devtools/floating-scrollbars-light.css
@@ -0,0 +1,10 @@
+/* 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/. */
+
+@import url("floating-scrollbars.css");
+
+scrollbar thumb {
+  background-color: rgba(170,170,170,0.2) !important;
+}
--- a/browser/themes/osx/devtools/font-inspector.css
+++ b/browser/themes/osx/devtools/font-inspector.css
@@ -1,14 +1,13 @@
 * {
   -moz-box-sizing: border-box;
 }
 
 body {
-  background: #F9F9F9;
   margin: 0;
   padding-bottom: 20px;
 }
 
 #all-fonts {
   padding: 0 5px;
   margin: 0;
 }
@@ -19,26 +18,33 @@ body {
   margin: 3px;
   cursor: pointer;
   position: fixed;
   bottom: 0;
   right: 0;
 }
 
 .font {
-  border-bottom: 1px solid #DDD;
   padding: 10px 5px;
   font-size: 0;
 }
 
+.theme-dark .font {
+  border-bottom: 1px solid #444;
+}
+
+.theme-light .font {
+  border-bottom: 1px solid #DDD;
+}
+
 .font:last-of-type {
   border-bottom: 0;
 }
 
-.font:nth-child(even) {
+.theme-light .font:nth-child(even) {
   background: #F4F4F4;
 }
 
 .font-preview {
   height: 60px;
   width: 100%;
   border: 0;
   display: block;
@@ -52,28 +58,23 @@ body {
 .font-name {
   display: inline;
 }
 
 .font-css-code {
   max-width: 100%;
   overflow: hidden;
   text-overflow: ellipsis;
-  background: white;
   padding: 5px;
-  border: 1px dotted #CCC;
 }
 
-.font-is-local,
-.font-is-remote,
-.font-format-url,
-.font-css {
-  color: #999
+.theme-light .font-css-code,
+.theme-light .font-url {
+  border: 1px solid #CCC;
+  background: white;
 }
 
-.font-url {
-  border: 1px solid #CCC;
-  color: #888;
+.theme-dark .font-css-code,
+.theme-dark .font-url {
+  border: 1px solid #333;
+  background: black;
+  color: white;
 }
-
-.font-url:focus {
-  color: black;
-}
--- a/browser/themes/osx/devtools/layoutview.css
+++ b/browser/themes/osx/devtools/layoutview.css
@@ -1,22 +1,25 @@
 /* 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/. */
 
-body {
-  background: url(layout-background-grid.png), -moz-radial-gradient(50% 70%, circle cover, hsl(210,53%,45%) 0%, hsl(210,54%,33%) 100%);
+.theme-dark .theme-body {
+  background-image: url(layout-background-grid.png);
+}
+
+.theme-light .theme-body {
+  background-image: url(layout-background-grid.png), -moz-radial-gradient(50% 70%, circle cover, hsl(210,53%,45%) 0%, hsl(210,54%,33%) 100%);
+}
+
+.theme-body {
   color: hsl(210,100%,85%);
   -moz-box-sizing: border-box;
 }
 
-#element-size {
-  color: hsl(210,100%,95%);
-}
-
 #main {
   border-color: hsla(210,100%,85%,0.7);
   border-style: dotted;
 }
 
 #main > .border {
   color: hsl(210,53%,45%);
 }
new file mode 100644
--- /dev/null
+++ b/browser/themes/osx/devtools/light-theme.css
@@ -0,0 +1,107 @@
+/* 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/. */
+
+/* According to:
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=715472#c17
+ */
+.theme-body {
+  background: white;
+  color: black;
+}
+
+.theme-twisty {
+  cursor: pointer;
+  margin-right: 5px;
+}
+
+.theme-twisty:-moz-focusring {
+  outline-style: none;
+}
+
+.theme-twisty:not([open]) {
+  width: 0;
+  height: 0;
+  border-top: 5px solid transparent;
+  border-bottom: 5px solid transparent;
+  border-left: 5px solid rgba(0,0,0,0.5);
+  margin-left: 5px;
+}
+
+.theme-twisty[open] {
+  width: 10px;
+  height: 10px;
+  background-image: linear-gradient(to bottom right, transparent 68%,  rgba(0,0,0,0.5) 68%);
+}
+
+.theme-checkbox {
+  display: inline-block;
+  border: 1px solid rgba(160,160,160,0.4);
+  width: 6px;
+  height: 6px;
+  padding: 2px;
+  background-color: transparent;
+  background-repeat: no-repeat;
+  outline: none;
+}
+
+.theme-checkbox[checked] {
+  background-clip: content-box;
+  background-image: linear-gradient(to bottom right, transparent 48%, rgba(160,160,160,1) 48%, rgba(160,160,160,1) 52%, transparent 52%),
+                    linear-gradient(to bottom left, transparent 48%, rgba(160,160,160,1) 48%, rgba(160,160,160,1) 52%, transparent 52%);
+}
+
+.theme-selected {
+  background-color: hsl(0,0%,90%);
+}
+
+.theme-bg-darker {
+  background: #F9F9F9;
+}
+
+.theme-link { /* blue */
+  color: hsl(208,56%,40%);
+}
+
+.theme-comment { /* grey */
+  color: hsl(90,2%,46%);
+}
+
+.theme-gutter {
+  background-color: hsl(0,0%,90%);
+  color: #667380;
+  border-color: hsl(0,0%,65%);
+}
+
+.theme-separator { /* grey */
+  border-color: #cddae5;
+}
+
+.theme-fg-color1 { /* green */
+  color: hsl(72,100%,27%);
+}
+
+.theme-fg-color2 { /* blue */
+  color: hsl(208,56%,40%);
+}
+
+.theme-fg-color3 { /* dark blue */
+  color: hsl(208,81%,21%)
+}
+
+.theme-fg-color4 { /* Orange */
+  color: hsl(24,85%,39%);
+}
+
+.theme-fg-color5 { /* Yellow */
+  color: #a18650;
+}
+
+.theme-fg-color6 { /* Orange */
+  color: hsl(24,85%,39%);
+}
+
+.theme-fg-color7 { /* Red */
+  color: #bf5656;
+}
--- a/browser/themes/osx/devtools/markup-view.css
+++ b/browser/themes/osx/devtools/markup-view.css
@@ -2,24 +2,16 @@
  * 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/. */
 
 * {
   padding: 0;
   margin: 0;
 }
 
-body {
-  color: hsl(0,0%,50%);
-}
-
-.text {
-  color: black;
-}
-
 .newattr {
   cursor: pointer;
 }
 
 .selected {
   background-color: hsl(0,0%,90%);
 }
 
@@ -36,25 +28,16 @@ li.container {
 }
 
 .codebox {
   padding-left: 14px;
 }
 
 .expander {
   position: absolute;
-  -moz-appearance: treetwisty;
-  top: 0;
-  left: 0;
-  width: 14px;
-  height: 14px;
-}
-
-.expander[expanded] {
-  -moz-appearance: treetwistyopen;
 }
 
 .more-nodes {
   padding-left: 16px;
 }
 
 .styleinspector-propertyeditor {
   border: 1px solid #CCC;
new file mode 100644
--- /dev/null
+++ b/browser/themes/osx/devtools/ruleview.css
@@ -0,0 +1,126 @@
+/* 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/. */
+
+.ruleview {
+  height: 100%;
+}
+
+.ruleview-rule-source {
+  -moz-padding-start: 5px;
+  cursor: pointer;
+  text-align: right;
+  float: right;
+  -moz-user-select: none;
+}
+
+.ruleview-rule-inheritance {
+  border-top-width: 1px;
+  border-bottom-width: 1px;
+  border-top-style: solid;
+  border-bottom-style: solid;
+  padding: 1px 4px;
+  margin-top: 4px;
+  -moz-user-select: none;
+}
+
+.ruleview-rule-source:hover {
+  text-decoration: underline;
+}
+
+.ruleview-rule {
+  padding: 2px 4px;
+}
+
+.ruleview-rule + .ruleview-rule {
+  border-top-width: 1px;
+  border-top-style: dotted;
+}
+
+.ruleview-warning {
+  background: url("chrome://browser/skin/devtools/alerticon-warning.png");
+  -moz-margin-start: 5px;
+  vertical-align: middle;
+  width: 13px;
+  height: 12px;
+}
+
+.ruleview-ruleopen {
+  -moz-padding-end: 5px;
+}
+
+.ruleview-ruleclose {
+  cursor: text;
+  padding-right: 20px;
+}
+
+.ruleview-propertylist {
+  list-style: none;
+  padding: 0;
+  margin: 0;
+}
+
+.ruleview-rule:not(:hover) .ruleview-enableproperty {
+  visibility: hidden;
+}
+
+.ruleview-expander {
+  display: inline-block;
+}
+
+.ruleview-newproperty {
+  /* (enable checkbox width: 12px) + (expander width: 15px) */
+  -moz-margin-start: 27px;
+}
+
+.ruleview-namecontainer,
+.ruleview-propertycontainer,
+.ruleview-propertyname,
+.ruleview-propertyvalue {
+  text-decoration: inherit;
+}
+
+.ruleview-computedlist {
+  list-style: none;
+  padding: 0;
+}
+
+.ruleview-computed {
+  -moz-margin-start: 35px;
+}
+
+.ruleview-overridden {
+  text-decoration: line-through;
+}
+
+.styleinspector-propertyeditor {
+  border: 1px solid #CCC;
+  padding: 0;
+}
+
+.ruleview-property {
+  border-left: 2px solid transparent;
+}
+
+.ruleview-property  > * {
+  vertical-align: middle;
+}
+
+.ruleview-property[dirty] {
+  border-left-color: #68E268;
+}
+
+.ruleview-namecontainer > .ruleview-propertyname,
+.ruleview-propertycontainer > .ruleview-propertyvalue {
+  border-bottom: 1px dashed transparent;
+}
+
+.ruleview-namecontainer:hover > .ruleview-propertyname,
+.ruleview-propertycontainer:hover > .ruleview-propertyvalue {
+  border-bottom-color: hsl(0,0%,50%);
+}
+
+.ruleview-selector-separator, .ruleview-selector-unmatched {
+  color: #888;
+}
+
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -194,27 +194,29 @@ browser.jar:
   skin/classic/browser/tabbrowser/tabDragIndicator@2x.png                (tabbrowser/tabDragIndicator.png)
   skin/classic/browser/tabview/close.png                    (tabview/close.png)
   skin/classic/browser/tabview/edit-light.png               (tabview/edit-light.png)
   skin/classic/browser/tabview/search.png                   (tabview/search.png)
   skin/classic/browser/tabview/stack-expander.png           (tabview/stack-expander.png)
   skin/classic/browser/tabview/tabview.png                  (tabview/tabview.png)
   skin/classic/browser/tabview/tabview.css                  (tabview/tabview.css)
 * skin/classic/browser/devtools/common.css                  (devtools/common.css)
+  skin/classic/browser/devtools/dark-theme.css              (devtools/dark-theme.css)
+  skin/classic/browser/devtools/light-theme.css             (devtools/light-theme.css)
   skin/classic/browser/devtools/widgets.css                 (devtools/widgets.css)
   skin/classic/browser/devtools/arrows.png                  (devtools/arrows.png)
   skin/classic/browser/devtools/commandline.png             (devtools/commandline.png)
   skin/classic/browser/devtools/command-paintflashing.png   (devtools/command-paintflashing.png)
   skin/classic/browser/devtools/command-responsivemode.png  (devtools/command-responsivemode.png)
   skin/classic/browser/devtools/command-scratchpad.png      (devtools/command-scratchpad.png)
   skin/classic/browser/devtools/command-tilt.png            (devtools/command-tilt.png)
   skin/classic/browser/devtools/alerticon-warning.png       (devtools/alerticon-warning.png)
   skin/classic/browser/devtools/goto-mdn.png                (devtools/goto-mdn.png)
-  skin/classic/browser/devtools/csshtmltree.css             (devtools/csshtmltree.css)
-  skin/classic/browser/devtools/commandline.css                    (devtools/commandline.css)
+  skin/classic/browser/devtools/ruleview.css                (devtools/ruleview.css)
+  skin/classic/browser/devtools/commandline.css             (devtools/commandline.css)
   skin/classic/browser/devtools/markup-view.css             (devtools/markup-view.css)
   skin/classic/browser/devtools/orion.css                   (devtools/orion.css)
   skin/classic/browser/devtools/orion-container.css         (devtools/orion-container.css)
   skin/classic/browser/devtools/orion-task.png              (devtools/orion-task.png)
   skin/classic/browser/devtools/orion-breakpoint.png        (devtools/orion-breakpoint.png)
   skin/classic/browser/devtools/orion-debug-location.png    (devtools/orion-debug-location.png)
   skin/classic/browser/devtools/toolbarbutton-close.png     (devtools/toolbarbutton-close.png)
 * skin/classic/browser/devtools/webconsole.css                  (devtools/webconsole.css)
@@ -267,26 +269,29 @@ browser.jar:
   skin/classic/browser/devtools/debugger-step-over.png      (devtools/debugger-step-over.png)
   skin/classic/browser/devtools/responsive-se-resizer.png   (devtools/responsive-se-resizer.png)
   skin/classic/browser/devtools/responsive-vertical-resizer.png (devtools/responsive-vertical-resizer.png)
   skin/classic/browser/devtools/responsive-background.png   (devtools/responsive-background.png)
   skin/classic/browser/devtools/tools-icons-small.png       (devtools/tools-icons-small.png)
   skin/classic/browser/devtools/dock-bottom.png             (devtools/dock-bottom.png)
   skin/classic/browser/devtools/dock-side.png               (devtools/dock-side.png)
   skin/classic/browser/devtools/floating-scrollbars.css     (devtools/floating-scrollbars.css)
+  skin/classic/browser/devtools/floating-scrollbars-light.css (devtools/floating-scrollbars-light.css)
 * skin/classic/browser/devtools/inspector.css               (devtools/inspector.css)
   skin/classic/browser/devtools/toolbox.css                 (devtools/toolbox.css)
   skin/classic/browser/devtools/tool-webconsole.png         (devtools/tool-webconsole.png)
   skin/classic/browser/devtools/tool-debugger.png           (devtools/tool-debugger.png)
   skin/classic/browser/devtools/tool-inspector.png          (devtools/tool-inspector.png)
   skin/classic/browser/devtools/tool-styleeditor.png        (devtools/tool-styleeditor.png)
   skin/classic/browser/devtools/tool-profiler.png           (devtools/tool-profiler.png)
   skin/classic/browser/devtools/close.png                   (devtools/close.png)
   skin/classic/browser/devtools/undock.png                  (devtools/undock.png)
   skin/classic/browser/devtools/font-inspector.css          (devtools/font-inspector.css)
+  skin/classic/browser/devtools/computedview.css            (devtools/computedview.css)
+  skin/classic/browser/devtools/arrow-e.png                 (devtools/arrow-e.png)
 #ifdef MOZ_SERVICES_SYNC
   skin/classic/browser/sync-throbber.png
   skin/classic/browser/sync-16.png
   skin/classic/browser/sync-32.png
   skin/classic/browser/sync-bg.png
   skin/classic/browser/sync-128.png
   skin/classic/browser/sync-desktopIcon.png
   skin/classic/browser/sync-mobileIcon.png
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..09691bc5e540079d8b83efbd4aa78f2b71e03d95
GIT binary patch
literal 177
zc%17D@N?(olHy`uVBq!ia0vp^tU%1c!3HD^Kbl$tsR~aQ#}JO_<QXREoZJG>84N%F
z|NmdoVMgMiHL8}{+htBLUKCE#OJm{IySrmLYe%uej`UxLWHu;tL~JM%2Wn_2IR2#4
zGU4Df&f<vlwRh$*ALjXLyE#JoSK4!**j=JM2XAHXN_*lHu|u#;QS9rP=<_^BEES}l
bsW2G6i@Q8)Zo@918yGxY{an^LB{Ts5rvyQk
--- a/browser/themes/windows/devtools/common.css
+++ b/browser/themes/windows/devtools/common.css
@@ -137,16 +137,17 @@
 }
 
 /* Text input */
 
 .devtools-textinput,
 .devtools-searchinput {
   -moz-appearance: none;
   margin: 0 3px;
+  min-height: 22px;
   border: 1px solid hsla(211,68%,6%,.6);
   box-shadow: inset 0 1px 0 hsla(211,68%,6%,.05), 0 0 0 1px hsla(210,40%,83%,.1);
   border-radius: 2px;
   background-color: transparent;
   background-image: -moz-linear-gradient(hsla(210,16%,76%,.15), hsla(210,16%,76%,.35));
   padding: 3px;
   transition-property: background-color, border-color, box-shadow;
   transition-duration: 150ms;
@@ -358,39 +359,9 @@
   box-shadow: 0 1px 0 hsla(0,0%,100%,.05) inset, 0 -2px 0 hsla(206,37%,4%,.05) inset, 0 -1px 1px hsla(206,37%,4%,.1) inset;
 }
 
 .devtools-sidebar-tabs > tabs > tab[selected=true]:hover:active {
   background-image: linear-gradient(#1f3e4f, #1b3243), @solidSeparator@;
   box-shadow: 0 1px 0 hsla(0,0%,100%,.05) inset, 0 -2px 0 hsla(206,37%,4%,.05) inset, 0 -1px 1px hsla(206,37%,4%,.1) inset;
 }
 
-/* Theme */
-
-.devtools-theme-background {
-  background-color: white;
-}
-
-.devtools-theme-comment {
-  color: hsl(90,2%,46%); /* grey */
-}
-
-.devtools-theme-keyword {
-  color: hsl(276,44%,45%); /* purple */
-}
-
-.devtools-theme-string {
-  color: hsl(72,100%,27%); /* green */
-}
-
-.devtools-theme-tagname {
-  color: hsl(208,81%,21%); /* dark blue */
-}
-
-.devtools-theme-attrname {
-  color: hsl(208,56%,40%); /* blue */
-}
-
-.devtools-theme-attrvalue {
-  color: hsl(24,85%,39%); /* orange */
-}
-
 %include ../../shared/devtools/common.inc.css
new file mode 100644
--- /dev/null
+++ b/browser/themes/windows/devtools/computedview.css
@@ -0,0 +1,175 @@
+/* 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/. */
+
+/* Take away these two :visited rules to get a core dumper     */
+/* See https://bugzilla.mozilla.org/show_bug.cgi?id=575675#c30 */
+.link,
+.link:visited {
+  color: #0091ff;
+}
+.link,
+.helplink,
+.link:visited,
+.helplink:visited {
+  text-decoration: none;
+}
+.link:hover {
+  text-decoration: underline;
+}
+
+/* From content */
+
+* {
+  -moz-box-sizing: border-box;
+}
+
+:root {
+  height: 100%;
+}
+
+body {
+  margin: 0;
+  display : flex;
+  flex-direction: column;
+  height: 100%;
+}
+
+#propertyContainer {
+  -moz-user-select: text;
+  overflow: auto;
+  min-height: 0;
+  flex: 1;
+}
+
+.property-view-hidden,
+.property-content-hidden {
+  display: none;
+}
+
+.property-view {
+  clear: both;
+  padding: 2px 0 2px 17px;
+}
+
+.property-view > * {
+  display: inline-block;
+  vertical-align: middle;
+}
+
+.property-name {
+  width: 50%;
+  overflow-x: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.property-value {
+  width: 50%;
+  max-width: 100%;
+  overflow-x: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  background-image: url(arrow-e.png);
+  background-repeat: no-repeat;
+  background-size: 5px 8px;
+  background-position: 2px center;
+  padding-left: 10px;
+}
+
+.other-property-value {
+  background-image: url(arrow-e.png);
+  background-repeat: no-repeat;
+  background-size: 5px 8px;
+  background-position: left center;
+  padding-left: 8px;
+}
+
+@media (min-width: 400px) {
+  .property-name {
+    width: 200px;
+  }
+  .property-value {
+    width: auto;
+  }
+}
+
+.property-content {
+  padding-left: 17px;
+}
+
+/* From skin */
+.helplink {
+  /* FIXME: remove this image 
+  background-image: url("chrome://browser/skin/devtools/goto-mdn.png");
+  */
+}
+
+.expander {
+  visibility: hidden;
+  margin-left: -12px!important;
+}
+
+.expander[open] {
+  margin-left: -17px!important;
+}
+
+.expandable {
+  visibility: visible;
+}
+
+.match {
+  visibility: hidden;
+}
+
+.matchedselectors > p {
+  clear: both;
+  margin: 0 2px 0 0;
+  padding: 2px;
+  overflow-x: hidden;
+  border-style: dotted;
+  border-color: rgba(128,128,128,0.4);
+  border-width: 1px 1px 0 1px;
+}
+
+.matchedselectors > p:last-of-type {
+  border-bottom-width: 1px;
+}
+
+/* This rule is necessary because Templater.jsm breaks LTR TDs in RTL docs */
+.rule-text {
+  direction: ltr;
+}
+
+.matched {
+  text-decoration: line-through;
+}
+
+.parentmatch {
+  opacity: 0.5;
+}
+
+#noResults {
+  font-size: 110%;
+  margin: 5px;
+  text-align: center;
+}
+
+.onlyuserstyles {
+  cursor: pointer;
+}
+
+.legendKey {
+  margin: 0 5px;
+}
+
+.devtools-toolbar {
+  width: 100%;
+}
+
+.link {
+  padding: 0 3px;
+  cursor: pointer;
+  float: right;
+}
deleted file mode 100644
--- a/browser/themes/windows/devtools/csshtmltree.css
+++ /dev/null
@@ -1,295 +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/. */
-
-:root {
-  -moz-appearance: none;
-  background: -moz-Field;
-  color: -moz-FieldText;
-  font: message-box;
-  font-family: monospace;
-}
-
-/* Take away these two :visited rules to get a core dumper     */
-/* See https://bugzilla.mozilla.org/show_bug.cgi?id=575675#c30 */
-.link,
-.link:visited {
-  color: #0091ff;
-}
-.link,
-.helplink,
-.link:visited,
-.helplink:visited {
-  text-decoration: none;
-}
-.link:hover {
-  text-decoration: underline;
-}
-
-.helplink {
-  height: 14px;
-  width: 0;
-  overflow: hidden;
-  -moz-padding-start: 14px;
-  background-image: url("chrome://browser/skin/devtools/goto-mdn.png");
-  -moz-margin-end: 2px;
-  cursor: pointer;
-}
-
-.property-view:not(:hover) > .helplink-container {
-  visibility: hidden;
-}
-
-.rulelink {
-  color: -moz-dialogtext;
-  padding: 0;
-}
-
-.expander {
-  width: 9px;
-  height: 9px;
-  -moz-margin-start: 5px;
-  -moz-margin-end: 5px;
-  background: url("chrome://global/skin/tree/twisty-clsd.png") center center no-repeat;
-  vertical-align: middle;
-}
-
-.expander[open] {
-  background-image: url("chrome://global/skin/tree/twisty-open.png");
-}
-
-.match {
-  visibility: hidden;
-}
-
-.expandable {
-  cursor: pointer;
-  visibility: visible;
-}
-
-.expander-container {
-  vertical-align: text-top;
-}
-
-.property-name {
-  padding: 2px 0;
-  color: -moz-FieldText;
-}
-
-.property-value {
-  padding: 0;
-  -moz-padding-end: 6px;
-  color: grey;
-  width: 100%;
-}
-
-.rule-link {
-  text-align: end;
-  -moz-padding-start: 10px;
-  cursor: pointer;
-}
-
-/* This rule is necessary because Templater.jsm breaks LTR TDs in RTL docs */
-.rule-text {
-  direction: ltr;
-  padding: 0;
-  -moz-padding-start: 20px;
-  vertical-align: text-bottom;
-}
-
-.bestmatch {
-  color: black;
-}
-.matched {
-  text-decoration: line-through;
-}
-.parentmatch {
-  color: #666;
-}
-
-#propertyContainer {
-  border-collapse: collapse;
-}
-
-.darkrow {
-  background-color: rgba(0,0,0,.022);
-}
-
-#noResults {
-  font-size: 18px;
-  margin-top: 5px;
-  text-align: center;
-}
-
-.headerControls {
-  color: -moz-dialogtext;
-  background-color: -moz-dialog;
-}
-
-.includebrowserstyles {
-  cursor: pointer;
-  font-size: 11px;
-}
-
-#footer {
-  border-top: 1px solid -moz-dialog;
-}
-
-.legendKey {
-  margin: 0 5px;
-}
-
-/**
- * CSS Rule View
- */
-
-.ruleview {
-  background-color: white;
-}
-
-.ruleview-rule-source {
-  color: hsl(90,2%,46%); /* grey */
-  -moz-padding-start: 5px;
-  cursor: pointer;
-  text-align: right;
-  float: right;
-  -moz-user-select: none;
-}
-
-.ruleview-rule-inheritance {
-  background-color: hsl(0,0%,90%);
-  color: hsl(0,0%,50%);
-  border-top: 1px solid hsl(0,0%,65%);
-  border-bottom: 1px solid hsl(0,0%,65%);
-  padding: 1px 4px;
-  margin-top: 4px;
-  -moz-user-select: none;
-}
-
-.ruleview-rule-source:hover {
-  text-decoration: underline;
-}
-
-.ruleview-rule {
-  padding: 2px 4px;
-}
-
-.ruleview-rule + .ruleview-rule {
-  border-top: 1px dotted #cddae5;
-}
-
-.ruleview-warning {
-  background: url("chrome://browser/skin/devtools/alerticon-warning.png");
-  -moz-margin-start: 5px;
-  vertical-align: middle;
-  width: 13px;
-  height: 12px;
-}
-
-.ruleview-ruleopen {
-  -moz-padding-end: 5px;
-}
-
-.ruleview-ruleclose {
-  cursor: text;
-  padding-right: 20px;
-}
-
-.ruleview-propertylist {
-  list-style: none;
-  padding: 0;
-  margin: 0;
-}
-
-.ruleview-enableproperty {
-  height: 10px;
-  width: 10px;
-  -moz-margin-start: 1px;
-  -moz-margin-end: 0;
-  transition: opacity 100ms;
-  transition-delay: 200ms;
-}
-
-.ruleview-property:not(:hover) > .ruleview-enableproperty[checked] {
-  opacity: 0;
-  transition: none;
-}
-
-.ruleview-expander {
-  width: 8px;
-  height: 8px;
-  background: url("chrome://browser/skin/devtools/arrows.png") 24px 0;
-  cursor: pointer;
-  -moz-margin-start: 2px;
-  -moz-margin-end: 5px;
-}
-
-.ruleview-expander.styleinspector-open {
-  background-position: 8px 0;
-}
-
-.ruleview-newproperty {
-  /* (enable checkbox width: 12px) + (expander width: 15px) */
-  -moz-margin-start: 27px;
-}
-
-.ruleview-propertyname {
-  padding: 1px 0;
-  color: hsl(276,44%,45%); /* purple */
-}
-
-.ruleview-propertyvalue {
-  padding: 1px 0;
-}
-
-.ruleview-namecontainer,
-.ruleview-propertycontainer,
-.ruleview-propertyname,
-.ruleview-propertyvalue {
-  text-decoration: inherit;
-}
-
-.ruleview-computedlist {
-  list-style: none;
-  padding: 0;
-}
-
-.ruleview-computed {
-  -moz-margin-start: 4em;
-}
-
-.ruleview-overridden {
-  text-decoration: line-through;
-}
-
-.styleinspector-propertyeditor {
-  border: 1px solid #CCC;
-  padding: 0;
-  box-shadow: 2px 2px 2px #CCC;
-}
-
-.ruleview-property {
-  border-left: 2px solid transparent;
-}
-
-.ruleview-property  > * {
-  vertical-align: middle;
-}
-
-.ruleview-property[dirty] {
-  border-left-color: #68E268;
-}
-
-.ruleview-namecontainer > .ruleview-propertyname,
-.ruleview-propertycontainer > .ruleview-propertyvalue {
-  border-bottom: 1px dashed transparent;
-}
-
-.ruleview-namecontainer:hover > .ruleview-propertyname,
-.ruleview-propertycontainer:hover > .ruleview-propertyvalue {
-  border-bottom-color: hsl(0,0%,50%);
-}
-
-.ruleview-selector-separator, .ruleview-selector-unmatched {
-  color: #888;
-}
new file mode 100644
--- /dev/null
+++ b/browser/themes/windows/devtools/dark-theme.css
@@ -0,0 +1,107 @@
+/* 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/. */
+
+/* According to:
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=715472#c17
+ */
+.theme-body {
+  background: #131c26;
+  color: #8fa1b2
+}
+
+.theme-twisty {
+  cursor: pointer;
+  margin-right: 5px;
+}
+
+.theme-twisty:-moz-focusring {
+  outline-style: none;
+}
+
+.theme-twisty:not([open]) {
+  width: 0;
+  height: 0;
+  border-top: 5px solid transparent;
+  border-bottom: 5px solid transparent;
+  border-left: 5px solid rgba(255,255,255,0.5);
+  margin-left: 5px;
+}
+
+.theme-twisty[open] {
+  width: 10px;
+  height: 10px;
+  background-image: linear-gradient(to bottom right, transparent 68%,  rgba(255,255,255,0.5) 68%);
+}
+
+.theme-checkbox {
+  display: inline-block;
+  border: 1px solid rgba(160,160,160,0.4);
+  width: 6px;
+  height: 6px;
+  padding: 2px;
+  background-color: transparent;
+  background-repeat: no-repeat;
+  outline: none;
+}
+
+.theme-checkbox[checked] {
+  background-clip: content-box;
+  background-image: linear-gradient(to bottom right, transparent 48%, rgba(160,160,160,1) 48%, rgba(160,160,160,1) 52%, transparent 52%),
+                    linear-gradient(to bottom left, transparent 48%, rgba(160,160,160,1) 48%, rgba(160,160,160,1) 52%, transparent 52%);
+}
+
+.theme-selected {
+  background: #26384E;
+}
+
+.theme-bg-darker {
+  background-color: rgba(0,0,0,0.1);
+}
+
+.theme-link { /* blue */
+  color: #3689b2;
+}
+
+.theme-comment { /* grey */
+  color: #5c6773;
+}
+
+.theme-gutter {
+  background-color: #0f171f;
+  color: #667380;
+  border-color: #303b47;
+}
+
+.theme-separator { /* grey */
+  border-color: #303b47;
+}
+
+.theme-fg-color1 { /* green */
+  color: #5c9966;
+}
+
+.theme-fg-color2 { /* blue */
+  color: #3689b2;
+}
+
+.theme-fg-color3 { /* pink/lavender */
+  color: #a673bf;
+}
+
+.theme-fg-color4 { /* purple/violet */
+  color: #6270b2;
+}
+
+.theme-fg-color5 { /* Yellow */
+  color: #a18650;
+}
+
+.theme-fg-color6 { /* Orange */
+  color: #b26b47;
+}
+
+.theme-fg-color7 { /* Red */
+  color: #bf5656;
+}
new file mode 100644
--- /dev/null
+++ b/browser/themes/windows/devtools/floating-scrollbars-light.css
@@ -0,0 +1,10 @@
+/* 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/. */
+
+@import url("floating-scrollbars.css");
+
+scrollbar thumb {
+  background-color: rgba(170,170,170,0.2) !important;
+}
--- a/browser/themes/windows/devtools/floating-scrollbars.css
+++ b/browser/themes/windows/devtools/floating-scrollbars.css
@@ -5,29 +5,29 @@ scrollbar {
   position: relative;
   background-color: transparent;
   background-image: none;
   z-index: 2147483647;
   padding: 2px;
 }
 
 scrollbar[orient="vertical"] {
-  -moz-margin-start: -8px;
-  min-width: 8px;
-  max-width: 8px;
+  -moz-margin-start: -10px;
+  min-width: 10px;
+  max-width: 10px;
 }
 
 scrollbar[orient="horizontal"] {
-  margin-top: -8px;
-  min-height: 8px;
-  max-height: 8px;
+  margin-top: -10px;
+  min-height: 10px;
+  max-height: 10px;
 }
 
 scrollbar thumb {
   -moz-appearance: none !important;
   border-width: 0px !important;
-  background-color: rgba(0,0,0,0.2) !important;
+  background-color: rgba(170,170,170,0.2) !important;
   border-radius: 3px !important;
 }
 
 scrollbar scrollbarbutton, scrollbar gripper {
   display: none;
 }
--- a/browser/themes/windows/devtools/font-inspector.css
+++ b/browser/themes/windows/devtools/font-inspector.css
@@ -1,14 +1,13 @@
 * {
   -moz-box-sizing: border-box;
 }
 
 body {
-  background: #F9F9F9;
   margin: 0;
   padding-bottom: 20px;
 }
 
 #all-fonts {
   padding: 0 5px;
   margin: 0;
 }
@@ -19,26 +18,33 @@ body {
   margin: 3px;
   cursor: pointer;
   position: fixed;
   bottom: 0;
   right: 0;
 }
 
 .font {
-  border-bottom: 1px solid #DDD;
   padding: 10px 5px;
   font-size: 0;
 }
 
+.theme-dark .font {
+  border-bottom: 1px solid #444;
+}
+
+.theme-light .font {
+  border-bottom: 1px solid #DDD;
+}
+
 .font:last-of-type {
   border-bottom: 0;
 }
 
-.font:nth-child(even) {
+.theme-light .font:nth-child(even) {
   background: #F4F4F4;
 }
 
 .font-preview {
   height: 60px;
   width: 100%;
   border: 0;
   display: block;
@@ -52,28 +58,23 @@ body {
 .font-name {
   display: inline;
 }
 
 .font-css-code {
   max-width: 100%;
   overflow: hidden;
   text-overflow: ellipsis;
-  background: white;
   padding: 5px;
-  border: 1px dotted #CCC;
 }
 
-.font-is-local,
-.font-is-remote,
-.font-format-url,
-.font-css {
-  color: #999
+.theme-light .font-css-code,
+.theme-light .font-url {
+  border: 1px solid #CCC;
+  background: white;
 }
 
-.font-url {
-  border: 1px solid #CCC;
-  color: #888;
+.theme-dark .font-css-code,
+.theme-dark .font-url {
+  border: 1px solid #333;
+  background: black;
+  color: white;
 }
-
-.font-url:focus {
-  color: black;
-}
--- a/browser/themes/windows/devtools/layoutview.css
+++ b/browser/themes/windows/devtools/layoutview.css
@@ -1,22 +1,25 @@
 /* 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/. */
 
-body {
-  background: url(layout-background-grid.png), -moz-radial-gradient(50% 70%, circle cover, hsl(210,53%,45%) 0%, hsl(210,54%,33%) 100%);
+.theme-dark .theme-body {
+  background-image: url(layout-background-grid.png);
+}
+
+.theme-light .theme-body {
+  background-image: url(layout-background-grid.png), -moz-radial-gradient(50% 70%, circle cover, hsl(210,53%,45%) 0%, hsl(210,54%,33%) 100%);
+}
+
+.theme-body {
   color: hsl(210,100%,85%);
   -moz-box-sizing: border-box;
 }
 
-#element-size {
-  color: hsl(210,100%,95%);
-}
-
 #main {
   border-color: hsla(210,100%,85%,0.7);
   border-style: dotted;
 }
 
 #main > .border {
   color: hsl(210,53%,45%);
 }
new file mode 100644
--- /dev/null
+++ b/browser/themes/windows/devtools/light-theme.css
@@ -0,0 +1,107 @@
+/* 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/. */
+
+/* According to:
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=715472#c17
+ */
+.theme-body {
+  background: white;
+  color: black;
+}
+
+.theme-twisty {
+  cursor: pointer;
+  margin-right: 5px;
+}
+
+.theme-twisty:-moz-focusring {
+  outline-style: none;
+}
+
+.theme-twisty:not([open]) {
+  width: 0;
+  height: 0;
+  border-top: 5px solid transparent;
+  border-bottom: 5px solid transparent;
+  border-left: 5px solid rgba(0,0,0,0.5);
+  margin-left: 5px;
+}
+
+.theme-twisty[open] {
+  width: 10px;
+  height: 10px;
+  background-image: linear-gradient(to bottom right, transparent 68%,  rgba(0,0,0,0.5) 68%);
+}
+
+.theme-checkbox {
+  display: inline-block;
+  border: 1px solid rgba(160,160,160,0.4);
+  width: 6px;
+  height: 6px;
+  padding: 2px;
+  background-color: transparent;
+  background-repeat: no-repeat;
+  outline: none;
+}
+
+.theme-checkbox[checked] {
+  background-clip: content-box;
+  background-image: linear-gradient(to bottom right, transparent 48%, rgba(160,160,160,1) 48%, rgba(160,160,160,1) 52%, transparent 52%),
+                    linear-gradient(to bottom left, transparent 48%, rgba(160,160,160,1) 48%, rgba(160,160,160,1) 52%, transparent 52%);
+}
+
+.theme-selected {
+  background-color: hsl(0,0%,90%);
+}
+
+.theme-bg-darker {
+  background: #F9F9F9;
+}
+
+.theme-link { /* blue */
+  color: hsl(208,56%,40%);
+}
+
+.theme-comment { /* grey */
+  color: hsl(90,2%,46%);
+}
+
+.theme-gutter {
+  background-color: hsl(0,0%,90%);
+  color: #667380;
+  border-color: hsl(0,0%,65%);
+}
+
+.theme-separator { /* grey */
+  border-color: #cddae5;
+}
+
+.theme-fg-color1 { /* green */
+  color: hsl(72,100%,27%);
+}
+
+.theme-fg-color2 { /* blue */
+  color: hsl(208,56%,40%);
+}
+
+.theme-fg-color3 { /* dark blue */
+  color: hsl(208,81%,21%)
+}
+
+.theme-fg-color4 { /* Orange */
+  color: hsl(24,85%,39%);
+}
+
+.theme-fg-color5 { /* Yellow */
+  color: #a18650;
+}
+
+.theme-fg-color6 { /* Orange */
+  color: hsl(24,85%,39%);
+}
+
+.theme-fg-color7 { /* Red */
+  color: #bf5656;
+}
--- a/browser/themes/windows/devtools/markup-view.css
+++ b/browser/themes/windows/devtools/markup-view.css
@@ -2,24 +2,16 @@
  * 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/. */
 
 * {
   padding: 0;
   margin: 0;
 }
 
-body {
-  color: hsl(0,0%,50%);
-}
-
-.text {
-  color: black;
-}
-
 .newattr {
   cursor: pointer;
 }
 
 .selected {
   background-color: hsl(0,0%,90%);
 }
 
@@ -36,27 +28,16 @@ li.container {
 }
 
 .codebox {
   padding-left: 14px;
 }
 
 .expander {
   position: absolute;
-  top: 5px;
-  left: 0;
-  width: 14px;
-  height: 14px;
-  background-repeat: no-repeat;
-  background-position: center;
-  background-image: url("chrome://global/skin/tree/twisty-clsd.png");
-}
-
-.expander[expanded] {
-  background-image: url("chrome://global/skin/tree/twisty-open.png");
 }
 
 .more-nodes {
   padding-left: 16px;
 }
 
 .styleinspector-propertyeditor {
   border: 1px solid #CCC;
new file mode 100644
--- /dev/null
+++ b/browser/themes/windows/devtools/ruleview.css
@@ -0,0 +1,125 @@
+/* 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/. */
+
+.ruleview {
+  height: 100%;
+}
+
+.ruleview-rule-source {
+  -moz-padding-start: 5px;
+  cursor: pointer;
+  text-align: right;
+  float: right;
+  -moz-user-select: none;
+}
+
+.ruleview-rule-inheritance {
+  border-top-width: 1px;
+  border-bottom-width: 1px;
+  border-top-style: solid;
+  border-bottom-style: solid;
+  padding: 1px 4px;
+  margin-top: 4px;
+  -moz-user-select: none;
+}
+
+.ruleview-rule-source:hover {
+  text-decoration: underline;
+}
+
+.ruleview-rule {
+  padding: 2px 4px;
+}
+
+.ruleview-rule + .ruleview-rule {
+  border-top-width: 1px;
+  border-top-style: dotted;
+}
+
+.ruleview-warning {
+  background: url("chrome://browser/skin/devtools/alerticon-warning.png");
+  -moz-margin-start: 5px;
+  vertical-align: middle;
+  width: 13px;
+  height: 12px;
+}
+
+.ruleview-ruleopen {
+  -moz-padding-end: 5px;
+}
+
+.ruleview-ruleclose {
+  cursor: text;
+  padding-right: 20px;
+}
+
+.ruleview-propertylist {
+  list-style: none;
+  padding: 0;
+  margin: 0;
+}
+
+.ruleview-rule:not(:hover) .ruleview-enableproperty {
+  visibility: hidden;
+}
+
+.ruleview-expander {
+  display: inline-block;
+}
+
+.ruleview-newproperty {
+  /* (enable checkbox width: 12px) + (expander width: 15px) */
+  -moz-margin-start: 27px;
+}
+
+.ruleview-namecontainer,
+.ruleview-propertycontainer,
+.ruleview-propertyname,
+.ruleview-propertyvalue {
+  text-decoration: inherit;
+}
+
+.ruleview-computedlist {
+  list-style: none;
+  padding: 0;
+}
+
+.ruleview-computed {
+  -moz-margin-start: 35px;
+}
+
+.ruleview-overridden {
+  text-decoration: line-through;
+}
+
+.styleinspector-propertyeditor {
+  border: 1px solid #CCC;
+  padding: 0;
+}
+
+.ruleview-property {
+  border-left: 2px solid transparent;
+}
+
+.ruleview-property  > * {
+  vertical-align: middle;
+}
+
+.ruleview-property[dirty] {
+  border-left-color: #68E268;
+}
+
+.ruleview-namecontainer > .ruleview-propertyname,
+.ruleview-propertycontainer > .ruleview-propertyvalue {
+  border-bottom: 1px dashed transparent;
+}
+
+.ruleview-namecontainer:hover > .ruleview-propertyname,
+.ruleview-propertycontainer:hover > .ruleview-propertyvalue {
+  border-bottom-color: hsl(0,0%,50%);
+}
+
+.ruleview-selector-separator, .ruleview-selector-unmatched {
+  color: #888;
+}
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -136,22 +136,24 @@ browser.jar:
         skin/classic/browser/tabview/edit-light.png                 (tabview/edit-light.png)
         skin/classic/browser/tabview/grain.png                      (tabview/grain.png)
         skin/classic/browser/tabview/search.png                     (tabview/search.png)
         skin/classic/browser/tabview/stack-expander.png             (tabview/stack-expander.png)
         skin/classic/browser/tabview/tabview.png                    (tabview/tabview.png)
         skin/classic/browser/tabview/tabview-inverted.png           (tabview/tabview-inverted.png)
         skin/classic/browser/tabview/tabview.css                    (tabview/tabview.css)
 *       skin/classic/browser/devtools/common.css                    (devtools/common.css)
+        skin/classic/browser/devtools/dark-theme.css                (devtools/dark-theme.css)
+        skin/classic/browser/devtools/light-theme.css               (devtools/light-theme.css)
         skin/classic/browser/devtools/widgets.css                   (devtools/widgets.css)
         skin/classic/browser/devtools/arrows.png                    (devtools/arrows.png)
         skin/classic/browser/devtools/commandline.png               (devtools/commandline.png)
         skin/classic/browser/devtools/alerticon-warning.png         (devtools/alerticon-warning.png)
         skin/classic/browser/devtools/goto-mdn.png                  (devtools/goto-mdn.png)
-        skin/classic/browser/devtools/csshtmltree.css               (devtools/csshtmltree.css)
+        skin/classic/browser/devtools/ruleview.css                  (devtools/ruleview.css)
         skin/classic/browser/devtools/commandline.css               (devtools/commandline.css)
         skin/classic/browser/devtools/command-paintflashing.png     (devtools/command-paintflashing.png)
         skin/classic/browser/devtools/command-responsivemode.png    (devtools/command-responsivemode.png)
         skin/classic/browser/devtools/command-scratchpad.png        (devtools/command-scratchpad.png)
         skin/classic/browser/devtools/command-tilt.png              (devtools/command-tilt.png)
         skin/classic/browser/devtools/markup-view.css               (devtools/markup-view.css)
         skin/classic/browser/devtools/orion.css                     (devtools/orion.css)
         skin/classic/browser/devtools/orion-container.css           (devtools/orion-container.css)
@@ -209,26 +211,29 @@ browser.jar:
         skin/classic/browser/devtools/debugger-step-over.png        (devtools/debugger-step-over.png)
         skin/classic/browser/devtools/responsive-se-resizer.png     (devtools/responsive-se-resizer.png)
         skin/classic/browser/devtools/responsive-vertical-resizer.png (devtools/responsive-vertical-resizer.png)
         skin/classic/browser/devtools/responsive-background.png     (devtools/responsive-background.png)
         skin/classic/browser/devtools/tools-icons-small.png         (devtools/tools-icons-small.png)
         skin/classic/browser/devtools/dock-bottom.png               (devtools/dock-bottom.png)
         skin/classic/browser/devtools/dock-side.png                 (devtools/dock-side.png)
         skin/classic/browser/devtools/floating-scrollbars.css       (devtools/floating-scrollbars.css)
+        skin/classic/browser/devtools/floating-scrollbars-light.css (devtools/floating-scrollbars-light.css)
         skin/classic/browser/devtools/inspector.css                 (devtools/inspector.css)
         skin/classic/browser/devtools/toolbox.css                   (devtools/toolbox.css)
         skin/classic/browser/devtools/tool-webconsole.png           (devtools/tool-webconsole.png)
         skin/classic/browser/devtools/tool-debugger.png             (devtools/tool-debugger.png)
         skin/classic/browser/devtools/tool-inspector.png            (devtools/tool-inspector.png)
         skin/classic/browser/devtools/tool-styleeditor.png          (devtools/tool-styleeditor.png)
         skin/classic/browser/devtools/tool-profiler.png             (devtools/tool-profiler.png)
         skin/classic/browser/devtools/close.png                     (devtools/close.png)
         skin/classic/browser/devtools/undock.png                    (devtools/undock.png)
         skin/classic/browser/devtools/font-inspector.css            (devtools/font-inspector.css)
+        skin/classic/browser/devtools/computedview.css              (devtools/computedview.css)
+        skin/classic/browser/devtools/arrow-e.png                   (devtools/arrow-e.png)
 #ifdef MOZ_SERVICES_SYNC
         skin/classic/browser/sync-throbber.png
         skin/classic/browser/sync-16.png
         skin/classic/browser/sync-32.png
         skin/classic/browser/sync-128.png
         skin/classic/browser/sync-bg.png
         skin/classic/browser/sync-desktopIcon.png
         skin/classic/browser/sync-mobileIcon.png
@@ -371,26 +376,28 @@ browser.jar:
         skin/classic/aero/browser/tabview/edit-light.png             (tabview/edit-light.png)
         skin/classic/aero/browser/tabview/grain.png                  (tabview/grain.png)
         skin/classic/aero/browser/tabview/search.png                 (tabview/search.png)
         skin/classic/aero/browser/tabview/stack-expander.png         (tabview/stack-expander.png)
         skin/classic/aero/browser/tabview/tabview.png                (tabview/tabview.png)
         skin/classic/aero/browser/tabview/tabview-inverted.png       (tabview/tabview-inverted.png)
         skin/classic/aero/browser/tabview/tabview.css                (tabview/tabview.css)
 *       skin/classic/aero/browser/devtools/common.css                (devtools/common.css)
+        skin/classic/aero/browser/devtools/dark-theme.css            (devtools/dark-theme.css)
+        skin/classic/aero/browser/devtools/light-theme.css           (devtools/light-theme.css)
         skin/classic/aero/browser/devtools/widgets.css               (devtools/widgets.css)
         skin/classic/aero/browser/devtools/arrows.png                (devtools/arrows.png)
         skin/classic/aero/browser/devtools/commandline.png           (devtools/commandline.png)
         skin/classic/aero/browser/devtools/command-paintflashing.png  (devtools/command-paintflashing.png)
         skin/classic/aero/browser/devtools/command-responsivemode.png (devtools/command-responsivemode.png)
         skin/classic/aero/browser/devtools/command-scratchpad.png    (devtools/command-scratchpad.png)
         skin/classic/aero/browser/devtools/command-tilt.png          (devtools/command-tilt.png)
         skin/classic/aero/browser/devtools/alerticon-warning.png     (devtools/alerticon-warning.png)
         skin/classic/aero/browser/devtools/goto-mdn.png              (devtools/goto-mdn.png)
-        skin/classic/aero/browser/devtools/csshtmltree.css           (devtools/csshtmltree.css)
+        skin/classic/aero/browser/devtools/ruleview.css              (devtools/ruleview.css)
         skin/classic/aero/browser/devtools/commandline.css           (devtools/commandline.css)
         skin/classic/aero/browser/devtools/markup-view.css           (devtools/markup-view.css)
         skin/classic/aero/browser/devtools/orion.css                 (devtools/orion.css)
         skin/classic/aero/browser/devtools/orion-container.css       (devtools/orion-container.css)
         skin/classic/aero/browser/devtools/orion-task.png            (devtools/orion-task.png)
         skin/classic/aero/browser/devtools/orion-breakpoint.png      (devtools/orion-breakpoint.png)
         skin/classic/aero/browser/devtools/orion-debug-location.png  (devtools/orion-debug-location.png)
         skin/classic/aero/browser/devtools/toolbarbutton-close.png   (devtools/toolbarbutton-close.png)
@@ -444,26 +451,29 @@ browser.jar:
         skin/classic/aero/browser/devtools/debugger-step-over.png    (devtools/debugger-step-over.png)
         skin/classic/aero/browser/devtools/responsive-se-resizer.png (devtools/responsive-se-resizer.png)
         skin/classic/aero/browser/devtools/responsive-vertical-resizer.png (devtools/responsive-vertical-resizer.png)
         skin/classic/aero/browser/devtools/responsive-background.png (devtools/responsive-background.png)
         skin/classic/aero/browser/devtools/tools-icons-small.png     (devtools/tools-icons-small.png)
         skin/classic/aero/browser/devtools/dock-bottom.png           (devtools/dock-bottom.png)
         skin/classic/aero/browser/devtools/dock-side.png             (devtools/dock-side.png)
         skin/classic/aero/browser/devtools/floating-scrollbars.css   (devtools/floating-scrollbars.css)
+        skin/classic/aero/browser/devtools/floating-scrollbars-light.css (devtools/floating-scrollbars-light.css)
         skin/classic/aero/browser/devtools/inspector.css             (devtools/inspector.css)
         skin/classic/aero/browser/devtools/toolbox.css               (devtools/toolbox.css)
         skin/classic/aero/browser/devtools/tool-webconsole.png       (devtools/tool-webconsole.png)
         skin/classic/aero/browser/devtools/tool-debugger.png         (devtools/tool-debugger.png)
         skin/classic/aero/browser/devtools/tool-inspector.png        (devtools/tool-inspector.png)
         skin/classic/aero/browser/devtools/tool-styleeditor.png      (devtools/tool-styleeditor.png)
         skin/classic/aero/browser/devtools/tool-profiler.png         (devtools/tool-profiler.png)
         skin/classic/aero/browser/devtools/close.png                 (devtools/close.png)
         skin/classic/aero/browser/devtools/undock.png                (devtools/undock.png)
         skin/classic/aero/browser/devtools/font-inspector.css        (devtools/font-inspector.css)
+        skin/classic/aero/browser/devtools/computedview.css          (devtools/computedview.css)
+        skin/classic/aero/browser/devtools/arrow-e.png               (devtools/arrow-e.png)
 #ifdef MOZ_SERVICES_SYNC
         skin/classic/aero/browser/sync-throbber.png
         skin/classic/aero/browser/sync-16.png
         skin/classic/aero/browser/sync-32.png
         skin/classic/aero/browser/sync-128.png
         skin/classic/aero/browser/sync-bg.png
         skin/classic/aero/browser/sync-desktopIcon.png
         skin/classic/aero/browser/sync-mobileIcon.png
--- a/configure.in
+++ b/configure.in
@@ -7801,30 +7801,32 @@ MOZ_ARG_ENABLE_BOOL(xterm-updates,
 [  --enable-xterm-updates  Update XTERM titles with current command.],
     MOZ_UPDATE_XTERM=1,
     MOZ_UPDATE_XTERM= )
 
 dnl =========================================================
 dnl = Chrome format
 dnl =========================================================
 MOZ_ARG_ENABLE_STRING([chrome-format],
-[  --enable-chrome-format=jar|flat|both|symlink|omni
-                          Select FORMAT of chrome files (default=jar)],
+[  --enable-chrome-format=jar|flat|omni
+                          Select FORMAT of chrome files during packaging],
     MOZ_CHROME_FILE_FORMAT=`echo $enableval | tr A-Z a-z`)
 
 if test -z "$MOZ_CHROME_FILE_FORMAT"; then
     MOZ_CHROME_FILE_FORMAT=jar
 fi
 
+if test "$MOZ_CHROME_FILE_FORMAT" = "symlink"; then
+    AC_MSG_ERROR([--enable-chrome-format=symlink has been deprecated. It is now used by default in $DIST/bin on platforms that support it])
+fi
+
 if test "$MOZ_CHROME_FILE_FORMAT" != "jar" &&
     test "$MOZ_CHROME_FILE_FORMAT" != "flat" &&
-    test "$MOZ_CHROME_FILE_FORMAT" != "symlink" &&
-    test "$MOZ_CHROME_FILE_FORMAT" != "both" &&
     test "$MOZ_CHROME_FILE_FORMAT" != "omni"; then
-    AC_MSG_ERROR([--enable-chrome-format must be set to either jar, flat, both, symlink, or omni])
+    AC_MSG_ERROR([--enable-chrome-format must be set to either jar, flat, or omni])
 fi
 
 dnl =========================================================
 dnl Omnijar packaging (bug 552121)
 dnl =========================================================
 dnl Omnijar packaging is compatible with flat packaging.
 dnl In unpackaged builds, omnijar looks for files as if
 dnl things were flat packaged. After packaging, all files
--- a/toolkit/components/thumbnails/PageThumbs.jsm
+++ b/toolkit/components/thumbnails/PageThumbs.jsm
@@ -22,17 +22,20 @@ const EXPIRATION_INTERVAL_SECS = 3600;
  */
 const THUMBNAIL_DIRECTORY = "thumbnails";
 
 /**
  * The default background color for page thumbnails.
  */
 const THUMBNAIL_BG_COLOR = "#fff";
 
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
+Cu.import("resource://gre/modules/osfile/_PromiseWorker.jsm", this);
+Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", this);
+Cu.import("resource://gre/modules/osfile.jsm", this);
 
 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
   "resource://gre/modules/NetUtil.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Services",
   "resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
@@ -50,16 +53,80 @@ XPCOMUtils.defineLazyGetter(this, "gCryp
 
 XPCOMUtils.defineLazyGetter(this, "gUnicodeConverter", function () {
   let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
                     .createInstance(Ci.nsIScriptableUnicodeConverter);
   converter.charset = 'utf8';
   return converter;
 });
 
+XPCOMUtils.defineLazyModuleGetter(this, "Task",
+  "resource://gre/modules/Task.jsm");
+
+/**
+ * Utilities for dealing with promises and Task.jsm
+ */
+const TaskUtils = {
+  /**
+   * Add logging to a promise.
+   *
+   * @param {Promise} promise
+   * @return {Promise} A promise behaving as |promise|, but with additional
+   * logging in case of uncaught error.
+   */
+  captureErrors: function captureErrors(promise) {
+    return promise.then(
+      null,
+      function onError(reason) {
+        Cu.reportError("Uncaught asynchronous error: " + reason + " at\n"
+          + reason.stack + "\n");
+        throw reason;
+      }
+    );
+  },
+
+  /**
+   * Spawn a new Task from a generator.
+   *
+   * This function behaves as |Task.spawn|, with the exception that it
+   * adds logging in case of uncaught error. For more information, see
+   * the documentation of |Task.jsm|.
+   *
+   * @param {generator} gen Some generator.
+   * @return {Promise} A promise built from |gen|, with the same semantics
+   * as |Task.spawn(gen)|.
+   */
+  spawn: function spawn(gen) {
+    return this.captureErrors(Task.spawn(gen));
+  },
+  /**
+   * Read the bytes from a blob, asynchronously.
+   *
+   * @return {Promise}
+   * @resolve {ArrayBuffer} In case of success, the bytes contained in the blob.
+   * @reject {DOMError} In case of error, the underlying DOMError.
+   */
+  readBlob: function readBlob(blob) {
+    let deferred = Promise.defer();
+    let reader = Cc["@mozilla.org/files/filereader;1"].createInstance(Ci.nsIDOMFileReader);
+    reader.onloadend = function onloadend() {
+      if (reader.readyState != Ci.nsIDOMFileReader.DONE) {
+        deferred.reject(reader.error);
+      } else {
+        deferred.resolve(reader.result);
+      }
+    };
+    reader.readAsArrayBuffer(blob);
+    return deferred.promise;
+  }
+};
+
+
+
+
 /**
  * Singleton providing functionality for capturing web page thumbnails and for
  * accessing them if already cached.
  */
 this.PageThumbs = {
   _initialized: false,
 
   /**
@@ -129,16 +196,43 @@ this.PageThumbs = {
     // Fetch the canvas data on the next event loop tick so that we allow
     // some event processing in between drawing to the canvas and encoding
     // its data. We want to block the UI as short as possible. See bug 744100.
     Services.tm.currentThread.dispatch(function () {
       canvas.mozFetchAsStream(aCallback, this.contentType);
     }.bind(this), Ci.nsIThread.DISPATCH_NORMAL);
   },
 
+
+  /**
+   * Captures a thumbnail for the given window.
+   *
+   * @param aWindow The DOM window to capture a thumbnail from.
+   * @return {Promise}
+   * @resolve {Blob} The thumbnail, as a Blob.
+   */
+  captureToBlob: function PageThumbs_captureToBlob(aWindow) {
+    if (!this._prefEnabled()) {
+      return null;
+    }
+
+    let canvas = this._createCanvas();
+    this.captureToCanvas(aWindow, canvas);
+
+    let deferred = Promise.defer();
+    let type = this.contentType;
+    // Fetch the canvas data on the next event loop tick so that we allow
+    // some event processing in between drawing to the canvas and encoding
+    // its data. We want to block the UI as short as possible. See bug 744100.
+    canvas.toBlob(function asBlob(blob) {
+      deferred.resolve(blob, type);
+    });
+    return deferred.promise;
+  },
+
   /**
    * Captures a thumbnail from a given window and draws it to the given canvas.
    * @param aWindow The DOM window to capture a thumbnail from.
    * @param aCanvas The canvas to draw to.
    */
   captureToCanvas: function PageThumbs_captureToCanvas(aWindow, aCanvas) {
     let telemetryCaptureTime = new Date();
     let [sw, sh, scale] = this._determineCropSize(aWindow, aCanvas);
@@ -172,45 +266,49 @@ this.PageThumbs = {
     if (!this._prefEnabled()) {
       return;
     }
 
     let url = aBrowser.currentURI.spec;
     let channel = aBrowser.docShell.currentDocumentChannel;
     let originalURL = channel.originalURI.spec;
 
-    this.capture(aBrowser.contentWindow, function (aInputStream) {
-      let telemetryStoreTime = new Date();
+    TaskUtils.spawn((function task() {
+      let isSuccess = true;
+      try {
+        let blob = yield this.captureToBlob(aBrowser.contentWindow);
+        let buffer = yield TaskUtils.readBlob(blob);
 
-      function finish(aSuccessful) {
-        if (aSuccessful) {
-          Services.telemetry.getHistogramById("FX_THUMBNAILS_STORE_TIME_MS")
-            .add(new Date() - telemetryStoreTime);
+        let telemetryStoreTime = new Date();
+        yield PageThumbsStorage.writeData(url, new Uint8Array(buffer));
+
+        Services.telemetry.getHistogramById("FX_THUMBNAILS_STORE_TIME_MS")
+          .add(new Date() - telemetryStoreTime);
 
-          // We've been redirected. Create a copy of the current thumbnail for
-          // the redirect source. We need to do this because:
-          //
-          // 1) Users can drag any kind of links onto the newtab page. If those
-          //    links redirect to a different URL then we want to be able to
-          //    provide thumbnails for both of them.
-          //
-          // 2) The newtab page should actually display redirect targets, only.
-          //    Because of bug 559175 this information can get lost when using
-          //    Sync and therefore also redirect sources appear on the newtab
-          //    page. We also want thumbnails for those.
-          if (url != originalURL)
-            PageThumbsStorage.copy(url, originalURL);
+        // We've been redirected. Create a copy of the current thumbnail for
+        // the redirect source. We need to do this because:
+        //
+        // 1) Users can drag any kind of links onto the newtab page. If those
+        //    links redirect to a different URL then we want to be able to
+        //    provide thumbnails for both of them.
+        //
+        // 2) The newtab page should actually display redirect targets, only.
+        //    Because of bug 559175 this information can get lost when using
+        //    Sync and therefore also redirect sources appear on the newtab
+        //    page. We also want thumbnails for those.
+        if (url != originalURL) {
+          yield PageThumbsStorage.copy(url, originalURL);
         }
-
-        if (aCallback)
-          aCallback(aSuccessful);
+      } catch (_) {
+        isSuccess = false;
       }
-
-      PageThumbsStorage.write(url, aInputStream, finish);
-    });
+      if (aCallback) {
+        aCallback(isSuccess);
+      }
+    }).bind(this));
   },
 
   /**
    * Register an expiration filter.
    *
    * When thumbnails are going to expire, each registered filter is asked for a
    * list of thumbnails to keep.
    *
@@ -309,65 +407,109 @@ this.PageThumbs = {
     }
     catch (e) {
       return true;
     }
   },
 };
 
 this.PageThumbsStorage = {
-  getDirectory: function Storage_getDirectory(aCreate = true) {
-    return FileUtils.getDir("ProfLD", [THUMBNAIL_DIRECTORY], aCreate);
+  // The path for the storage
+  _path: null,
+  get path() {
+    if (!this._path) {
+      this._path = OS.Path.join(OS.Constants.Path.localProfileDir, THUMBNAIL_DIRECTORY);
+    }
+    return this._path;
+  },
+
+  ensurePath: function Storage_ensurePath() {
+    // Create the directory (ignore any error if the directory
+    // already exists). As all writes are done from the PageThumbsWorker
+    // thread, which serializes its operations, this ensures that
+    // future operations can proceed without having to check whether
+    // the directory exists.
+    return PageThumbsWorker.post("makeDir",
+      [this.path, {ignoreExisting: true}]).then(
+        null,
+        function onError(aReason) {
+          Components.utils.reportError("Could not create thumbnails directory" + aReason);
+        });
   },
 
   getLeafNameForURL: function Storage_getLeafNameForURL(aURL) {
+    if (typeof aURL != "string") {
+      throw new TypeError("Expecting a string");
+    }
     let hash = this._calculateMD5Hash(aURL);
     return hash + ".png";
   },
 
-  getFileForURL: function Storage_getFileForURL(aURL) {
-    let file = this.getDirectory();
-    file.append(this.getLeafNameForURL(aURL));
-    return file;
+  getFilePathForURL: function Storage_getFilePathForURL(aURL) {
+    return OS.Path.join(this.path, this.getLeafNameForURL(aURL));
   },
 
-  write: function Storage_write(aURL, aDataStream, aCallback) {
-    let file = this.getFileForURL(aURL);
-    let fos = FileUtils.openSafeFileOutputStream(file);
-
-    NetUtil.asyncCopy(aDataStream, fos, function (aResult) {
-      FileUtils.closeSafeFileOutputStream(fos);
-      aCallback(Components.isSuccessCode(aResult));
-    });
+  /**
+   * Write the contents of a thumbnail, off the main thread.
+   *
+   * @param {string} aURL The url for which to store a thumbnail.
+   * @param {string} aData The data to store in the thumbnail, as
+   * an ArrayBuffer. This array buffer is neutered and cannot be
+   * reused after the copy.
+   *
+   * @return {Promise}
+   */
+  writeData: function Storage_write(aURL, aData) {
+    let path = this.getFilePathForURL(aURL);
+    this.ensurePath();
+    let msg = [
+      path,
+      aData,
+      {
+        tmpPath: path + ".tmp",
+        bytes: aData.byteLength,
+        flush: false /*thumbnails do not require the level of guarantee provided by flush*/
+      }];
+    return PageThumbsWorker.post("writeAtomic", msg,
+      msg /*we don't want that message garbage-collected,
+           as OS.Shared.Type.void_t.in_ptr.toMsg uses C-level
+           memory tricks to enforce zero-copy*/);
   },
 
+  /**
+   * Copy a thumbnail, off the main thread.
+   *
+   * @param {string} aSourceURL The url of the thumbnail to copy.
+   * @param {string} aTargetURL The url of the target thumbnail.
+   *
+   * @return {Promise}
+   */
   copy: function Storage_copy(aSourceURL, aTargetURL) {
-    let sourceFile = this.getFileForURL(aSourceURL);
-    let targetFile = this.getFileForURL(aTargetURL);
-
-    try {
-      sourceFile.copyTo(targetFile.parent, targetFile.leafName);
-    } catch (e) {
-      /* We might not be permitted to write to the file. */
-    }
+    this.ensurePath();
+    let sourceFile = this.getFilePathForURL(aSourceURL);
+    let targetFile = this.getFilePathForURL(aTargetURL);
+    return PageThumbsWorker.post("copy", [sourceFile, targetFile]);
   },
 
+  /**
+   * Remove a single thumbnail, off the main thread.
+   *
+   * @return {Promise}
+   */
   remove: function Storage_remove(aURL) {
-    let file = this.getFileForURL(aURL);
-    PageThumbsWorker.postMessage({type: "removeFile", path: file.path});
+    return PageThumbsWorker.post("remove", [this.getFilePathForURL(aURL)]);
   },
 
+  /**
+   * Remove all thumbnails, off the main thread.
+   *
+   * @return {Promise}
+   */
   wipe: function Storage_wipe() {
-    let dir = this.getDirectory(false);
-    dir.followLinks = false;
-    try {
-      dir.remove(true);
-    } catch (e) {
-      /* The directory might not exist or we're not permitted to remove it. */
-    }
+    return PageThumbsWorker.post("wipe", [this.path]);
   },
 
   _calculateMD5Hash: function Storage_calculateMD5Hash(aValue) {
     let hash = gCryptoHash;
     let value = gUnicodeConverter.convertToByteArray(aValue);
 
     hash.init(hash.MD5);
     hash.update(value, value.length);
@@ -418,28 +560,30 @@ let PageThumbsStorageMigrator = {
   },
 
   /**
    * Bug 239254 added support for having the disk cache and thumbnail
    * directories on a local path (i.e. ~/.cache/) under Linux. We'll first
    * try to move the old thumbnails to their new location. If that's not
    * possible (because ProfD might be on a different file system than
    * ProfLD) we'll just discard them.
+   *
+   * @param {string*} local The path to the local profile directory.
+   * Used for testing. Default argument is good for all non-testing uses.
+   * @param {string*} roaming The path to the roaming profile directory.
+   * Used for testing. Default argument is good for all non-testing uses.
    */
-  migrateToVersion3: function Migrator_migrateToVersion3() {
-    let local = FileUtils.getDir("ProfLD", [THUMBNAIL_DIRECTORY], true);
-    let roaming = FileUtils.getDir("ProfD", [THUMBNAIL_DIRECTORY]);
-
-    if (!roaming.equals(local)) {
-      PageThumbsWorker.postMessage({
-        type: "moveOrDeleteAllThumbnails",
-        from: roaming.path,
-        to: local.path
-      });
-    }
+  migrateToVersion3: function Migrator_migrateToVersion3(
+    local = OS.Constants.Path.localProfileDir,
+    roaming = OS.Constants.Path.profileDir) {
+    PageThumbsWorker.post(
+      "moveOrDeleteAllThumbnails",
+      [OS.Path.join(roaming, THUMBNAIL_DIRECTORY),
+       OS.Path.join(local, THUMBNAIL_DIRECTORY)]
+    );
   }
 };
 
 let PageThumbsExpiration = {
   _filters: [],
 
   init: function Expiration_init() {
     gUpdateTimerManager.registerTimer("browser-cleanup-thumbnails", this,
@@ -479,70 +623,56 @@ let PageThumbsExpiration = {
     for (let filter of this._filters) {
       if (typeof filter == "function")
         filter(filterCallback)
       else
         filter.filterForThumbnailExpiration(filterCallback);
     }
   },
 
-  expireThumbnails: function Expiration_expireThumbnails(aURLsToKeep, aCallback) {
-    PageThumbsWorker.postMessage({
-      type: "expireFilesInDirectory",
-      minChunkSize: EXPIRATION_MIN_CHUNK_SIZE,
-      path: PageThumbsStorage.getDirectory().path,
-      filesToKeep: [PageThumbsStorage.getLeafNameForURL(url) for (url of aURLsToKeep)]
-    }, aCallback);
+  expireThumbnails: function Expiration_expireThumbnails(aURLsToKeep) {
+    let path = this.path;
+    let keep = [PageThumbsStorage.getLeafNameForURL(url) for (url of aURLsToKeep)];
+    let msg = [
+      PageThumbsStorage.path,
+      keep,
+      EXPIRATION_MIN_CHUNK_SIZE
+    ];
+
+    return PageThumbsWorker.post(
+      "expireFilesInDirectory",
+      msg
+    );
   }
 };
 
 /**
  * Interface to a dedicated thread handling I/O
  */
-let PageThumbsWorker = {
-  /**
-   * A (fifo) queue of callbacks registered for execution
-   * upon completion of calls to the worker.
-   */
-  _callbacks: [],
 
-  /**
-   * Get the worker, spawning it if necessary.
-   * Code of the worker is in companion file PageThumbsWorker.js
-   */
-  get _worker() {
-    delete this._worker;
-    this._worker = new ChromeWorker("resource://gre/modules/PageThumbsWorker.js");
-    this._worker.addEventListener("message", this);
-    return this._worker;
-  },
-
-  /**
-   * Post a message to the dedicated thread, registering a callback
-   * to be executed once the reply has been received.
-   *
-   * See PageThumbsWorker.js for the format of messages and replies.
-   *
-   * @param {*} message A JSON message.
-   * @param {Function=} callback An optional callback.
-   */
-  postMessage: function Worker_postMessage(message, callback) {
-    this._callbacks.push(callback);
-    this._worker.postMessage(message);
-  },
-
-  /**
-   * Handle a message from the dedicated thread.
-   */
-  handleEvent: function Worker_handleEvent(aEvent) {
-    let callback = this._callbacks.shift();
-    if (callback)
-      callback(aEvent.data);
-  }
-};
+let PageThumbsWorker = (function() {
+  let worker = new PromiseWorker("resource://gre/modules/PageThumbsWorker.js",
+    OS.Shared.LOG.bind("PageThumbs"));
+  return {
+    post: function post(...args) {
+      let promise = worker.post.apply(worker, args);
+      return promise.then(
+        null,
+        function onError(error) {
+          // Decode any serialized error
+          if (error instanceof PromiseWorker.WorkerError) {
+            throw OS.File.Error.fromMsg(error.data);
+          } else {
+            throw error;
+          }
+        }
+      );
+    }
+  };
+})();
 
 let PageThumbsHistoryObserver = {
   onDeleteURI: function Thumbnails_onDeleteURI(aURI, aGUID) {
     PageThumbsStorage.remove(aURI.spec);
   },
 
   onClearHistory: function Thumbnails_onClearHistory() {
     PageThumbsStorage.wipe();
--- a/toolkit/components/thumbnails/PageThumbsProtocol.js
+++ b/toolkit/components/thumbnails/PageThumbsProtocol.js
@@ -22,16 +22,18 @@ const Cc = Components.classes;
 const Cr = Components.results;
 const Ci = Components.interfaces;
 
 Cu.import("resource://gre/modules/PageThumbs.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Services",
   "resource://gre/modules/Services.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
+  "resource://gre/modules/FileUtils.jsm");
 
 /**
  * Implements the thumbnail protocol handler responsible for moz-page-thumb: URIs.
  */
 function Protocol() {
 }
 
 Protocol.prototype = {
@@ -68,18 +70,18 @@ Protocol.prototype = {
 
   /**
    * Constructs a new channel from the given URI for this protocol handler.
    * @param aURI The URI for which to construct a channel.
    * @return The newly created channel.
    */
   newChannel: function Proto_newChannel(aURI) {
     let {url} = parseURI(aURI);
-    let file = PageThumbsStorage.getFileForURL(url);
-    let fileuri = Services.io.newFileURI(file);
+    let file = PageThumbsStorage.getFilePathForURL(url);
+    let fileuri = Services.io.newFileURI(new FileUtils.File(file));
     return Services.io.newChannelFromURI(fileuri);
   },
 
   /**
    * Decides whether to allow a blacklisted port.
    * @return Always false, we'll never allow ports.
    */
   allowPort: function () false,
--- a/toolkit/components/thumbnails/PageThumbsWorker.js
+++ b/toolkit/components/thumbnails/PageThumbsWorker.js
@@ -8,101 +8,160 @@
  * Do not rely on the API of this worker. In a future version, it might be
  * fully replaced by a OS.File global I/O worker.
  */
 
 "use strict";
 
 importScripts("resource://gre/modules/osfile.jsm");
 
-let PageThumbsWorker = {
-  handleMessage: function Worker_handleMessage(aEvent) {
-    let msg = aEvent.data;
-    let data = {result: null, data: null};
+let File = OS.File;
+let Type = OS.Shared.Type;
 
-    switch (msg.type) {
-      case "removeFile":
-        data.result = this.removeFile(msg);
-        break;
-      case "expireFilesInDirectory":
-        data.result = this.expireFilesInDirectory(msg);
-        break;
-      case "moveOrDeleteAllThumbnails":
-        data.result = this.moveOrDeleteAllThumbnails(msg);
-        break;
-      default:
-        data.result = false;
-        data.detail = "message not understood";
-        break;
-    }
+/**
+ * Communications with the controller.
+ *
+ * Accepts messages:
+ * {fun:function_name, args:array_of_arguments_or_null}
+ *
+ * Sends messages:
+ * {ok: result} / {fail: serialized_form_of_OS.File.Error}
+ */
+self.onmessage = function onmessage(msg) {
+  let data = msg.data;
+  let id = data.id;
+  let result;
+  if (!(data.fun in Agent)) {
+    throw new Error("Cannot find method " + data.fun);
+  }
+  try {
+    result = Agent[data.fun].apply(Agent, data.args);
+  } catch (ex if ex instanceof StopIteration) {
+    // StopIteration cannot be serialized automatically
+    self.postMessage({StopIteration: true, id: id});
+    return;
+  } catch (ex if ex instanceof OS.File.Error) {
+    // Instances of OS.File.Error know how to serialize themselves
+    // (deserialization ensures that we end up with OS-specific
+    // instances of |OS.File.Error|)
+    self.postMessage({fail: OS.File.Error.toMsg(ex), id:id});
+    return;
+  }
+  // Other exceptions do not, and should be propagated through DOM's
+  // built-in mechanism for uncaught errors, although this mechanism
+  // may lose interesting information.
+  self.postMessage({ok: result, id:id});
+};
 
-    self.postMessage(data);
-  },
 
-  removeFile: function Worker_removeFile(msg) {
+let Agent = {
+  remove: function Agent_removeFile(path) {
     try {
-      OS.File.remove(msg.path);
+      OS.File.remove(path);
       return true;
     } catch (e) {
       return false;
     }
   },
 
-  expireFilesInDirectory: function Worker_expireFilesInDirectory(msg) {
-    let entries = this.getFileEntriesInDirectory(msg.path, msg.filesToKeep);
-    let limit = Math.max(msg.minChunkSize, Math.round(entries.length / 2));
+  expireFilesInDirectory:
+  function Agent_expireFilesInDirectory(path, filesToKeep, minChunkSize) {
+    let entries = this.getFileEntriesInDirectory(path, filesToKeep);
+    let limit = Math.max(minChunkSize, Math.round(entries.length / 2));
 
     for (let entry of entries) {
-      this.removeFile(entry);
+      this.remove(entry.path);
 
       // Check if we reached the limit of files to remove.
       if (--limit <= 0) {
         break;
       }
     }
 
     return true;
   },
 
   getFileEntriesInDirectory:
-  function Worker_getFileEntriesInDirectory(aPath, aSkipFiles) {
-    let skip = new Set(aSkipFiles);
-    let iter = new OS.File.DirectoryIterator(aPath);
+  function Agent_getFileEntriesInDirectory(path, skipFiles) {
+    let iter = new OS.File.DirectoryIterator(path);
+    if (!iter.exists()) {
+      return [];
+    }
+
+    let skip = new Set(skipFiles);
 
     return [entry
             for (entry in iter)
             if (!entry.isDir && !entry.isSymLink && !skip.has(entry.name))];
   },
 
   moveOrDeleteAllThumbnails:
-  function Worker_moveOrDeleteAllThumbnails(msg) {
-    if (!OS.File.exists(msg.from))
+  function Agent_moveOrDeleteAllThumbnails(pathFrom, pathTo) {
+    OS.File.makeDir(pathTo, {ignoreExisting: true});
+    if (pathFrom == pathTo) {
       return true;
+    }
+    let iter = new OS.File.DirectoryIterator(pathFrom);
+    if (iter.exists()) {
+      for (let entry in iter) {
+        if (entry.isDir || entry.isSymLink) {
+          continue;
+        }
 
-    let iter = new OS.File.DirectoryIterator(msg.from);
-    for (let entry in iter) {
-      if (entry.isDir || entry.isSymLink) {
-        continue;
-      }
+
+        let from = OS.Path.join(pathFrom, entry.name);
+        let to = OS.Path.join(pathTo, entry.name);
 
-      let from = OS.Path.join(msg.from, entry.name);
-      let to = OS.Path.join(msg.to, entry.name);
-
-      try {
-        OS.File.move(from, to, {noOverwrite: true, noCopy: true});
-      } catch (e) {
-        OS.File.remove(from);
+        try {
+          OS.File.move(from, to, {noOverwrite: true, noCopy: true});
+        } catch (e) {
+          OS.File.remove(from);
+        }
       }
     }
     iter.close();
 
     try {
-      OS.File.removeEmptyDir(msg.from);
+      OS.File.removeEmptyDir(pathFrom);
     } catch (e) {
       // This could fail if there's something in
       // the folder we're not permitted to remove.
     }
 
     return true;
-  }
+  },
+
+  writeAtomic: function Agent_writeAtomic(path, buffer, options) {
+    return File.writeAtomic(path,
+      buffer,
+      options);
+  },
+
+  makeDir: function Agent_makeDir(path, options) {
+    return File.makeDir(path, options);
+  },
+
+  copy: function Agent_copy(source, dest) {
+    return File.copy(source, dest);
+  },
+
+  wipe: function Agent_wipe(path) {
+    let iterator = new File.DirectoryIterator(path);
+    try {
+      for (let entry in iterator) {
+        try {
+          File.remove(entry.path);
+        } catch (ex) {
+          // If a file cannot be removed, we should still continue.
+          // This can happen at least for any of the following reasons:
+          // - access denied;
+          // - file has been removed recently during a previous wipe
+          //  and the file system has not flushed that yet (yes, this
+          //  can happen under Windows);
+          // - file has been removed by the user or another process.
+        }
+      }
+    } finally {
+      iterator.close();
+    }
+   }
 };
 
-self.onmessage = PageThumbsWorker.handleMessage.bind(PageThumbsWorker);
--- a/toolkit/components/thumbnails/test/browser_thumbnails_expiration.js
+++ b/toolkit/components/thumbnails/test/browser_thumbnails_expiration.js
@@ -1,35 +1,32 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const URL = "http://mochi.test:8888/?t=" + Date.now();
 const URL1 = URL + "#1";
 const URL2 = URL + "#2";
 const URL3 = URL + "#3";
 
-XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
-  "resource://gre/modules/FileUtils.jsm");
-
 let tmp = {};
 Cc["@mozilla.org/moz/jssubscript-loader;1"]
   .getService(Ci.mozIJSSubScriptLoader)
   .loadSubScript("resource://gre/modules/PageThumbs.jsm", tmp);
 
 const {EXPIRATION_MIN_CHUNK_SIZE, PageThumbsExpiration} = tmp;
 
 function runTests() {
   // Create three thumbnails.
-  createDummyThumbnail(URL1);
+  yield createDummyThumbnail(URL1);
   ok(thumbnailExists(URL1), "first thumbnail created");
 
-  createDummyThumbnail(URL2);
+  yield createDummyThumbnail(URL2);
   ok(thumbnailExists(URL2), "second thumbnail created");
 
-  createDummyThumbnail(URL3);
+  yield createDummyThumbnail(URL3);
   ok(thumbnailExists(URL3), "third thumbnail created");
 
   // Remove the third thumbnail.
   yield expireThumbnails([URL1, URL2]);
   ok(thumbnailExists(URL1), "first thumbnail still exists");
   ok(thumbnailExists(URL2), "second thumbnail still exists");
   ok(!thumbnailExists(URL3), "third thumbnail has been removed");
 
@@ -40,20 +37,21 @@ function runTests() {
 
   // Remove all thumbnails.
   yield expireThumbnails([]);
   ok(!thumbnailExists(URL1), "all thumbnails have been removed");
 
   // Create some more files than the min chunk size.
   let urls = [];
   for (let i = 0; i < EXPIRATION_MIN_CHUNK_SIZE + 10; i++) {
-    urls.push(URL + "#dummy" + i);
+    let url = URL + "#dummy" + i;
+    urls.push(url);
+    yield createDummyThumbnail(url);
   }
 
-  urls.forEach(createDummyThumbnail);
   ok(urls.every(thumbnailExists), "all dummy thumbnails created");
 
   // Make sure our dummy thumbnails aren't expired too early.
   let dontExpireDummyURLs = function (cb) cb(urls);
   PageThumbs.addExpirationFilter(dontExpireDummyURLs);
 
   registerCleanupFunction(function () {
     PageThumbs.removeExpirationFilter(dontExpireDummyURLs);
@@ -66,21 +64,35 @@ function runTests() {
 
   // Expire thumbnails again. All should be gone by now.
   yield expireThumbnails([]);
   remainingURLs = [u for (u of remainingURLs) if (thumbnailExists(u))];
   is(remainingURLs.length, 0, "no dummy thumbnails remaining");
 }
 
 function createDummyThumbnail(aURL) {
-  let file = PageThumbsStorage.getFileForURL(aURL);
-  let fos = FileUtils.openSafeFileOutputStream(file);
-
-  let data = "dummy";
-  fos.write(data, data.length);
-  FileUtils.closeSafeFileOutputStream(fos);
+  info("Creating dummy thumbnail for " + aURL);
+  let dummy = new Uint8Array(10);
+  for (let i = 0; i < 10; ++i) {
+    dummy[i] = i;
+  }
+  PageThumbsStorage.writeData(aURL, dummy).then(
+    function onSuccess() {
+      info("createDummyThumbnail succeeded");
+      executeSoon(next);
+    },
+    function onFailure(error) {
+      ok(false, "createDummyThumbnail failed " + error);
+    }
+  );
 }
 
 function expireThumbnails(aKeep) {
-  PageThumbsExpiration.expireThumbnails(aKeep, function () {
-    executeSoon(next);
-  });
+  PageThumbsExpiration.expireThumbnails(aKeep).then(
+    function onSuccess() {
+      info("expireThumbnails succeeded");
+      executeSoon(next);
+    },
+    function onFailure(error) {
+      ok(false, "expireThumbnails failed " + error);
+    }
+  );
 }
--- a/toolkit/components/thumbnails/test/browser_thumbnails_storage.js
+++ b/toolkit/components/thumbnails/test/browser_thumbnails_storage.js
@@ -17,26 +17,26 @@ XPCOMUtils.defineLazyGetter(this, "Sanit
  * Newly captured thumbnails should be saved as files and they should as well
  * be removed when the user sanitizes their history.
  */
 function runTests() {
   yield clearHistory();
   yield createThumbnail();
 
   // Make sure Storage.copy() updates an existing file.
-  PageThumbsStorage.copy(URL, URL_COPY);
-  let copy = PageThumbsStorage.getFileForURL(URL_COPY);
+  yield PageThumbsStorage.copy(URL, URL_COPY);
+  let copy = new FileUtils.File(PageThumbsStorage.getFilePathForURL(URL_COPY));
   let mtime = copy.lastModifiedTime -= 60;
 
-  PageThumbsStorage.copy(URL, URL_COPY);
-  isnot(PageThumbsStorage.getFileForURL(URL_COPY).lastModifiedTime, mtime,
+  yield PageThumbsStorage.copy(URL, URL_COPY);
+  isnot(new FileUtils.File(PageThumbsStorage.getFilePathForURL(URL_COPY)).lastModifiedTime, mtime,
         "thumbnail file was updated");
 
-  let file = PageThumbsStorage.getFileForURL(URL);
-  let fileCopy = PageThumbsStorage.getFileForURL(URL_COPY);
+  let file = new FileUtils.File(PageThumbsStorage.getFilePathForURL(URL));
+  let fileCopy = new FileUtils.File(PageThumbsStorage.getFilePathForURL(URL_COPY));
 
   // Clear the browser history. Retry until the files are gone because Windows
   // locks them sometimes.
   while (file.exists() || fileCopy.exists()) {
     yield clearHistory();
   }
 
   yield createThumbnail();
--- a/toolkit/components/thumbnails/test/browser_thumbnails_storage_migrate3.js
+++ b/toolkit/components/thumbnails/test/browser_thumbnails_storage_migrate3.js
@@ -8,19 +8,16 @@ const THUMBNAIL_DIRECTORY = "thumbnails"
 const PREF_STORAGE_VERSION = "browser.pagethumbnails.storage_version";
 
 let tmp = {};
 Cc["@mozilla.org/moz/jssubscript-loader;1"]
   .getService(Ci.mozIJSSubScriptLoader)
   .loadSubScript("resource://gre/modules/PageThumbs.jsm", tmp);
 let {PageThumbsStorageMigrator} = tmp;
 
-XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
-  "resource://gre/modules/FileUtils.jsm");
-
 XPCOMUtils.defineLazyServiceGetter(this, "gDirSvc",
   "@mozilla.org/file/directory_service;1", "nsIProperties");
 
 /**
  * This test makes sure we correctly migrate to thumbnail storage version 3.
  * This means copying existing thumbnails from the roaming to the local profile
  * directory and should just apply to Linux.
  */
@@ -50,17 +47,17 @@ function runTests() {
 
   // Pretend to have one of the thumbnails
   // already in place at the new storage site.
   name = PageThumbsStorage.getLeafNameForURL(URL3);
   file = FileUtils.getFile("ProfLD", [THUMBNAIL_DIRECTORY, name]);
   writeDummyFile(file, "no-overwrite-plz");
 
   // Kick off thumbnail storage migration.
-  PageThumbsStorageMigrator.migrateToVersion3();
+  PageThumbsStorageMigrator.migrateToVersion3(localProfile.path);
   ok(true, "migration finished");
 
   // Wait until the first thumbnail was moved to its new location.
   yield whenFileExists(URL);
   ok(true, "first thumbnail moved");
 
   // Wait for the second thumbnail to be moved as well.
   yield whenFileExists(URL2);
--- a/toolkit/components/thumbnails/test/head.js
+++ b/toolkit/components/thumbnails/test/head.js
@@ -1,15 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 let tmp = {};
 Cu.import("resource://gre/modules/PageThumbs.jsm", tmp);
 Cu.import("resource:///modules/sessionstore/SessionStore.jsm", tmp);
-let {PageThumbs, PageThumbsStorage, SessionStore} = tmp;
+Cu.import("resource://gre/modules/FileUtils.jsm", tmp);
+Cu.import("resource://gre/modules/osfile.jsm", tmp);
+let {PageThumbs, PageThumbsStorage, SessionStore, FileUtils, OS} = tmp;
 
 Cu.import("resource://gre/modules/PlacesUtils.jsm");
 
 registerCleanupFunction(function () {
   while (gBrowser.tabs.length > 1)
     gBrowser.removeTab(gBrowser.tabs[1]);
 });
 
@@ -40,17 +42,19 @@ let TestRunner = {
     }.bind(this));
   },
 
   /**
    * Runs the next available test or finishes if there's no test left.
    */
   next: function () {
     try {
-      TestRunner._iter.next();
+      let value = TestRunner._iter.next();
+      if (value && typeof value.then == "function")
+        value.then(next);
     } catch (e if e instanceof StopIteration) {
       finish();
     }
   }
 };
 
 /**
  * Continues the current test execution.
@@ -80,20 +84,20 @@ function navigateTo(aURI) {
 }
 
 /**
  * Continues the current test execution when a load event for the given element
  * has been received.
  * @param aElement The DOM element to listen on.
  * @param aCallback The function to call when the load event was dispatched.
  */
-function whenLoaded(aElement, aCallback) {
+function whenLoaded(aElement, aCallback = next) {
   aElement.addEventListener("load", function onLoad() {
     aElement.removeEventListener("load", onLoad, true);
-    executeSoon(aCallback || next);
+    executeSoon(aCallback);
   }, true);
 }
 
 /**
  * Captures a screenshot for the currently selected tab, stores it in the cache,
  * retrieves it from the cache and compares pixel color values.
  * @param aRed The red component's intensity.
  * @param aGreen The green component's intensity.
@@ -138,17 +142,17 @@ function retrieveImageDataForURL(aURL, a
   });
 }
 
 /**
  * Checks if a thumbnail for the given URL exists.
  * @param aURL The url associated to the thumbnail.
  */
 function thumbnailExists(aURL) {
-  let file = PageThumbsStorage.getFileForURL(aURL);
+  let file = new FileUtils.File(PageThumbsStorage.getFilePathForURL(aURL));
   return file.exists() && file.fileSize;
 }
 
 /**
  * Asynchronously adds visits to a page, invoking a callback function when done.
  *
  * @param aPlaceInfo
  *        Can be an nsIURI, in such a case a single LINK visit will be added.
@@ -206,23 +210,23 @@ function addVisits(aPlaceInfo, aCallback
 /**
  * Calls a given callback when the thumbnail for a given URL has been found
  * on disk. Keeps trying until the thumbnail has been created.
  *
  * @param aURL The URL of the thumbnail's page.
  * @param [optional] aCallback
  *        Function to be invoked on completion.
  */
-function whenFileExists(aURL, aCallback) {
+function whenFileExists(aURL, aCallback = next) {
   let callback = aCallback;
   if (!thumbnailExists(aURL)) {
     callback = function () whenFileExists(aURL, aCallback);
   }
 
-  executeSoon(callback || next);
+  executeSoon(callback);
 }
 
 /**
  * Calls a given callback when the given file has been removed.
  * Keeps trying until the file is removed.
  *
  * @param aFile The file that is being removed
  * @param [optional] aCallback
--- a/toolkit/mozapps/installer/packager.py
+++ b/toolkit/mozapps/installer/packager.py
@@ -266,17 +266,17 @@ def main():
     elif args.format == 'jar':
         formatter = JarFormatter(copier, optimize=args.optimizejars)
     elif args.format == 'omni':
         formatter = OmniJarFormatter(copier,
                                      buildconfig.substs['OMNIJAR_NAME'],
                                      optimize=args.optimizejars,
                                      non_resources=args.non_resource)
     else:
-        errors.fatal('Unknown format: %s', format)
+        errors.fatal('Unknown format: %s' % args.format)
 
     # Adjust defines according to the requested format.
     if isinstance(formatter, OmniJarFormatter):
         defines['MOZ_OMNIJAR'] = 1
     elif 'MOZ_OMNIJAR' in defines:
         del defines['MOZ_OMNIJAR']
 
     binpath = ''