mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-10-10 13:44:39 +08:00
The logic for checking if two media queries were the same was broken, meaning that we didn't render as many as we should. See bug for screenshot and more details. Fixed: chromium:1174885 Change-Id: Iead0095d601a4491578fcd7985274d0331e08a23 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2678086 Commit-Queue: Jack Franklin <jacktfranklin@chromium.org> Commit-Queue: Tim van der Lippe <tvanderlippe@chromium.org> Auto-Submit: Jack Franklin <jacktfranklin@chromium.org> Reviewed-by: Tim van der Lippe <tvanderlippe@chromium.org>
567 lines
19 KiB
JavaScript
567 lines
19 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.
|
|
|
|
import * as Bindings from '../bindings/bindings.js';
|
|
import * as Common from '../common/common.js';
|
|
import * as Platform from '../platform/platform.js';
|
|
import * as SDK from '../sdk/sdk.js';
|
|
import * as UI from '../ui/ui.js';
|
|
import * as Workspace from '../workspace/workspace.js'; // eslint-disable-line no-unused-vars
|
|
|
|
/**
|
|
* @implements {SDK.SDKModel.SDKModelObserver<!SDK.CSSModel.CSSModel>}
|
|
*/
|
|
export class MediaQueryInspector extends UI.Widget.Widget {
|
|
/**
|
|
* @param {function():number} getWidthCallback
|
|
* @param {function(number):void} setWidthCallback
|
|
*/
|
|
constructor(getWidthCallback, setWidthCallback) {
|
|
super(true);
|
|
this.registerRequiredCSS('emulation/mediaQueryInspector.css', {enableLegacyPatching: true});
|
|
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.Throttler(0);
|
|
|
|
this._getWidthCallback = getWidthCallback;
|
|
this._setWidthCallback = setWidthCallback;
|
|
this._scale = 1;
|
|
|
|
/** @type {!WeakMap<!Element, !MediaQueryUIModel>} */
|
|
this.elementsToMediaQueryModel = new WeakMap();
|
|
/** @type {!WeakMap<!Element, !Array<!SDK.CSSModel.CSSLocation>>} */
|
|
this.elementsToCSSLocations = new WeakMap();
|
|
|
|
SDK.SDKModel.TargetManager.instance().observeModels(SDK.CSSModel.CSSModel, this);
|
|
UI.ZoomManager.ZoomManager.instance().addEventListener(
|
|
UI.ZoomManager.Events.ZoomChanged, this._renderMediaQueries.bind(this), this);
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
* @param {!SDK.CSSModel.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} 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 = /** @type {!Node} */ (event.target).enclosingNodeOrSelfWithClass('media-inspector-bar');
|
|
if (!mediaQueryMarker) {
|
|
return;
|
|
}
|
|
|
|
const model = this.elementsToMediaQueryModel.get(mediaQueryMarker);
|
|
if (!model) {
|
|
return;
|
|
}
|
|
const modelMaxWidth = model.maxWidthExpression();
|
|
const modelMinWidth = model.minWidthExpression();
|
|
|
|
if (model.section() === Section.Max) {
|
|
this._setWidthCallback(modelMaxWidth ? modelMaxWidth.computedLength() || 0 : 0);
|
|
return;
|
|
}
|
|
if (model.section() === Section.Min) {
|
|
this._setWidthCallback(modelMinWidth ? modelMinWidth.computedLength() || 0 : 0);
|
|
return;
|
|
}
|
|
const currentWidth = this._getWidthCallback();
|
|
if (modelMinWidth && currentWidth !== modelMinWidth.computedLength()) {
|
|
this._setWidthCallback(modelMinWidth.computedLength() || 0);
|
|
} else {
|
|
this._setWidthCallback(modelMaxWidth ? modelMaxWidth.computedLength() || 0 : 0);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {!Event} event
|
|
*/
|
|
_onContextMenu(event) {
|
|
if (!this._cssModel || !this._cssModel.isEnabled()) {
|
|
return;
|
|
}
|
|
|
|
const mediaQueryMarker = /** @type {!Node} */ (event.target).enclosingNodeOrSelfWithClass('media-inspector-bar');
|
|
if (!mediaQueryMarker) {
|
|
return;
|
|
}
|
|
|
|
const locations = this.elementsToCSSLocations.get(mediaQueryMarker) || [];
|
|
const uiLocations = new Map();
|
|
for (let i = 0; i < locations.length; ++i) {
|
|
const uiLocation =
|
|
Bindings.CSSWorkspaceBinding.CSSWorkspaceBinding.instance().rawLocationToUILocation(locations[i]);
|
|
if (!uiLocation) {
|
|
continue;
|
|
}
|
|
const descriptor = typeof uiLocation.columnNumber === 'number' ?
|
|
Platform.StringUtilities.sprintf(
|
|
'%s:%d:%d', uiLocation.uiSourceCode.url(), uiLocation.lineNumber + 1, uiLocation.columnNumber + 1) :
|
|
Platform.StringUtilities.sprintf('%s:%d', uiLocation.uiSourceCode.url(), uiLocation.lineNumber + 1);
|
|
uiLocations.set(descriptor, uiLocation);
|
|
}
|
|
|
|
const contextMenuItems = [...uiLocations.keys()].sort();
|
|
const contextMenu = new UI.ContextMenu.ContextMenu(event);
|
|
const subMenuItem =
|
|
contextMenu.defaultSection().appendSubMenuItem(Common.UIString.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.UISourceCode.UILocation} */ (uiLocations.get(title))));
|
|
}
|
|
contextMenu.show();
|
|
}
|
|
|
|
/**
|
|
* @param {!Workspace.UISourceCode.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.<!MediaQueryUIModel>} models
|
|
* @return {!Array.<!MediaQueryUIModel>}
|
|
*/
|
|
_squashAdjacentEqual(models) {
|
|
const filtered = [];
|
|
for (let i = 0; i < models.length; ++i) {
|
|
const last = filtered[filtered.length - 1];
|
|
if (!last || !last.equals(models[i])) {
|
|
filtered.push(models[i]);
|
|
}
|
|
}
|
|
return filtered;
|
|
}
|
|
|
|
/**
|
|
* @param {!Array.<!SDK.CSSMedia.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 = MediaQueryUIModel.createFromMediaQuery(cssMedia, mediaQuery);
|
|
if (queryModel) {
|
|
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 && this._cachedQueryModels[i].equals(queryModels[i]);
|
|
}
|
|
if (allEqual) {
|
|
return;
|
|
}
|
|
this._cachedQueryModels = queryModels;
|
|
this._renderMediaQueries();
|
|
|
|
/**
|
|
* @param {!MediaQueryUIModel} model1
|
|
* @param {!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.active = lastMarker.active || model.active();
|
|
} else {
|
|
lastMarker = {
|
|
active: model.active(),
|
|
model,
|
|
locations: /** @type {!Array<!SDK.CSSModel.CSSLocation>} */ ([]),
|
|
};
|
|
markers.push(lastMarker);
|
|
}
|
|
const rawLocation = model.rawLocation();
|
|
if (rawLocation) {
|
|
lastMarker.locations.push(rawLocation);
|
|
}
|
|
}
|
|
|
|
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);
|
|
this.elementsToMediaQueryModel.set(bar, marker.model);
|
|
this.elementsToCSSLocations.set(bar, marker.locations);
|
|
bar.classList.toggle('media-inspector-marker-inactive', !marker.active);
|
|
if (!container) {
|
|
throw new Error('Could not find container to render media queries into.');
|
|
}
|
|
container.appendChild(bar);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return {number}
|
|
*/
|
|
_zoomFactor() {
|
|
return UI.ZoomManager.ZoomManager.instance().zoomFactor() / this._scale;
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
*/
|
|
wasShown() {
|
|
this._scheduleMediaQueriesUpdate();
|
|
}
|
|
|
|
/**
|
|
* @param {!MediaQueryUIModel} model
|
|
* @return {!Element}
|
|
*/
|
|
_createElementFromMediaQueryModel(model) {
|
|
const zoomFactor = this._zoomFactor();
|
|
const minWidthExpression = model.minWidthExpression();
|
|
const maxWidthExpression = model.maxWidthExpression();
|
|
const minWidthValue = minWidthExpression ? (minWidthExpression.computedLength() || 0) / zoomFactor : 0;
|
|
const maxWidthValue = maxWidthExpression ? (maxWidthExpression.computedLength() || 0) / zoomFactor : 0;
|
|
const result = document.createElement('div');
|
|
result.classList.add('media-inspector-bar');
|
|
|
|
if (model.section() === 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';
|
|
UI.Tooltip.Tooltip.install(markerElement, model.mediaText());
|
|
appendLabel(markerElement, model.maxWidthExpression(), false, false);
|
|
appendLabel(markerElement, model.maxWidthExpression(), true, true);
|
|
result.createChild('div', 'media-inspector-marker-spacer');
|
|
}
|
|
|
|
if (model.section() === 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';
|
|
UI.Tooltip.Tooltip.install(leftElement, model.mediaText());
|
|
appendLabel(leftElement, model.maxWidthExpression(), true, false);
|
|
appendLabel(leftElement, model.minWidthExpression(), 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';
|
|
UI.Tooltip.Tooltip.install(rightElement, model.mediaText());
|
|
appendLabel(rightElement, model.minWidthExpression(), true, false);
|
|
appendLabel(rightElement, model.maxWidthExpression(), false, true);
|
|
result.createChild('div', 'media-inspector-marker-spacer');
|
|
}
|
|
|
|
if (model.section() === Section.Min) {
|
|
const leftElement = result.createChild(
|
|
'div', 'media-inspector-marker media-inspector-marker-min-width media-inspector-marker-min-width-left');
|
|
UI.Tooltip.Tooltip.install(leftElement, 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');
|
|
UI.Tooltip.Tooltip.install(rightElement, model.mediaText());
|
|
appendLabel(rightElement, model.minWidthExpression(), true, true);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {!Element} marker
|
|
* @param {?SDK.CSSMedia.CSSMediaQueryExpression} expression
|
|
* @param {boolean} atLeft
|
|
* @param {boolean} leftAlign
|
|
*/
|
|
function appendLabel(marker, expression, atLeft, leftAlign) {
|
|
if (!expression) {
|
|
return;
|
|
}
|
|
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}
|
|
*/
|
|
export const Section = {
|
|
Max: 0,
|
|
MinMax: 1,
|
|
Min: 2
|
|
};
|
|
|
|
export class MediaQueryUIModel {
|
|
/**
|
|
* @param {!SDK.CSSMedia.CSSMedia} cssMedia
|
|
* @param {?SDK.CSSMedia.CSSMediaQueryExpression} minWidthExpression
|
|
* @param {?SDK.CSSMedia.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 = Section.Max;
|
|
} else if (minWidthExpression && maxWidthExpression) {
|
|
this._section = Section.MinMax;
|
|
} else {
|
|
this._section = Section.Min;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.CSSMedia.CSSMedia} cssMedia
|
|
* @param {!SDK.CSSMedia.CSSMediaQuery} mediaQuery
|
|
* @return {?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();
|
|
if (!expressions) {
|
|
return null;
|
|
}
|
|
|
|
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 && pixels < maxWidthPixels) {
|
|
maxWidthExpression = expression;
|
|
maxWidthPixels = pixels;
|
|
} else if (feature.startsWith('min-') && pixels && pixels > minWidthPixels) {
|
|
minWidthExpression = expression;
|
|
minWidthPixels = pixels;
|
|
}
|
|
}
|
|
if (minWidthPixels > maxWidthPixels || (!maxWidthExpression && !minWidthExpression)) {
|
|
return null;
|
|
}
|
|
|
|
return new MediaQueryUIModel(cssMedia, minWidthExpression, maxWidthExpression, mediaQuery.active());
|
|
}
|
|
|
|
/**
|
|
* @param {!MediaQueryUIModel} other
|
|
* @return {boolean}
|
|
*/
|
|
equals(other) {
|
|
return this.compareTo(other) === 0;
|
|
}
|
|
|
|
/**
|
|
* @param {!MediaQueryUIModel} other
|
|
* @return {boolean}
|
|
*/
|
|
dimensionsEqual(other) {
|
|
const thisMinWidthExpression = this.minWidthExpression();
|
|
const otherMinWidthExpression = other.minWidthExpression();
|
|
const thisMaxWidthExpression = this.maxWidthExpression();
|
|
const otherMaxWidthExpression = other.maxWidthExpression();
|
|
|
|
const sectionsEqual = this.section() === other.section();
|
|
// If there isn't an other min width expression, they aren't equal, so the optional chaining operator is safe to use here.
|
|
const minWidthEqual = !thisMinWidthExpression ||
|
|
thisMinWidthExpression.computedLength() === otherMinWidthExpression?.computedLength();
|
|
const maxWidthEqual = !thisMaxWidthExpression ||
|
|
thisMaxWidthExpression.computedLength() === otherMaxWidthExpression?.computedLength();
|
|
|
|
return sectionsEqual && minWidthEqual && maxWidthEqual;
|
|
}
|
|
|
|
/**
|
|
* @param {!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 Platform.StringUtilities.compare(this.mediaText(), other.mediaText());
|
|
}
|
|
if (myLocation && !otherLocation) {
|
|
return 1;
|
|
}
|
|
if (!myLocation && otherLocation) {
|
|
return -1;
|
|
}
|
|
if (this.active() !== other.active()) {
|
|
return this.active() ? -1 : 1;
|
|
}
|
|
|
|
if (!myLocation || !otherLocation) {
|
|
// This conditional never runs, because it's dealt with above, but
|
|
// TypeScript can't follow that by this point both myLocation and
|
|
// otherLocation must exist.
|
|
return 0;
|
|
}
|
|
|
|
return Platform.StringUtilities.compare(myLocation.url, otherLocation.url) ||
|
|
myLocation.lineNumber - otherLocation.lineNumber || myLocation.columnNumber - otherLocation.columnNumber;
|
|
}
|
|
|
|
const thisMaxWidthExpression = this.maxWidthExpression();
|
|
const otherMaxWidthExpression = other.maxWidthExpression();
|
|
const thisMaxLength = thisMaxWidthExpression ? thisMaxWidthExpression.computedLength() || 0 : 0;
|
|
const otherMaxLength = otherMaxWidthExpression ? otherMaxWidthExpression.computedLength() || 0 : 0;
|
|
|
|
const thisMinWidthExpression = this.minWidthExpression();
|
|
const otherMinWidthExpression = other.minWidthExpression();
|
|
const thisMinLength = thisMinWidthExpression ? thisMinWidthExpression.computedLength() || 0 : 0;
|
|
const otherMinLength = otherMinWidthExpression ? otherMinWidthExpression.computedLength() || 0 : 0;
|
|
|
|
if (this.section() === Section.Max) {
|
|
return otherMaxLength - thisMaxLength;
|
|
}
|
|
if (this.section() === Section.Min) {
|
|
return thisMinLength - otherMinLength;
|
|
}
|
|
return thisMinLength - otherMinLength || otherMaxLength - thisMaxLength;
|
|
}
|
|
|
|
/**
|
|
* @return {!Section}
|
|
*/
|
|
section() {
|
|
return this._section;
|
|
}
|
|
|
|
/**
|
|
* @return {string}
|
|
*/
|
|
mediaText() {
|
|
return this._cssMedia.text || '';
|
|
}
|
|
|
|
/**
|
|
* @return {?SDK.CSSModel.CSSLocation}
|
|
*/
|
|
rawLocation() {
|
|
if (!this._rawLocation) {
|
|
this._rawLocation = this._cssMedia.rawLocation();
|
|
}
|
|
return this._rawLocation;
|
|
}
|
|
|
|
/**
|
|
* @return {?SDK.CSSMedia.CSSMediaQueryExpression}
|
|
*/
|
|
minWidthExpression() {
|
|
return this._minWidthExpression;
|
|
}
|
|
|
|
/**
|
|
* @return {?SDK.CSSMedia.CSSMediaQueryExpression}
|
|
*/
|
|
maxWidthExpression() {
|
|
return this._maxWidthExpression;
|
|
}
|
|
|
|
/**
|
|
* @return {boolean}
|
|
*/
|
|
active() {
|
|
return this._active;
|
|
}
|
|
}
|