Bug 1261133 - Use pseudoClass for style editor transition draft
authorRex Lee <rexboy@mozilla.com>
Mon, 23 May 2016 19:55:54 +0800
changeset 384810 950c511420f066b834e28282abf53187299e714c
parent 384778 09221c72fcb03a7c2b4ab27c75304eae3247e3d1
child 524794 4af1d6cc38779a88bcd6274356da4293467e02a5
push id22368
push userbmo:rexboy@mozilla.com
push dateThu, 07 Jul 2016 07:24:42 +0000
bugs1261133
milestone50.0a1
Bug 1261133 - Use pseudoClass for style editor transition MozReview-Commit-ID: Ck5JTXRR0mp
devtools/server/actors/inspector.js
devtools/server/actors/stylesheets.js
dom/events/EventStates.h
layout/style/nsCSSPseudoClassList.h
--- a/devtools/server/actors/inspector.js
+++ b/devtools/server/actors/inspector.js
@@ -1605,20 +1605,16 @@ var WalkerActor = protocol.ActorClassWit
           nodes = this._multiFrameQuerySelectorAll(query);
         }
         for (let node of nodes) {
           for (let className of node.classList) {
             sugs.classes.set(className, (sugs.classes.get(className)|0) + 1);
           }
         }
         sugs.classes.delete("");
-        // Editing the style editor may make the stylesheet have errors and
-        // thus the page's elements' styles start changing with a transition.
-        // That transition comes from the `moz-styleeditor-transitioning` class.
-        sugs.classes.delete("moz-styleeditor-transitioning");
         sugs.classes.delete(HIDDEN_CLASS);
         for (let [className, count] of sugs.classes) {
           if (className.startsWith(completing)) {
             result.push(["." + CSS.escape(className), count, selectorState]);
           }
         }
         break;
 
@@ -1680,20 +1676,16 @@ var WalkerActor = protocol.ActorClassWit
         }
         for (let [tag, count] of sugs.tags) {
           tag && result.push([tag, count]);
         }
         for (let [id, count] of sugs.ids) {
           id && result.push(["#" + id, count]);
         }
         sugs.classes.delete("");
-        // Editing the style editor may make the stylesheet have errors and
-        // thus the page's elements' styles start changing with a transition.
-        // That transition comes from the `moz-styleeditor-transitioning` class.
-        sugs.classes.delete("moz-styleeditor-transitioning");
         sugs.classes.delete(HIDDEN_CLASS);
         for (let [className, count] of sugs.classes) {
           className && result.push(["." + className, count]);
         }
     }
 
     // Sort by count (desc) and name (asc)
     result = result.sort((a, b) => {
--- a/devtools/server/actors/stylesheets.js
+++ b/devtools/server/actors/stylesheets.js
@@ -12,34 +12,36 @@ const {Task} = require("devtools/shared/
 const events = require("sdk/event/core");
 const protocol = require("devtools/shared/protocol");
 const {LongStringActor} = require("devtools/server/actors/string");
 const {fetch} = require("devtools/shared/DevToolsUtils");
 const {listenOnce} = require("devtools/shared/async-utils");
 const {originalSourceSpec, mediaRuleSpec, styleSheetSpec,
        styleSheetsSpec} = require("devtools/shared/specs/stylesheets");
 const {SourceMapConsumer} = require("source-map");
+const { installHelperSheet,
+  addPseudoClassLock, removePseudoClassLock } = require("devtools/server/actors/highlighters/utils/markup");
 
 loader.lazyGetter(this, "CssLogic", () => require("devtools/shared/inspector/css-logic"));
 
 XPCOMUtils.defineLazyGetter(this, "DOMUtils", function () {
   return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
 });
 
-var TRANSITION_CLASS = "moz-styleeditor-transitioning";
+var TRANSITION_PSEUDO_CLASS = ":-moz-styleeditor-transitioning";
 var TRANSITION_DURATION_MS = 500;
 var TRANSITION_BUFFER_MS = 1000;
 var TRANSITION_RULE_SELECTOR =
-".moz-styleeditor-transitioning:root, .moz-styleeditor-transitioning:root *";
-var TRANSITION_RULE = TRANSITION_RULE_SELECTOR + " {\
-transition-duration: " + TRANSITION_DURATION_MS + "ms !important; \
-transition-delay: 0ms !important;\
-transition-timing-function: ease-out !important;\
-transition-property: all !important;\
-}";
+`:root${TRANSITION_PSEUDO_CLASS}, :root${TRANSITION_PSEUDO_CLASS} *`;
+var TRANSITION_RULE = `${TRANSITION_RULE_SELECTOR} {
+  transition-duration: ${TRANSITION_DURATION_MS}ms !important;
+  transition-delay: 0ms !important;
+  transition-timing-function: ease-out !important;
+  transition-property: all !important;
+}`;
 
 var LOAD_ERROR = "error-load";
 
 // The possible kinds of style-applied events.
 // UPDATE_PRESERVING_RULES means that the update is guaranteed to
 // preserve the number and order of rules on the style sheet.
 // UPDATE_GENERAL covers any other kind of change to the style sheet.
 const UPDATE_PRESERVING_RULES = 0;
@@ -243,30 +245,44 @@ var StyleSheetActor = protocol.ActorClas
           this._styleSheetIndex = i;
           break;
         }
       }
     }
     return this._styleSheetIndex;
   },
 
