From a8d042b241c09f8f4cb44224e6e6617e902a30e1 Mon Sep 17 00:00:00 2001
From: Guillaume Delhumeau <guillaume.delhumeau@xwiki.com>
Date: Thu, 23 Oct 2014 13:28:30 +0200
Subject: [PATCH] XWIKI-10776: The cache of the LESSCompiler is badly designed.

---
 xwiki-platform-core/pom.xml                   |   4 +
 .../java/org/xwiki/lesscss/LESSCache.java     |  27 +++--
 .../xwiki/lesscss/internal/AbstractCache.java | 100 ++++++++++++----
 .../internal/AbstractCachedCompiler.java      |  46 ++------
 ...eListener.java => ColorThemeListener.java} |  35 +++---
 .../internal/CurrentColorThemeGetter.java     |  38 +++++++
 .../DefaultCurrentColorThemeGetter.java       |  79 +++++++++++++
 .../internal/DefaultLESSSkinFileCompiler.java |   4 +
 .../internal/LESSExportActionListener.java    |  11 +-
 .../main/resources/META-INF/components.txt    |   5 +-
 ...rTest.java => ColorThemeListenerTest.java} |  58 ++++------
 .../DefaultCurrentColorThemeGetterTest.java   | 107 ++++++++++++++++++
 .../DefaultLESSSkinFileCacheTest.java         |  73 +++++++++---
 .../DefaultLESSSkinFileCompilerTest.java      |  96 ++++------------
 .../LESSExportActionListenerTest.java         |   9 +-
 .../lesscss/LessCompilerScriptService.java    |  38 ++++++-
 .../LessCompilerScriptServiceTest.java        |  71 ++++++++++--
 17 files changed, 576 insertions(+), 225 deletions(-)
 rename xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/main/java/org/xwiki/lesscss/internal/{SkinAndColorThemeListener.java => ColorThemeListener.java} (76%)
 create mode 100644 xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/main/java/org/xwiki/lesscss/internal/CurrentColorThemeGetter.java
 create mode 100644 xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/main/java/org/xwiki/lesscss/internal/DefaultCurrentColorThemeGetter.java
 rename xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/test/java/org/xwiki/lesscss/internal/{SkinAndColorThemeListenerTest.java => ColorThemeListenerTest.java} (77%)
 create mode 100644 xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/test/java/org/xwiki/lesscss/internal/DefaultCurrentColorThemeGetterTest.java

diff --git a/xwiki-platform-core/pom.xml b/xwiki-platform-core/pom.xml
index 8af028369c3..1f988f5e9f2 100644
--- a/xwiki-platform-core/pom.xml
+++ b/xwiki-platform-core/pom.xml
@@ -364,6 +364,10 @@
             <exclude>**/internal/**</exclude>
             <exclude>**/test/**</exclude>
             <!-- Remove the following excludes after we release the current version as final -->
+            <!-- Changed the LESSCache interface and the LESS Script Service because the cache was poorly designed
+                 (see: http://jira.xwiki.org/browse/XWIKI-10776). -->
+            <exclude>**/LESSCache*</exclude>
+            <exclude>**/LessCompilerScriptService*</exclude>
           </excludes>
         </configuration>
       </plugin>
diff --git a/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-api/src/main/java/org/xwiki/lesscss/LESSCache.java b/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-api/src/main/java/org/xwiki/lesscss/LESSCache.java
index d5ce683ba0d..361832aae0e 100644
--- a/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-api/src/main/java/org/xwiki/lesscss/LESSCache.java
+++ b/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-api/src/main/java/org/xwiki/lesscss/LESSCache.java
@@ -32,23 +32,25 @@ public interface LESSCache<T>
     /**
      * Get an object from the name of the LESS source, the wiki ID, the skin and the name of the color theme.
      * @param fileName name of the LESS source
-     * @param wikiId id of the wiki
      * @param skin name of the skin
      * @param colorTheme name of the color theme
      * @return the corresponding CSS
+     *
+     * @since 6.3M2
      */
-    T get(String fileName, String wikiId, String skin, String colorTheme);
+    T get(String fileName, String skin, String colorTheme);
 
     /**
      * Add an object in the cache.
      *
      * @param fileName name of the LESS source
-     * @param wikiId if of the wiki
      * @param fileSystemSkin name of the skin
      * @param colorThemeName name of the color theme
      * @param object the object to cache
+     *
+     * @since 6.3M2
      */
-    void set(String fileName, String wikiId, String fileSystemSkin, String colorThemeName, T object);
+    void set(String fileName, String fileSystemSkin, String colorThemeName, T object);
 
     /**
      * Clear the cache.
@@ -56,9 +58,20 @@ public interface LESSCache<T>
     void clear();
 
     /**
-     * Clear all the cached files related to a wiki.
+     * Clear all the cached files related to a skin.
+     *
+     * @param fileSystemSkin name of the filesystem skin
+     *
+     * @since 6.3M2
+     */
+    void clearFromFileSystemSkin(String fileSystemSkin);
+
+    /**
+     * Clear all the cached files related to a color theme.
+     *
+     * @param colorTheme name of the color theme
      *
-     * @param wikiId id of the wiki
+     * @since 6.3M2
      */
-    void clear(String wikiId);
+    void clearFromColorTheme(String colorTheme);
 }
diff --git a/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/main/java/org/xwiki/lesscss/internal/AbstractCache.java b/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/main/java/org/xwiki/lesscss/internal/AbstractCache.java
index 5c86393b92e..71883335e76 100644
--- a/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/main/java/org/xwiki/lesscss/internal/AbstractCache.java
+++ b/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/main/java/org/xwiki/lesscss/internal/AbstractCache.java
@@ -29,6 +29,11 @@
 import org.xwiki.cache.Cache;
 import org.xwiki.cache.CacheManager;
 import org.xwiki.lesscss.LESSCache;
+import org.xwiki.model.reference.DocumentReference;
+import org.xwiki.model.reference.DocumentReferenceResolver;
+import org.xwiki.model.reference.EntityReferenceSerializer;
+import org.xwiki.model.reference.WikiReference;
+import org.xwiki.wiki.descriptor.WikiDescriptorManager;
 
 /**
  * Default and abstract implementation of {@link org.xwiki.lesscss.LESSCache}.
@@ -51,29 +56,62 @@ public abstract class AbstractCache<T> implements LESSCache<T>
     protected Cache<T> cache;
 
     /**
-     * This map stores the list of the cached files keys corresponding to a wiki.
+     * This map stores the list of the cached files keys corresponding to a skin, in order to clear the corresponding
+     * cache when a skin is saved.
      */
-    private Map<String, List<String>> cachedFilesKeysMap = new HashMap<>();
+    private Map<String, List<String>> cachedFilesKeysMapPerSkin = new HashMap<>();
+
+    /**
+     * This map stores the list of the cached files keys corresponding to a color theme, in order to clear the
+     * corresponding cache when a color theme is saved.
+     */
+    private Map<String, List<String>> cachedFilesKeysMapPerColorTheme = new HashMap<>();
+
+    @Inject
+    private DocumentReferenceResolver<String> documentReferenceResolver;
+
+    @Inject
+    private EntityReferenceSerializer<String> entityReferenceSerializer;
+
+    @Inject
+    private WikiDescriptorManager wikiDescriptorManager;
 
     @Override
-    public T get(String fileName, String wikiId, String fileSystemSkin, String colorTheme)
+    public T get(String fileName, String fileSystemSkin, String colorTheme)
     {
-        return cache.get(getCacheKey(fileName, wikiId, fileSystemSkin, colorTheme));
+        return cache.get(getCacheKey(fileName, fileSystemSkin, getColorThemeFullName(colorTheme)));
     }
 
     @Override
