Make text in the ValueInterpreter selectable Now, a user can select text in the MemoryViewer's ValueInterpreter by either double-clicking or manually highlighting the text-range. before: https://imgur.com/a/HGpUkL9 after: https://imgur.com/a/nR0j6iX Fixed: 1324962 Change-Id: I8db585c9aa5e04f69aaae657eb384e5fdae529e9 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/3695369 Commit-Queue: Michal Pitr <michalpitr@google.com> Reviewed-by: Kim-Anh Tran <kimanh@chromium.org>
diff --git a/front_end/ui/components/linear_memory_inspector/ValueInterpreterDisplay.ts b/front_end/ui/components/linear_memory_inspector/ValueInterpreterDisplay.ts index 068285e..58b0667 100644 --- a/front_end/ui/components/linear_memory_inspector/ValueInterpreterDisplay.ts +++ b/front_end/ui/components/linear_memory_inspector/ValueInterpreterDisplay.ts
@@ -153,10 +153,10 @@ // Disabled until https://crbug.com/1079231 is fixed. // clang-format off return html` - <span class="value-type-cell-no-mode value-type-cell">${i18n.i18n.lockedString(type)}</span> + <span class="value-type-cell-no-mode value-type-cell selectable-text">${i18n.i18n.lockedString(type)}</span> <div class="value-type-cell"> <div class="value-type-value-with-link" data-value="true"> - <span>${unsignedValue}</span> + <span class="selectable-text">${unsignedValue}</span> ${ html` <button class="jump-to-button" data-jump="true" title=${buttonTitle} ?disabled=${jumpDisabled} @@ -179,7 +179,7 @@ // Disabled until https://crbug.com/1079231 is fixed. // clang-format off return html` - <span class="value-type-cell">${i18n.i18n.lockedString(type)}</span> + <span class="value-type-cell selectable-text">${i18n.i18n.lockedString(type)}</span> <div> <select title=${i18nString(UIStrings.changeValueTypeMode)} data-mode-settings="true" @@ -206,7 +206,7 @@ const showSignedAndUnsigned = signedValue !== unsignedValue && mode !== ValueTypeMode.Hexadecimal && mode !== ValueTypeMode.Octal; - const unsignedRendered = html`<span class="value-type-cell" title=${ + const unsignedRendered = html`<span class="value-type-cell selectable-text" title=${ i18nString(UIStrings.unsignedValue)} data-value="true">${unsignedValue}</span>`; if (!showSignedAndUnsigned) { return unsignedRendered; @@ -214,8 +214,8 @@ // Some values are too long to show in one line, we're putting them into the next line. const showInMultipleLines = type === ValueType.Int32 || type === ValueType.Int64; - const signedRendered = - html`<span data-value="true" title=${i18nString(UIStrings.signedValue)}>${signedValue}</span>`; + const signedRendered = html`<span class="selectable-text" data-value="true" title=${ + i18nString(UIStrings.signedValue)}>${signedValue}</span>`; if (showInMultipleLines) { return html`
diff --git a/front_end/ui/components/linear_memory_inspector/valueInterpreterDisplay.css b/front_end/ui/components/linear_memory_inspector/valueInterpreterDisplay.css index a11ba1e..29b2db6 100644 --- a/front_end/ui/components/linear_memory_inspector/valueInterpreterDisplay.css +++ b/front_end/ui/components/linear_memory_inspector/valueInterpreterDisplay.css
@@ -61,3 +61,11 @@ background-color: var(--color-details-hairline); margin: 0 4px; } + +.selectable-text { + user-select: text; +} + +.selectable-text::selection { + background-color: var(--legacy-item-selection-bg-color); +}
diff --git a/test/unittests/front_end/ui/components/linear_memory_inspector/ValueInterpreterDisplay_test.ts b/test/unittests/front_end/ui/components/linear_memory_inspector/ValueInterpreterDisplay_test.ts index 2f59ee0..c61dfa8 100644 --- a/test/unittests/front_end/ui/components/linear_memory_inspector/ValueInterpreterDisplay_test.ts +++ b/test/unittests/front_end/ui/components/linear_memory_inspector/ValueInterpreterDisplay_test.ts
@@ -382,4 +382,64 @@ assert.isTrue(buttons[0].disabled); assert.isTrue(buttons[1].disabled); }); + + it('selects text in data-value elements if user selects it', () => { + // To test the failing case, set .value-type user-select to `none`. + // This is necessary as we render the component in isolation, so it doesn't + // inherit this property from its parent. + + const component = new LinearMemoryInspector.ValueInterpreterDisplay.ValueInterpreterDisplay(); + const array = [1, 132, 172, 71]; + component.data = { + buffer: new Uint8Array(array).buffer, + endianness: LinearMemoryInspector.ValueInterpreterDisplayUtils.Endianness.Little, + valueTypes: new Set([ + LinearMemoryInspector.ValueInterpreterDisplayUtils.ValueType.Int8, + LinearMemoryInspector.ValueInterpreterDisplayUtils.ValueType.Int16, + LinearMemoryInspector.ValueInterpreterDisplayUtils.ValueType.Float32, + LinearMemoryInspector.ValueInterpreterDisplayUtils.ValueType.Pointer32, + ]), + memoryLength: array.length, + }; + renderElementIntoDOM(component); + + const dataValues = getElementsWithinComponent(component, '.selectable-text', HTMLSpanElement); + assert.lengthOf(dataValues, 9); + + const expectedValues = [ + 'Integer 8-bit', + '1', + 'Integer 16-bit', + '33793', + '-31743', + 'Float 32-bit', + '88328.01', + 'Pointer 32-bit', + '0x47AC8401', + ]; + + // Workaround for selecting text (instead of double-clicking it). + // We can use a range to specify an element. Range can be converted into + // a selection. We then check if the selected text meets our expectations. + + // Continuous part of a document, independent of any visual representation. + const range = document.createRange(); + // Represents user's highlighted text. + const selection = document.getSelection(); + + for (let i = 0; i < dataValues.length; ++i) { + if (selection === null) { + assert.fail('Selection is null'); + } + // Set range around the element. + range.selectNodeContents(dataValues[i]); + // Remove ranges associated with selection. + selection?.removeAllRanges(); + // Select element using range. + selection?.addRange(range); + + const text = window.getSelection()?.toString(); + assert.strictEqual(text, expectedValues[i]); + } + }); });