Merge m-c to inbound.
authorRyan VanderMeulen <ryanvm@gmail.com>
Wed, 27 Mar 2013 22:23:59 -0400
changeset 126511 da133fd0dbb72e9c57cd3e04a6f09761808e8283
parent 126510 4afb61ad2964cc442753d7607e0d193b2ec34c27 (current diff)
parent 126478 962f5293f87f652fe3e891a971c4ae0e4177a717 (diff)
child 126512 bc6dfc2e65f07c10eb63c9970a8d0c63b8b04adc
push id24485
push userryanvm@gmail.com
push dateThu, 28 Mar 2013 12:31:20 +0000
treeherdermozilla-central@293498096b28 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone22.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to inbound.
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
@@ -7806,30 +7806,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 = ''