-    public void set(String fileName, String wikiId, String fileSystemSkin, String colorTheme, T content)
+    public void set(String fileName, String fileSystemSkin, String colorTheme, T content)
     {
+        // Get the fullname of the color theme
+        String colorThemeFullName = getColorThemeFullName(colorTheme);
+
         // Store the content in the cache
-        String cacheKey = getCacheKey(fileName, wikiId, fileSystemSkin, colorTheme);
+        String cacheKey = getCacheKey(fileName, fileSystemSkin, colorThemeFullName);
         cache.set(cacheKey, content);
 
-        // Add the new key to cachedFilesKeysMap.
-        List<String> cachedFilesKeys = cachedFilesKeysMap.get(wikiId);
+        // Add the new key to maps
+        registerCacheKey(cachedFilesKeysMapPerSkin, cacheKey, fileSystemSkin);
+        registerCacheKey(cachedFilesKeysMapPerColorTheme, cacheKey, colorTheme);
+    }
+
+    /**
+     * Add the cache key in the specified map (cachedFilesKeysMapPerSkin or cachedFilesKeysMapPerColorTheme), to be
+     * able to clear the cache when one skin or one color theme is modified.
+     *
+     * @param cachedFilesKeysMap could be cachedFilesKeysMapPerSkin or cachedFilesKeysMapPerColorTheme
+     * @param cacheKey the cache key to register
+     * @param name name of the skin or of the color theme
+     */
+    private void registerCacheKey(Map<String, List<String>> cachedFilesKeysMap, String cacheKey, String name)
+    {
+        List<String> cachedFilesKeys = cachedFilesKeysMap.get(name);
         if (cachedFilesKeys == null) {
-            // if the list of cached files keys corresponding to the wiki does not exist, we create it
+            // if the list of cached files keys corresponding to the skin/colortheme name does not exist, we create it
             cachedFilesKeys = new ArrayList<>();
-            cachedFilesKeysMap.put(wikiId, cachedFilesKeys);
+            cachedFilesKeysMap.put(name, cachedFilesKeys);
         }
         if (!cachedFilesKeys.contains(cacheKey)) {
             cachedFilesKeys.add(cacheKey);
@@ -84,14 +122,14 @@ public void set(String fileName, String wikiId, String fileSystemSkin, String co
     public void clear()
     {
         cache.removeAll();
-        cachedFilesKeysMap.clear();
+        cachedFilesKeysMapPerSkin.clear();
+        cachedFilesKeysMapPerColorTheme.clear();
     }
 
-    @Override
-    public void clear(String wikiId)
+    private void clearFromCriteria(Map<String, List<String>> cachedFilesKeysMap, String criteria)
     {
-        // Get the list of cached files keys corresponding to the wiki
-        List<String> cachedFilesKeys = cachedFilesKeysMap.get(wikiId);
+        // Get the list of cached files keys corresponding to the criteria
+        List<String> cachedFilesKeys = cachedFilesKeysMap.get(criteria);
         if (cachedFilesKeys == null) {
             return;
         }
@@ -99,13 +137,35 @@ public void clear(String wikiId)
         for (String cachedFileKey : cachedFilesKeys) {
             cache.remove(cachedFileKey);
         }
-        // Remove the list of cached keys corresponding to the wiki
-        cachedFilesKeysMap.remove(wikiId);
+        // Remove the list of cached keys corresponding to the criteria
+        cachedFilesKeysMap.remove(criteria);
+    }
+
+    @Override
+    public void clearFromFileSystemSkin(String fileSystemSkin)
+    {
+        clearFromCriteria(cachedFilesKeysMapPerSkin, fileSystemSkin);
+    }
+
+    @Override
+    public void clearFromColorTheme(String colorTheme)
+    {
+        clearFromCriteria(cachedFilesKeysMapPerColorTheme, colorTheme);
+    }
+
+    private String getColorThemeFullName(String colorTheme)
+    {
+        // Current Wiki Reference
+        WikiReference currentWikiRef = new WikiReference(wikiDescriptorManager.getCurrentWikiId());
+        // Get the full reference of the color theme
+        DocumentReference colorThemeRef = documentReferenceResolver.resolve(colorTheme, currentWikiRef);
+        // Return the serialized reference
+        return entityReferenceSerializer.serialize(colorThemeRef);
     }
 
-    private String getCacheKey(String fileName, String wikiId, String skin, String colorTheme)
+    private String getCacheKey(String fileName, String skin, String colorThemeFullName)
     {
-        return wikiId.length() + wikiId + CACHE_KEY_SEPARATOR + skin.length() + skin + CACHE_KEY_SEPARATOR
-            + colorTheme.length() + colorTheme + CACHE_KEY_SEPARATOR + fileName.length() + fileName;
+        return skin.length() + skin + CACHE_KEY_SEPARATOR  + colorThemeFullName.length() + colorThemeFullName
+                + CACHE_KEY_SEPARATOR + fileName.length() + fileName;
     }
 }
diff --git a/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/main/java/org/xwiki/lesscss/internal/AbstractCachedCompiler.java b/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/main/java/org/xwiki/lesscss/internal/AbstractCachedCompiler.java
index 7884f2fe4e2..6717009c739 100644
--- a/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/main/java/org/xwiki/lesscss/internal/AbstractCachedCompiler.java
+++ b/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/main/java/org/xwiki/lesscss/internal/AbstractCachedCompiler.java
@@ -27,14 +27,8 @@
 
 import org.xwiki.lesscss.LESSCache;
 import org.xwiki.lesscss.LESSCompilerException;
-import org.xwiki.model.reference.DocumentReference;
-import org.xwiki.model.reference.DocumentReferenceResolver;
-import org.xwiki.model.reference.EntityReferenceSerializer;
-import org.xwiki.model.reference.WikiReference;
-import org.xwiki.wiki.descriptor.WikiDescriptorManager;
 
 import com.xpn.xwiki.XWikiContext;
-import com.xpn.xwiki.web.XWikiRequest;
 
 /**
  * Implements a cache system to prevent the compiler to be called too often.
@@ -52,13 +46,7 @@ public abstract class AbstractCachedCompiler<T>
     protected Provider<XWikiContext> xcontextProvider;
 
     @Inject
-    protected WikiDescriptorManager wikiDescriptorManager;
-
-    @Inject
-    protected DocumentReferenceResolver<String> referenceResolver;
-
-    @Inject
-    protected EntityReferenceSerializer<String> referenceSerializer;
+    private CurrentColorThemeGetter currentColorThemeGetter;
 
     private Map<String, Object> mutexList = new HashMap<>();
 
@@ -88,29 +76,15 @@ public T compileFromSkinFile(String fileName, String skin, boolean force) throws
     {
         T result;
 
-        // Get information about the context
-        String wikiId = wikiDescriptorManager.getCurrentWikiId();
-        XWikiContext context = xcontextProvider.get();
-        XWikiRequest request = context.getRequest();
-
-        // Getting the full name representation of colorTheme
-        DocumentReference colorThemeReference = referenceResolver.resolve(request.getParameter("colorTheme"),
-                new WikiReference(wikiId));
-        String colorTheme = referenceSerializer.serialize(colorThemeReference);
-
-        // Check that the color theme exists, to avoid a DOS if some user tries to compile a skin file
-        // with random colorTheme names
-        if (!context.getWiki().exists(colorThemeReference, context)) {
-            colorTheme = "default";
-        }
+        String colorTheme = currentColorThemeGetter.getCurrentColorTheme("default");
 
-        // Only one computation is allowed in the same time on a wiki, then the waiting threads will be able to use
-        // the last result stored in the cache
-        synchronized (getMutex(wikiId)) {
+        // Only one computation is allowed in the same time per color theme, then the waiting threads will be able to
+        // use the last result stored in the cache
+        synchronized (getMutex(colorTheme)) {
 
             // Check if the result is in the cache
             if (!force) {
-                result = cache.get(fileName, wikiId, skin, colorTheme);
+                result = cache.get(fileName, skin, colorTheme);
                 if (result != null) {
                     return result;
                 }
@@ -118,7 +92,7 @@ public T compileFromSkinFile(String fileName, String skin, boolean force) throws
 
             // Either the result was in the cache or the force flag is set to true, we need to compile
             result = compile(fileName, skin, force);
-            cache.set(fileName, wikiId, skin, colorTheme, result);
+            cache.set(fileName, skin, colorTheme, result);
 
         }
 
@@ -127,12 +101,12 @@ public T compileFromSkinFile(String fileName, String skin, boolean force) throws
 
     protected abstract T compile(String fileName, String skin, boolean force) throws LESSCompilerException;
 
-    private synchronized Object getMutex(String wikiId)
+    private synchronized Object getMutex(String colorThemeFullName)
     {
-        Object mutex = mutexList.get(wikiId);
+        Object mutex = mutexList.get(colorThemeFullName);
         if (mutex == null) {
             mutex = new Object();
-            mutexList.put(wikiId, mutex);
+            mutexList.put(colorThemeFullName, mutex);
         }
         return mutex;
     }
diff --git a/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/main/java/org/xwiki/lesscss/internal/SkinAndColorThemeListener.java b/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/main/java/org/xwiki/lesscss/internal/ColorThemeListener.java
similarity index 76%
rename from xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/main/java/org/xwiki/lesscss/internal/SkinAndColorThemeListener.java
rename to xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/main/java/org/xwiki/lesscss/internal/ColorThemeListener.java
index 3bd77a8ee24..eeae9e146df 100644
--- a/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/main/java/org/xwiki/lesscss/internal/SkinAndColorThemeListener.java
+++ b/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/main/java/org/xwiki/lesscss/internal/ColorThemeListener.java
@@ -32,7 +32,7 @@
 import org.xwiki.component.annotation.Component;
 import org.xwiki.lesscss.ColorThemeCache;
 import org.xwiki.lesscss.LESSSkinFileCache;
-import org.xwiki.model.reference.DocumentReference;
+import org.xwiki.model.reference.EntityReferenceSerializer;
 import org.xwiki.model.reference.LocalDocumentReference;
 import org.xwiki.observation.EventListener;
 import org.xwiki.observation.event.Event;
@@ -41,15 +41,15 @@
 import com.xpn.xwiki.objects.BaseObject;
 
 /**
- * Listener that clears the cache of compiled LESS Skin file when a skin or a color theme is changed.
+ * Listener that clears the cache of compiled LESS Skin file when a color theme is changed.
  *
- * @since 6.1M2
+ * @since 6.3M2
  * @version $Id$
  */
 @Component
-@Named("lessSkinColorTheme")
+@Named("lessColorTheme")
 @Singleton
-public class SkinAndColorThemeListener implements EventListener
+public class ColorThemeListener implements EventListener
 {
     private static final LocalDocumentReference COLOR_THEME_CLASS =
             new LocalDocumentReference("ColorThemes", "ColorThemeClass");
@@ -57,14 +57,15 @@ public class SkinAndColorThemeListener implements EventListener
     private static final LocalDocumentReference FLAMINGO_THEME_CLASS =
             new LocalDocumentReference("FlamingoThemesCode", "ThemeClass");
 
-    private static final LocalDocumentReference SKIN_CLASS = new LocalDocumentReference("XWiki", "XWikiSkins");
-
     @Inject
     private LESSSkinFileCache lessSkinFileCache;
 
     @Inject
     private ColorThemeCache colorThemeCache;
 
+    @Inject
+    private EntityReferenceSerializer<String> entityReferenceSerializer;
+
     @Override
     public String getName()
     {
@@ -87,29 +88,21 @@ public void onEvent(Event event, Object source, Object data)
 
         List<BaseObject> flamingoThemeObjects = document.getXObjects(FLAMINGO_THEME_CLASS);
         if (flamingoThemeObjects != null && !flamingoThemeObjects.isEmpty()) {
-            clearCache(document);
+            clearCacheFromColorTheme(document);
             return;
         }
 
         List<BaseObject> colorThemeObjects = document.getXObjects(COLOR_THEME_CLASS);
         if (colorThemeObjects != null && !colorThemeObjects.isEmpty()) {
-            clearCache(document);
+            clearCacheFromColorTheme(document);
             return;
         }
-
-        List<BaseObject> skinObjects = document.getXObjects(SKIN_CLASS);
-        if (skinObjects != null && !skinObjects.isEmpty()) {
-            clearCache(document);
-        }
     }
 
-    private void clearCache(XWikiDocument document)
+    private void clearCacheFromColorTheme(XWikiDocument document)
     {
-        DocumentReference documentReference = document.getDocumentReference();
-
-        // Clear the cache for the specified wiki and color theme
-        String wiki = documentReference.getWikiReference().getName();
-        lessSkinFileCache.clear(wiki);
-        colorThemeCache.clear(wiki);
+        String fullName = entityReferenceSerializer.serialize(document.getDocumentReference());
+        lessSkinFileCache.clearFromColorTheme(fullName);
+        colorThemeCache.clearFromColorTheme(fullName);
     }
 }
diff --git a/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/main/java/org/xwiki/lesscss/internal/CurrentColorThemeGetter.java b/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/main/java/org/xwiki/lesscss/internal/CurrentColorThemeGetter.java
new file mode 100644
index 00000000000..9ead290cc94
--- /dev/null
+++ b/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/main/java/org/xwiki/lesscss/internal/CurrentColorThemeGetter.java
@@ -0,0 +1,38 @@
+/*
+ * See the NOTICE file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.xwiki.lesscss.internal;
+
+import org.xwiki.component.annotation.Role;
+
+/**
+ * Component to get the current color theme set by the request.
+ *
+ * @since 6.3M2
+ * @version $Id$
+ */
+@Role
+public interface CurrentColorThemeGetter
+{
+    /**
+     * @param fallbackValue value to return if the current color theme is invalid
+     * @return the full name of the current color theme or fallbackValue if the current color theme is invalid
+     */
+    String getCurrentColorTheme(String fallbackValue);
+}
diff --git a/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/main/java/org/xwiki/lesscss/internal/DefaultCurrentColorThemeGetter.java b/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/main/java/org/xwiki/lesscss/internal/DefaultCurrentColorThemeGetter.java
new file mode 100644
index 00000000000..8468e1d7ac2
--- /dev/null
+++ b/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/main/java/org/xwiki/lesscss/internal/DefaultCurrentColorThemeGetter.java
@@ -0,0 +1,79 @@
+/*
+ * See the NOTICE file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.xwiki.lesscss.internal;
+
+import javax.inject.Inject;
+import javax.inject.Provider;
+
+import org.xwiki.component.annotation.Component;
+import org.xwiki.model.reference.DocumentReference;
+import org.xwiki.model.reference.DocumentReferenceResolver;
+import org.xwiki.model.reference.EntityReferenceSerializer;
+import org.xwiki.model.reference.WikiReference;
+import org.xwiki.wiki.descriptor.WikiDescriptorManager;
+
+import com.xpn.xwiki.XWikiContext;
+import com.xpn.xwiki.web.XWikiRequest;
+
+/**
+ * Default implementation for {@link org.xwiki.lesscss.internal.CurrentColorThemeGetter}.
+ *
+ * @since 6.3M2
+ * @version $Id$
+ */
+@Component
+public class DefaultCurrentColorThemeGetter implements CurrentColorThemeGetter
+{
+    @Inject
+    private DocumentReferenceResolver<String> documentReferenceResolver;
+
+    @Inject
+    private EntityReferenceSerializer<String> entityReferenceSerializer;
+
+    @Inject
+    private Provider<XWikiContext> xcontextProvider;
+
+    @Inject
+    private WikiDescriptorManager wikiDescriptorManager;
+
+    @Override
+    public String getCurrentColorTheme(String fallbackValue)
+    {
+        // Get information about the context
+        String wikiId = wikiDescriptorManager.getCurrentWikiId();
+        XWikiContext context = xcontextProvider.get();
+        XWikiRequest request = context.getRequest();
+
+        // Get the current color theme
+        // Getting the full name representation of colorTheme
+        DocumentReference colorThemeReference = documentReferenceResolver.resolve(request.getParameter("colorTheme"),
+                new WikiReference(wikiId));
+        String colorTheme = entityReferenceSerializer.serialize(colorThemeReference);
+
+        // Check that the color theme exists, to avoid a DOS if some user tries to compile a skin file
+        // with random colorTheme names
+        if (!context.getWiki().exists(colorThemeReference, context)) {
+            colorTheme = fallbackValue;
+        }
+
+        return colorTheme;
+    }
+
+}
diff --git a/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/main/java/org/xwiki/lesscss/internal/DefaultLESSSkinFileCompiler.java b/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/main/java/org/xwiki/lesscss/internal/DefaultLESSSkinFileCompiler.java
index 9d3c5c23fb4..937f2d727be 100644
--- a/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/main/java/org/xwiki/lesscss/internal/DefaultLESSSkinFileCompiler.java
+++ b/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/main/java/org/xwiki/lesscss/internal/DefaultLESSSkinFileCompiler.java
@@ -42,6 +42,7 @@
 import org.xwiki.model.reference.DocumentReference;
 import org.xwiki.model.reference.DocumentReferenceResolver;
 import org.xwiki.model.reference.WikiReference;
+import org.xwiki.wiki.descriptor.WikiDescriptorManager;
 
 import com.xpn.xwiki.XWiki;
 import com.xpn.xwiki.XWikiContext;
@@ -70,6 +71,9 @@ public class DefaultLESSSkinFileCompiler extends AbstractCachedCompiler<String>
     @Inject
     private DocumentReferenceResolver<String> documentReferenceResolver;
 
+    @Inject
+    private WikiDescriptorManager wikiDescriptorManager;
+
     @Override
     public void initialize() throws InitializationException
     {
diff --git a/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/main/java/org/xwiki/lesscss/internal/LESSExportActionListener.java b/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/main/java/org/xwiki/lesscss/internal/LESSExportActionListener.java
index b12cf3ecdda..56fa74ee68d 100644
--- a/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/main/java/org/xwiki/lesscss/internal/LESSExportActionListener.java
+++ b/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/main/java/org/xwiki/lesscss/internal/LESSExportActionListener.java
@@ -27,6 +27,7 @@
 
 import org.xwiki.bridge.event.ActionExecutingEvent;
 import org.xwiki.component.annotation.Component;
+import org.xwiki.lesscss.ColorThemeCache;
 import org.xwiki.lesscss.LESSSkinFileCache;
 import org.xwiki.observation.EventListener;
 import org.xwiki.observation.event.Event;
@@ -48,6 +49,12 @@ public class LESSExportActionListener implements EventListener
     @Inject
     private LESSSkinFileCache lessSkinFileCache;
 
+    @Inject
+    private ColorThemeCache colorThemeCache;
+
+    @Inject
+    private CurrentColorThemeGetter currentColorThemeGetter;
+
     @Override
     public String getName()
     {
@@ -69,7 +76,9 @@ public void onEvent(Event event, Object source, Object data)
         XWikiRequest request = xcontext.getRequest();
         String format = request.get("format");
         if ("html".equals(format)) {
-            this.lessSkinFileCache.clear(xcontext.getWikiId());
+            String colorTheme = currentColorThemeGetter.getCurrentColorTheme("default");
+            this.lessSkinFileCache.clearFromColorTheme(colorTheme);
+            this.colorThemeCache.clearFromColorTheme(colorTheme);
         }
     }
 }
diff --git a/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/main/resources/META-INF/components.txt b/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/main/resources/META-INF/components.txt
index 1cea549b558..5a6d1a3d4de 100644
--- a/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/main/resources/META-INF/components.txt
+++ b/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/main/resources/META-INF/components.txt
@@ -1,7 +1,8 @@
+org.xwiki.lesscss.internal.ColorThemeListener
 org.xwiki.lesscss.internal.DefaultColorThemeCache
+org.xwiki.lesscss.internal.DefaultCurrentColorThemeGetter
 org.xwiki.lesscss.internal.DefaultLESSColorThemeConverter
 org.xwiki.lesscss.internal.DefaultLESSCompiler
 org.xwiki.lesscss.internal.DefaultLESSSkinFileCache
 org.xwiki.lesscss.internal.DefaultLESSSkinFileCompiler
-org.xwiki.lesscss.internal.SkinAndColorThemeListener
-org.xwiki.lesscss.internal.LESSExportActionListener
\ No newline at end of file
+org.xwiki.lesscss.internal.LESSExportActionListener
diff --git a/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/test/java/org/xwiki/lesscss/internal/SkinAndColorThemeListenerTest.java b/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/test/java/org/xwiki/lesscss/internal/ColorThemeListenerTest.java
similarity index 77%
rename from xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/test/java/org/xwiki/lesscss/internal/SkinAndColorThemeListenerTest.java
rename to xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/test/java/org/xwiki/lesscss/internal/ColorThemeListenerTest.java
index 51a19c46309..cfbd93f0683 100644
--- a/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/test/java/org/xwiki/lesscss/internal/SkinAndColorThemeListenerTest.java
+++ b/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/test/java/org/xwiki/lesscss/internal/ColorThemeListenerTest.java
@@ -29,10 +29,13 @@
 import org.xwiki.bridge.event.DocumentCreatedEvent;
 import org.xwiki.bridge.event.DocumentDeletedEvent;
 import org.xwiki.bridge.event.DocumentUpdatedEvent;
+import org.xwiki.component.util.DefaultParameterizedType;
+import org.xwiki.lesscss.ColorThemeCache;
 import org.xwiki.lesscss.LESSSkinFileCache;
 import org.xwiki.model.EntityType;
 import org.xwiki.model.reference.DocumentReference;
 import org.xwiki.model.reference.EntityReference;
+import org.xwiki.model.reference.EntityReferenceSerializer;
 import org.xwiki.model.reference.LocalDocumentReference;
 import org.xwiki.observation.event.Event;
 import org.xwiki.test.mockito.MockitoComponentMockingRule;
@@ -42,27 +45,35 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
 /**
- * Test class for {@link SkinAndColorThemeListener}.
+ * Test class for {@link ColorThemeListener}.
  *
- * @since 6.1M2
+ * @since 6.3M2
  * @version $Id$
  */
-public class SkinAndColorThemeListenerTest
+public class ColorThemeListenerTest
 {
     @Rule
-    public MockitoComponentMockingRule<SkinAndColorThemeListener> mocker =
-            new MockitoComponentMockingRule<>(SkinAndColorThemeListener.class);
+    public MockitoComponentMockingRule<ColorThemeListener> mocker =
+            new MockitoComponentMockingRule<>(ColorThemeListener.class);
 
     private LESSSkinFileCache lessSkinFileCache;
+
+    private ColorThemeCache colorThemeCache;
+
+    private EntityReferenceSerializer<String> entityReferenceSerializer;
+
     @Before
     public void setUp() throws Exception
     {
         lessSkinFileCache = mocker.getInstance(LESSSkinFileCache.class);
+        colorThemeCache = mocker.getInstance(ColorThemeCache.class);
+        entityReferenceSerializer = mocker.getInstance(
+                new DefaultParameterizedType(null, EntityReferenceSerializer.class, String.class));
     }
 
     @Test
@@ -98,12 +109,14 @@ public void onEventWhenFlamingoThemeChanged() throws Exception
 
         DocumentReference documentReference = new DocumentReference("wiki", "space", "page");
         when(doc.getDocumentReference()).thenReturn(documentReference);
+        when(entityReferenceSerializer.serialize(documentReference)).thenReturn("wiki:space.page");
 
         // Test
         mocker.getComponentUnderTest().onEvent(event, doc, data);
 
         // Verify
-        verify(lessSkinFileCache).clear("wiki");
+        verify(lessSkinFileCache).clearFromColorTheme("wiki:space.page");
+        verify(colorThemeCache).clearFromColorTheme("wiki:space.page");
     }
 
     @Test
@@ -122,36 +135,14 @@ public void onEventWhenColorThemeChanged() throws Exception
 
         DocumentReference documentReference = new DocumentReference("wiki", "space", "page");
         when(doc.getDocumentReference()).thenReturn(documentReference);
+        when(entityReferenceSerializer.serialize(documentReference)).thenReturn("wiki:space.page");
 
         // Test
         mocker.getComponentUnderTest().onEvent(event, doc, data);
 
         // Verify
-        verify(lessSkinFileCache).clear("wiki");
-    }
-
-    @Test
-    public void onEventWhenSkinChanged() throws Exception
-    {
-        // Mocks
-        Event event = mock(Event.class);
-        XWikiDocument doc = mock(XWikiDocument.class);
-        Object data = new Object();
-
-        EntityReference classReference = new LocalDocumentReference("XWiki", "XWikiSkins");
-        List<BaseObject> objects = new ArrayList<>();
-        BaseObject object = mock(BaseObject.class);
-        objects.add(object);
-        when(doc.getXObjects(classReference)).thenReturn(objects);
-
-        DocumentReference documentReference = new DocumentReference("wiki", "space", "page");
-        when(doc.getDocumentReference()).thenReturn(documentReference);
-
-        // Test
-        mocker.getComponentUnderTest().onEvent(event, doc, data);
-
-        // Verify
-        verify(lessSkinFileCache).clear("wiki");
+        verify(lessSkinFileCache).clearFromColorTheme("wiki:space.page");
+        verify(colorThemeCache).clearFromColorTheme("wiki:space.page");
     }
 
     @Test
@@ -171,6 +162,7 @@ public void onEventWhenNoObject() throws Exception
         mocker.getComponentUnderTest().onEvent(event, doc, data);
 
         // Verify
-        verify(lessSkinFileCache, never()).clear("wikiId");
+        verifyZeroInteractions(lessSkinFileCache);
+        verifyZeroInteractions(colorThemeCache);
     }
 }
