Commit 62b32f4e authored by Anubha Mathur's avatar Anubha Mathur Committed by Commit Bot

DevTools: Add context menu to computed styles tab to navigate to styles source

This PR enables navigation to the source of the computed styles using keyboard.

1. Adds a context menu item to navigate to the style in styles pane
2. Adds a context menu item to navigate to the style source in the sources panel

Gif: https://imgur.com/a/E2KGmrv

Bug: 963183
Change-Id: Iea561af075fcb89b0cd5ebe7a63fb8afbdb2aa05
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1825790Reviewed-by: default avatarLorne Mitchell <lomitch@microsoft.com>
Commit-Queue: Anubha Mathur <anubmat@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#704362}
parent 2218d9e7
...@@ -224,6 +224,8 @@ Elements.ComputedStyleWidget = class extends UI.ThrottledWidget { ...@@ -224,6 +224,8 @@ Elements.ComputedStyleWidget = class extends UI.ThrottledWidget {
treeElement.listItemElement.addEventListener('mousedown', e => e.consume(), false); treeElement.listItemElement.addEventListener('mousedown', e => e.consume(), false);
treeElement.listItemElement.addEventListener('dblclick', e => e.consume(), false); treeElement.listItemElement.addEventListener('dblclick', e => e.consume(), false);
treeElement.listItemElement.addEventListener('click', handleClick.bind(null, treeElement), false); treeElement.listItemElement.addEventListener('click', handleClick.bind(null, treeElement), false);
treeElement.listItemElement.addEventListener(
'contextmenu', this._handleContextMenuEvent.bind(this, matchedStyles, activeProperty));
const gotoSourceElement = UI.Icon.create('mediumicon-arrow-in-circle', 'goto-source-icon'); const gotoSourceElement = UI.Icon.create('mediumicon-arrow-in-circle', 'goto-source-icon');
gotoSourceElement.addEventListener('click', this._navigateToSource.bind(this, activeProperty)); gotoSourceElement.addEventListener('click', this._navigateToSource.bind(this, activeProperty));
propertyValueElement.appendChild(gotoSourceElement); propertyValueElement.appendChild(gotoSourceElement);
...@@ -319,11 +321,36 @@ Elements.ComputedStyleWidget = class extends UI.ThrottledWidget { ...@@ -319,11 +321,36 @@ Elements.ComputedStyleWidget = class extends UI.ThrottledWidget {
const traceTreeElement = new UI.TreeElement(); const traceTreeElement = new UI.TreeElement();
traceTreeElement.title = trace; traceTreeElement.title = trace;
traceTreeElement.listItemElement.addEventListener(
'contextmenu', this._handleContextMenuEvent.bind(this, matchedStyles, property));
rootTreeElement.appendChild(traceTreeElement); rootTreeElement.appendChild(traceTreeElement);
} }
return /** @type {!SDK.CSSProperty} */ (activeProperty); return /** @type {!SDK.CSSProperty} */ (activeProperty);
} }
/**
* @param {!SDK.CSSMatchedStyles} matchedStyles
* @param {!SDK.CSSProperty} property
* @param {!Event} event
*/
_handleContextMenuEvent(matchedStyles, property, event) {
const contextMenu = new UI.ContextMenu(event);
const rule = property.ownerStyle.parentRule;
if (rule) {
const header = rule.styleSheetId ? matchedStyles.cssModel().styleSheetHeaderForId(rule.styleSheetId) : null;
if (header && !header.isAnonymousInlineStyleSheet()) {
contextMenu.defaultSection().appendItem(ls`Navigate to selector source`, () => {
Elements.StylePropertiesSection.tryNavigateToRuleLocation(matchedStyles, rule);
});
}
}
contextMenu.defaultSection().appendItem(ls`Navigate to style`, () => Common.Revealer.reveal(property));
contextMenu.show();
}
/** /**
* @param {!SDK.CSSMatchedStyles} matchedStyles * @param {!SDK.CSSMatchedStyles} matchedStyles
* @return {!Map<string, !Array<!SDK.CSSProperty>>} * @return {!Map<string, !Array<!SDK.CSSProperty>>}
......
...@@ -979,12 +979,7 @@ Elements.StylePropertiesSection = class { ...@@ -979,12 +979,7 @@ Elements.StylePropertiesSection = class {
return createTextNode(''); return createTextNode('');
} }
let ruleLocation; const ruleLocation = this._getRuleLocationFromCSSRule(rule);
if (rule instanceof SDK.CSSStyleRule) {
ruleLocation = rule.style.range;
} else if (rule instanceof SDK.CSSKeyframeRule) {
ruleLocation = rule.key().range;
}
const header = rule.styleSheetId ? matchedStyles.cssModel().styleSheetHeaderForId(rule.styleSheetId) : null; const header = rule.styleSheetId ? matchedStyles.cssModel().styleSheetHeaderForId(rule.styleSheetId) : null;
if (ruleLocation && rule.styleSheetId && header && !header.isAnonymousInlineStyleSheet()) { if (ruleLocation && rule.styleSheetId && header && !header.isAnonymousInlineStyleSheet()) {
...@@ -1011,6 +1006,39 @@ Elements.StylePropertiesSection = class { ...@@ -1011,6 +1006,39 @@ Elements.StylePropertiesSection = class {
return createTextNode(''); return createTextNode('');
} }
/**
* @param {!SDK.CSSRule} rule
* @return {?TextUtils.TextRange}
*/
static _getRuleLocationFromCSSRule(rule) {
let ruleLocation = null;
if (rule instanceof SDK.CSSStyleRule) {
ruleLocation = rule.style.range;
} else if (rule instanceof SDK.CSSKeyframeRule) {
ruleLocation = rule.key().range;
}
return ruleLocation;
}
/**
* @param {!SDK.CSSMatchedStyles} matchedStyles
* @param {?SDK.CSSRule} rule
*/
static tryNavigateToRuleLocation(matchedStyles, rule) {
if (!rule) {
return;
}
const ruleLocation = this._getRuleLocationFromCSSRule(rule);
const header = rule.styleSheetId ? matchedStyles.cssModel().styleSheetHeaderForId(rule.styleSheetId) : null;
if (ruleLocation && rule.styleSheetId && header && !header.isAnonymousInlineStyleSheet()) {
const matchingSelectorLocation =
this._getCSSSelectorLocation(matchedStyles.cssModel(), rule.styleSheetId, ruleLocation);
this._revealSelectorSource(matchingSelectorLocation, true);
}
}
/** /**
* @param {!SDK.CSSModel} cssModel * @param {!SDK.CSSModel} cssModel
* @param {!Components.Linkifier} linkifier * @param {!Components.Linkifier} linkifier
...@@ -1019,11 +1047,21 @@ Elements.StylePropertiesSection = class { ...@@ -1019,11 +1047,21 @@ Elements.StylePropertiesSection = class {
* @return {!Node} * @return {!Node}
*/ */
static _linkifyRuleLocation(cssModel, linkifier, styleSheetId, ruleLocation) { static _linkifyRuleLocation(cssModel, linkifier, styleSheetId, ruleLocation) {
const matchingSelectorLocation = this._getCSSSelectorLocation(cssModel, styleSheetId, ruleLocation);
return linkifier.linkifyCSSLocation(matchingSelectorLocation);
}
/**
* @param {!SDK.CSSModel} cssModel
* @param {string} styleSheetId
* @param {!TextUtils.TextRange} ruleLocation
* @return {!SDK.CSSLocation}
*/
static _getCSSSelectorLocation(cssModel, styleSheetId, ruleLocation) {
const styleSheetHeader = cssModel.styleSheetHeaderForId(styleSheetId); const styleSheetHeader = cssModel.styleSheetHeaderForId(styleSheetId);
const lineNumber = styleSheetHeader.lineNumberInSource(ruleLocation.startLine); const lineNumber = styleSheetHeader.lineNumberInSource(ruleLocation.startLine);
const columnNumber = styleSheetHeader.columnNumberInSource(ruleLocation.startLine, ruleLocation.startColumn); const columnNumber = styleSheetHeader.columnNumberInSource(ruleLocation.startLine, ruleLocation.startColumn);
const matchingSelectorLocation = new SDK.CSSLocation(styleSheetHeader, lineNumber, columnNumber); return new SDK.CSSLocation(styleSheetHeader, lineNumber, columnNumber);
return linkifier.linkifyCSSLocation(matchingSelectorLocation);
} }
/** /**
...@@ -1828,6 +1866,14 @@ Elements.StylePropertiesSection = class { ...@@ -1828,6 +1866,14 @@ Elements.StylePropertiesSection = class {
return; return;
} }
const rawLocation = new SDK.CSSLocation(header, rule.lineNumberInSource(index), rule.columnNumberInSource(index)); const rawLocation = new SDK.CSSLocation(header, rule.lineNumberInSource(index), rule.columnNumberInSource(index));
Elements.StylePropertiesSection._revealSelectorSource(rawLocation, focus);
}
/**
* @param {!SDK.CSSLocation} rawLocation
* @param {boolean} focus
*/
static _revealSelectorSource(rawLocation, focus) {
const uiLocation = Bindings.cssWorkspaceBinding.rawLocationToUILocation(rawLocation); const uiLocation = Bindings.cssWorkspaceBinding.rawLocationToUILocation(rawLocation);
if (uiLocation) { if (uiLocation) {
Common.Revealer.reveal(uiLocation, !focus); Common.Revealer.reveal(uiLocation, !focus);
......
...@@ -42,6 +42,9 @@ ...@@ -42,6 +42,9 @@
<message name="IDS_DEVTOOLS_26396cca3eae28fc6e39123edb2f1510" desc="Context menu item for style property in edit mode"> <message name="IDS_DEVTOOLS_26396cca3eae28fc6e39123edb2f1510" desc="Context menu item for style property in edit mode">
Reveal in Sources panel Reveal in Sources panel
</message> </message>
<message name="IDS_DEVTOOLS_2864a2d95e17e3032a3a6e69a51d0256" desc="Context menu item in Elements panel to navigate to style in styles pane">
Navigate to style
</message>
<message name="IDS_DEVTOOLS_28c0a3050f784583d52339ddb0bc1274" desc="Text in Elements Tree Element of the Elements panel"> <message name="IDS_DEVTOOLS_28c0a3050f784583d52339ddb0bc1274" desc="Text in Elements Tree Element of the Elements panel">
Paste element Paste element
</message> </message>
...@@ -183,6 +186,9 @@ ...@@ -183,6 +186,9 @@
<message name="IDS_DEVTOOLS_91d17eff6a1529e8a4f67a5aa4cabec7" desc="No matches element text content in Computed Style Widget of the Elements panel"> <message name="IDS_DEVTOOLS_91d17eff6a1529e8a4f67a5aa4cabec7" desc="No matches element text content in Computed Style Widget of the Elements panel">
No matching property No matching property
</message> </message>
<message name="IDS_DEVTOOLS_924843c5b24bc69ed7da2bff699de4f1" desc="Context menu item in Elements panel to navigate to css selector source">
Navigate to selector source
</message>
<message name="IDS_DEVTOOLS_97d28fbc573f9f331f5b1886b5c1eeef" desc="Text in Elements Tree Element of the Elements panel"> <message name="IDS_DEVTOOLS_97d28fbc573f9f331f5b1886b5c1eeef" desc="Text in Elements Tree Element of the Elements panel">
Copy full XPath Copy full XPath
</message> </message>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment