function htmlToCSSProvider(hostEditor, pos) {

        // Only provide a CSS editor when cursor is in HTML content
        if (hostEditor.getLanguageForSelection().getId() !== "html") {
            return null;
        }

        //Send analytics data for QuickEdit open
        HealthLogger.sendAnalyticsData(
            "QuickEditOpen",
            "usage",
            "quickEdit",
            "open"
        );

        // Only provide CSS editor if the selection is within a single line
        var sel = hostEditor.getSelection();
        if (sel.start.line !== sel.end.line) {
            return null;
        }

        // Always use the selection start for determining selector name. The pos
        // parameter is usually the selection end.
        var selectorResult = _getSelectorName(hostEditor, sel.start);
        if (selectorResult.selectorName === "") {
            return selectorResult.reason || null;
        }

        var selectorName = selectorResult.selectorName;

        var result = new $.Deferred(),
            cssInlineEditor,
            cssFileInfos = [],
            newRuleButton;

        /**
         * @private
         * Callback when item from dropdown list is selected
         */
        function _onDropdownSelect(event, fileInfo) {
            _addRule(selectorName, cssInlineEditor, fileInfo.fullPath);
        }

        /**
         * @private
         * Checks to see if there are any stylesheets in the project, and returns the appropriate
         * "no rules"/"no stylesheets" message accordingly.
         * @return {$.Promise} a promise that is resolved with the message to show. Never rejected.
         */
        function _getNoRulesMsg() {
            var result = new $.Deferred();
            _getCSSFilesInProject().done(function (fileInfos) {
                result.resolve(fileInfos.length ? Strings.CSS_QUICK_EDIT_NO_MATCHES : Strings.CSS_QUICK_EDIT_NO_STYLESHEETS);
            });
            return result;
        }

        /**
         * @private
         * Update the enablement of associated menu commands.
         */
        function _updateCommands() {
            _newRuleCmd.setEnabled(cssInlineEditor.hasFocus() && !newRuleButton.$button.hasClass("disabled"));
        }

        /**
         * @private
         * Create a new rule on click.
         */
        function _handleNewRuleClick(e) {
            if (!newRuleButton.$button.hasClass("disabled")) {
                if (cssFileInfos.length === 1) {
                    // Just go ahead and create the rule.
                    _addRule(selectorName, cssInlineEditor, cssFileInfos[0].fullPath);
                } else {
                    // Although not attached to button click in 'dropdown mode', this handler can still be
                    // invoked via the command shortcut. Just toggle dropdown open/closed in that case.
                    newRuleButton.toggleDropdown();
                }
            }
        }

        /**
         * @private
         * Sort files with LESS/SCSS above CSS, and then within each grouping sort by path & filename
         * (the same order we use for Find in Files)
         * @param {!File} a, b
         * @return {number}
         */
        function _fileComparator(a, b) {
            var aIsCSS = LanguageManager.getLanguageForPath(a.fullPath).getId() === "css",
                bIsCSS = LanguageManager.getLanguageForPath(b.fullPath).getId() === "css";
            if (aIsCSS && !bIsCSS) {
                return 1;
            } else if (!aIsCSS && bIsCSS) {
                return -1;
            } else {
                return FileUtils.comparePaths(a.fullPath, b.fullPath);
            }
        }

        /**
         * @private
         * Prepare file list for display
         */
        function _prepFileList(files) {
            // First, sort list (the same ordering we use for the results list)
            files.sort(_fileComparator);

            // Find any files that share the same name (with different path)
            var fileNames = {};
            files.forEach(function (file) {
                if (!fileNames[file.name]) {
                    fileNames[file.name] = [];
                }
                fileNames[file.name].push(file);
            });

            // For any duplicate filenames, set subDirStr to a path snippet the helps
            // the user distinguish each file in the list.
            _.forEach(fileNames, function (files) {
                if (files.length > 1) {
                    var displayPaths = ViewUtils.getDirNamesForDuplicateFiles(files);
                    files.forEach(function (file, i) {
                        file.subDirStr = displayPaths[i];
                    });
                }
            });

            return files;
        }

        function _onHostEditorScroll() {
            newRuleButton.closeDropdown();
        }

        CSSUtils.findMatchingRules(selectorName, hostEditor.document)
            .done(function (rules) {
                var inlineEditorDeferred = new $.Deferred();
                cssInlineEditor = new MultiRangeInlineEditor.MultiRangeInlineEditor(CSSUtils.consolidateRules(rules),
                                                                                    _getNoRulesMsg, CSSUtils.getRangeSelectors,
                                                                                    _fileComparator);
                cssInlineEditor.load(hostEditor);
                cssInlineEditor.$htmlContent
                    .on("focusin", _updateCommands)
                    .on("focusout", _updateCommands);
                cssInlineEditor.on("add", function () {
                    inlineEditorDeferred.resolve();
                });
                cssInlineEditor.on("close", function () {
                    newRuleButton.closeDropdown();
                    hostEditor.off("scroll", _onHostEditorScroll);
                });

                var $header = $(".inline-editor-header", cssInlineEditor.$htmlContent);
                newRuleButton = new DropdownButton(Strings.BUTTON_NEW_RULE, [], _stylesheetListRenderer); // actual item list populated later, below
                newRuleButton.$button.addClass("disabled");  // disabled until list is known
                newRuleButton.$button.addClass("btn-mini stylesheet-button");
                $header.append(newRuleButton.$button);
                _newRuleHandlers.push({inlineEditor: cssInlineEditor, handler: _handleNewRuleClick});

                hostEditor.on("scroll", _onHostEditorScroll);

                result.resolve(cssInlineEditor);


                // Now that dialog has been built, collect list of stylesheets
                var stylesheetsPromise = _getCSSFilesInProject();

                // After both the stylesheets are loaded and the inline editor has been added to the DOM,
                // update the UI accordingly. (Those can happen in either order, so we need to wait for both.)
                // Note that the stylesheetsPromise needs to be passed first in order for the fileInfos to be
                // properly passed to the handler, since $.when() passes the results in order of the argument
                // list.
                $.when(stylesheetsPromise, inlineEditorDeferred.promise())
                    .done(function (fileInfos) {
                        cssFileInfos = _prepFileList(fileInfos);

                        // "New Rule" button is disabled by default and gets enabled
                        // here if there are any stylesheets in project
                        if (cssFileInfos.length > 0) {
                            newRuleButton.$button.removeClass("disabled");
                            if (!rules.length) {
                                // Force focus to the button so the user can create a new rule from the keyboard.
                                newRuleButton.$button.focus();
                            }

                            if (cssFileInfos.length === 1) {
                                // Make it look & feel like a plain button in this case
                                newRuleButton.$button.removeClass("btn-dropdown");
                                newRuleButton.$button.on("click", _handleNewRuleClick);
                            } else {
                                // Fill out remaining dropdown attributes otherwise
                                newRuleButton.items = cssFileInfos;
                                newRuleButton.on("select", _onDropdownSelect);
                            }
                        }

                        _updateCommands();
                    });
            })
            .fail(function (error) {
                console.warn("Error in findMatchingRules()", error);
                result.reject();
            });

        return result.promise();
    }
This function is registered with EditManager as an inline editor provider. It creates a CSSInlineEditor
when cursor is on an HTML tag name, class attribute, or id attribute, find associated
CSS rules and show (one/all of them) in an inline editor.

@param {!Editor} editor
@param {!{line:Number, ch:Number}} pos
@return {?$.Promise} synchronously resolved with an InlineWidget; or error
        {string} if pos is in tag but not in tag name, class attr, or id attr; or null if the
        selection isn't even close to a context where we could provide anything.