diff --git a/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/test/java/org/xwiki/lesscss/internal/DefaultCurrentColorThemeGetterTest.java b/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/test/java/org/xwiki/lesscss/internal/DefaultCurrentColorThemeGetterTest.java
new file mode 100644
index 00000000000..1bbdd33c44d
--- /dev/null
+++ b/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/test/java/org/xwiki/lesscss/internal/DefaultCurrentColorThemeGetterTest.java
@@ -0,0 +1,107 @@
+/*
+ * See the NOTICE file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.xwiki.lesscss.internal;
+
+import javax.inject.Provider;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.xwiki.component.util.DefaultParameterizedType;
+import org.xwiki.model.reference.DocumentReference;
+import org.xwiki.model.reference.DocumentReferenceResolver;
+import org.xwiki.model.reference.EntityReferenceSerializer;
+import org.xwiki.model.reference.WikiReference;
+import org.xwiki.test.mockito.MockitoComponentMockingRule;
+import org.xwiki.wiki.descriptor.WikiDescriptorManager;
+
+import com.xpn.xwiki.XWiki;
+import com.xpn.xwiki.XWikiContext;
+import com.xpn.xwiki.web.XWikiRequest;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * Test class for {@link org.xwiki.lesscss.internal.CurrentColorThemeGetter}.
+ *
+ * @since 6.3M2
+ * @version $Id$
+ */
+public class DefaultCurrentColorThemeGetterTest
+{
+    @Rule
+    public MockitoComponentMockingRule<DefaultCurrentColorThemeGetter> mocker =
+            new MockitoComponentMockingRule<>(DefaultCurrentColorThemeGetter.class);
+
+    private DocumentReferenceResolver<String> documentReferenceResolver;
+
+    private EntityReferenceSerializer<String> entityReferenceSerializer;
+
+    private Provider<XWikiContext> xcontextProvider;
+
+    private WikiDescriptorManager wikiDescriptorManager;
+
+    private XWikiContext xcontext;
+
+    private XWiki xwiki;
+
+    @Before
+    public void setUp() throws Exception
+    {
+        wikiDescriptorManager = mocker.getInstance(WikiDescriptorManager.class);
+        documentReferenceResolver = mocker.getInstance(new DefaultParameterizedType(null, DocumentReferenceResolver.class,
+                String.class));
+        entityReferenceSerializer = mocker.getInstance(new DefaultParameterizedType(null, EntityReferenceSerializer.class,
+                String.class));
+        xcontextProvider = mocker.getInstance(new DefaultParameterizedType(null, Provider.class, XWikiContext.class));
+        xcontext = mock(XWikiContext.class);
+        when(xcontextProvider.get()).thenReturn(xcontext);
+        xwiki = mock(XWiki.class);
+        when(xcontext.getWiki()).thenReturn(xwiki);
+
+        when(wikiDescriptorManager.getCurrentWikiId()).thenReturn("wikiId");
+        XWikiRequest request = mock(XWikiRequest.class);
+        when(xcontext.getRequest()).thenReturn(request);
+        when(request.getParameter("colorTheme")).thenReturn("myColorTheme");
+        DocumentReference colorThemeReference = new DocumentReference("wikiId", "XWiki", "MyColorTheme");
+        WikiReference mainWikiReference = new WikiReference("wikiId");
+        when(documentReferenceResolver.resolve(eq("myColorTheme"), eq(mainWikiReference))).thenReturn(colorThemeReference);
+        when(entityReferenceSerializer.serialize(colorThemeReference)).thenReturn("wikiId:ColorTheme.MyColorTheme");
+        when(xwiki.exists(colorThemeReference, xcontext)).thenReturn(true);
+    }
+
+    @Test
+    public void getCurrentColorThemeTest() throws Exception
+    {
+         assertEquals("wikiId:ColorTheme.MyColorTheme", mocker.getComponentUnderTest().getCurrentColorTheme("default"));
+    }
+
+    @Test
+    public void getCurrentColorThemeFallbackTest() throws Exception
+    {
+        when(xwiki.exists(any(DocumentReference.class), eq(xcontext))).thenReturn(false);
+        assertEquals("fallback", mocker.getComponentUnderTest().getCurrentColorTheme("fallback"));
+        assertEquals("error", mocker.getComponentUnderTest().getCurrentColorTheme("error"));
+    }
+}
diff --git a/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/test/java/org/xwiki/lesscss/internal/DefaultLESSSkinFileCacheTest.java b/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/test/java/org/xwiki/lesscss/internal/DefaultLESSSkinFileCacheTest.java
index 0e8992de639..ab7744ca6cc 100644
--- a/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/test/java/org/xwiki/lesscss/internal/DefaultLESSSkinFileCacheTest.java
+++ b/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/test/java/org/xwiki/lesscss/internal/DefaultLESSSkinFileCacheTest.java
@@ -26,9 +26,16 @@
 import org.xwiki.cache.CacheFactory;
 import org.xwiki.cache.CacheManager;
 import org.xwiki.cache.config.CacheConfiguration;
