Changeset 205754 in webkit
- Timestamp:
- Sep 9, 2016 11:25:14 AM (8 years ago)
- Location:
- trunk/Source/WebInspectorUI
- Files:
-
- 7 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebInspectorUI/ChangeLog
r205693 r205754 1 2016-09-09 Devin Rousso <dcrousso+webkit@gmail.com> 2 3 Web Inspector: Command-Z doesn't work when editing CSS selectors in Styles sidebar 4 https://bugs.webkit.org/show_bug.cgi?id=159734 5 6 Reviewed by Brian Burg. 7 8 Replace the current usage of -webkit-user-select with a textarea sized exactly the same as 9 the selector text which holds the current value of the selector. 10 11 * UserInterface/Test.html: Add WrappedPromise. 12 13 * UserInterface/Models/DOMNodeStyles.js: 14 (WebInspector.DOMNodeStyles.prototype.refresh.fetchedMatchedStyles): 15 (WebInspector.DOMNodeStyles.prototype.refresh.fetchedInlineStyles): 16 (WebInspector.DOMNodeStyles.prototype.refresh.fetchedComputedStyle): 17 (WebInspector.DOMNodeStyles.prototype.refresh): 18 (WebInspector.DOMNodeStyles.prototype.changeRuleSelector.ruleSelectorChanged): 19 (WebInspector.DOMNodeStyles.prototype.changeRuleSelector): 20 Ensure that the promise returned by changeRuleSelector is only resolved once the initiated 21 refresh has completed, ensuring that all matched selectors are parsed and available. 22 23 * UserInterface/Views/CSSStyleDeclarationSection.css: 24 (.style-declaration-section > .header): 25 (.style-declaration-section.locked > .header::before): 26 (.style-declaration-section.rule-disabled > .header > .icon): 27 (.style-declaration-section > .header > textarea): 28 (.style-declaration-section > .header > textarea:focus): 29 (.style-declaration-section > .header > textarea:focus + .selector): 30 (.style-declaration-section > .header > .selector): 31 (.style-declaration-section > .header > .selector:empty): 32 (.style-declaration-section > .header > .selector .matched): 33 (.style-declaration-section:not(.invalid-selector).rule-disabled > .header > .icon): Deleted. 34 (.style-declaration-section > .header > .selector:empty::before): Deleted. 35 (.style-declaration-section > .header > .selector:focus): Deleted. 36 (.style-declaration-section:matches(.locked, .selector-locked) > .header > .selector): Deleted. 37 (.style-declaration-section > .header > .selector > .matched): Deleted. 38 (.style-declaration-section.invalid-selector > .header > .icon): Deleted. 39 (.style-declaration-section.invalid-selector > .header > .selector): Deleted. 40 Added styling to make textarea invisible when not focused. 41 Also removed the .invalid-selector styles as the section now refreshed on all changes. 42 43 * UserInterface/Views/CSSStyleDeclarationSection.js: 44 (WebInspector.CSSStyleDeclarationSection): 45 (WebInspector.CSSStyleDeclarationSection.prototype.refresh.appendSelector): 46 (WebInspector.CSSStyleDeclarationSection.prototype.refresh.appendSelectorTextKnownToMatch): 47 (WebInspector.CSSStyleDeclarationSection.prototype.refresh): 48 (WebInspector.CSSStyleDeclarationSection.prototype.focusRuleSelector): 49 (WebInspector.CSSStyleDeclarationSection.prototype.get selectorEditable): 50 (WebInspector.CSSStyleDeclarationSection.prototype.get _currentSelectorText): 51 (WebInspector.CSSStyleDeclarationSection.prototype._handleSelectorPaste.parseTextForRule): 52 (WebInspector.CSSStyleDeclarationSection.prototype._handleSelectorPaste): 53 (WebInspector.CSSStyleDeclarationSection.prototype.get selectorLocked): Deleted. 54 Added a hidden textarea for modifying the selector of the represented style. 55 56 * UserInterface/Views/RulesStyleDetailsPanel.js: 57 (WebInspector.RulesStyleDetailsPanel.prototype.cssStyleDeclarationSectionEditorPreviousRule): 58 Now use new getters on CSSSTyleDeclarationSection for determining if a selector is editable. 59 60 * UserInterface/Views/Variables.css: 61 (:root): 62 Added --style-declaration-section-header-padding CSS variable. 63 1 64 2016-09-08 Brian Burg <bburg@apple.com> 2 65 -
trunk/Source/WebInspectorUI/UserInterface/Models/DOMNodeStyles.js
r205426 r205754 46 46 this._propertyNameToEffectivePropertyMap = {}; 47 47 48 this._pendingRefreshTask = null; 48 49 this.refresh(); 49 50 } … … 58 59 get needsRefresh() 59 60 { 60 return this._ refreshPending|| this._needsRefresh;61 return this._pendingRefreshTask || this._needsRefresh; 61 62 } 62 63 … … 70 71 refresh() 71 72 { 72 if (this._ refreshPending)73 return ;73 if (this._pendingRefreshTask) 74 return this._pendingRefreshTask; 74 75 75 76 this._needsRefresh = false; 76 this._refreshPending = true; 77 78 let fetchedMatchedStylesPromise = new WebInspector.WrappedPromise; 79 let fetchedInlineStylesPromise = new WebInspector.WrappedPromise; 80 let fetchedComputedStylesPromise = new WebInspector.WrappedPromise; 77 81 78 82 function parseRuleMatchArrayPayload(matchArray, node, inherited) … … 131 135 ++i; 132 136 } 137 138 fetchedMatchedStylesPromise.resolve(); 133 139 } 134 140 … … 139 145 140 146 this._updateStyleCascade(); 147 148 fetchedComputedStylesPromise.resolve(); 141 149 } 142 150 … … 160 168 else 161 169 this._computedStyle = new WebInspector.CSSStyleDeclaration(this, null, null, WebInspector.CSSStyleDeclaration.Type.Computed, this._node, false, null, properties); 162 163 this._refreshPending = false;164 170 165 171 let significantChange = false; … … 227 233 228 234 this.dispatchEventToListeners(WebInspector.DOMNodeStyles.Event.Refreshed, {significantChange}); 235 236 fetchedComputedStylesPromise.resolve(); 229 237 } 230 238 … … 235 243 CSSAgent.getInlineStylesForNode.invoke({nodeId: this._node.id}, fetchedInlineStyles.bind(this)); 236 244 CSSAgent.getComputedStyleForNode.invoke({nodeId: this._node.id}, fetchedComputedStyle.bind(this)); 245 246 this._pendingRefreshTask = Promise.all([fetchedMatchedStylesPromise, fetchedInlineStylesPromise, fetchedComputedStylesPromise]) 247 .then(() => { 248 this._pendingRefreshTask = null; 249 }); 250 251 return this._pendingRefreshTask; 237 252 } 238 253 … … 409 424 { 410 425 selector = selector || ""; 411 varresult = new WebInspector.WrappedPromise;426 let result = new WebInspector.WrappedPromise; 412 427 413 428 function ruleSelectorChanged(error, rulePayload) … … 422 437 // Do a full refresh incase the rule no longer matches the node or the 423 438 // matched selector indices changed. 424 this.refresh() ;425 426 result.resolve(rulePayload);439 this.refresh().then(() => { 440 result.resolve(rulePayload); 441 }); 427 442 } 428 443 -
trunk/Source/WebInspectorUI/UserInterface/Test.html
r205418 r205754 162 162 <script src="Models/TimelineMarker.js"></script> 163 163 <script src="Models/TimelineRecording.js"></script> 164 <script src="Models/WrappedPromise.js"></script> 164 165 165 166 <script src="Proxies/FormatterWorkerProxy.js"></script> -
trunk/Source/WebInspectorUI/UserInterface/Views/CSSStyleDeclarationSection.css
r194437 r205754 56 56 .style-declaration-section > .header { 57 57 position: relative; 58 59 padding: 4px 5px 3px 25px; 60 58 padding: var(--style-declaration-section-header-padding); 61 59 font-size: 11px; 62 60 line-height: 12px; 61 } 62 63 .style-declaration-section.locked > .header::before { 64 float: right; 65 width: 8px; 66 height: 10px; 67 margin-left: 5px; 68 content: ""; 69 background-image: url(../Images/Locked.svg); 70 background-repeat: no-repeat; 71 background-position: center; 72 background-size: 8px 10px; 63 73 } 64 74 … … 81 91 } 82 92 83 .style-declaration-section :not(.invalid-selector).rule-disabled > .header > .icon {93 .style-declaration-section.rule-disabled > .header > .icon { 84 94 opacity: 0.5; 95 } 96 97 .style-declaration-section > .header > textarea { 98 display: block; 99 position: absolute; 100 top: 0; 101 right: 0; 102 bottom: 0; 103 left: 0; 104 width: 100%; 105 height: 100%; 106 margin: 0; 107 padding: var(--style-declaration-section-header-padding); 108 font-family: Menlo, monospace; 109 color: black; 110 background: transparent; 111 border: none; 112 outline: none; 113 opacity: 0; 114 resize: none; 115 -webkit-appearance: none; 116 } 117 118 .style-declaration-section > .header > textarea:focus { 119 opacity: 1; 120 } 121 122 .style-declaration-section > .header > textarea:focus + .selector { 123 opacity: 0; 85 124 } 86 125 … … 88 127 font-family: Menlo, monospace; 89 128 color: hsl(0, 0%, 50%); 90 91 outline: none;92 93 cursor: text;94 95 129 word-wrap: break-word; 96 97 -webkit-user-select: text;98 -webkit-user-modify: read-write-plaintext-only;99 }100 101 .style-declaration-section > .header > .selector:empty {102 /* This prevents the cursor from disappearing when empty. */103 display: inline-block;104 min-width: 1px;105 }106 107 .style-declaration-section > .header > .selector:empty::before {108 /* This prevents the cursor from positioning badly when empty. */109 content: "";110 }111 112 .style-declaration-section > .header > .selector:focus {113 color: black;114 }115 116 .style-declaration-section:matches(.locked, .selector-locked) > .header > .selector {117 -webkit-user-modify: read-only;118 }119 120 .style-declaration-section.locked > .header::before {121 float: right;122 123 content: "";124 125 width: 8px;126 height: 10px;127 128 background-image: url(../Images/Locked.svg);129 background-repeat: no-repeat;130 background-position: center;131 background-size: 8px 10px;132 133 margin-left: 5px;134 130 } 135 131 … … 155 151 } 156 152 157 .style-declaration-section.invalid-selector > .header > .icon {158 top: 4px;159 left: 6px;160 width: 12px;161 height: 12px;162 content: url(../Images/Error.svg);163 }164 165 .style-declaration-section.invalid-selector > .header > .selector,166 .style-declaration-section.invalid-selector > .header > .selector> * {167 color: red;168 }169 170 153 @media (-webkit-min-device-pixel-ratio: 2) { 171 154 .style-declaration-section, -
trunk/Source/WebInspectorUI/UserInterface/Views/CSSStyleDeclarationSection.js
r205425 r205754 37 37 this._selectorElements = []; 38 38 this._ruleDisabled = false; 39 this._hasInvalidSelector = false;40 39 41 40 this._element = document.createElement("div"); … … 48 47 this._headerElement.classList.add("header"); 49 48 50 this._iconElement = document.createElement("img"); 51 this._iconElement.classList.add("icon"); 52 this._headerElement.appendChild(this._iconElement); 53 54 this._selectorElement = document.createElement("span"); 55 this._selectorElement.classList.add("selector"); 56 this._selectorElement.setAttribute("spellcheck", "false"); 57 this._selectorElement.addEventListener("mouseover", this._handleMouseOver.bind(this)); 58 this._selectorElement.addEventListener("mouseout", this._handleMouseOut.bind(this)); 59 this._selectorElement.addEventListener("keydown", this._handleKeyDown.bind(this)); 60 this._selectorElement.addEventListener("keyup", this._handleKeyUp.bind(this)); 61 this._selectorElement.addEventListener("paste", this._handleSelectorPaste.bind(this)); 62 this._headerElement.appendChild(this._selectorElement); 63 64 this._originElement = document.createElement("span"); 65 this._originElement.classList.add("origin"); 66 this._headerElement.appendChild(this._originElement); 49 this._iconElement = this._headerElement.createChild("img", "icon"); 50 51 if (this.selectorEditable) { 52 this._selectorInput = this._headerElement.createChild("textarea"); 53 this._selectorInput.spellcheck = false; 54 this._selectorInput.tabIndex = -1; 55 this._selectorInput.addEventListener("mouseover", this._handleMouseOver.bind(this)); 56 this._selectorInput.addEventListener("mouseout", this._handleMouseOut.bind(this)); 57 this._selectorInput.addEventListener("keydown", this._handleKeyDown.bind(this)); 58 this._selectorInput.addEventListener("keypress", this._handleKeyPress.bind(this)); 59 this._selectorInput.addEventListener("input", this._handleInput.bind(this)); 60 this._selectorInput.addEventListener("paste", this._handleSelectorPaste.bind(this)); 61 this._selectorInput.addEventListener("blur", this._handleBlur.bind(this)); 62 } 63 64 this._selectorElement = this._headerElement.createChild("span", "selector"); 65 if (!this.selectorEditable) { 66 this._selectorElement.addEventListener("mouseover", this._handleMouseOver.bind(this)); 67 this._selectorElement.addEventListener("mouseout", this._handleMouseOut.bind(this)); 68 } 69 70 this._originElement = this._headerElement.createChild("span", "origin"); 67 71 68 72 this._propertiesElement = document.createElement("div"); … … 78 82 this._element.appendChild(this._propertiesElement); 79 83 80 var iconClassName;84 let iconClassName = null; 81 85 switch (style.type) { 82 86 case WebInspector.CSSStyleDeclaration.Type.Rule: … … 115 119 if (!style.editable) 116 120 this._element.classList.add(WebInspector.CSSStyleDeclarationSection.LockedStyleClassName); 117 else if (style.ownerRule) { 118 this._style.ownerRule.addEventListener(WebInspector.CSSRule.Event.SelectorChanged, this._updateSelectorIcon.bind(this)); 119 this._commitSelectorKeyboardShortcut = new WebInspector.KeyboardShortcut(null, WebInspector.KeyboardShortcut.Key.Enter, this._commitSelector.bind(this), this._selectorElement); 120 this._selectorElement.addEventListener("blur", this._commitSelector.bind(this)); 121 } else 121 else if (style.ownerRule) 122 this._style.ownerRule.addEventListener(WebInspector.CSSRule.Event.SelectorChanged, this.refresh.bind(this)); 123 else 122 124 this._element.classList.add(WebInspector.CSSStyleDeclarationSection.SelectorLockedStyleClassName); 123 125 … … 168 170 this._selectorElements = []; 169 171 170 this._originElement.append( " \u2014 ");172 this._originElement.append(` ${emDash} `); 171 173 172 174 function appendSelector(selector, matched) … … 174 176 console.assert(selector instanceof WebInspector.CSSSelector); 175 177 176 varselectorElement = document.createElement("span");178 let selectorElement = document.createElement("span"); 177 179 selectorElement.textContent = selector.text; 178 180 … … 180 182 selectorElement.classList.add(WebInspector.CSSStyleDeclarationSection.MatchedSelectorElementStyleClassName); 181 183 182 varspecificity = selector.specificity;184 let specificity = selector.specificity; 183 185 if (specificity) { 184 vartooltip = WebInspector.UIString("Specificity: (%d, %d, %d)").format(specificity[0], specificity[1], specificity[2]);186 let tooltip = WebInspector.UIString("Specificity: (%d, %d, %d)").format(specificity[0], specificity[1], specificity[2]); 185 187 if (selector.dynamic) { 186 188 tooltip += "\n"; … … 192 194 selectorElement.title = tooltip; 193 195 } else if (selector.dynamic) { 194 vartooltip = WebInspector.UIString("Specificity: No value for selected element");196 let tooltip = WebInspector.UIString("Specificity: No value for selected element"); 195 197 tooltip += "\n"; 196 198 tooltip += WebInspector.UIString("Dynamically calculated for the selected element and did not match"); … … 204 206 function appendSelectorTextKnownToMatch(selectorText) 205 207 { 206 varselectorElement = document.createElement("span");208 let selectorElement = document.createElement("span"); 207 209 selectorElement.textContent = selectorText; 208 210 selectorElement.classList.add(WebInspector.CSSStyleDeclarationSection.MatchedSelectorElementStyleClassName); … … 214 216 console.assert(this._style.ownerRule); 215 217 216 varselectors = this._style.ownerRule.selectors;217 varmatchedSelectorIndices = this._style.ownerRule.matchedSelectorIndices;218 varalwaysMatch = !matchedSelectorIndices.length;218 let selectors = this._style.ownerRule.selectors; 219 let matchedSelectorIndices = this._style.ownerRule.matchedSelectorIndices; 220 let alwaysMatch = !matchedSelectorIndices.length; 219 221 if (selectors.length) { 220 varhasMatchingPseudoElementSelector = false;221 for ( vari = 0; i < selectors.length; ++i) {222 let hasMatchingPseudoElementSelector = false; 223 for (let i = 0; i < selectors.length; ++i) { 222 224 appendSelector.call(this, selectors[i], alwaysMatch || matchedSelectorIndices.includes(i)); 223 225 if (i < selectors.length - 1) … … 232 234 233 235 if (this._style.ownerRule.sourceCodeLocation) { 234 varsourceCodeLink = WebInspector.createSourceCodeLocationLink(this._style.ownerRule.sourceCodeLocation, true);236 let sourceCodeLink = WebInspector.createSourceCodeLocationLink(this._style.ownerRule.sourceCodeLocation, true); 235 237 this._originElement.appendChild(sourceCodeLink); 236 238 } else { 237 varoriginString;239 let originString; 238 240 switch (this._style.ownerRule.type) { 239 241 case WebInspector.CSSStyleSheet.Type.Author: … … 272 274 } 273 275 274 this._updateSelectorIcon(); 276 if (this._selectorInput) 277 this._selectorInput.value = this._selectorElement.textContent; 275 278 } 276 279 … … 344 347 focusRuleSelector(reverse) 345 348 { 346 if ( this.selectorLocked) {349 if (!this.selectorEditable && !this.locked) { 347 350 this.focus(); 348 351 return; … … 354 357 } 355 358 356 varselection = window.getSelection();359 let selection = window.getSelection(); 357 360 selection.removeAllRanges(); 358 361 359 362 this._element.scrollIntoViewIfNeeded(); 360 363 361 var range = document.createRange(); 362 range.selectNodeContents(this._selectorElement); 363 selection.addRange(range); 364 if (this._selectorInput) { 365 this._selectorInput.focus(); 366 this._selectorInput.selectionStart = 0; 367 this._selectorInput.selectionEnd = this._selectorInput.value.length; 368 } else { 369 let range = document.createRange(); 370 range.selectNodeContents(this._selectorElement); 371 selection.addRange(range); 372 } 364 373 } 365 374 … … 369 378 } 370 379 371 get selector Locked()372 { 373 return !this.locked && !this._style.ownerRule;380 get selectorEditable() 381 { 382 return !this.locked && this._style.ownerRule; 374 383 } 375 384 … … 388 397 get _currentSelectorText() 389 398 { 390 var selectorText =this._selectorElement.textContent;399 let selectorText = this.selectorEditable ? this._selectorInput.value : this._selectorElement.textContent; 391 400 if (!selectorText || !selectorText.length) { 392 401 if (!this._style.ownerRule) … … 407 416 return; 408 417 409 vardata = event.clipboardData.getData("text/plain");418 let data = event.clipboardData.getData("text/plain"); 410 419 if (!data) 411 420 return; … … 413 422 function parseTextForRule(text) 414 423 { 415 varcontainsBraces = /[\{\}]/;424 let containsBraces = /[\{\}]/; 416 425 if (!containsBraces.test(text)) 417 426 return null; 418 427 419 varmatch = text.match(/([^{]+){(.*)}/);428 let match = text.match(/([^{]+){(.*)}/); 420 429 if (!match) 421 430 return null; … … 426 435 } 427 436 428 var match = parseTextForRule(data); 429 if (!match) 430 return; 431 432 var selector = match[1].trim(); 433 this._selectorElement.textContent = selector; 434 this._style.nodeStyles.changeRule(this._style.ownerRule, selector, match[2]); 437 let [selector, value] = parseTextForRule(data); 438 if (!selector || !value) 439 return; 440 441 this._style.nodeStyles.changeRule(this._style.ownerRule, selector.trim(), value); 435 442 event.preventDefault(); 436 443 } … … 526 533 _handleIconElementClicked() 527 534 { 528 if (this._hasInvalidSelector) {529 // This will revert the selector text to the original valid value.530 this.refresh();531 return;532 }533 534 535 this._ruleDisabled = this._ruleDisabled ? !this._propertiesTextEditor.uncommentAllProperties() : this._propertiesTextEditor.commentAllProperties(); 535 536 this._iconElement.title = this._ruleDisabled ? WebInspector.UIString("Uncomment All Properties") : WebInspector.UIString("Comment All Properties"); … … 597 598 _handleKeyDown(event) 598 599 { 599 if (event.keyCode !== 9) { 600 if (event.keyCode === WebInspector.KeyboardShortcut.Key.Enter.keyCode) { 601 this._selectorInput.blur(); 602 return; 603 } 604 605 if (event.keyCode !== WebInspector.KeyboardShortcut.Key.Tab.keyCode) { 600 606 this._highlightNodesWithSelector(); 601 607 return; … … 616 622 } 617 623 618 _handleKeyUp(event) 619 { 624 _handleKeyPress(event) 625 { 626 if (!event.altGraphKey && !event.altKey && !event.ctrlKey && !event.metaKey) { 627 // Ensures that <textarea> does not scroll with added characters. Since a 628 // <textarea> does not expand to fit its content, appending the pressed character to the 629 // end of the original (non-editable) selector element will ensure that the <textarea> 630 // will be large enough to fit the selector without scrolling. 631 this._selectorElement.append(String.fromCharCode(event.keyCode)); 632 } 633 } 634 635 _handleInput(event) 636 { 637 this._selectorElement.textContent = this._selectorInput.value; 638 620 639 this._highlightNodesWithSelector(); 621 640 } 622 641 623 _commitSelector(mutations) 624 { 625 console.assert(this._style.ownerRule); 626 if (!this._style.ownerRule) 627 return; 628 629 var newSelectorText = this._selectorElement.textContent.trim(); 642 _handleBlur() 643 { 644 this._hideDOMNodeHighlight(); 645 646 let newSelectorText = this._currentSelectorText.trim(); 630 647 if (!newSelectorText) { 631 648 // Revert to the current selector (by doing a refresh) since the new selector is empty. … … 635 652 636 653 this._style.ownerRule.selectorText = newSelectorText; 637 }638 639 _updateSelectorIcon(event)640 {641 if (!this._style.ownerRule || !this._style.editable)642 return;643 644 this._hasInvalidSelector = event && event.data && !event.data.valid;645 this._element.classList.toggle("invalid-selector", !!this._hasInvalidSelector);646 if (!this._hasInvalidSelector) {647 this._iconElement.title = this._ruleDisabled ? WebInspector.UIString("Uncomment All Properties") : WebInspector.UIString("Comment All Properties");648 this._selectorElement.title = null;649 return;650 }651 652 this._iconElement.title = WebInspector.UIString("The selector “%s” is invalid.\nClick to revert to the previous selector.").format(this._selectorElement.textContent.trim());653 this._selectorElement.title = WebInspector.UIString("Using the previous selector “%s”.").format(this._style.ownerRule.selectorText);654 for (let i = 0; i < this._selectorElement.children.length; ++i)655 this._selectorElement.children[i].title = null;656 654 } 657 655 -
trunk/Source/WebInspectorUI/UserInterface/Views/RulesStyleDetailsPanel.js
r205425 r205754 317 317 } 318 318 319 cssStyleDeclarationSectionBlurActiveEditor()320 {321 }322 323 319 cssStyleDeclarationSectionEditorNextRule(currentSection) 324 320 { … … 332 328 currentSection.clearSelection(); 333 329 334 if (selectLastProperty || currentSection.selectorLocked) {330 if (selectLastProperty || !currentSection.selectorEditable) { 335 331 var index = this._sections.indexOf(currentSection); 336 332 index = index > 0 ? index - 1 : this._sections.length - 1; -
trunk/Source/WebInspectorUI/UserInterface/Views/Variables.css
r202064 r205754 77 77 --tab-bar-height: 24px; 78 78 --navigation-bar-height: 29px; 79 80 --style-declaration-section-header-padding: 4px 5px 3px 25px; 79 81 } 80 82
Note: See TracChangeset
for help on using the changeset viewer.