Bug 1013814 - add inIDOMUtils.getRelativeRuleLine; r=heycam,pbrosset
authorTom Tromey <tromey@mozilla.com>
Mon, 17 Aug 2015 15:19:21 -0700
changeset 258111 880092cace771f2e36d99222cddec3181ddc20ea
parent 258110 56fb0faa84a50beab847ae63fccd8676ee545c1d
child 258112 6c5c08b0df3dd22c20d71d25faf6470bb366de11
push id29244
push userryanvm@gmail.com
push dateTue, 18 Aug 2015 14:41:54 +0000
treeherdermozilla-central@d5cf4e7900df [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersheycam, pbrosset
bugs1013814
milestone43.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 1013814 - add inIDOMUtils.getRelativeRuleLine; r=heycam,pbrosset
dom/base/nsIStyleSheetLinkingElement.h
dom/base/nsStyleLinkElement.cpp
dom/base/nsStyleLinkElement.h
layout/inspector/inDOMUtils.cpp
layout/inspector/inIDOMUtils.idl
layout/inspector/tests/mochitest.ini
layout/inspector/tests/test_getRelativeRuleLine.html
toolkit/devtools/server/actors/csscoverage.js
toolkit/devtools/server/actors/stylesheets.js
--- a/dom/base/nsIStyleSheetLinkingElement.h
+++ b/dom/base/nsIStyleSheetLinkingElement.h
@@ -8,18 +8,18 @@
 
 
 #include "nsISupports.h"
 
 class nsICSSLoaderObserver;
 class nsIURI;
 
 #define NS_ISTYLESHEETLINKINGELEMENT_IID          \
-{ 0xe5855604, 0x8a9a, 0x4181, \
- { 0xbe, 0x41, 0xdd, 0xf7, 0x08, 0x70, 0x3f, 0xbe } }
+{ 0xa8b79f3b, 0x9d18, 0x4f9c, \
+  { 0xb1, 0xaa, 0x8c, 0x9b, 0x1b, 0xaa, 0xac, 0xad } }
 
 namespace mozilla {
 class CSSStyleSheet;
 } // namespace mozilla
 
 class nsIStyleSheetLinkingElement : public nsISupports {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_ISTYLESHEETLINKINGELEMENT_IID)
@@ -92,14 +92,22 @@ public:
    * @param aNewBaseURI the new base URI, nullptr to use the default base URI.
    */
   virtual void OverrideBaseURI(nsIURI* aNewBaseURI) = 0;
 
   // This doesn't entirely belong here since they only make sense for
   // some types of linking elements, but it's a better place than
   // anywhere else.
   virtual void SetLineNumber(uint32_t aLineNumber) = 0;
+
+  /**
+   * Get the line number, as previously set by SetLineNumber.
+   *
+   * @return the line number of this element; or 1 if no line number
+   *         was set
+   */
+  virtual uint32_t GetLineNumber() = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIStyleSheetLinkingElement,
                               NS_ISTYLESHEETLINKINGELEMENT_IID)
 
 #endif // nsILinkingElement_h__
--- a/dom/base/nsStyleLinkElement.cpp
+++ b/dom/base/nsStyleLinkElement.cpp
@@ -114,16 +114,22 @@ nsStyleLinkElement::OverrideBaseURI(nsIU
 }
 
 /* virtual */ void
 nsStyleLinkElement::SetLineNumber(uint32_t aLineNumber)
 {
   mLineNumber = aLineNumber;
 }
 
