Bug 871423 - Open correct inline stylesheet from style inspector link. r=bgrins
authorHeather Arthur <fayearthur@gmail.com>
Fri, 22 Aug 2014 15:30:00 -0400
changeset 222993 95584aa8a5a9b3958a1a626146029058d270bd4d
parent 222871 a662075602393d04900328a69118c0892754e50f
child 222994 ba1d285f2ddebb2b61ee4ce3a1cc6d4ecd5712e2
push id3979
push userraliiev@mozilla.com
push dateMon, 13 Oct 2014 16:35:44 +0000
treeherdermozilla-beta@30f2cc610691 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbgrins
bugs871423
milestone34.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 871423 - Open correct inline stylesheet from style inspector link. r=bgrins
browser/devtools/styleeditor/StyleEditorUI.jsm
browser/devtools/styleeditor/test/browser_styleeditor_private_perwindowpb.js
browser/devtools/styleinspector/computed-view.js
browser/devtools/styleinspector/style-inspector.js
browser/devtools/styleinspector/test/browser_computedview_style-editor-link.js
browser/devtools/styleinspector/test/browser_ruleview_style-editor-link.js
toolkit/devtools/server/actors/root.js
toolkit/devtools/server/actors/styles.js
toolkit/devtools/server/actors/stylesheets.js
toolkit/devtools/server/actors/webbrowser.js
--- a/browser/devtools/styleeditor/StyleEditorUI.jsm
+++ b/browser/devtools/styleeditor/StyleEditorUI.jsm
@@ -495,18 +495,17 @@ StyleEditorUI.prototype = {
           }
         });
 
         // autofocus if it's a new user-created stylesheet
         if (editor.isNew) {
           this._selectEditor(editor);
         }
 
-        if (this._styleSheetToSelect
-            && this._styleSheetToSelect.stylesheet == editor.styleSheet.href) {
+        if (this._isEditorToSelect(editor)) {
           this.switchToSelectedSheet();
         }
 
         // If this is the first stylesheet and there is no pending request to
         // select a particular style sheet, select this sheet.
         if (!this.selectedEditor && !this._styleSheetBoundToSelect
             && editor.styleSheet.styleSheetIndex == 0) {
           this._selectEditor(editor);
@@ -561,36 +560,53 @@ StyleEditorUI.prototype = {
 
   /**
    * Switch to the editor that has been marked to be selected.
    *
    * @return {Promise}
    *         Promise that will resolve when the editor is selected.
    */
   switchToSelectedSheet: function() {
-    let sheet = this._styleSheetToSelect;
-    let isHref = sheet.stylesheet === null || typeof sheet.stylesheet == "string";
+    let toSelect = this._styleSheetToSelect;
 
     for (let editor of this.editors) {
-      if ((isHref && editor.styleSheet.href == sheet.stylesheet) ||
-          sheet.stylesheet == editor.styleSheet) {
+      if (this._isEditorToSelect(editor)) {
         // The _styleSheetBoundToSelect will always hold the latest pending
         // requested style sheet (with line and column) which is not yet
         // selected by the source editor. Only after we select that particular
         // editor and go the required line and column, it will become null.
         this._styleSheetBoundToSelect = this._styleSheetToSelect;
         this._styleSheetToSelect = null;
-        return this._selectEditor(editor, sheet.line, sheet.col);
+        return this._selectEditor(editor, toSelect.line, toSelect.col);
       }
     }
 
     return promise.resolve();
   },
 
   /**
+   * Returns whether a given editor is the current editor to be selected. Tests
+   * based on href or underlying stylesheet.
+   *
+   * @param {StyleSheetEditor} editor
+   *        The editor to test.
+   */
+  _isEditorToSelect: function(editor) {
+    let toSelect = this._styleSheetToSelect;
+    if (!toSelect) {
+      return false;
+    }
+    let isHref = toSelect.stylesheet === null ||
+                 typeof toSelect.stylesheet == "string";
+
+    return (isHref && editor.styleSheet.href == toSelect.stylesheet) ||
+           (toSelect.stylesheet == editor.styleSheet);
+  },
+
+  /**
    * Select an editor in the UI.
    *
    * @param  {StyleSheetEditor} editor
    *         Editor to switch to.
    * @param  {number} line
    *         Line number to jump to
    * @param  {number} col
    *         Column number to jump to
--- a/browser/devtools/styleeditor/test/browser_styleeditor_private_perwindowpb.js
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_private_perwindowpb.js
@@ -37,15 +37,16 @@ function test() {
 
   function onEditorAdded(aEvent, aEditor) {
     info("The style editor is ready")
     aEditor.getSourceEditor().then(checkCache);
   }
 
   function checkCache() {
     checkDiskCacheFor(TEST_HOST, function() {
+      gUI.off("editor-added", onEditorAdded);
       win.close();
       win = null;
       gUI = null;
       finish();
     });
   }
 }
--- a/browser/devtools/styleinspector/computed-view.js
+++ b/browser/devtools/styleinspector/computed-view.js
@@ -1419,29 +1419,26 @@ SelectorView.prototype = {
           contentDoc = rawNode.ownerDocument;
         }
       }
       let viewSourceUtils = inspector.viewSourceUtils;
       viewSourceUtils.viewSource(rule.href, null, contentDoc, rule.line);
       return;
     }
 
-    let location = promise.resolve({
-      href: rule.href,
-      line: rule.line
-    });
-    if (rule.href && Services.prefs.getBoolPref(PREF_ORIG_SOURCES)) {
+    let location = promise.resolve(rule.location);
+    if (Services.prefs.getBoolPref(PREF_ORIG_SOURCES)) {
       location = rule.getOriginalLocation();
     }
-
-    location.then(({href, line}) => {
+    location.then(({source, href, line, column}) => {
       let target = inspector.target;
       if (ToolDefinitions.styleEditor.isTargetSupported(target)) {
         gDevTools.showToolbox(target, "styleeditor").then(function(toolbox) {
-          toolbox.getCurrentPanel().selectStyleSheet(href, line);
+          let sheet = source || href;
+          toolbox.getCurrentPanel().selectStyleSheet(sheet, line, column);
         });
       }
     });
   }
 };
 
 exports.CssHtmlTree = CssHtmlTree;
 exports.PropertyView = PropertyView;
--- a/browser/devtools/styleinspector/style-inspector.js
+++ b/browser/devtools/styleinspector/style-inspector.js
@@ -56,21 +56,22 @@ function RuleViewTool(aInspector, aWindo
       viewSourceUtils.viewSource(href, null, contentDoc, rule.line || 0);
       return;
     }
 
     let location = promise.resolve(rule.location);
     if (Services.prefs.getBoolPref(PREF_ORIG_SOURCES)) {
       location = rule.getOriginalLocation();
     }
-    location.then(({ href, line, column }) => {
+    location.then(({ source, href, line, column }) => {
       let target = this.inspector.target;
       if (ToolDefinitions.styleEditor.isTargetSupported(target)) {
         gDevTools.showToolbox(target, "styleeditor").then(function(toolbox) {
-          toolbox.getCurrentPanel().selectStyleSheet(href, line, column);
+          let sheet = source || href;
+          toolbox.getCurrentPanel().selectStyleSheet(sheet, line, column);
         });
       }
       return;
     })
   }
 
   this.view.element.addEventListener("CssRuleViewCSSLinkClicked",
                                      this._cssLinkHandler);
--- a/browser/devtools/styleinspector/test/browser_computedview_style-editor-link.js
+++ b/browser/devtools/styleinspector/test/browser_computedview_style-editor-link.js
@@ -16,16 +16,19 @@ const DOCUMENT_URL = "data:text/html;cha
    '<head>' +
    '<title>Computed view style editor link test</title>',
    '<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">',
    '</style>',
+   '<style>',
+   'div { color: #f06; }',
+   '</style>',
    '<link rel="stylesheet" type="text/css" href="'+STYLESHEET_URL+'">',
    '</head>',
    '<body>',
    '<h1>Some header text</h1>',
    '<p id="salutation" style="font-size: 12pt">hi.</p>',
    '<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" class="highlight">',
@@ -45,70 +48,92 @@ let test = asyncTest(function*() {
 
   info("Opening the computed-view");
   let {toolbox, inspector, view} = yield openComputedView();
 
   info("Selecting the test node");
   yield selectNode("span", inspector);
 
   yield testInlineStyle(view, inspector);
-  yield testInlineStyleSheet(view, toolbox);
+  yield testFirstInlineStyleSheet(view, toolbox);
+  yield testSecondInlineStyleSheet(view, toolbox);
   yield testExternalStyleSheet(view, toolbox);
 });
 
 function* testInlineStyle(view, inspector) {
   info("Testing inline style");
 
   yield expandComputedViewPropertyByIndex(view, inspector, 0);
 
   let onWindow = waitForWindow();
   info("Clicking on the first rule-link in the computed-view");
-  let link = getComputedViewLinkByIndex(view, 0);
-  link.click();
+  clickLinkByIndex(view, 0);
 
   let win = yield onWindow;
 
   let windowType = win.document.documentElement.getAttribute("windowtype");
   is(windowType, "navigator:view-source", "View source window is open");
   info("Closing window");
   win.close();
 }
 
-function* testInlineStyleSheet(view, toolbox) {
+function* testFirstInlineStyleSheet(view, toolbox) {
   info("Testing inline stylesheet");
 
   info("Listening for toolbox switch to the styleeditor");
   let onSwitch = waitForStyleEditor(toolbox);
 
   info("Clicking an inline stylesheet");
-  let link = getComputedViewLinkByIndex(view, 2);
-  link.click();
+  clickLinkByIndex(view, 2);
   let editor = yield onSwitch;
 
   ok(true, "Switched to the style-editor panel in the toolbox");
 
   validateStyleEditorSheet(editor, 0);
 }
 
+function* testSecondInlineStyleSheet(view, toolbox) {
+  info("Testing second inline stylesheet");
+
+  info("Waiting for the stylesheet editor to be selected");
+  let panel = toolbox.getCurrentPanel();
+  let onSelected = panel.UI.once("editor-selected");
+
+  info("Switching back to the inspector panel in the toolbox");
+  yield toolbox.selectTool("inspector");
+
+  info("Clicking on second inline stylesheet link");
+  clickLinkByIndex(view, 4);
+  let editor = yield onSelected;
+
+  is(toolbox.currentToolId, "styleeditor", "The style editor is selected again");
+  validateStyleEditorSheet(editor, 1);
+}
+
 function* testExternalStyleSheet(view, toolbox) {
   info("Testing external stylesheet");
 
   info("Waiting for the stylesheet editor to be selected");
   let panel = toolbox.getCurrentPanel();
   let onSelected = panel.UI.once("editor-selected");
 
   info("Switching back to the inspector panel in the toolbox");
   yield toolbox.selectTool("inspector");
 
   info("Clicking on an external stylesheet link");
-  let link =  getComputedViewLinkByIndex(view, 1);
-  link.click();
+  clickLinkByIndex(view, 1);
   let editor = yield onSelected;
 
   is(toolbox.currentToolId, "styleeditor", "The style editor is selected again");
-  validateStyleEditorSheet(editor, 1);
+  validateStyleEditorSheet(editor, 2);
 }
 
 function validateStyleEditorSheet(editor, expectedSheetIndex) {
   info("Validating style editor stylesheet");
   let sheet = content.document.styleSheets[expectedSheetIndex];
   is(editor.styleSheet.href, sheet.href, "loaded stylesheet matches document stylesheet");
 }
+
+function clickLinkByIndex(view, index) {
+  let link = getComputedViewLinkByIndex(view, index);
+  link.scrollIntoView();
+  link.click();
+}
--- a/browser/devtools/styleinspector/test/browser_ruleview_style-editor-link.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_style-editor-link.js
@@ -19,16 +19,19 @@ const DOCUMENT_URL = "data:text/html;cha
    '<head>' +
    '<title>Rule view style editor link test</title>',
    '<style type="text/css"> ',
    'html { color: #000000; } ',
    'div { 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">',
    '</style>',
+   '<style>',
+   'div { font-weight: bold; }',
+   '</style>',
    '<link rel="stylesheet" type="text/css" href="'+STYLESHEET_URL+'">',
    '<link rel="stylesheet" type="text/css" href="'+EXTERNAL_STYLESHEET_URL+'">',
    '</head>',
    '<body>',
    '<h1>Some header text</h1>',
    '<p id="salutation" style="font-size: 12pt">hi.</p>',
    '<p id="body" style="font-size: 12pt">I am a test-case. This text exists ',
    'solely to provide some things to ',
@@ -47,84 +50,107 @@ const DOCUMENT_URL = "data:text/html;cha
 let test = asyncTest(function*() {
   yield addTab(DOCUMENT_URL);
   let {toolbox, inspector, view} = yield openRuleView();
 
   info("Select the test node");
   yield selectNode("div", inspector);
 
   yield testInlineStyle(view, inspector);
-  yield testInlineStyleSheet(view, toolbox);
+  yield testFirstInlineStyleSheet(view, toolbox);
+  yield testSecondInlineStyleSheet(view, toolbox);
   yield testExternalStyleSheet(view, toolbox);
 });
 
 function* testInlineStyle(view, inspector) {
   info("Testing inline style");
 
   let onWindow = waitForWindow();
   info("Clicking on the first link in the rule-view");
-  let link = getRuleViewLinkByIndex(view, 0);
-  link.scrollIntoView();
-  link.click();
+  clickLinkByIndex(view, 0);
 
   let win = yield onWindow;
 
   let windowType = win.document.documentElement.getAttribute("windowtype");
   is(windowType, "navigator:view-source", "View source window is open");
   info("Closing window");
   win.close();
 }
 
-function* testInlineStyleSheet(view, toolbox) {
+function* testFirstInlineStyleSheet(view, toolbox) {
   info("Testing inline stylesheet");
 
   info("Listening for toolbox switch to the styleeditor");
   let onSwitch = waitForStyleEditor(toolbox);
 
   info("Clicking an inline stylesheet");
-  let link = getRuleViewLinkByIndex(view, 4);
-  link.scrollIntoView();
-  link.click();
+  clickLinkByIndex(view, 4);
   let editor = yield onSwitch;
 
   ok(true, "Switched to the style-editor panel in the toolbox");
 
   validateStyleEditorSheet(editor, 0);
 }
 
+function* testSecondInlineStyleSheet(view, toolbox) {
+  info("Testing second inline stylesheet");
+
+  info("Waiting for the stylesheet editor to be selected");
+  let panel = toolbox.getCurrentPanel();
+  let onSelected = panel.UI.once("editor-selected");
+
+  info("Switching back to the inspector panel in the toolbox");
+  yield toolbox.selectTool("inspector");
+
+  info("Clicking on second inline stylesheet link");
+  testRuleViewLinkLabel(view);
+  clickLinkByIndex(view, 3);
+  let editor = yield onSelected;
+
+  is(toolbox.currentToolId, "styleeditor", "The style editor is selected again");
+  validateStyleEditorSheet(editor, 1);
+}
+
 function* testExternalStyleSheet(view, toolbox) {
   info("Testing external stylesheet");
 
   info("Waiting for the stylesheet editor to be selected");
   let panel = toolbox.getCurrentPanel();
   let onSelected = panel.UI.once("editor-selected");
 
   info("Switching back to the inspector panel in the toolbox");
   yield toolbox.selectTool("inspector");
 
   info("Clicking on an external stylesheet link");
   testRuleViewLinkLabel(view);
-  let link =  getRuleViewLinkByIndex(view, 1);
-  link.scrollIntoView();
-  link.click();
+  clickLinkByIndex(view, 1);
   let editor = yield onSelected;
 
   is(toolbox.currentToolId, "styleeditor", "The style editor is selected again");
-  validateStyleEditorSheet(editor, 1);
+  validateStyleEditorSheet(editor, 2);
 }
 
 function validateStyleEditorSheet(editor, expectedSheetIndex) {
   info("validating style editor stylesheet");
+  is(editor.styleSheet.styleSheetIndex, expectedSheetIndex,
+     "loaded stylesheet index matches document stylesheet");
+
   let sheet = content.document.styleSheets[expectedSheetIndex];
-  is(editor.styleSheet.href, sheet.href, "loaded stylesheet matches document stylesheet");
+  is(editor.styleSheet.href, sheet.href, "loaded stylesheet href matches document stylesheet");
 }
 
 function testRuleViewLinkLabel(view) {
   let link = getRuleViewLinkByIndex(view, 2);
   let labelElem = link.querySelector(".source-link-label");
   let value = labelElem.getAttribute("value");
   let tooltipText = labelElem.getAttribute("tooltiptext");
 
   is(value, EXTERNAL_STYLESHEET_FILE_NAME + ":1",
     "rule view stylesheet display value matches filename and line number");
   is(tooltipText, EXTERNAL_STYLESHEET_URL,
     "rule view stylesheet tooltip text matches the full URI path");
 }
+
+function clickLinkByIndex(view, index) {
+  let link = getRuleViewLinkByIndex(view, index);
+  link.scrollIntoView();
+  link.click();
+}
--- a/toolkit/devtools/server/actors/root.js
+++ b/toolkit/devtools/server/actors/root.js
@@ -7,16 +7,21 @@
 "use strict";
 
 const { Ci, Cu } = require("chrome");
 const Services = require("Services");
 const { ActorPool, appendExtraActors, createExtraActors } = require("devtools/server/actors/common");
 const { DebuggerServer } = require("devtools/server/main");
 const { dumpProtocolSpec } = require("devtools/server/protocol");
 const makeDebugger = require("./utils/make-debugger");
+const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
+
+DevToolsUtils.defineLazyGetter(this, "StyleSheetActor", () => {
+  return require("devtools/server/actors/stylesheets").StyleSheetActor;
+});
 
 /* Root actor for the remote debugging protocol. */
 
 /**
  * Create a remote debugging protocol root actor.
  *
  * @param aConnection
  *     The DebuggerServerConnection whose root actor we are constructing.
@@ -89,16 +94,19 @@ const makeDebugger = require("./utils/ma
  */
 function RootActor(aConnection, aParameters) {
   this.conn = aConnection;
   this._parameters = aParameters;
   this._onTabListChanged = this.onTabListChanged.bind(this);
   this._onAddonListChanged = this.onAddonListChanged.bind(this);
   this._extraActors = {};
 
+  // Map of DOM stylesheets to StyleSheetActors
+  this._styleSheetActors = new Map();
+
   // This creates a Debugger instance for chrome debugging all globals.
   this.makeDebugger = makeDebugger.bind(null, {
     findDebuggees: dbg => dbg.findAllGlobals(),
     shouldAddNewGlobalAsDebuggee: () => true
   });
 }
 
 RootActor.prototype = {
@@ -221,16 +229,18 @@ RootActor.prototype = {
     }
     if (this._parameters.addonList) {
       this._parameters.addonList.onListChanged = null;
     }
     if (typeof this._parameters.onShutdown === 'function') {
       this._parameters.onShutdown();
     }
     this._extraActors = null;
+    this._styleSheetActors.clear();
+    this._styleSheetActors = null;
   },
 
   /* The 'listTabs' request and the 'tabListChanged' notification. */
 
   /**
    * Handles the listTabs request. The actors will survive until at least
    * the next listTabs request.
    */
@@ -382,16 +392,38 @@ RootActor.prototype = {
     let e = Services.wm.getEnumerator(null);
     while (e.hasMoreElements()) {
       let win = e.getNext();
       let windowUtils = win.QueryInterface(Ci.nsIInterfaceRequestor)
                            .getInterface(Ci.nsIDOMWindowUtils);
       windowUtils.resumeTimeouts();
       windowUtils.suppressEventHandling(false);
     }
+  },
+
+  /**
+   * Create or return the StyleSheetActor for a style sheet. This method
+   * is here because the Style Editor and Inspector share style sheet actors.
+   *
+   * @param DOMStyleSheet styleSheet
+   *        The style sheet to creat an actor for.
+   * @return StyleSheetActor actor
+   *         The actor for this style sheet.
+   *
+   */
+  createStyleSheetActor: function(styleSheet) {
+    if (this._styleSheetActors.has(styleSheet)) {
+      return this._styleSheetActors.get(styleSheet);
+    }
+    let actor = new StyleSheetActor(styleSheet, this);
+    this._styleSheetActors.set(styleSheet, actor);
+
+    this._globalActorPool.addActor(actor);
+
+    return actor;
   }
 };
 
 RootActor.prototype.requestTypes = {
   "listTabs": RootActor.prototype.onListTabs,
   "listAddons": RootActor.prototype.onListAddons,
   "echo": RootActor.prototype.onEcho,
   "protocolDescription": RootActor.prototype.onProtocolDescription
--- a/toolkit/devtools/server/actors/styles.js
+++ b/toolkit/devtools/server/actors/styles.js
@@ -7,17 +7,19 @@
 const {Cc, Ci, Cu} = require("chrome");
 const Services = require("Services");
 const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
 const protocol = require("devtools/server/protocol");
 const {Arg, Option, method, RetVal, types} = protocol;
 const events = require("sdk/event/core");
 const object = require("sdk/util/object");
 const { Class } = require("sdk/core/heritage");
-const { StyleSheetActor } = require("devtools/server/actors/stylesheets");
+
+// This will add the "stylesheet" actor type for protocol.js to recognize
+require("devtools/server/actors/stylesheets");
 
 loader.lazyGetter(this, "CssLogic", () => require("devtools/styleinspector/css-logic").CssLogic);
 loader.lazyGetter(this, "DOMUtils", () => Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils));
 
 // The PageStyle actor flattens the DOM CSS objects a little bit, merging
 // Rules and their Styles into one actor.  For elements (which have a style
 // but no associated rule) we fake a rule with the following style id.
 const ELEMENT_STYLE = 100;
@@ -107,27 +109,25 @@ var PageStyleActor = protocol.ActorClass
     let actor = StyleRuleActor(this, item);
     this.manage(actor);
     this.refMap.set(item, actor);
 
     return actor;
   },
 
   /**
-   * Return or create a StyleSheetActor for the given
-   * nsIDOMCSSStyleSheet
+   * Return or create a StyleSheetActor for the given nsIDOMCSSStyleSheet.
+   * @param  {DOMStyleSheet} sheet
+   *         The style sheet to create an actor for.
+   * @return {StyleSheetActor}
+   *         The actor for this style sheet
    */
   _sheetRef: function(sheet) {
-    if (this.refMap.has(sheet)) {
-      return this.refMap.get(sheet);
-    }
-    let actor = new StyleSheetActor(sheet, this, this.walker.rootWin);
-    this.manage(actor);
-    this.refMap.set(sheet, actor);
-
+    let tabActor = this.inspector.tabActor;
+    let actor = tabActor.createStyleSheetActor(sheet);
     return actor;
   },
 
   /**
    * Get the computed style for a node.
    *
    * @param NodeActor node
    * @param object options
@@ -970,16 +970,17 @@ var StyleRuleFront = protocol.FrontClass
   get nodeHref() {
     let sheet = this.parentStyleSheet;
     return sheet ? sheet.nodeHref : "";
   },
 
   get location()
   {
     return {
+      source: this.parentStyleSheet,
       href: this.href,
       line: this.line,
       column: this.column
     };
   },
 
   getOriginalLocation: function()
   {
@@ -987,22 +988,25 @@ var StyleRuleFront = protocol.FrontClass
       return promise.resolve(this._originalLocation);
     }
 
     let parentSheet = this.parentStyleSheet;
     if (!parentSheet) {
       return promise.resolve(this.location);
     }
     return parentSheet.getOriginalLocation(this.line, this.column)
-      .then(({ source, line, column }) => {
+      .then(({ fromSourceMap, source, line, column }) => {
         let location = {
           href: source,
           line: line,
           column: column
         }
+        if (fromSourceMap === false) {
+          location.source = this.parentStyleSheet;
+        }
         if (!source) {
           location.href = this.href;
         }
         this._originalLocation = location;
         return location;
       })
   }
 });
--- a/toolkit/devtools/server/actors/stylesheets.js
+++ b/toolkit/devtools/server/actors/stylesheets.js
@@ -69,27 +69,16 @@ let StyleSheetsActor = protocol.ActorCla
   {
     return { actor: this.actorID };
   },
 
   initialize: function (conn, tabActor) {
     protocol.Actor.prototype.initialize.call(this, null);
 
     this.parentActor = tabActor;
-
-    // keep a map of sheets-to-actors so we don't create two actors for one sheet
-    this._sheets = new Map();
-  },
-
-  /**
-   * Destroy the current StyleSheetsActor instance.
-   */
-  destroy: function()
-  {
-    this._sheets.clear();
   },
 
   /**
    * Protocol method for getting a list of StyleSheetActors representing
    * all the style sheets in this document.
    */
   getStyleSheets: method(function() {
     let deferred = promise.defer();
@@ -151,17 +140,17 @@ let StyleSheetsActor = protocol.ActorCla
    * @return {Promise}
    *         Promise that resolves to an array of StyleSheetActors
    */
   _addStyleSheets: function(styleSheets)
   {
     return Task.spawn(function() {
       let actors = [];
       for (let i = 0; i < styleSheets.length; i++) {
-        let actor = this._createStyleSheetActor(styleSheets[i]);
+        let actor = this.parentActor.createStyleSheetActor(styleSheets[i]);
         actors.push(actor);
 
         // Get all sheets, including imported ones
         let imports = yield this._getImported(actor);
         actors = actors.concat(imports);
       }
       throw new Task.Result(actors);
     }.bind(this));
@@ -183,63 +172,33 @@ let StyleSheetsActor = protocol.ActorCla
       for (let i = 0; i < rules.length; i++) {
         let rule = rules[i];
         if (rule.type == Ci.nsIDOMCSSRule.IMPORT_RULE) {
           // Associated styleSheet may be null if it has already been seen due
           // to duplicate @imports for the same URL.
           if (!rule.styleSheet) {
             continue;
           }
-          let actor = this._createStyleSheetActor(rule.styleSheet);
+          let actor = this.parentActor.createStyleSheetActor(rule.styleSheet);
           imported.push(actor);
 
           // recurse imports in this stylesheet as well
           let children = yield this._getImported(actor);
           imported = imported.concat(children);
         }
         else if (rule.type != Ci.nsIDOMCSSRule.CHARSET_RULE) {
           // @import rules must precede all others except @charset
           break;
         }
       }
 
       throw new Task.Result(imported);
     }.bind(this));
   },
 
-  /**
-   * Create a new actor for a style sheet, if it hasn't already been created.
-   *
-   * @param  {DOMStyleSheet} styleSheet
-   *         The style sheet to create an actor for.
-   * @return {StyleSheetActor}
-   *         The actor for this style sheet
-   */
-  _createStyleSheetActor: function(styleSheet)
-  {
-    if (this._sheets.has(styleSheet)) {
-      return this._sheets.get(styleSheet);
-    }
-    let actor = new StyleSheetActor(styleSheet, this);
-
-    this.manage(actor);
-    this._sheets.set(styleSheet, actor);
-
-    return actor;
-  },
-
-  /**
-   * Clear all the current stylesheet actors in map.
-   */
-  _clearStyleSheetActors: function() {
-    for (let actor in this._sheets) {
-      this.unmanage(this._sheets[actor]);
-    }
-    this._sheets.clear();
-  },
 
   /**
    * Create a new style sheet in the document with the given text.
    * Return an actor for it.
    *
    * @param  {object} request
    *         Debugging protocol request object, with 'text property'
    * @return {object}
@@ -250,17 +209,17 @@ let StyleSheetsActor = protocol.ActorCla
     let style = this.document.createElementNS("http://www.w3.org/1999/xhtml", "style");
     style.setAttribute("type", "text/css");
 
     if (text) {
       style.appendChild(this.document.createTextNode(text));
     }
     parent.appendChild(style);
 
-    let actor = this._createStyleSheetActor(style.sheet);
+    let actor = this.parentActor.createStyleSheetActor(style.sheet);
     return actor;
   }, {
     request: { text: Arg(0, "string") },
     response: { styleSheet: RetVal("stylesheet") }
   })
 });
 
 /**
@@ -318,16 +277,18 @@ let MediaRuleActor = protocol.ActorClass
     }
   },
 
   destroy: function()
   {
     if (this.mql) {
       this.mql.removeListener(this._matchesChange);
     }
+
+    protocol.Actor.prototype.destroy.call(this);
   },
 
   form: function(detail) {
     if (detail === "actorid") {
       return this.actorID;
     }
 
     let form = {
@@ -415,26 +376,16 @@ let StyleSheetActor = protocol.ActorClas
    */
   get window() this._window || this.parentActor.window,
 
   /**
    * Document of target.
    */
   get document() this.window.document,
 
-  /**
-   * Browser for the target.
-   */
-  get browser() {
-    if (this.parentActor.parentActor) {
-      return this.parentActor.parentActor.browser;
-    }
-    return null;
-  },
-
   get ownerNode() this.rawSheet.ownerNode,
 
   /**
    * URL of underlying stylesheet.
    */
   get href() this.rawSheet.href,
 
   /**
@@ -763,16 +714,17 @@ let StyleSheetActor = protocol.ActorClas
    * a promise of the same location.
    */
   getOriginalLocation: method(function(line, column) {
     return this.getSourceMap().then((sourceMap) => {
       if (sourceMap) {
         return sourceMap.originalPositionFor({ line: line, column: column });
       }
       return {
+        fromSourceMap: false,
         source: this.href,
         line: line,
         column: column
       }
     });
   }, {
     request: {
       line: Arg(0, "number"),
@@ -993,17 +945,16 @@ var StyleSheetFront = protocol.FrontClas
     protocol.Front.prototype.initialize.call(this, conn, form);
 
     this._onPropertyChange = this._onPropertyChange.bind(this);
     events.on(this, "property-change", this._onPropertyChange);
   },
 
   destroy: function() {
     events.off(this, "property-change", this._onPropertyChange);
-
     protocol.Front.prototype.destroy.call(this);
   },
 
   _onPropertyChange: function(property, value) {
     this._form[property] = value;
   },
 
   form: function(form, detail) {
--- a/toolkit/devtools/server/actors/webbrowser.js
+++ b/toolkit/devtools/server/actors/webbrowser.js
@@ -24,16 +24,20 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 
 // Assumptions on events module:
 // events needs to be dispatched synchronously,
 // by calling the listeners in the order or registration.
 XPCOMUtils.defineLazyGetter(this, "events", () => {
   return require("sdk/event/core");
 });
 
+XPCOMUtils.defineLazyGetter(this, "StyleSheetActor", () => {
+  return require("devtools/server/actors/stylesheets").StyleSheetActor;
+});
+
 // Also depends on following symbols, shared by common scope with main.js:
 // DebuggerServer, CommonCreateExtraActors, CommonAppendExtraActors, ActorPool,
 // ThreadActor
 
 /**
  * Browser-specific actors.
  */
 
@@ -540,16 +544,19 @@ exports.BrowserTabList = BrowserTabList;
 function TabActor(aConnection)
 {
   this.conn = aConnection;
   this._tabActorPool = null;
   // A map of actor names to actor instances provided by extensions.
   this._extraActors = {};
   this._exited = false;
 
+  // Map of DOM stylesheets to StyleSheetActors
+  this._styleSheetActors = new Map();
+
   this._shouldAddNewGlobalAsDebuggee = this._shouldAddNewGlobalAsDebuggee.bind(this);
 
   this.makeDebugger = makeDebugger.bind(null, {
     findDebuggees: () => this.windows,
     shouldAddNewGlobalAsDebuggee: this._shouldAddNewGlobalAsDebuggee
   });
 
   this.traits = { reconfigure: true };
@@ -717,16 +724,17 @@ TabActor.prototype = {
   },
 
   /**
    * Called when the actor is removed from the connection.
    */
   disconnect: function BTA_disconnect() {
     this._detach();
     this._extraActors = null;
+    this._styleSheetActors.clear();
     this._exited = true;
   },
 
   /**
    * Called by the root actor when the underlying tab is closed.
    */
   exit: function BTA_exit() {
     if (this.exited) {
@@ -1054,16 +1062,22 @@ TabActor.prototype = {
       threadActor.clearDebuggees();
       if (threadActor.dbg) {
         threadActor.dbg.enabled = true;
         threadActor.global = window;
         threadActor.maybePauseOnExceptions();
       }
     }
 
+    for (let sheetActor of this._styleSheetActors.values()) {
+      this._tabPool.removeActor(sheetActor);
+    }
+    this._styleSheetActors.clear();
+
+
     // Refresh the debuggee list when a new window object appears (top window or
     // iframe).
     if (threadActor.attached) {
       threadActor.dbg.addDebuggees();
     }
   },
 
   _windowDestroyed: function (window) {
@@ -1170,16 +1184,38 @@ TabActor.prototype = {
     try {
       // We are very explicitly examining the "console" property of
       // the non-Xrayed object here.
       let console = aWindow.wrappedJSObject.console;
       isNative = console instanceof aWindow.Console;
     }
     catch (ex) { }
     return isNative;
+  },
+
+  /**
+   * Create or return the StyleSheetActor for a style sheet. This method
+   * is here because the Style Editor and Inspector share style sheet actors.
+   *
+   * @param DOMStyleSheet styleSheet
+   *        The style sheet to creat an actor for.
+   * @return StyleSheetActor actor
+   *         The actor for this style sheet.
+   *
+   */
+  createStyleSheetActor: function BTA_createStyleSheetActor(styleSheet) {
+    if (this._styleSheetActors.has(styleSheet)) {
+      return this._styleSheetActors.get(styleSheet);
+    }
+    let actor = new StyleSheetActor(styleSheet, this);
+    this._styleSheetActors.set(styleSheet, actor);
+
+    this._tabPool.addActor(actor);
+
+    return actor;
   }
 };
 
 /**
  * The request types this actor can handle.
  */
 TabActor.prototype.requestTypes = {
   "attach": TabActor.prototype.onAttach,