Files
react-native-devtools-frontend/front_end/elements/LayoutSidebarPane.js
T
Patrick Brosset f398c10439 Honour the showUAShadowDOM setting in the layout panel
The backend always returns all nodes, including user-agent shadow DOM.
In the elements pane, there's some filtering logic on the front-end to
only show those nodes when the showUAShadowDOM setting is ON.

This logic was not added to the layout pane when grid containers are
fetched.
This means that sites that have, say, <video> or <audio> elements will
cause a lot of grids to show up in the layout pane because <video> and
<audio> elements do use a bunch of grids in their internal DOM.

This change:
- adds the filtering logic to the layout panel
- makes the panel re-render itself when the setting changes
- adds a new e2e test to cover this logic

Bug: 1136846
Change-Id: Ic2e3fd331c275667e0b646d764e7a18c834833a5
Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2468016
Commit-Queue: Patrick Brosset <patrick.brosset@microsoft.com>
Reviewed-by: Alex Rudenko <alexrudenko@chromium.org>
Reviewed-by: Brandon Goddard <brgoddar@microsoft.com>
2020-10-14 08:07:52 +00:00

179 lines
6.1 KiB
JavaScript

// Copyright (c) 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @ts-nocheck
// TODO(crbug.com/1011811): Enable TypeScript compiler checks
import * as Common from '../common/common.js';
import * as Host from '../host/host.js';
import * as SDK from '../sdk/sdk.js';
import * as UI from '../ui/ui.js';
import {ElementsPanel} from './ElementsPanel.js';
import {createLayoutPane, LayoutElement} from './LayoutPane_bridge.js'; // eslint-disable-line no-unused-vars
/**
* @param {!Array<!SDK.DOMModel.DOMNode>} nodes
* @return {!Array<!LayoutElement>}
*/
const gridNodesToElements = nodes => {
return nodes.map(node => {
const className = node.getAttribute('class');
return {
id: node.id,
color: node.domModel().overlayModel().colorOfGridInPersistentOverlay(node.id),
name: node.localName(),
domId: node.getAttribute('id'),
domClasses: className ? className.split(/\s+/).filter(s => !!s) : null,
enabled: node.domModel().overlayModel().isHighlightedGridInPersistentOverlay(node.id),
reveal: () => {
ElementsPanel.instance().revealAndSelectNode(node, true, true);
node.scrollIntoView();
},
highlight: () => {
node.highlight();
},
hideHighlight: () => {
SDK.OverlayModel.OverlayModel.hideDOMNodeHighlight();
},
toggle: value => {
if (value) {
node.domModel().overlayModel().highlightGridInPersistentOverlay(
node.id, Host.UserMetrics.GridOverlayOpener.LayoutPane);
} else {
node.domModel().overlayModel().hideGridInPersistentOverlay(node.id);
}
},
setColor(value) {
this.color = value;
node.domModel().overlayModel().setColorOfGridInPersistentOverlay(node.id, value);
},
};
});
};
/**
* @unrestricted
*/
export class LayoutSidebarPane extends UI.ThrottledWidget.ThrottledWidget {
constructor() {
super(true /* isWebComponent */);
this._layoutPane = createLayoutPane();
this.contentElement.appendChild(this._layoutPane);
this._settings = ['showGridLineLabels', 'showGridTrackSizes', 'showGridAreas', 'extendGridLines'];
this._uaShadowDOMSetting = Common.Settings.Settings.instance().moduleSetting('showUAShadowDOM');
this._boundOnSettingChanged = this.onSettingChanged.bind(this);
this._domModels = [];
}
/**
* @param {!SDK.DOMModel.DOMModel} domModel
*/
modelAdded(domModel) {
const overlayModel = domModel.overlayModel();
overlayModel.addEventListener(SDK.OverlayModel.Events.PersistentGridOverlayCleared, this.update, this);
overlayModel.addEventListener(SDK.OverlayModel.Events.PersistentGridOverlayStateChanged, this.update, this);
this._domModels.push(domModel);
}
/**
* @param {!SDK.DOMModel.DOMModel} domModel
*/
modelRemoved(domModel) {
const overlayModel = domModel.overlayModel();
overlayModel.removeEventListener(SDK.OverlayModel.Events.PersistentGridOverlayCleared, this.update, this);
overlayModel.removeEventListener(SDK.OverlayModel.Events.PersistentGridOverlayStateChanged, this.update, this);
this._domModels = this._domModels.filter(model => model !== domModel);
}
async _fetchGridNodes() {
const showUAShadowDOM = this._uaShadowDOMSetting.get();
const nodes = [];
for (const domModel of this._domModels) {
const nodeIds = await domModel.getNodesByStyle(
[{name: 'display', value: 'grid'}, {name: 'display', value: 'inline-grid'}], true /* pierce */);
nodes.push(...nodeIds.map(id => domModel.nodeForId(id)).filter(node => {
return node && (showUAShadowDOM || !node.ancestorUserAgentShadowRoot());
}));
}
return nodes;
}
_mapSettings() {
return this._settings
.map(settingName => {
const setting = Common.Settings.Settings.instance().moduleSetting(settingName);
const ext = setting.extension();
if (!ext) {
return null;
}
const descriptor = ext.descriptor();
return {
type: descriptor.settingType,
name: descriptor.settingName,
title: ls(descriptor.title),
value: setting.get(),
options: descriptor.options.map(opt => ({
title: ls(opt.title),
value: opt.value,
}))
};
})
.filter(descriptor => descriptor !== null);
}
/**
* @override
* @protected
* @return {!Promise<void>}
*/
async doUpdate() {
this._layoutPane.data = {
gridElements: gridNodesToElements(await this._fetchGridNodes()),
settings: this._mapSettings(),
};
}
/**
* @param {*} event
*/
onSettingChanged(event) {
Common.Settings.Settings.instance().moduleSetting(event.data.setting).set(event.data.value);
}
/**
* @override
*/
wasShown() {
for (const setting of this._settings) {
Common.Settings.Settings.instance().moduleSetting(setting).addChangeListener(this.update, this);
}
this._layoutPane.addEventListener('setting-changed', this._boundOnSettingChanged);
for (const domModel of this._domModels) {
this.modelRemoved(domModel);
}
this._domModels = [];
SDK.SDKModel.TargetManager.instance().observeModels(SDK.DOMModel.DOMModel, this);
UI.Context.Context.instance().addFlavorChangeListener(SDK.DOMModel.DOMNode, this.update, this);
this._uaShadowDOMSetting.addChangeListener(this.update, this);
this.update();
}
/**
* @override
*/
willHide() {
for (const setting of this._settings) {
Common.Settings.Settings.instance().moduleSetting(setting).removeChangeListener(this.update, this);
}
this._layoutPane.removeEventListener('setting-changed', this._boundOnSettingChanged);
SDK.SDKModel.TargetManager.instance().unobserveModels(SDK.DOMModel.DOMModel, this);
UI.Context.Context.instance().removeFlavorChangeListener(SDK.DOMModel.DOMNode, this.update, this);
this._uaShadowDOMSetting.removeChangeListener(this.update, this);
}
}