+import org.xwiki.component.util.DefaultParameterizedType;
+import org.xwiki.model.reference.DocumentReference;
+import org.xwiki.model.reference.DocumentReferenceResolver;
+import org.xwiki.model.reference.EntityReferenceSerializer;
+import org.xwiki.model.reference.WikiReference;
 import org.xwiki.test.mockito.MockitoComponentMockingRule;
+import org.xwiki.wiki.descriptor.WikiDescriptorManager;
 
 import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -52,6 +59,12 @@ public class DefaultLESSSkinFileCacheTest
 
     private Cache<String> cache;
 
+    private DocumentReferenceResolver<String> documentReferenceResolver;
+
+    private EntityReferenceSerializer<String> entityReferenceSerializer;
+
+    private WikiDescriptorManager wikiDescriptorManager;
+
     @Before
     public void setUp() throws Exception
     {
@@ -61,16 +74,25 @@ public void setUp() throws Exception
         when(cacheManager.getCacheFactory()).thenReturn(cacheFactory);
         CacheConfiguration configuration = new CacheConfiguration("lesscss.skinfiles.cache");
         when(cacheFactory.<String>newCache(eq(configuration))).thenReturn(cache);
+        documentReferenceResolver = mocker.getInstance(
+                new DefaultParameterizedType(null, DocumentReferenceResolver.class, String.class));
+        entityReferenceSerializer = mocker.getInstance(
+                new DefaultParameterizedType(null, EntityReferenceSerializer.class, String.class));
+        wikiDescriptorManager = mocker.getInstance(WikiDescriptorManager.class);
+        DocumentReference colorThemeRef = new DocumentReference("currentWiki", "ColorTheme", "CT");
+        when(documentReferenceResolver.resolve(eq("colorTheme"), any(WikiReference.class))).thenReturn(colorThemeRef);
+        when(entityReferenceSerializer.serialize(colorThemeRef)).thenReturn("currentWiki:ColorTheme.CT");
+        when(wikiDescriptorManager.getCurrentWikiId()).thenReturn("currentWiki");
     }
 
     @Test
     public void get() throws Exception
     {
         // Mock
-        when(cache.get("6wikiId_4skin_10colorTheme_4file")).thenReturn("Expected output");
+        when(cache.get("4skin_25currentWiki:ColorTheme.CT_4file")).thenReturn("Expected output");
 
         // Test
-        String result = mocker.getComponentUnderTest().get("file", "wikiId", "skin", "colorTheme");
+        String result = mocker.getComponentUnderTest().get("file", "skin", "colorTheme");
 
         // Verify
         assertEquals("Expected output", result);
@@ -80,10 +102,10 @@ public void get() throws Exception
     public void set() throws Exception
     {
         // Test
-        mocker.getComponentUnderTest().set("file", "wikiId", "skin", "colorTheme", "css");
+        mocker.getComponentUnderTest().set("file", "skin", "colorTheme", "css");
 
         // Verify
-        verify(cache).set(eq("6wikiId_4skin_10colorTheme_4file"), eq("css"));
+        verify(cache).set(eq("4skin_25currentWiki:ColorTheme.CT_4file"), eq("css"));
     }
 
     @Test
@@ -97,25 +119,50 @@ public void clear() throws Exception
     }
 
     @Test