+/* virtual */ uint32_t
+nsStyleLinkElement::GetLineNumber()
+{
+  return mLineNumber;
+}
+
 /* static */ bool
 nsStyleLinkElement::IsImportEnabled()
 {
   static bool sAdded = false;
   static bool sImportsEnabled;
   if (!sAdded) {
     // This part runs only once because of the static flag.
     Preferences::AddBoolVarCache(&sImportsEnabled,
--- a/dom/base/nsStyleLinkElement.h
+++ b/dom/base/nsStyleLinkElement.h
@@ -47,16 +47,17 @@ public:
                               bool* aWillNotify,
                               bool* aIsAlternate,
                               bool aForceReload) override;
   NS_IMETHOD SetEnableUpdates(bool aEnableUpdates) override;
   NS_IMETHOD GetCharset(nsAString& aCharset) override;
 
   virtual void OverrideBaseURI(nsIURI* aNewBaseURI) override;
   virtual void SetLineNumber(uint32_t aLineNumber) override;
+  virtual uint32_t GetLineNumber() override;
 
   enum RelValue {
     ePREFETCH =     0x00000001,
     eDNS_PREFETCH = 0x00000002,
     eSTYLESHEET =   0x00000004,
     eNEXT =         0x00000008,
     eALTERNATE =    0x00000010,
     eHTMLIMPORT =   0x00000020,
--- a/layout/inspector/inDOMUtils.cpp
+++ b/layout/inspector/inDOMUtils.cpp
@@ -7,16 +7,17 @@
 #include "mozilla/EventStates.h"
 
 #include "inDOMUtils.h"
 #include "inLayoutUtils.h"
 
 #include "nsIServiceManager.h"
 #include "nsISupportsArray.h"
 #include "nsString.h"
+#include "nsIStyleSheetLinkingElement.h"
 #include "nsIDOMElement.h"
 #include "nsIDocument.h"
 #include "nsIPresShell.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMCharacterData.h"
 #include "nsRuleNode.h"
 #include "nsIStyleRule.h"
 #include "mozilla/css/StyleRule.h"
@@ -286,16 +287,43 @@ inDOMUtils::GetRuleColumn(nsIDOMCSSRule*
     return NS_ERROR_FAILURE;
   }
 
   *_retval = rule->GetColumnNumber();
   return NS_OK;
 }
 
 NS_IMETHODIMP
+inDOMUtils::GetRelativeRuleLine(nsIDOMCSSRule* aRule, uint32_t* _retval)
+{
+  NS_ENSURE_ARG_POINTER(aRule);
+
+  Rule* rule = aRule->GetCSSRule();
+  if (!rule) {
+    return NS_ERROR_FAILURE;
+  }
+
+  uint32_t lineNumber = rule->GetLineNumber();
+  CSSStyleSheet* sheet = rule->GetStyleSheet();
+  if (sheet) {
+    nsINode* owningNode = sheet->GetOwnerNode();
+    if (owningNode) {
+      nsCOMPtr<nsIStyleSheetLinkingElement> link =
+        do_QueryInterface(owningNode);
+      if (link) {
+        lineNumber -= link->GetLineNumber() - 1;
+      }
+    }
+  }
+
+  *_retval = lineNumber;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 inDOMUtils::GetCSSLexer(const nsAString& aText, JSContext* aCx,
                         JS::MutableHandleValue aResult)
 {
   MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
   JS::Rooted<JSObject*> scope(aCx, JS::CurrentGlobalOrNull(aCx));
   nsAutoPtr<CSSLexer> lexer(new CSSLexer(aText));
   if (!WrapNewBindingNonWrapperCachedObject(aCx, scope, lexer, aResult)) {
     return NS_ERROR_FAILURE;
--- a/layout/inspector/inIDOMUtils.idl
+++ b/layout/inspector/inIDOMUtils.idl
@@ -12,27 +12,37 @@ interface nsIDOMDocument;
 interface nsIDOMCSSRule;
 interface nsIDOMCSSStyleRule;
 interface nsIDOMNode;
 interface nsIDOMNodeList;
 interface nsIDOMFontFaceList;
 interface nsIDOMRange;
 interface nsIDOMCSSStyleSheet;
 
-[scriptable, uuid(60b4cbf7-2a08-4419-8937-6ef495417824)]
+[scriptable, uuid(d67c0463-592e-4d7c-b67e-923ee3f6c643)]
 interface inIDOMUtils : nsISupports
 {
   // CSS utilities
   void getAllStyleSheets (in nsIDOMDocument aDoc,
                           [optional] out unsigned long aLength,
                           [array, size_is (aLength), retval] out nsISupports aSheets);
   nsISupportsArray getCSSStyleRules(in nsIDOMElement aElement, [optional] in DOMString aPseudo);
   unsigned long getRuleLine(in nsIDOMCSSRule aRule);
   unsigned long getRuleColumn(in nsIDOMCSSRule aRule);
 
+  /**
+   * Like getRuleLine, but if the rule is in a <style> element,
+   * returns a line number relative to the start of the element.
+   *
+   * @param nsIDOMCSSRule aRule the rule to examine
+   * @return the line number of the rule, possibly relative to the
+   *         <style> element
+   */
+  unsigned long getRelativeRuleLine(in nsIDOMCSSRule aRule);
+
   [implicit_jscontext]
   jsval getCSSLexer(in DOMString aText);
 
   // Utilities for working with selectors.  We don't have a JS OM representation
   // of a single selector or a selector list yet, but given a rule we can index
   // into the selector list.
   //
   // This is a somewhat backwards API; once we move StyleRule to WebIDL we
--- a/layout/inspector/tests/mochitest.ini
+++ b/layout/inspector/tests/mochitest.ini
@@ -11,12 +11,13 @@ support-files =
 [test_bug557726.html]
 [test_bug609549.xhtml]
 [test_bug806192.html]
 [test_bug856317.html]
 [test_bug877690.html]
 [test_bug1006595.html]
 [test_color_to_rgba.html]
 [test_css_property_is_valid.html]
+[test_getRelativeRuleLine.html]
 [test_get_all_style_sheets.html]
 [test_is_valid_css_color.html]
 [test_isinheritableproperty.html]
 [test_selectormatcheselement.html]
new file mode 100644
--- /dev/null
+++ b/layout/inspector/tests/test_getRelativeRuleLine.html
@@ -0,0 +1,67 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test inDOMUtils::getRelativeRuleLine</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <style>
+    @supports (not (whatever: 72 zq)) {
+      #test {
+	background-color: #f0c;
+      }
+    }
+
+    #test {
+      color: #f0c;
+    }
+  </style>
+  <style>#test { color: red; }</style>
+  <style>
+     @invalidatkeyword {
+     }
+
+     #test {
+       color: blue;
+     }
+  </style>
+  <script type="application/javascript;version=1.8">
+  let utils = SpecialPowers.Cc["@mozilla.org/inspector/dom-utils;1"]
+                           .getService(SpecialPowers.Ci.inIDOMUtils);
+
+  let tests = [
+    { sheetNo: 0, ruleNo: 0, lineNo: 1, columnNo: 1 },
+    { sheetNo: 1, ruleNo: 0, lineNo: 2, columnNo: 15 },
+    { sheetNo: 1, ruleNo: 1, lineNo: 8, columnNo: 5 },
+    { sheetNo: 2, ruleNo: 0, lineNo: 1, columnNo: 1 },
+    { sheetNo: 3, ruleNo: 0, lineNo: 5, columnNo: 6 },
+  ];
+
+  function doTest() {
+    for (let test of tests) {
+      let sheet = document.styleSheets[test.sheetNo];
+      let rule = sheet.cssRules[test.ruleNo];
+      let line = utils.getRelativeRuleLine(rule);
+      let column = utils.getRuleColumn(rule);
+      info("testing sheet " + test.sheetNo + ", rule " + test.ruleNo);
+      is(line, test.lineNo, "line number is correct");
+      is(column, test.columnNo, "column number is correct");
+    }
+
+    SimpleTest.finish();
+  }
+
+  SimpleTest.waitForExplicitFinish();
+  addLoadEvent(doTest);
+  </script>
+</head>
+<body>
+<h1>Test inDOMUtils::getRelativeRuleLine</h1>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
--- a/toolkit/devtools/server/actors/csscoverage.js
+++ b/toolkit/devtools/server/actors/csscoverage.js
@@ -559,18 +559,19 @@ function getImportedSheets(stylesheet) {
 }
 
 /**
  * Get a unique identifier for a rule. This is currently the string
  * <CSS-URL>|<START-LINE>|<START-COLUMN>
  * @see deconstructRuleId(ruleId)
  */
 function ruleToId(rule) {
-  let loc = stylesheets.getRuleLocation(rule);
-  return sheetToUrl(rule.parentStyleSheet) + "|" + loc.line + "|" + loc.column;
+  let line = DOMUtils.getRelativeRuleLine(rule);
+  let column = DOMUtils.getRuleColumn(rule);
+  return sheetToUrl(rule.parentStyleSheet) + "|" + line + "|" + column;
 }
 
 /**
  * Convert a ruleId to an object with { url, line, column } properties
  * @see ruleToId(rule)
  */
 const deconstructRuleId = exports.deconstructRuleId = function(ruleId) {
   let split = ruleId.split("|");
--- a/toolkit/devtools/server/actors/stylesheets.js
+++ b/toolkit/devtools/server/actors/stylesheets.js
@@ -928,63 +928,16 @@ let StyleSheetActor = protocol.ActorClas
       this.rawSheet.deleteRule(index);
     }
 
     events.emit(this, "style-applied");
   }
 })
 
 /**
- * Find the line/column for a rule.
- * This is like DOMUtils.getRule[Line|Column] except for inline <style> sheets,
- * the line number returned here is relative to the <style> tag rather than the
- * containing HTML document (which is what DOMUtils does).
- * This is hacky, but we don't know of a better implementation right now.
- */
-const getRuleLocation = exports.getRuleLocation = function(rule) {
-  let reply = {
-    line: DOMUtils.getRuleLine(rule),
-    column: DOMUtils.getRuleColumn(rule)
-  };
-
-  let sheet = rule.parentStyleSheet;
-  if (sheet.ownerNode && sheet.ownerNode.localName === "style") {
-     // For inline sheets, the line is relative to HTML not the stylesheet, so
-     // Get the location of the first { to know the line num of the first rule,
-     // relative to this sheet, to get the offset
-     let text = sheet.ownerNode.textContent;
-     // Hacky for now, because this will fail if { appears in a comment before
-     // but better than nothing, and faster than parsing the whole text
-     let start = text.substring(0, text.indexOf("{"));
-     let relativeStartLine = start.split("\n").length;
-
-     let absoluteStartLine;
-     let i = 0;
-     while (absoluteStartLine == null) {
-       let irule = sheet.cssRules[i];
-       if (irule instanceof Ci.nsIDOMCSSStyleRule) {
-         absoluteStartLine = DOMUtils.getRuleLine(irule);
-       }
-       else if (irule == null) {
-         break;
-       }
-
-       i++;
-     }
-
-     if (absoluteStartLine != null) {
-       let offset = absoluteStartLine - relativeStartLine;
-       reply.line -= offset;
-     }
-  }
-
-  return reply;
-};
-
-/**
  * StyleSheetFront is the client-side counterpart to a StyleSheetActor.
  */
 var StyleSheetFront = protocol.FrontClass(StyleSheetActor, {
   initialize: function(conn, form) {
     protocol.Front.prototype.initialize.call(this, conn, form);
 
     this._onPropertyChange = this._onPropertyChange.bind(this);
     events.on(this, "property-change", this._onPropertyChange);