2020 */
2121
2222/*jslint regexp: true */
23- /*global jsPromise*/
23+ /*global jsPromise, catchToNull, path */
2424
2525/**
2626 * Set of utilities for simple parsing of CSS text.
@@ -40,8 +40,9 @@ define(function (require, exports, module) {
4040 IndexingWorker = require ( "worker/IndexingWorker" ) ,
4141 _ = require ( "thirdparty/lodash" ) ;
4242
43+ const MAX_CONTENT_LENGTH = 10 * 1024 * 1024 ; // 10MB
4344 // Constants
44- var SELECTOR = "selector" ,
45+ const SELECTOR = "selector" ,
4546 PROP_NAME = "prop.name" ,
4647 PROP_VALUE = "prop.value" ,
4748 IMPORT_URL = "import.url" ;
@@ -1847,7 +1848,7 @@ define(function (require, exports, module) {
18471848 }
18481849 }
18491850
1850- const MODE_MAP = {
1851+ const CSS_MODE_MAP = {
18511852 css : "CSS" ,
18521853 less : "LESS" ,
18531854 scss : "SCSS"
@@ -1866,14 +1867,14 @@ define(function (require, exports, module) {
18661867 return ;
18671868 }
18681869 const langID = doc . getLanguage ( ) . getId ( ) ;
1869- if ( ! MODE_MAP [ langID ] ) {
1870+ if ( ! CSS_MODE_MAP [ langID ] ) {
18701871 console . log ( "Cannot parse CSS for mode :" , langID , "ignoring" , fullPath ) ;
18711872 resolve ( selectors ) ;
18721873 return ;
18731874 }
18741875 console . log ( "scanning file for css selector collation: " , fullPath ) ;
18751876 IndexingWorker . execPeer ( "css_getAllSymbols" ,
1876- { text : doc . getText ( ) , cssMode : "CSS" , filePath : fullPath } )
1877+ { text : doc . getText ( ) , cssMode : CSS_MODE_MAP [ langID ] , filePath : fullPath } )
18771878 . then ( ( selectorArray ) => {
18781879 selectors = _extractSelectorSet ( selectorArray ) ;
18791880 CSSSelectorCache . set ( fullPath , selectors ) ;
@@ -1913,34 +1914,99 @@ define(function (require, exports, module) {
19131914 return ( _htmlLikeFileExts . indexOf ( LanguageManager . getLanguageForPath ( fullPath ) . getId ( ) || "" ) !== - 1 ) ;
19141915 }
19151916
1916- function _getAllSelectorsInCurrentHTMLEditor ( ) {
1917- return new Promise ( resolve => {
1918- let selectors = new Set ( ) ;
1919- const htmlEditor = EditorManager . getCurrentFullEditor ( ) ;
1920- if ( ! htmlEditor || ! _isHtmlLike ( htmlEditor ) ) {
1921- resolve ( selectors ) ;
1917+ const cacheInProgress = new Set ( ) ;
1918+ async function _precacheExternalStyleSheet ( link ) {
1919+ try {
1920+ if ( cacheInProgress . has ( link ) ) {
1921+ return ;
1922+ }
1923+ cacheInProgress . add ( link ) ;
1924+ const extension = path . extname ( new URL ( link ) . pathname ) . slice ( 1 ) ;
1925+ if ( ! extension || ! CSS_MODE_MAP [ extension ] ) {
1926+ console . log ( `Not a valid stylesheet type ${ extension } , ignoring` , link ) ;
1927+ return ;
1928+ }
1929+ const responseHead = await fetch ( link , { method : 'HEAD' } ) ;
1930+ const contentLength = responseHead . headers . get ( 'Content-Length' ) ;
1931+ if ( contentLength > MAX_CONTENT_LENGTH ) {
1932+ console . log ( `Stylesheet is larger than ${ MAX_CONTENT_LENGTH } bytes, ignoring` , link ) ;
19221933 return ;
19231934 }
19241935
1925- // Find all <style> blocks in the HTML file
1926- const styleBlocks = HTMLUtils . findStyleBlocks ( htmlEditor ) ;
1927- let cssText = "" ;
1928-
1929- styleBlocks . forEach ( function ( styleBlockInfo ) {
1930- // Search this one <style> block's content
1931- cssText += styleBlockInfo . text ;
1932- } ) ;
1933- const fullPath = htmlEditor . document . file . fullPath ;
1934- IndexingWorker . execPeer ( "css_getAllSymbols" , { text : cssText , cssMode : "CSS" , filePath : fullPath } )
1936+ const response = await fetch ( link ) ;
1937+ const styleSheetText = await response . text ( ) ;
1938+ IndexingWorker . execPeer ( "css_getAllSymbols" ,
1939+ { text : styleSheetText , cssMode : CSS_MODE_MAP [ extension ] , filePath : link } )
19351940 . then ( ( selectorArray ) => {
1936- selectors = _extractSelectorSet ( selectorArray ) ;
1937- CSSSelectorCache . set ( fullPath , selectors ) ;
1938- resolve ( selectors ) ;
1941+ CSSSelectorCache . set ( link , _extractSelectorSet ( selectorArray ) ) ;
19391942 } ) . catch ( err => {
1940- console . warn ( "CSS language service unable to get selectors for" + fullPath , err ) ;
1941- resolve ( selectors ) ; // still resolve, so the overall result doesn't reject
1943+ console . warn ( "CSS language service unable to get selectors for link" + link , err ) ;
19421944 } ) ;
1945+ } catch ( e ) {
1946+ console . error ( "Error pre caching externally linked style sheet " , link ) ;
1947+ }
1948+ cacheInProgress . delete ( link ) ;
1949+ }
1950+
1951+ const MAX_ALLOWED_EXTERNAL_STYLE_SHEETS = 20 ;
1952+
1953+ /**
1954+ * html files may have embedded link style sheets to external CDN urls. We will parse them to get all selectors.
1955+ * @param htmlFileContent
1956+ * @param fileMode
1957+ * @param fullPath
1958+ * @return {Promise<void> }
1959+ * @private
1960+ */
1961+ async function _getLinkedCSSFileSelectors ( htmlFileContent , fileMode , fullPath ) {
1962+ const linkedFiles = await catchToNull ( IndexingWorker . execPeer (
1963+ "html_getAllLinks" , { text : htmlFileContent , htmlMode : fileMode , filePath : fullPath } ) ,
1964+ "error extracting linked css files from" + fullPath ) || [ ] ;
1965+ let selectors = new Set ( ) ;
1966+ let externalStyleSheetCount = 0 ;
1967+ for ( const link of linkedFiles ) {
1968+ if ( externalStyleSheetCount >= MAX_ALLOWED_EXTERNAL_STYLE_SHEETS ) {
1969+ break ;
1970+ }
1971+ if ( link . startsWith ( "http://" ) || link . startsWith ( "https://" ) ) {
1972+ externalStyleSheetCount ++ ;
1973+ const cachedSelectors = CSSSelectorCache . get ( link ) ;
1974+ if ( cachedSelectors ) {
1975+ cachedSelectors . forEach ( value => {
1976+ selectors . add ( value ) ;
1977+ } ) ;
1978+ } else {
1979+ _precacheExternalStyleSheet ( link ) ;
1980+ }
1981+ }
1982+ }
1983+ return selectors ;
1984+ }
1985+
1986+ async function _getAllSelectorsInCurrentHTMLEditor ( ) {
1987+ let selectors = new Set ( ) ;
1988+ const htmlEditor = EditorManager . getCurrentFullEditor ( ) ;
1989+ if ( ! htmlEditor || ! _isHtmlLike ( htmlEditor ) ) {
1990+ return selectors ;
1991+ }
1992+
1993+ // Find all <style> blocks in the HTML file
1994+ const styleBlocks = HTMLUtils . findStyleBlocks ( htmlEditor ) ;
1995+ let cssText = "" ;
1996+
1997+ styleBlocks . forEach ( function ( styleBlockInfo ) {
1998+ // Search this one <style> block's content
1999+ cssText += styleBlockInfo . text ;
19432000 } ) ;
2001+ const fullPath = htmlEditor . document . file . fullPath ;
2002+ const selectorsPromise = IndexingWorker . execPeer (
2003+ "css_getAllSymbols" , { text : cssText , cssMode : "CSS" , filePath : fullPath } ) ;
2004+ const htmlLanguageID = LanguageManager . getLanguageForPath ( fullPath ) . getId ( ) ;
2005+ const remoteLinkedSelectors = await _getLinkedCSSFileSelectors ( htmlEditor . document . getText ( ) ,
2006+ htmlLanguageID . toUpperCase ( ) , fullPath ) ;
2007+ selectors = await catchToNull ( selectorsPromise , "CSS language service unable to get selectors for" + fullPath ) ;
2008+ selectors = selectors || new Set ( ) ;
2009+ return new Set ( [ ...selectors , ...remoteLinkedSelectors ] ) ;
19442010 }
19452011
19462012 let globalPrecacheRun = 0 ;
0 commit comments