-    public void clearWithParams() throws Exception
+    public void clearFromFileSystemSkin() throws Exception
+    {
+        // Init
+
+        // Add the first one twice
+        mocker.getComponentUnderTest().set("file1", "skin1", "colorTheme", "css1");
+        mocker.getComponentUnderTest().set("file1", "skin1", "colorTheme", "css1");
+
+        // Others
+        mocker.getComponentUnderTest().set("file1", "skin2", "colorTheme", "css2");
+        mocker.getComponentUnderTest().set("file2", "skin1", "colorTheme", "css3");
+
+        // Testskin1
+        mocker.getComponentUnderTest().clearFromFileSystemSkin("skin1");
+
+        // Verify
+        verify(cache, times(1)).remove("5skin1_25currentWiki:ColorTheme.CT_5file1");
+        verify(cache).remove("5skin1_25currentWiki:ColorTheme.CT_5file2");
+        verify(cache, never()).remove("5skin2_25currentWiki:ColorTheme.CT_5file1");
+    }
+
+    @Test
+    public void clearFromColorTheme() throws Exception
     {
         // Init
+        DocumentReference colorThemeRef2 = new DocumentReference("currentWiki", "ColorTheme", "CT2");
+        when(documentReferenceResolver.resolve(eq("colorTheme2"), any(WikiReference.class))).thenReturn(colorThemeRef2);
+        when(entityReferenceSerializer.serialize(colorThemeRef2)).thenReturn("currentWiki:ColorTheme.CT2");
 
         // Add the first one twice
-        mocker.getComponentUnderTest().set("file1", "wiki1", "skin1", "colorTheme1", "css1");
-        mocker.getComponentUnderTest().set("file1", "wiki1", "skin1", "colorTheme1", "css1");
+        mocker.getComponentUnderTest().set("file1", "skin1", "colorTheme", "css1");
+        mocker.getComponentUnderTest().set("file1", "skin1", "colorTheme", "css1");
 
         // Others
-        mocker.getComponentUnderTest().set("file1", "wiki1", "skin2", "colorTheme1", "css2");
-        mocker.getComponentUnderTest().set("file1", "wiki2", "skin1", "colorTheme1", "css3");
+        mocker.getComponentUnderTest().set("file1", "skin1", "colorTheme2", "css2");
+        mocker.getComponentUnderTest().set("file1", "skin2", "colorTheme", "css3");
 
         // Test
-        mocker.getComponentUnderTest().clear("wiki1");
+        mocker.getComponentUnderTest().clearFromColorTheme("colorTheme");
 
         // Verify
-        verify(cache, times(1)).remove("5wiki1_5skin1_11colorTheme1_5file1");
-        verify(cache).remove("5wiki1_5skin2_11colorTheme1_5file1");
-        verify(cache, never()).remove("5wiki2_5skin1_11colorTheme1_5file1");
+        verify(cache, times(1)).remove("5skin1_25currentWiki:ColorTheme.CT_5file1");
+        verify(cache).remove("5skin2_25currentWiki:ColorTheme.CT_5file1");
+        verify(cache, never()).remove("5skin2_26currentWiki:ColorTheme.CT2_5file1");
     }
 
 }