+  destroy: function () {
+    if (this._transitionTimeout) {
+      this.window.clearTimeout(this._transitionTimeout);
+      removePseudoClassLock(
+                   this.document.documentElement, TRANSITION_PSEUDO_CLASS);
+    }
+  },
+
+  /**
+   * Since StyleSheetActor doesn't have a protocol.js parent actor that take
+   * care of its lifetime, implementing disconnect is required to cleanup.
+   */
+  disconnect: function () {
+    this.destroy();
+  },
+
   initialize: function (aStyleSheet, aParentActor, aWindow) {
     protocol.Actor.prototype.initialize.call(this, null);
 
     this.rawSheet = aStyleSheet;
     this.parentActor = aParentActor;
     this.conn = this.parentActor.conn;
 
     this._window = aWindow;
 
     // text and index are unknown until source load
     this.text = null;
     this._styleSheetIndex = -1;
-
-    this._transitionRefCount = 0;
   },
 
   /**
    * Test whether all the rules in this sheet have associated source.
    * @return {Boolean} true if all the rules have source; false if
    *         some rule was created via CSSOM.
    */
   allRulesHaveSource: function () {
@@ -716,17 +732,17 @@ var StyleSheetActor = protocol.ActorClas
     });
   },
 
   /**
    * Insert a catch-all transition rule into the document. Set a timeout
    * to remove the rule after a certain time.
    */
   _insertTransistionRule: function (kind) {
-    this.document.documentElement.classList.add(TRANSITION_CLASS);
+    addPseudoClassLock(this.document.documentElement, TRANSITION_PSEUDO_CLASS);
 
     // We always add the rule since we've just reset all the rules
     this.rawSheet.insertRule(TRANSITION_RULE, this.rawSheet.cssRules.length);
 
     // Set up clean up and commit after transition duration (+buffer)
     // @see _onTransitionEnd
     this.window.clearTimeout(this._transitionTimeout);
     this._transitionTimeout = this.window.setTimeout(this._onTransitionEnd.bind(this, kind),
@@ -734,17 +750,18 @@ var StyleSheetActor = protocol.ActorClas
   },
 
   /**
    * This cleans up class and rule added for transition effect and then
    * notifies that the style has been applied.
    */
   _onTransitionEnd: function (kind)
   {
-    this.document.documentElement.classList.remove(TRANSITION_CLASS);
+    this._transitionTimeout = null;
+    removePseudoClassLock(this.document.documentElement, TRANSITION_PSEUDO_CLASS);
 
     let index = this.rawSheet.cssRules.length - 1;
     let rule = this.rawSheet.cssRules[index];
     if (rule.selectorText == TRANSITION_RULE_SELECTOR) {
       this.rawSheet.deleteRule(index);
     }
 
     events.emit(this, "style-applied", kind, this);
--- a/dom/events/EventStates.h
+++ b/dom/events/EventStates.h
@@ -281,16 +281,18 @@ private:
 // Element is ltr (for :dir pseudo-class)
 #define NS_EVENT_STATE_LTR NS_DEFINE_EVENT_STATE_MACRO(42)
 // Element is rtl (for :dir pseudo-class)
 #define NS_EVENT_STATE_RTL NS_DEFINE_EVENT_STATE_MACRO(43)
 // Element is highlighted (devtools inspector)
 #define NS_EVENT_STATE_DEVTOOLS_HIGHLIGHTED NS_DEFINE_EVENT_STATE_MACRO(45)
 // Element is an unresolved custom element candidate
 #define NS_EVENT_STATE_UNRESOLVED NS_DEFINE_EVENT_STATE_MACRO(46)
+// Element is transitioning for rules changed by style editor
+#define NS_EVENT_STATE_STYLEEDITOR_TRANSITIONING NS_DEFINE_EVENT_STATE_MACRO(47)
 
 // Event state that is used for values that need to be parsed but do nothing.
 #define NS_EVENT_STATE_IGNORE NS_DEFINE_EVENT_STATE_MACRO(63)
 
 /**
  * NOTE: do not go over 63 without updating EventStates::InternalType!
  */
 
@@ -299,9 +301,8 @@ private:
 #define ESM_MANAGED_STATES (NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_FOCUS |     \
                             NS_EVENT_STATE_HOVER | NS_EVENT_STATE_DRAGOVER |   \
                             NS_EVENT_STATE_URLTARGET | NS_EVENT_STATE_FOCUSRING | \
                             NS_EVENT_STATE_FULL_SCREEN | NS_EVENT_STATE_UNRESOLVED)
 
 #define INTRINSIC_STATES (~ESM_MANAGED_STATES)
 
 #endif // mozilla_EventStates_h_
-
--- a/layout/style/nsCSSPseudoClassList.h
+++ b/layout/style/nsCSSPseudoClassList.h
@@ -156,16 +156,18 @@ CSS_STATE_PSEUDO_CLASS(focus, ":focus", 
 CSS_STATE_PSEUDO_CLASS(hover, ":hover", 0, "", NS_EVENT_STATE_HOVER)
 CSS_STATE_PSEUDO_CLASS(mozDragOver, ":-moz-drag-over", 0, "", NS_EVENT_STATE_DRAGOVER)
 CSS_STATE_PSEUDO_CLASS(target, ":target", 0, "", NS_EVENT_STATE_URLTARGET)
 CSS_STATE_PSEUDO_CLASS(indeterminate, ":indeterminate", 0, "",
                        NS_EVENT_STATE_INDETERMINATE)
 
 CSS_STATE_PSEUDO_CLASS(mozDevtoolsHighlighted, ":-moz-devtools-highlighted", 0, "",
                        NS_EVENT_STATE_DEVTOOLS_HIGHLIGHTED)
+CSS_STATE_PSEUDO_CLASS(mozStyleeditorTransitioning, ":-moz-styleeditor-transitioning", 0, "",
+                       NS_EVENT_STATE_STYLEEDITOR_TRANSITIONING)
 
 // Matches the element which is being displayed full-screen, and
 // any containing frames.
 CSS_STATE_PSEUDO_CLASS(fullscreen, ":fullscreen",
                        CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME,
                        "full-screen-api.unprefix.enabled",
                        NS_EVENT_STATE_FULL_SCREEN)
 CSS_STATE_PSEUDO_CLASS(mozFullScreen, ":-moz-full-screen", 0, "", NS_EVENT_STATE_FULL_SCREEN)