mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-10-10 13:44:39 +08:00
Move and rename files. NOAUTOREVERT=true NOPRESUBMIT=true NOTREECHECKS=true Bug: 768828 TBR=darin@chromium.org NOTRY=true Change-Id: I66d3b155808bc5bdbf237b80208e1e552bcf7f28 Reviewed-on: https://chromium-review.googlesource.com/1001153 Reviewed-by: Blink Reformat <blink-reformat@chromium.org> Commit-Queue: Blink Reformat <blink-reformat@chromium.org> Cr-Original-Commit-Position: refs/heads/master@{#549061} Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src Cr-Mirrored-Commit: 0aee4434a4dba42a42abaea9bfbc0cd196a63bc1
465 lines
16 KiB
JavaScript
465 lines
16 KiB
JavaScript
// Copyright 2014 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.
|
|
/**
|
|
* @implements {SDK.SDKModelObserver<!SDK.CSSModel>}
|
|
* @unrestricted
|
|
*/
|
|
Emulation.MediaQueryInspector = class extends UI.Widget {
|
|
/**
|
|
* @param {function():number} getWidthCallback
|
|
* @param {function(number)} setWidthCallback
|
|
*/
|
|
constructor(getWidthCallback, setWidthCallback) {
|
|
super(true);
|
|
this.registerRequiredCSS('emulation/mediaQueryInspector.css');
|
|
this.contentElement.classList.add('media-inspector-view');
|
|
this.contentElement.addEventListener('click', this._onMediaQueryClicked.bind(this), false);
|
|
this.contentElement.addEventListener('contextmenu', this._onContextMenu.bind(this), false);
|
|
this._mediaThrottler = new Common.Throttler(0);
|
|
|
|
this._getWidthCallback = getWidthCallback;
|
|
this._setWidthCallback = setWidthCallback;
|
|
this._scale = 1;
|
|
|
|
SDK.targetManager.observeModels(SDK.CSSModel, this);
|
|
UI.zoomManager.addEventListener(UI.ZoomManager.Events.ZoomChanged, this._renderMediaQueries.bind(this), this);
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
* @param {!SDK.CSSModel} cssModel
|
|
*/
|
|
modelAdded(cssModel) {
|
|
// FIXME: adapt this to multiple targets.
|
|
if (this._cssModel)
|
|
return;
|
|
this._cssModel = cssModel;
|
|
this._cssModel.addEventListener(SDK.CSSModel.Events.StyleSheetAdded, this._scheduleMediaQueriesUpdate, this);
|
|
this._cssModel.addEventListener(SDK.CSSModel.Events.StyleSheetRemoved, this._scheduleMediaQueriesUpdate, this);
|
|
this._cssModel.addEventListener(SDK.CSSModel.Events.StyleSheetChanged, this._scheduleMediaQueriesUpdate, this);
|
|
this._cssModel.addEventListener(
|
|
SDK.CSSModel.Events.MediaQueryResultChanged, this._scheduleMediaQueriesUpdate, this);
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
* @param {!SDK.CSSModel} cssModel
|
|
*/
|
|
modelRemoved(cssModel) {
|
|
if (cssModel !== this._cssModel)
|
|
return;
|
|
this._cssModel.removeEventListener(SDK.CSSModel.Events.StyleSheetAdded, this._scheduleMediaQueriesUpdate, this);
|
|
this._cssModel.removeEventListener(SDK.CSSModel.Events.StyleSheetRemoved, this._scheduleMediaQueriesUpdate, this);
|
|
this._cssModel.removeEventListener(SDK.CSSModel.Events.StyleSheetChanged, this._scheduleMediaQueriesUpdate, this);
|
|
this._cssModel.removeEventListener(
|
|
SDK.CSSModel.Events.MediaQueryResultChanged, this._scheduleMediaQueriesUpdate, this);
|
|
delete this._cssModel;
|
|
}
|
|
|
|
/**
|
|
* @param {number} scale
|
|
*/
|
|
setAxisTransform(scale) {
|
|
if (Math.abs(this._scale - scale) < 1e-8)
|
|
return;
|
|
this._scale = scale;
|
|
this._renderMediaQueries();
|
|
}
|
|
|
|
/**
|
|
* @param {!Event} event
|
|
*/
|
|
_onMediaQueryClicked(event) {
|
|
const mediaQueryMarker = event.target.enclosingNodeOrSelfWithClass('media-inspector-bar');
|
|
if (!mediaQueryMarker)
|
|
return;
|
|
|
|
const model = mediaQueryMarker._model;
|
|
if (model.section() === Emulation.MediaQueryInspector.Section.Max) {
|
|
this._setWidthCallback(model.maxWidthExpression().computedLength());
|
|
return;
|
|
}
|
|
if (model.section() === Emulation.MediaQueryInspector.Section.Min) {
|
|
this._setWidthCallback(model.minWidthExpression().computedLength());
|
|
return;
|
|
}
|
|
const currentWidth = this._getWidthCallback();
|
|
if (currentWidth !== model.minWidthExpression().computedLength())
|
|
this._setWidthCallback(model.minWidthExpression().computedLength());
|
|
else
|
|
this._setWidthCallback(model.maxWidthExpression().computedLength());
|
|
}
|
|
|
|
/**
|
|
* @param {!Event} event
|
|
*/
|
|
_onContextMenu(event) {
|
|
if (!this._cssModel || !this._cssModel.isEnabled())
|
|
return;
|
|
|
|
const mediaQueryMarker = event.target.enclosingNodeOrSelfWithClass('media-inspector-bar');
|
|
if (!mediaQueryMarker)
|
|
return;
|
|
|
|
const locations = mediaQueryMarker._locations;
|
|
const uiLocations = new Map();
|
|
for (let i = 0; i < locations.length; ++i) {
|
|
const uiLocation = Bindings.cssWorkspaceBinding.rawLocationToUILocation(locations[i]);
|
|
if (!uiLocation)
|
|
continue;
|
|
const descriptor = String.sprintf(
|
|
'%s:%d:%d', uiLocation.uiSourceCode.url(), uiLocation.lineNumber + 1, uiLocation.columnNumber + 1);
|
|
uiLocations.set(descriptor, uiLocation);
|
|
}
|
|
|
|
const contextMenuItems = uiLocations.keysArray().sort();
|
|
const contextMenu = new UI.ContextMenu(event);
|
|
const subMenuItem = contextMenu.defaultSection().appendSubMenuItem(Common.UIString('Reveal in source code'));
|
|
for (let i = 0; i < contextMenuItems.length; ++i) {
|
|
const title = contextMenuItems[i];
|
|
subMenuItem.defaultSection().appendItem(
|
|
title, this._revealSourceLocation.bind(this, /** @type {!Workspace.UILocation} */ (uiLocations.get(title))));
|
|
}
|
|
contextMenu.show();
|
|
}
|
|
|
|
/**
|
|
* @param {!Workspace.UILocation} location
|
|
*/
|
|
_revealSourceLocation(location) {
|
|
Common.Revealer.reveal(location);
|
|
}
|
|
|
|
_scheduleMediaQueriesUpdate() {
|
|
if (!this.isShowing())
|
|
return;
|
|
this._mediaThrottler.schedule(this._refetchMediaQueries.bind(this));
|
|
}
|
|
|
|
_refetchMediaQueries() {
|
|
if (!this.isShowing() || !this._cssModel)
|
|
return Promise.resolve();
|
|
|
|
return this._cssModel.mediaQueriesPromise().then(this._rebuildMediaQueries.bind(this));
|
|
}
|
|
|
|
/**
|
|
* @param {!Array.<!Emulation.MediaQueryInspector.MediaQueryUIModel>} models
|
|
* @return {!Array.<!Emulation.MediaQueryInspector.MediaQueryUIModel>}
|
|
*/
|
|
_squashAdjacentEqual(models) {
|
|
const filtered = [];
|
|
for (let i = 0; i < models.length; ++i) {
|
|
const last = filtered.peekLast();
|
|
if (!last || !last.equals(models[i]))
|
|
filtered.push(models[i]);
|
|
}
|
|
return filtered;
|
|
}
|
|
|
|
/**
|
|
* @param {!Array.<!SDK.CSSMedia>} cssMedias
|
|
*/
|
|
_rebuildMediaQueries(cssMedias) {
|
|
let queryModels = [];
|
|
for (let i = 0; i < cssMedias.length; ++i) {
|
|
const cssMedia = cssMedias[i];
|
|
if (!cssMedia.mediaList)
|
|
continue;
|
|
for (let j = 0; j < cssMedia.mediaList.length; ++j) {
|
|
const mediaQuery = cssMedia.mediaList[j];
|
|
const queryModel = Emulation.MediaQueryInspector.MediaQueryUIModel.createFromMediaQuery(cssMedia, mediaQuery);
|
|
if (queryModel && queryModel.rawLocation())
|
|
queryModels.push(queryModel);
|
|
}
|
|
}
|
|
queryModels.sort(compareModels);
|
|
queryModels = this._squashAdjacentEqual(queryModels);
|
|
|
|
let allEqual = this._cachedQueryModels && this._cachedQueryModels.length === queryModels.length;
|
|
for (let i = 0; allEqual && i < queryModels.length; ++i)
|
|
allEqual = allEqual && this._cachedQueryModels[i].equals(queryModels[i]);
|
|
if (allEqual)
|
|
return;
|
|
this._cachedQueryModels = queryModels;
|
|
this._renderMediaQueries();
|
|
|
|
/**
|
|
* @param {!Emulation.MediaQueryInspector.MediaQueryUIModel} model1
|
|
* @param {!Emulation.MediaQueryInspector.MediaQueryUIModel} model2
|
|
* @return {number}
|
|
*/
|
|
function compareModels(model1, model2) {
|
|
return model1.compareTo(model2);
|
|
}
|
|
}
|
|
|
|
_renderMediaQueries() {
|
|
if (!this._cachedQueryModels || !this.isShowing())
|
|
return;
|
|
|
|
const markers = [];
|
|
let lastMarker = null;
|
|
for (let i = 0; i < this._cachedQueryModels.length; ++i) {
|
|
const model = this._cachedQueryModels[i];
|
|
if (lastMarker && lastMarker.model.dimensionsEqual(model)) {
|
|
lastMarker.locations.push(model.rawLocation());
|
|
lastMarker.active = lastMarker.active || model.active();
|
|
} else {
|
|
lastMarker = {active: model.active(), model: model, locations: [model.rawLocation()]};
|
|
markers.push(lastMarker);
|
|
}
|
|
}
|
|
|
|
this.contentElement.removeChildren();
|
|
|
|
let container = null;
|
|
for (let i = 0; i < markers.length; ++i) {
|
|
if (!i || markers[i].model.section() !== markers[i - 1].model.section())
|
|
container = this.contentElement.createChild('div', 'media-inspector-marker-container');
|
|
const marker = markers[i];
|
|
const bar = this._createElementFromMediaQueryModel(marker.model);
|
|
bar._model = marker.model;
|
|
bar._locations = marker.locations;
|
|
bar.classList.toggle('media-inspector-marker-inactive', !marker.active);
|
|
container.appendChild(bar);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return {number}
|
|
*/
|
|
_zoomFactor() {
|
|
return UI.zoomManager.zoomFactor() / this._scale;
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
*/
|
|
wasShown() {
|
|
this._scheduleMediaQueriesUpdate();
|
|
}
|
|
|
|
/**
|
|
* @param {!Emulation.MediaQueryInspector.MediaQueryUIModel} model
|
|
* @return {!Element}
|
|
*/
|
|
_createElementFromMediaQueryModel(model) {
|
|
const zoomFactor = this._zoomFactor();
|
|
const minWidthValue = model.minWidthExpression() ? model.minWidthExpression().computedLength() / zoomFactor : 0;
|
|
const maxWidthValue = model.maxWidthExpression() ? model.maxWidthExpression().computedLength() / zoomFactor : 0;
|
|
const result = createElementWithClass('div', 'media-inspector-bar');
|
|
|
|
if (model.section() === Emulation.MediaQueryInspector.Section.Max) {
|
|
result.createChild('div', 'media-inspector-marker-spacer');
|
|
const markerElement = result.createChild('div', 'media-inspector-marker media-inspector-marker-max-width');
|
|
markerElement.style.width = maxWidthValue + 'px';
|
|
markerElement.title = model.mediaText();
|
|
appendLabel(markerElement, model.maxWidthExpression(), false, false);
|
|
appendLabel(markerElement, model.maxWidthExpression(), true, true);
|
|
result.createChild('div', 'media-inspector-marker-spacer');
|
|
}
|
|
|
|
if (model.section() === Emulation.MediaQueryInspector.Section.MinMax) {
|
|
result.createChild('div', 'media-inspector-marker-spacer');
|
|
const leftElement = result.createChild('div', 'media-inspector-marker media-inspector-marker-min-max-width');
|
|
leftElement.style.width = (maxWidthValue - minWidthValue) * 0.5 + 'px';
|
|
leftElement.title = model.mediaText();
|
|
appendLabel(leftElement, model.minWidthExpression(), true, false);
|
|
appendLabel(leftElement, model.maxWidthExpression(), false, true);
|
|
result.createChild('div', 'media-inspector-marker-spacer').style.flex = '0 0 ' + minWidthValue + 'px';
|
|
const rightElement = result.createChild('div', 'media-inspector-marker media-inspector-marker-min-max-width');
|
|
rightElement.style.width = (maxWidthValue - minWidthValue) * 0.5 + 'px';
|
|
rightElement.title = model.mediaText();
|
|
appendLabel(rightElement, model.minWidthExpression(), true, false);
|
|
appendLabel(rightElement, model.maxWidthExpression(), false, true);
|
|
result.createChild('div', 'media-inspector-marker-spacer');
|
|
}
|
|
|
|
if (model.section() === Emulation.MediaQueryInspector.Section.Min) {
|
|
const leftElement = result.createChild(
|
|
'div', 'media-inspector-marker media-inspector-marker-min-width media-inspector-marker-min-width-left');
|
|
leftElement.title = model.mediaText();
|
|
appendLabel(leftElement, model.minWidthExpression(), false, false);
|
|
result.createChild('div', 'media-inspector-marker-spacer').style.flex = '0 0 ' + minWidthValue + 'px';
|
|
const rightElement = result.createChild(
|
|
'div', 'media-inspector-marker media-inspector-marker-min-width media-inspector-marker-min-width-right');
|
|
rightElement.title = model.mediaText();
|
|
appendLabel(rightElement, model.minWidthExpression(), true, true);
|
|
}
|
|
|
|
function appendLabel(marker, expression, atLeft, leftAlign) {
|
|
marker
|
|
.createChild(
|
|
'div',
|
|
'media-inspector-marker-label-container ' + (atLeft ? 'media-inspector-marker-label-container-left' :
|
|
'media-inspector-marker-label-container-right'))
|
|
.createChild(
|
|
'span', 'media-inspector-marker-label ' +
|
|
(leftAlign ? 'media-inspector-label-left' : 'media-inspector-label-right'))
|
|
.textContent = expression.value() + expression.unit();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @enum {number}
|
|
*/
|
|
Emulation.MediaQueryInspector.Section = {
|
|
Max: 0,
|
|
MinMax: 1,
|
|
Min: 2
|
|
};
|
|
|
|
/**
|
|
* @unrestricted
|
|
*/
|
|
Emulation.MediaQueryInspector.MediaQueryUIModel = class {
|
|
/**
|
|
* @param {!SDK.CSSMedia} cssMedia
|
|
* @param {?SDK.CSSMediaQueryExpression} minWidthExpression
|
|
* @param {?SDK.CSSMediaQueryExpression} maxWidthExpression
|
|
* @param {boolean} active
|
|
*/
|
|
constructor(cssMedia, minWidthExpression, maxWidthExpression, active) {
|
|
this._cssMedia = cssMedia;
|
|
this._minWidthExpression = minWidthExpression;
|
|
this._maxWidthExpression = maxWidthExpression;
|
|
this._active = active;
|
|
if (maxWidthExpression && !minWidthExpression)
|
|
this._section = Emulation.MediaQueryInspector.Section.Max;
|
|
else if (minWidthExpression && maxWidthExpression)
|
|
this._section = Emulation.MediaQueryInspector.Section.MinMax;
|
|
else
|
|
this._section = Emulation.MediaQueryInspector.Section.Min;
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.CSSMedia} cssMedia
|
|
* @param {!SDK.CSSMediaQuery} mediaQuery
|
|
* @return {?Emulation.MediaQueryInspector.MediaQueryUIModel}
|
|
*/
|
|
static createFromMediaQuery(cssMedia, mediaQuery) {
|
|
let maxWidthExpression = null;
|
|
let maxWidthPixels = Number.MAX_VALUE;
|
|
let minWidthExpression = null;
|
|
let minWidthPixels = Number.MIN_VALUE;
|
|
const expressions = mediaQuery.expressions();
|
|
for (let i = 0; i < expressions.length; ++i) {
|
|
const expression = expressions[i];
|
|
const feature = expression.feature();
|
|
if (feature.indexOf('width') === -1)
|
|
continue;
|
|
const pixels = expression.computedLength();
|
|
if (feature.startsWith('max-') && pixels < maxWidthPixels) {
|
|
maxWidthExpression = expression;
|
|
maxWidthPixels = pixels;
|
|
} else if (feature.startsWith('min-') && pixels > minWidthPixels) {
|
|
minWidthExpression = expression;
|
|
minWidthPixels = pixels;
|
|
}
|
|
}
|
|
if (minWidthPixels > maxWidthPixels || (!maxWidthExpression && !minWidthExpression))
|
|
return null;
|
|
|
|
return new Emulation.MediaQueryInspector.MediaQueryUIModel(
|
|
cssMedia, minWidthExpression, maxWidthExpression, mediaQuery.active());
|
|
}
|
|
|
|
/**
|
|
* @param {!Emulation.MediaQueryInspector.MediaQueryUIModel} other
|
|
* @return {boolean}
|
|
*/
|
|
equals(other) {
|
|
return this.compareTo(other) === 0;
|
|
}
|
|
|
|
/**
|
|
* @param {!Emulation.MediaQueryInspector.MediaQueryUIModel} other
|
|
* @return {boolean}
|
|
*/
|
|
dimensionsEqual(other) {
|
|
return this.section() === other.section() &&
|
|
(!this.minWidthExpression() ||
|
|
(this.minWidthExpression().computedLength() === other.minWidthExpression().computedLength())) &&
|
|
(!this.maxWidthExpression() ||
|
|
(this.maxWidthExpression().computedLength() === other.maxWidthExpression().computedLength()));
|
|
}
|
|
|
|
/**
|
|
* @param {!Emulation.MediaQueryInspector.MediaQueryUIModel} other
|
|
* @return {number}
|
|
*/
|
|
compareTo(other) {
|
|
if (this.section() !== other.section())
|
|
return this.section() - other.section();
|
|
if (this.dimensionsEqual(other)) {
|
|
const myLocation = this.rawLocation();
|
|
const otherLocation = other.rawLocation();
|
|
if (!myLocation && !otherLocation)
|
|
return this.mediaText().compareTo(other.mediaText());
|
|
if (myLocation && !otherLocation)
|
|
return 1;
|
|
if (!myLocation && otherLocation)
|
|
return -1;
|
|
if (this.active() !== other.active())
|
|
return this.active() ? -1 : 1;
|
|
return myLocation.url.compareTo(otherLocation.url) || myLocation.lineNumber - otherLocation.lineNumber ||
|
|
myLocation.columnNumber - otherLocation.columnNumber;
|
|
}
|
|
if (this.section() === Emulation.MediaQueryInspector.Section.Max)
|
|
return other.maxWidthExpression().computedLength() - this.maxWidthExpression().computedLength();
|
|
if (this.section() === Emulation.MediaQueryInspector.Section.Min)
|
|
return this.minWidthExpression().computedLength() - other.minWidthExpression().computedLength();
|
|
return this.minWidthExpression().computedLength() - other.minWidthExpression().computedLength() ||
|
|
other.maxWidthExpression().computedLength() - this.maxWidthExpression().computedLength();
|
|
}
|
|
|
|
/**
|
|
* @return {!Emulation.MediaQueryInspector.Section}
|
|
*/
|
|
section() {
|
|
return this._section;
|
|
}
|
|
|
|
/**
|
|
* @return {string}
|
|
*/
|
|
mediaText() {
|
|
return this._cssMedia.text;
|
|
}
|
|
|
|
/**
|
|
* @return {?SDK.CSSLocation}
|
|
*/
|
|
rawLocation() {
|
|
if (!this._rawLocation)
|
|
this._rawLocation = this._cssMedia.rawLocation();
|
|
return this._rawLocation;
|
|
}
|
|
|
|
/**
|
|
* @return {?SDK.CSSMediaQueryExpression}
|
|
*/
|
|
minWidthExpression() {
|
|
return this._minWidthExpression;
|
|
}
|
|
|
|
/**
|
|
* @return {?SDK.CSSMediaQueryExpression}
|
|
*/
|
|
maxWidthExpression() {
|
|
return this._maxWidthExpression;
|
|
}
|
|
|
|
/**
|
|
* @return {boolean}
|
|
*/
|
|
active() {
|
|
return this._active;
|
|
}
|
|
};
|