diff --git a/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/test/java/org/xwiki/lesscss/internal/DefaultLESSSkinFileCompilerTest.java b/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/test/java/org/xwiki/lesscss/internal/DefaultLESSSkinFileCompilerTest.java
index 6eafd5acbc1..9a2f7095144 100644
--- a/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/test/java/org/xwiki/lesscss/internal/DefaultLESSSkinFileCompilerTest.java
+++ b/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/test/java/org/xwiki/lesscss/internal/DefaultLESSSkinFileCompilerTest.java
@@ -34,7 +34,6 @@
 import org.xwiki.lesscss.LESSSkinFileCache;
 import org.xwiki.model.reference.DocumentReference;
 import org.xwiki.model.reference.DocumentReferenceResolver;
-import org.xwiki.model.reference.EntityReferenceSerializer;
 import org.xwiki.model.reference.WikiReference;
 import org.xwiki.test.mockito.MockitoComponentMockingRule;
 import org.xwiki.wiki.descriptor.WikiDescriptorManager;
@@ -45,7 +44,6 @@
 import com.xpn.xwiki.doc.XWikiDocument;
 import com.xpn.xwiki.objects.BaseObject;
 import com.xpn.xwiki.web.XWikiEngineContext;
-import com.xpn.xwiki.web.XWikiRequest;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -77,26 +75,25 @@ public class DefaultLESSSkinFileCompilerTest
 
     private Provider<XWikiContext> xcontextProvider;
 
+    private CurrentColorThemeGetter currentColorThemeGetter;
+
+    private DocumentReferenceResolver<String> referenceResolver;
+
     private XWikiContext xcontext;
 
     private XWiki xwiki;
 
     private XWikiEngineContext engineContext;
 
-    private DocumentReferenceResolver<String> referenceResolver;
-
-    private EntityReferenceSerializer<String> referenceSerializer;
-
     @Before
     public void setUp() throws Exception
     {
         lessCompiler = mocker.getInstance(LESSCompiler.class);
         wikiDescriptorManager = mocker.getInstance(WikiDescriptorManager.class);
-        referenceResolver = mocker.getInstance(new DefaultParameterizedType(null, DocumentReferenceResolver.class,
-                        String.class));
-        referenceSerializer = mocker.getInstance(new DefaultParameterizedType(null, EntityReferenceSerializer.class,
-                String.class));        
         cache = mocker.getInstance(LESSSkinFileCache.class);
+        currentColorThemeGetter = mocker.getInstance(CurrentColorThemeGetter.class);
+        referenceResolver = mocker.getInstance(new DefaultParameterizedType(null, DocumentReferenceResolver.class,
+                String.class));
         xcontextProvider = mocker.getInstance(new DefaultParameterizedType(null, Provider.class, XWikiContext.class));
         xcontext = mock(XWikiContext.class);
         when(xcontextProvider.get()).thenReturn(xcontext);
@@ -104,16 +101,12 @@ public void setUp() throws Exception
         when(xcontext.getWiki()).thenReturn(xwiki);
         engineContext = mock(XWikiEngineContext.class);
         when(xwiki.getEngineContext()).thenReturn(engineContext);
-        XWikiRequest request = mock(XWikiRequest.class);
-        when(xcontext.getRequest()).thenReturn(request);
-        when(request.getParameter("colorTheme")).thenReturn("myColorTheme");
+
         when(wikiDescriptorManager.getCurrentWikiId()).thenReturn("wikiId");
         when(xwiki.getSkin(xcontext)).thenReturn("skin");
-        DocumentReference colorThemeReference = new DocumentReference("wikiId", "XWiki", "MyColorTheme");
-        WikiReference mainWikiReference = new WikiReference("wikiId");
-        when(referenceResolver.resolve(eq("myColorTheme"), eq(mainWikiReference))).thenReturn(colorThemeReference);
-        when(referenceSerializer.serialize(colorThemeReference)).thenReturn("wikiId:ColorTheme.MyColorTheme");
-        when(xwiki.exists(colorThemeReference, xcontext)).thenReturn(true);
+
+        when(currentColorThemeGetter.getCurrentColorTheme("default")).thenReturn("wikiId:ColorTheme.MyColorTheme");
+
     }
 
     private void prepareMocksForCompilation() throws Exception
@@ -158,8 +151,8 @@ public void compileSkinFile() throws Exception
         assertEquals("OUTPUT", mocker.getComponentUnderTest().compileSkinFile("style2.less", false));
 
         // Verify
-        verify(cache).get(eq("style2.less"), eq("wikiId"), eq("skin"), eq("wikiId:ColorTheme.MyColorTheme"));
-        verify(cache).set(eq("style2.less"), eq("wikiId"), eq("skin"), eq("wikiId:ColorTheme.MyColorTheme"),
+        verify(cache).get(eq("style2.less"), eq("skin"), eq("wikiId:ColorTheme.MyColorTheme"));
+        verify(cache).set(eq("style2.less"), eq("skin"), eq("wikiId:ColorTheme.MyColorTheme"),
                 eq("OUTPUT"));
     }
 
@@ -167,7 +160,7 @@ public void compileSkinFile() throws Exception
     public void compileSkinFileWhenInCache() throws Exception
     {
         // Mock
-        when(cache.get("style2.less", "wikiId", "skin", "wikiId:ColorTheme.MyColorTheme")).thenReturn("OUTPUT");
+        when(cache.get("style2.less", "skin", "wikiId:ColorTheme.MyColorTheme")).thenReturn("OUTPUT");
 
         // Test
         assertEquals("OUTPUT", mocker.getComponentUnderTest().compileSkinFile("style2.less", false));
@@ -181,32 +174,14 @@ public void compileSkinFileWhenInCache() throws Exception
     public void compileSkinFileWhenInCacheButForce() throws Exception
     {
         // Mock
-        when(cache.get("style2.less", "wikiId", "skin", "wikiId:ColorTheme.MyColorTheme")).thenReturn("OLD OUTPUT");
+        when(cache.get("style2.less", "skin", "wikiId:ColorTheme.MyColorTheme")).thenReturn("OLD OUTPUT");
         prepareMocksForCompilation();
 
         // Test
         assertEquals("OUTPUT", mocker.getComponentUnderTest().compileSkinFile("style2.less", true));
 
         // Verify
-        verify(cache).set(eq("style2.less"), eq("wikiId"), eq("skin"), eq("wikiId:ColorTheme.MyColorTheme"),
-                eq("OUTPUT"));
-    }
-
-    @Test
-    public void compileSkinFileWhenColorThemeDoesNotExist() throws Exception
-    {
-        // Mock
-        when(cache.get("style2.less", "wikiId", "skin", "default")).thenReturn("DEFAULT COLOR THEME");
-        DocumentReference colorThemeReference = new DocumentReference("wikiId", "XWiki", "invalidColorTheme");
-        when(referenceResolver.resolve("invalidColorTheme")).thenReturn(colorThemeReference);
-        when(xwiki.exists(colorThemeReference, xcontext)).thenReturn(false);
-        prepareMocksForCompilation();
-        XWikiRequest request = mock(XWikiRequest.class);
-        when(xcontext.getRequest()).thenReturn(request);
-        when(request.getParameter("colorTheme")).thenReturn("invalidColorTheme");
-
-        // Test
-        assertEquals("DEFAULT COLOR THEME", mocker.getComponentUnderTest().compileSkinFile("style2.less", false));
+        verify(cache).set(eq("style2.less"), eq("skin"), eq("wikiId:ColorTheme.MyColorTheme"), eq("OUTPUT"));
     }
 
     @Test
@@ -254,10 +229,9 @@ public void compileSkinFileWhenSkinIsOnDB() throws Exception
         assertEquals("OUTPUT", mocker.getComponentUnderTest().compileSkinFile("style2.less", false));
 
         // Verify
-        verify(cache).get(eq("style2.less"), eq("wikiId"), eq("XWiki.DefaultSkin"),
-                eq("wikiId:ColorTheme.MyColorTheme"));
-        verify(cache).set(eq("style2.less"), eq("wikiId"), eq("XWiki.DefaultSkin"),
-                eq("wikiId:ColorTheme.MyColorTheme"), eq("OUTPUT"));
+        verify(cache).get(eq("style2.less"), eq("XWiki.DefaultSkin"), eq("wikiId:ColorTheme.MyColorTheme"));
+        verify(cache).set(eq("style2.less"), eq("XWiki.DefaultSkin"), eq("wikiId:ColorTheme.MyColorTheme"),
+                eq("OUTPUT"));
         verify(xcontext, never()).put(anyString(), anyString());
     }
 
@@ -377,9 +351,8 @@ public void compileSkinFileWhenSkinDocumentHasNoObject() throws Exception
         assertEquals("OUTPUT", mocker.getComponentUnderTest().compileSkinFile("style2.less", false));
 
         // Verify
-        verify(cache).get(eq("style2.less"), eq("wikiId"), eq("flamingo"), eq("wikiId:ColorTheme.MyColorTheme"));
-        verify(cache).set(eq("style2.less"), eq("wikiId"), eq("flamingo"), eq("wikiId:ColorTheme.MyColorTheme"),
-            eq("OUTPUT"));
+        verify(cache).get(eq("style2.less"), eq("flamingo"), eq("wikiId:ColorTheme.MyColorTheme"));
+        verify(cache).set(eq("style2.less"), eq("flamingo"), eq("wikiId:ColorTheme.MyColorTheme"), eq("OUTPUT"));
         verify(xcontext, never()).put(anyString(), anyString());
     }
 
@@ -390,12 +363,8 @@ public void compileSkinFileOnSubwiki() throws Exception
         prepareMocksForCompilation();
         when(xwiki.getSkin(xcontext)).thenReturn("XWiki.DefaultSkin");
         WikiReference currentWikiReference = new WikiReference("wikiId");
-        DocumentReference colorThemeRef = new DocumentReference("wikiId", "ColorTheme", "MyColorTheme");
-        when(referenceResolver.resolve(eq("myColorTheme"), eq(currentWikiReference))).thenReturn(colorThemeRef);
-        when(referenceSerializer.serialize(colorThemeRef)).thenReturn("wikiId:ColorTheme.MyColorTheme");
-        when(xwiki.exists(colorThemeRef, xcontext)).thenReturn(true);
 
-        when(cache.get(eq("style.less"), eq("wikiId"), eq("XWiki.DefaultSkin"), eq("wikiId:ColorTheme.MyColorTheme"))).
+        when(cache.get(eq("style.less"), eq("XWiki.DefaultSkin"), eq("wikiId:ColorTheme.MyColorTheme"))).
                 thenReturn("SUBWIKI OUTPUT");
 
         // Test
@@ -407,11 +376,6 @@ public void compileSkinFileOnOtherSkin() throws Exception
     {
         // Mocks
         prepareMocksForCompilationOnFlamingo();
-        WikiReference currentWikiReference = new WikiReference("wikiId");
-        DocumentReference colorThemeRef = new DocumentReference("wikiId", "ColorTheme", "MyColorTheme");
-        when(referenceResolver.resolve(eq("myColorTheme"), eq(currentWikiReference))).thenReturn(colorThemeRef);
-        when(referenceSerializer.serialize(colorThemeRef)).thenReturn("wikiId:ColorTheme.MyColorTheme");
-        when(xwiki.exists(colorThemeRef, xcontext)).thenReturn(true);
 
         // Test
         assertEquals("OUTPUT", mocker.getComponentUnderTest().compileSkinFile("style2.less", "flamingo", false));
@@ -426,11 +390,6 @@ public void compileSkinFileOnOtherSkinWithException() throws Exception
     {
         // Mocks
         prepareMocksForCompilationOnFlamingo();
-        WikiReference currentWikiReference = new WikiReference("wikiId");
-        DocumentReference colorThemeRef = new DocumentReference("wikiId", "ColorTheme", "MyColorTheme");
-        when(referenceResolver.resolve(eq("myColorTheme"), eq(currentWikiReference))).thenReturn(colorThemeRef);
-        when(referenceSerializer.serialize(colorThemeRef)).thenReturn("wikiId:ColorTheme.MyColorTheme");
-        when(xwiki.exists(colorThemeRef, xcontext)).thenReturn(true);
 
         Exception exception = new LESSCompilerException("Exception with LESS", null);
         when(lessCompiler.compile(anyString(), any(Path[].class))).thenThrow(exception);
@@ -454,12 +413,6 @@ public void compileSkinFileWhenDirectoryDoesNotExist() throws Exception
     {
         when(engineContext.getRealPath("/skins/flamingo/less")).thenReturn("ighgzuheubigvugvbzekvbzekvuzkkkhguiiiii");
 
-        WikiReference currentWikiReference = new WikiReference("wikiId");
-        DocumentReference colorThemeRef = new DocumentReference("wikiId", "ColorTheme", "MyColorTheme");
-        when(referenceResolver.resolve(eq("myColorTheme"), eq(currentWikiReference))).thenReturn(colorThemeRef);
-        when(referenceSerializer.serialize(colorThemeRef)).thenReturn("wikiId:ColorTheme.MyColorTheme");
-        when(xwiki.exists(colorThemeRef, xcontext)).thenReturn(true);
-
         // Test
         Exception exceptionCaught = null;
         try {
@@ -503,11 +456,6 @@ public void compileSkinFileOnSubwikiWhenSkinIsOnMainWiki() throws Exception
         // Mocks
         prepareMocksForCompilation();
         when(xwiki.getSkin(xcontext)).thenReturn("mainWiki:XWiki.DefaultSkin");
-        WikiReference currentWikiReference = new WikiReference("wikiId");
-        DocumentReference colorThemeRef = new DocumentReference("wikiId", "ColorTheme", "MyColorTheme");
-        when(referenceResolver.resolve(eq("myColorTheme"), eq(currentWikiReference))).thenReturn(colorThemeRef);
-        when(referenceSerializer.serialize(colorThemeRef)).thenReturn("wikiId:ColorTheme.MyColorTheme");
-        when(xwiki.exists(colorThemeRef, xcontext)).thenReturn(true);
 
         DocumentReference skinDocRef = new DocumentReference("mainWiki", "XWiki", "DefaultSkin");
         when(referenceResolver.resolve(eq("mainWiki:XWiki.DefaultSkin"), any(WikiReference.class)))
diff --git a/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/test/java/org/xwiki/lesscss/internal/LESSExportActionListenerTest.java b/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/test/java/org/xwiki/lesscss/internal/LESSExportActionListenerTest.java
index 0fec3348e01..617fe725ed3 100644
--- a/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/test/java/org/xwiki/lesscss/internal/LESSExportActionListenerTest.java
+++ b/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-default/src/test/java/org/xwiki/lesscss/internal/LESSExportActionListenerTest.java
@@ -22,6 +22,7 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.xwiki.bridge.event.ActionExecutingEvent;
+import org.xwiki.lesscss.ColorThemeCache;
 import org.xwiki.lesscss.LESSSkinFileCache;
 import org.xwiki.test.mockito.MockitoComponentMockingRule;
 
@@ -64,12 +65,16 @@ public void onEventWhenHTMLExport() throws Exception
         when(xcontext.getRequest()).thenReturn(request);
         when(request.get("format")).thenReturn("html");
         when(xcontext.getWikiId()).thenReturn("wiki");
+        CurrentColorThemeGetter currentColorThemeGetter = this.mocker.getInstance(CurrentColorThemeGetter.class);
+        when(currentColorThemeGetter.getCurrentColorTheme("default")).thenReturn("colorTheme");
 
         this.mocker.getComponentUnderTest().onEvent(new ActionExecutingEvent("export"), null, xcontext);
 
         // The test is here: we verify that the clear API was called!
         LESSSkinFileCache cache = this.mocker.getInstance(LESSSkinFileCache.class);
-        verify(cache).clear("wiki");
+        ColorThemeCache cache2 = this.mocker.getInstance(ColorThemeCache.class);
+        verify(cache).clearFromColorTheme("colorTheme");
+        verify(cache2).clearFromColorTheme("colorTheme");
     }
 
     @Test
@@ -86,5 +91,7 @@ public void onEventWhenNonHTMLExport() throws Exception
         // Actually that the cache object was not called at all...
         LESSSkinFileCache cache = this.mocker.getInstance(LESSSkinFileCache.class);
         verifyZeroInteractions(cache);
+        CurrentColorThemeGetter currentColorThemeGetter = this.mocker.getInstance(CurrentColorThemeGetter.class);
+        verifyZeroInteractions(currentColorThemeGetter);
     }
 }
diff --git a/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-script/src/main/java/org/xwiki/lesscss/LessCompilerScriptService.java b/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-script/src/main/java/org/xwiki/lesscss/LessCompilerScriptService.java
index 24b42295313..bef2b346f41 100644
--- a/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-script/src/main/java/org/xwiki/lesscss/LessCompilerScriptService.java
+++ b/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-script/src/main/java/org/xwiki/lesscss/LessCompilerScriptService.java
@@ -46,7 +46,10 @@ public class LessCompilerScriptService implements ScriptService
     private LESSSkinFileCompiler lessCompiler;
 
     @Inject
-    private LESSSkinFileCache cache;
+    private LESSSkinFileCache lessCache;
+
+    @Inject
+    private ColorThemeCache colorThemeCache;
 
     @Inject
     private Provider<XWikiContext> xcontextProvider;
@@ -166,17 +169,39 @@ public boolean clearCache()
             return false;
         }
 
-        cache.clear();
+        lessCache.clear();
+        colorThemeCache.clear();
+        return true;
+    }
+
+    /**
+     * Remove every generated files corresponding to a color theme.
+     * The script calling this method needs the programming rights.
+     * @param colorTheme fullname of the color theme
+     * @return true if the operation succeed
+     */
+    public boolean clearCacheFromColorTheme(String colorTheme)
+    {
+        XWikiContext xcontext = xcontextProvider.get();
+
+        // Check if the current script has the programing rights
+        if (!authorizationManager.hasAccess(Right.PROGRAM, xcontext.getDoc().getAuthorReference(),
+                xcontext.getDoc().getDocumentReference())) {
+            return false;
+        }
+
+        lessCache.clearFromColorTheme(colorTheme);
+        colorThemeCache.clearFromColorTheme(colorTheme);
         return true;
     }
 
     /**
-     * Remove every generated files corresponding to a wiki.
+     * Remove every generated files corresponding to a filesystem skin.
      * The script calling this method needs the programming rights.
-     * @param wikiId id of the wiki
+     * @param skin name of the filesystem skin
      * @return true if the operation succeed
      */
-    public boolean clearCache(String wikiId)
+    public boolean clearCacheFromFileSystemSkin(String skin)
     {
         XWikiContext xcontext = xcontextProvider.get();
 
@@ -186,7 +211,8 @@ public boolean clearCache(String wikiId)
             return false;
         }
 
-        cache.clear(wikiId);
+        lessCache.clearFromFileSystemSkin(skin);
+        colorThemeCache.clearFromFileSystemSkin(skin);
         return true;
     }
 }
diff --git a/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-script/src/test/java/org/xwiki/lesscss/LessCompilerScriptServiceTest.java b/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-script/src/test/java/org/xwiki/lesscss/LessCompilerScriptServiceTest.java
index ebd72a1e994..a46437d8c5e 100644
--- a/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-script/src/test/java/org/xwiki/lesscss/LessCompilerScriptServiceTest.java
+++ b/xwiki-platform-core/xwiki-platform-lesscss/xwiki-platform-lesscss-script/src/test/java/org/xwiki/lesscss/LessCompilerScriptServiceTest.java
@@ -38,8 +38,8 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
 /**
@@ -56,7 +56,9 @@ public class LessCompilerScriptServiceTest
 
     private LESSSkinFileCompiler lessCompiler;
 
-    private LESSSkinFileCache cache;
+    private LESSSkinFileCache lessCache;
+
+    private ColorThemeCache colorThemeCache;
 
     private LESSColorThemeConverter lessColorThemeConverter;
 
@@ -71,7 +73,8 @@ public void setUp() throws Exception
     {
         lessCompiler = mocker.getInstance(LESSSkinFileCompiler.class);
         lessColorThemeConverter = mocker.getInstance(LESSColorThemeConverter.class);
-        cache = mocker.getInstance(LESSSkinFileCache.class);
+        lessCache = mocker.getInstance(LESSSkinFileCache.class);
+        colorThemeCache = mocker.getInstance(ColorThemeCache.class);
         authorizationManager = mocker.getInstance(AuthorizationManager.class);
         xcontextProvider = mocker.getInstance(new DefaultParameterizedType(null, Provider.class, XWikiContext.class));
         xcontext = mock(XWikiContext.class);
@@ -212,7 +215,8 @@ public void clearCacheWithRights() throws Exception
         assertTrue(mocker.getComponentUnderTest().clearCache());
 
         // Verify
-        verify(cache).clear();
+        verify(lessCache).clear();
+        verify(colorThemeCache).clear();
     }
 
     @Test
@@ -232,11 +236,54 @@ public void clearCacheWithoutRights() throws Exception
         assertFalse(mocker.getComponentUnderTest().clearCache());
 
         // Verify
-        verify(cache, never()).clear();
+        verifyZeroInteractions(lessCache);
+        verifyZeroInteractions(colorThemeCache);
+    }
+
+    @Test
+    public void clearCacheFromColorThemeWithRights() throws Exception
+    {
+        // Mocks
+        XWikiDocument doc = mock(XWikiDocument.class);
+        DocumentReference authorReference = new DocumentReference("wiki", "Space", "User");
+        when(xcontext.getDoc()).thenReturn(doc);
+        when(doc.getAuthorReference()).thenReturn(authorReference);
+        DocumentReference currentDocReference = new DocumentReference("wiki", "Space", "Page");
+        when(doc.getDocumentReference()).thenReturn(currentDocReference);
+
+        when(authorizationManager.hasAccess(Right.PROGRAM, authorReference, currentDocReference)).thenReturn(true);
+
+        // Tests
+        assertTrue(mocker.getComponentUnderTest().clearCacheFromColorTheme("colorTheme"));
+
+        // Verify
+        verify(lessCache).clearFromColorTheme(eq("colorTheme"));
+        verify(colorThemeCache).clearFromColorTheme(eq("colorTheme"));
+    }
+
+    @Test
+    public void clearCacheFromColorThemeWithoutRights() throws Exception
+    {
+        // Mocks
+        XWikiDocument doc = mock(XWikiDocument.class);
+        DocumentReference authorReference = new DocumentReference("wiki", "Space", "User");
+        when(xcontext.getDoc()).thenReturn(doc);
+        when(doc.getAuthorReference()).thenReturn(authorReference);
+        DocumentReference currentDocReference = new DocumentReference("wiki", "Space", "Page");
+        when(doc.getDocumentReference()).thenReturn(currentDocReference);
+
+        when(authorizationManager.hasAccess(Right.PROGRAM, authorReference, currentDocReference)).thenReturn(false);
+
+        // Tests
+        assertFalse(mocker.getComponentUnderTest().clearCacheFromColorTheme("colorTheme"));
+
+        // Verify
+        verifyZeroInteractions(lessCache);
+        verifyZeroInteractions(colorThemeCache);
     }
 
     @Test
-    public void clearCacheWithParamsWithRights() throws Exception
+    public void clearCacheFromSkinWithRights() throws Exception
     {
         // Mocks
         XWikiDocument doc = mock(XWikiDocument.class);
@@ -249,14 +296,15 @@ public void clearCacheWithParamsWithRights() throws Exception
         when(authorizationManager.hasAccess(Right.PROGRAM, authorReference, currentDocReference)).thenReturn(true);
 
         // Tests
-        assertTrue(mocker.getComponentUnderTest().clearCache("wiki"));
+        assertTrue(mocker.getComponentUnderTest().clearCacheFromFileSystemSkin("skin"));
 
         // Verify
-        verify(cache).clear(eq("wiki"));
+        verify(lessCache).clearFromFileSystemSkin(eq("skin"));
+        verify(colorThemeCache).clearFromFileSystemSkin(eq("skin"));
     }
 
     @Test
-    public void clearCacheWithParamsWithoutRights() throws Exception
+    public void clearCacheFromSkinWithoutRights() throws Exception
     {
         // Mocks
         XWikiDocument doc = mock(XWikiDocument.class);
@@ -269,10 +317,11 @@ public void clearCacheWithParamsWithoutRights() throws Exception
         when(authorizationManager.hasAccess(Right.PROGRAM, authorReference, currentDocReference)).thenReturn(false);
 
         // Tests
-        assertFalse(mocker.getComponentUnderTest().clearCache("wiki"));
+        assertFalse(mocker.getComponentUnderTest().clearCacheFromFileSystemSkin("skin"));
 
         // Verify
-        verify(cache, never()).clear(eq("wiki"));
+        verifyZeroInteractions(lessCache);
+        verifyZeroInteractions(colorThemeCache);
     }
 
 }
-- 
GitLab