mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 13:51:41 +00:00
Merge fx-team to m-c. a=merge
This commit is contained in:
commit
bfdd703c49
@ -762,6 +762,7 @@ html|*#fullscreen-exit-button {
|
||||
-moz-user-focus: normal;
|
||||
}
|
||||
|
||||
.blocked-permission-icon:not([showing]),
|
||||
.notification-anchor-icon:not([showing]) {
|
||||
display: none;
|
||||
}
|
||||
|
@ -692,19 +692,19 @@
|
||||
onclick="PageProxyClickHandler(event);"/>
|
||||
<image id="sharing-icon" mousethrough="always"/>
|
||||
<box id="blocked-permissions-container" align="center" tooltiptext="">
|
||||
<image data-permission-id="geo" class="notification-anchor-icon geo-icon blocked" role="button"
|
||||
<image data-permission-id="geo" class="blocked-permission-icon geo-icon" role="button"
|
||||
aria-label="&urlbar.geolocationNotificationAnchor.label;"/>
|
||||
<image data-permission-id="desktop-notification" class="notification-anchor-icon desktop-notification-icon blocked" role="button"
|
||||
<image data-permission-id="desktop-notification" class="blocked-permission-icon desktop-notification-icon" role="button"
|
||||
aria-label="&urlbar.webNotsNotificationAnchor3.label;"/>
|
||||
<image data-permission-id="camera" class="notification-anchor-icon camera-icon blocked" role="button"
|
||||
<image data-permission-id="camera" class="blocked-permission-icon camera-icon" role="button"
|
||||
aria-label="&urlbar.webRTCShareDevicesNotificationAnchor.label;"/>
|
||||
<image data-permission-id="indexedDB" class="notification-anchor-icon indexedDB-icon blocked" role="button"
|
||||
<image data-permission-id="indexedDB" class="blocked-permission-icon indexedDB-icon" role="button"
|
||||
aria-label="&urlbar.indexedDBNotificationAnchor.label;"/>
|
||||
<image data-permission-id="microphone" class="notification-anchor-icon microphone-icon blocked" role="button"
|
||||
<image data-permission-id="microphone" class="blocked-permission-icon microphone-icon" role="button"
|
||||
aria-label="&urlbar.webRTCShareMicrophoneNotificationAnchor.label;"/>
|
||||
<image data-permission-id="screen" class="notification-anchor-icon screen-icon blocked" role="button"
|
||||
<image data-permission-id="screen" class="blocked-permission-icon screen-icon" role="button"
|
||||
aria-label="&urlbar.webRTCShareScreenNotificationAnchor.label;"/>
|
||||
<image data-permission-id="pointerLock" class="notification-anchor-icon pointerLock-icon blocked" role="button"
|
||||
<image data-permission-id="pointerLock" class="blocked-permission-icon pointerLock-icon" role="button"
|
||||
aria-label="&urlbar.pointerLockNotificationAnchor.label;"/>
|
||||
</box>
|
||||
<box id="notification-popup-box"
|
||||
|
@ -186,8 +186,6 @@ add_task(function* testPermissionIcons() {
|
||||
|
||||
let geoIcon = gIdentityHandler._identityBox.querySelector("[data-permission-id='geo']");
|
||||
ok(geoIcon.hasAttribute("showing"), "blocked permission icon is shown");
|
||||
ok(geoIcon.classList.contains("blocked"),
|
||||
"blocked permission icon is shown as blocked");
|
||||
|
||||
let cameraIcon = gIdentityHandler._identityBox.querySelector("[data-permission-id='camera']");
|
||||
ok(!cameraIcon.hasAttribute("showing"),
|
||||
|
@ -11,13 +11,17 @@
|
||||
padding-inline-end: 5px;
|
||||
}
|
||||
|
||||
.notification-anchor-icon,
|
||||
.blocked-permission-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-inline-start: 2px;
|
||||
}
|
||||
|
||||
/* This class can be used alone or in combination with the class defining the
|
||||
type of icon displayed. This rule must be defined before the others in order
|
||||
for its list-style-image to be overridden. */
|
||||
.notification-anchor-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-inline-start: 2px;
|
||||
%ifdef MOZ_WIDGET_GTK
|
||||
list-style-image: url(moz-icon://stock/gtk-dialog-info?size=16);
|
||||
%else
|
||||
@ -76,7 +80,7 @@
|
||||
list-style-image: url(chrome://browser/skin/notification-icons.svg#desktop-notification);
|
||||
}
|
||||
|
||||
.desktop-notification-icon.blocked {
|
||||
.desktop-notification-icon.blocked-permission-icon {
|
||||
list-style-image: url(chrome://browser/skin/notification-icons.svg#desktop-notification-blocked);
|
||||
}
|
||||
|
||||
@ -90,7 +94,7 @@
|
||||
%endif
|
||||
}
|
||||
|
||||
.geo-icon.blocked {
|
||||
.geo-icon.blocked-permission-icon {
|
||||
%ifdef XP_MACOSX
|
||||
list-style-image: url(chrome://browser/skin/notification-icons.svg#geo-osx-blocked);
|
||||
%elif defined(MOZ_WIDGET_GTK)
|
||||
@ -115,7 +119,7 @@
|
||||
list-style-image: url(chrome://browser/skin/notification-icons.svg#indexedDB);
|
||||
}
|
||||
|
||||
.indexedDB-icon.blocked {
|
||||
.indexedDB-icon.blocked-permission-icon {
|
||||
list-style-image: url(chrome://browser/skin/notification-icons.svg#indexedDB-blocked);
|
||||
}
|
||||
|
||||
@ -137,7 +141,7 @@
|
||||
list-style-image: url(chrome://browser/skin/notification-icons.svg#camera);
|
||||
}
|
||||
|
||||
.camera-icon.blocked {
|
||||
.camera-icon.blocked-permission-icon {
|
||||
list-style-image: url(chrome://browser/skin/notification-icons.svg#camera-blocked);
|
||||
}
|
||||
|
||||
@ -145,7 +149,7 @@
|
||||
list-style-image: url(chrome://browser/skin/notification-icons.svg#microphone);
|
||||
}
|
||||
|
||||
.microphone-icon.blocked {
|
||||
.microphone-icon.blocked-permission-icon {
|
||||
list-style-image: url(chrome://browser/skin/notification-icons.svg#microphone-blocked);
|
||||
}
|
||||
|
||||
@ -158,7 +162,7 @@
|
||||
list-style-image: url(chrome://browser/skin/notification-icons.svg#screen);
|
||||
}
|
||||
|
||||
.screen-icon.blocked {
|
||||
.screen-icon.blocked-permission-icon {
|
||||
list-style-image: url(chrome://browser/skin/notification-icons.svg#screen-blocked);
|
||||
}
|
||||
|
||||
@ -167,7 +171,7 @@
|
||||
list-style-image: url(chrome://browser/skin/notification-icons.svg#pointerLock);
|
||||
}
|
||||
|
||||
.pointerLock-icon.blocked {
|
||||
.pointerLock-icon.blocked-permission-icon {
|
||||
list-style-image: url(chrome://browser/skin/notification-icons.svg#pointerLock-blocked);
|
||||
}
|
||||
|
||||
|
@ -34,13 +34,16 @@ function SourceMapService(target) {
|
||||
target.on("source-updated", this._onSourceUpdated);
|
||||
target.on("navigate", this.reset);
|
||||
target.on("will-navigate", this.reset);
|
||||
target.on("close", this.destroy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the store containing the cached promised locations
|
||||
*/
|
||||
SourceMapService.prototype.reset = function () {
|
||||
// Guard to prevent clearing the store when it is not initialized yet.
|
||||
if (!this._locationStore) {
|
||||
return;
|
||||
}
|
||||
this._locationStore.clear();
|
||||
this._isNotSourceMapped.clear();
|
||||
};
|
||||
@ -84,9 +87,10 @@ SourceMapService.prototype.unsubscribe = function (location, callback) {
|
||||
// Check to see if the store exists before attempting to clear a location
|
||||
// Sometimes un-subscribe happens during the destruction cascades and this
|
||||
// condition is to protect against that. Could be looked into in the future.
|
||||
if (this._locationStore) {
|
||||
this._locationStore.clearByURL(location.url);
|
||||
if (!this._locationStore) {
|
||||
return;
|
||||
}
|
||||
this._locationStore.clearByURL(location.url);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -11,6 +11,8 @@ const {EyeDropper, HighlighterEnvironment} = require("devtools/server/actors/hig
|
||||
/* eslint-enable mozilla/reject-some-requires */
|
||||
const Telemetry = require("devtools/client/shared/telemetry");
|
||||
|
||||
const windowEyeDroppers = new WeakMap();
|
||||
|
||||
exports.items = [{
|
||||
item: "command",
|
||||
runAt: "client",
|
||||
@ -48,9 +50,28 @@ exports.items = [{
|
||||
name: "frommenu",
|
||||
type: "boolean",
|
||||
hidden: true
|
||||
}, {
|
||||
name: "hide",
|
||||
type: "boolean",
|
||||
hidden: true
|
||||
}]
|
||||
}],
|
||||
exec: function (args, context) {
|
||||
exec: function* (args, context) {
|
||||
if (args.hide) {
|
||||
context.updateExec("eyedropper_server_hide").catch(e => console.error(e));
|
||||
return;
|
||||
}
|
||||
|
||||
// If the inspector is already picking a color from the page, cancel it.
|
||||
let target = context.environment.target;
|
||||
let toolbox = gDevTools.getToolbox(target);
|
||||
if (toolbox) {
|
||||
let inspector = toolbox.getPanel("inspector");
|
||||
if (inspector) {
|
||||
yield inspector.hideEyeDropper();
|
||||
}
|
||||
}
|
||||
|
||||
let telemetry = new Telemetry();
|
||||
telemetry.toolOpened(args.frommenu ? "menueyedropper" : "eyedropper");
|
||||
context.updateExec("eyedropper_server").catch(e => console.error(e));
|
||||
@ -61,15 +82,33 @@ exports.items = [{
|
||||
name: "eyedropper_server",
|
||||
hidden: true,
|
||||
exec: function (args, {environment}) {
|
||||
let env = new HighlighterEnvironment();
|
||||
env.initFromWindow(environment.window);
|
||||
let eyeDropper = new EyeDropper(env);
|
||||
let eyeDropper = windowEyeDroppers.get(environment.window);
|
||||
|
||||
if (!eyeDropper) {
|
||||
let env = new HighlighterEnvironment();
|
||||
env.initFromWindow(environment.window);
|
||||
|
||||
eyeDropper = new EyeDropper(env);
|
||||
eyeDropper.once("hidden", () => {
|
||||
eyeDropper.destroy();
|
||||
env.destroy();
|
||||
windowEyeDroppers.delete(environment.window);
|
||||
});
|
||||
|
||||
windowEyeDroppers.set(environment.window, eyeDropper);
|
||||
}
|
||||
|
||||
eyeDropper.show(environment.document.documentElement, {copyOnSelect: true});
|
||||
|
||||
eyeDropper.once("hidden", () => {
|
||||
eyeDropper.destroy();
|
||||
env.destroy();
|
||||
});
|
||||
}
|
||||
}, {
|
||||
item: "command",
|
||||
runAt: "server",
|
||||
name: "eyedropper_server_hide",
|
||||
hidden: true,
|
||||
exec: function (args, {environment}) {
|
||||
let eyeDropper = windowEyeDroppers.get(environment.window);
|
||||
if (eyeDropper) {
|
||||
eyeDropper.hide();
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
@ -1304,7 +1304,7 @@ InspectorPanel.prototype = {
|
||||
this.telemetry.toolOpened("toolbareyedropper");
|
||||
this.eyeDropperButton.setAttribute("checked", "true");
|
||||
this.startEyeDropperListeners();
|
||||
return this.inspector.pickColorFromPage({copyOnSelect: true})
|
||||
return this.inspector.pickColorFromPage(this.toolbox, {copyOnSelect: true})
|
||||
.catch(e => console.error(e));
|
||||
},
|
||||
|
||||
|
@ -43,6 +43,7 @@ function test() {
|
||||
emptyText: "This is dummy empty text",
|
||||
highlightUpdated: true,
|
||||
removableColumns: true,
|
||||
wrapTextInElements: true,
|
||||
});
|
||||
startTests();
|
||||
});
|
||||
@ -143,34 +144,48 @@ var testMouseInteraction = Task.async(function* () {
|
||||
ok(!table.selectedRow, "Nothing should be selected beforehand");
|
||||
|
||||
let event = table.once(TableWidget.EVENTS.ROW_SELECTED);
|
||||
let node = table.tbody.firstChild.firstChild.children[1];
|
||||
let firstColumnFirstRowCell = table.tbody.firstChild.firstChild.children[1];
|
||||
info("clicking on the first row");
|
||||
ok(!node.classList.contains("theme-selected"),
|
||||
ok(!firstColumnFirstRowCell.classList.contains("theme-selected"),
|
||||
"Node should not have selected class before clicking");
|
||||
click(node);
|
||||
click(firstColumnFirstRowCell);
|
||||
let id = yield event;
|
||||
ok(node.classList.contains("theme-selected"),
|
||||
ok(firstColumnFirstRowCell.classList.contains("theme-selected"),
|
||||
"Node has selected class after click");
|
||||
is(id, "id1", "Correct row was selected");
|
||||
|
||||
info("clicking on third row to select it");
|
||||
info("clicking on second row to select it");
|
||||
event = table.once(TableWidget.EVENTS.ROW_SELECTED);
|
||||
let node2 = table.tbody.firstChild.firstChild.children[3];
|
||||
let firstColumnSecondRowCell = table.tbody.firstChild.firstChild.children[2];
|
||||
// node should not have selected class
|
||||
ok(!node2.classList.contains("theme-selected"),
|
||||
ok(!firstColumnSecondRowCell.classList.contains("theme-selected"),
|
||||
"New node should not have selected class before clicking");
|
||||
click(node2);
|
||||
click(firstColumnSecondRowCell);
|
||||
id = yield event;
|
||||
ok(node2.classList.contains("theme-selected"),
|
||||
ok(firstColumnSecondRowCell.classList.contains("theme-selected"),
|
||||
"New node has selected class after clicking");
|
||||
is(id, "id3", "Correct table path is emitted for new node");
|
||||
isnot(node, node2, "Old and new node are different");
|
||||
ok(!node.classList.contains("theme-selected"),
|
||||
is(id, "id2", "Correct table path is emitted for new node");
|
||||
isnot(firstColumnFirstRowCell, firstColumnSecondRowCell,
|
||||
"Old and new node are different");
|
||||
ok(!firstColumnFirstRowCell.classList.contains("theme-selected"),
|
||||
"Old node should not have selected class after the click on new node");
|
||||
|
||||
info("clicking on the third row cell content to select third row");
|
||||
event = table.once(TableWidget.EVENTS.ROW_SELECTED);
|
||||
let firstColumnThirdRowCell = table.tbody.firstChild.firstChild.children[3];
|
||||
let firstColumnThirdRowCellInnerNode = firstColumnThirdRowCell.querySelector("span");
|
||||
// node should not have selected class
|
||||
ok(!firstColumnThirdRowCell.classList.contains("theme-selected"),
|
||||
"New node should not have selected class before clicking");
|
||||
click(firstColumnThirdRowCellInnerNode);
|
||||
id = yield event;
|
||||
ok(firstColumnThirdRowCell.classList.contains("theme-selected"),
|
||||
"New node has selected class after clicking the cell content");
|
||||
is(id, "id3", "Correct table path is emitted for new node");
|
||||
|
||||
// clicking on table header to sort by it
|
||||
event = table.once(TableWidget.EVENTS.COLUMN_SORTED);
|
||||
node = table.tbody.children[6].firstChild.children[0];
|
||||
let node = table.tbody.children[6].firstChild.children[0];
|
||||
info("clicking on the 4th coulmn header to sort the table by it");
|
||||
ok(!node.hasAttribute("sorted"),
|
||||
"Node should not have sorted attribute before clicking");
|
||||
|
@ -1428,7 +1428,7 @@ Column.prototype = {
|
||||
return;
|
||||
}
|
||||
|
||||
let dataid = target.getAttribute("data-id");
|
||||
let dataid = closest.getAttribute("data-id");
|
||||
this.table.emit(EVENTS.ROW_SELECTED, dataid);
|
||||
}
|
||||
},
|
||||
|
@ -776,7 +776,14 @@ Heritage.extend(SwatchBasedEditorTooltip.prototype, {
|
||||
_openEyeDropper: function () {
|
||||
let {inspector, toolbox, telemetry} = this.inspector;
|
||||
telemetry.toolOpened("pickereyedropper");
|
||||
inspector.pickColorFromPage({copyOnSelect: false}).catch(e => console.error(e));
|
||||
inspector.pickColorFromPage(toolbox, {copyOnSelect: false}).then(() => {
|
||||
this.eyedropperOpen = true;
|
||||
|
||||
// close the colorpicker tooltip so that only the eyedropper is open.
|
||||
this.hide();
|
||||
|
||||
this.tooltip.emit("eyedropper-opened");
|
||||
}, e => console.error(e));
|
||||
|
||||
inspector.once("color-picked", color => {
|
||||
toolbox.win.focus();
|
||||
@ -787,13 +794,6 @@ Heritage.extend(SwatchBasedEditorTooltip.prototype, {
|
||||
inspector.once("color-pick-canceled", () => {
|
||||
this._onEyeDropperDone();
|
||||
});
|
||||
|
||||
this.eyedropperOpen = true;
|
||||
|
||||
// close the colorpicker tooltip so that only the eyedropper is open.
|
||||
this.hide();
|
||||
|
||||
this.tooltip.emit("eyedropper-opened");
|
||||
},
|
||||
|
||||
_onEyeDropperDone: function () {
|
||||
|
@ -232,3 +232,471 @@ input[type="checkbox"]:-moz-focusring,
|
||||
checkbox:-moz-focusring {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* Toolbar buttons */
|
||||
.devtools-menulist,
|
||||
.devtools-toolbarbutton,
|
||||
.devtools-button {
|
||||
-moz-appearance: none;
|
||||
background: transparent;
|
||||
min-height: 18px;
|
||||
text-shadow: none;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
color: var(--theme-body-color);
|
||||
transition: background 0.05s ease-in-out;
|
||||
}
|
||||
|
||||
.devtools-menulist,
|
||||
.devtools-toolbarbutton {
|
||||
-moz-box-align: center;
|
||||
min-width: 78px;
|
||||
padding: 1px;
|
||||
margin: 2px 1px;
|
||||
}
|
||||
|
||||
.devtools-toolbarbutton:not([label]) > .toolbarbutton-icon,
|
||||
.devtools-button::before {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
transition: opacity 0.05s ease-in-out;
|
||||
}
|
||||
|
||||
/* HTML buttons */
|
||||
.devtools-button {
|
||||
margin: 2px 1px;
|
||||
padding: 1px;
|
||||
min-width: 32px;
|
||||
/* The icon is absolutely positioned in the button using ::before */
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.devtools-button::before {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
margin: -8px 0 0 -8px;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
transition: opacity 0.05s ease-in-out;
|
||||
}
|
||||
|
||||
.devtools-button:-moz-focusring {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* Standalone buttons */
|
||||
.devtools-button[standalone],
|
||||
.devtools-button[data-standalone],
|
||||
.devtools-toolbarbutton[standalone],
|
||||
.devtools-toolbarbutton[data-standalone] {
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
min-height: 32px;
|
||||
background-color: var(--theme-toolbar-background);
|
||||
}
|
||||
|
||||
.devtools-toolbarbutton[standalone], .devtools-toolbarbutton[data-standalone] {
|
||||
margin-inline-end: 5px;
|
||||
}
|
||||
|
||||
.devtools-toolbarbutton[label][standalone] {
|
||||
min-height: 2em;
|
||||
}
|
||||
|
||||
.devtools-menulist,
|
||||
.devtools-toolbarbutton,
|
||||
.devtools-button {
|
||||
border-color: var(--toolbar-button-border-color);
|
||||
}
|
||||
|
||||
/* Icon button styles */
|
||||
.devtools-toolbarbutton:not([label]),
|
||||
.devtools-toolbarbutton[text-as-image] {
|
||||
min-width: 32px;
|
||||
}
|
||||
|
||||
.devtools-toolbarbutton:not([label]) > .toolbarbutton-text {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.devtools-toolbarbutton > .toolbarbutton-icon {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Menu button styles (eg. web console filters) */
|
||||
.devtools-toolbarbutton[type=menu-button] > .toolbarbutton-menubutton-button {
|
||||
-moz-appearance: none;
|
||||
color: inherit;
|
||||
border-width: 0;
|
||||
-moz-box-orient: horizontal;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.devtools-toolbarbutton[type=menu-button] {
|
||||
padding: 0 1px;
|
||||
-moz-box-align: stretch;
|
||||
}
|
||||
|
||||
.devtools-toolbarbutton > .toolbarbutton-menubutton-button > .toolbarbutton-icon {
|
||||
margin-inline-end: 4px;
|
||||
}
|
||||
|
||||
.devtools-menulist > .menulist-dropmarker {
|
||||
-moz-appearance: none;
|
||||
display: -moz-box;
|
||||
list-style-image: url("chrome://devtools/skin/images/dropmarker.svg");
|
||||
-moz-box-align: center;
|
||||
min-width: 16px;
|
||||
}
|
||||
|
||||
.devtools-toolbarbutton[type=menu] > .toolbarbutton-menu-dropmarker,
|
||||
.devtools-toolbarbutton[type=menu-button] > .toolbarbutton-menubutton-dropmarker {
|
||||
-moz-appearance: none !important;
|
||||
list-style-image: url("chrome://devtools/skin/images/dropmarker.svg");
|
||||
-moz-box-align: center;
|
||||
padding: 0 3px;
|
||||
}
|
||||
|
||||
/* Icon-only buttons */
|
||||
.devtools-button:empty::before,
|
||||
.devtools-toolbarbutton:not([label]):not([disabled]) > image {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.devtools-button:hover:empty::before,
|
||||
.devtools-button[checked]:empty::before,
|
||||
.devtools-button[open]:empty::before,
|
||||
.devtools-toolbarbutton:not([label]):hover > image,
|
||||
.devtools-toolbarbutton:not([label])[checked=true] > image,
|
||||
.devtools-toolbarbutton:not([label])[open=true] > image {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.devtools-button:disabled,
|
||||
.devtools-button[disabled],
|
||||
.devtools-toolbarbutton[disabled] {
|
||||
opacity: 0.5 !important;
|
||||
}
|
||||
|
||||
.devtools-button[checked]:empty::before,
|
||||
.devtools-button[open]:empty::before,
|
||||
.devtools-button.checked::before,
|
||||
.devtools-toolbarbutton:not([label])[checked=true] > image,
|
||||
.devtools-toolbarbutton:not([label])[open=true] > image {
|
||||
filter: var(--checked-icon-filter);
|
||||
}
|
||||
|
||||
/* Icon-and-text buttons */
|
||||
.devtools-toolbarbutton.icon-and-text .toolbarbutton-text {
|
||||
margin-inline-start: .5em !important;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Text-only buttons */
|
||||
.theme-light .devtools-toolbarbutton[label]:not([text-as-image]):not([type=menu-button]),
|
||||
.theme-light .devtools-toolbarbutton[data-text-only] {
|
||||
background-color: var(--toolbar-tab-hover);
|
||||
}
|
||||
.theme-dark .devtools-toolbarbutton[label]:not([text-as-image]):not([type=menu-button]),
|
||||
.theme-dark .devtools-toolbarbutton[data-text-only] {
|
||||
background-color: rgba(0, 0, 0, .2); /* Splitter */
|
||||
}
|
||||
|
||||
/* Text-only button states */
|
||||
.theme-dark .devtools-button:not(:empty):not([disabled]):hover,
|
||||
.theme-dark .devtools-toolbarbutton:not(:-moz-any([checked=true],[disabled],[text-as-image]))[label]:hover {
|
||||
background: rgba(0, 0, 0, .3); /* Splitters */
|
||||
}
|
||||
.theme-light .devtools-button:not(:empty):not([disabled]):hover,
|
||||
.theme-light .devtools-toolbarbutton:not(:-moz-any([checked=true],[disabled],[text-as-image]))[label]:hover {
|
||||
background: rgba(170, 170, 170, .3); /* Splitters */
|
||||
}
|
||||
|
||||
.theme-dark .devtools-button:not(:empty):not([disabled]):hover:active,
|
||||
.theme-dark .devtools-toolbarbutton:not(:-moz-any([checked=true],[disabled],[text-as-image]))[label]:hover:active {
|
||||
background: rgba(0, 0, 0, .4); /* Splitters */
|
||||
}
|
||||
.theme-light .devtools-button:not(:empty):not([disabled]):hover:active,
|
||||
.theme-light .devtools-toolbarbutton:not(:-moz-any([checked=true],[disabled],[text-as-image]))[label]:hover:active {
|
||||
background: var(--toolbar-tab-hover-active);
|
||||
}
|
||||
|
||||
.theme-dark .devtools-toolbarbutton:not([disabled])[label][checked=true],
|
||||
.theme-dark .devtools-toolbarbutton:not([disabled])[label][open],
|
||||
.theme-dark .devtools-button:not(:empty)[checked=true] {
|
||||
background: var(--theme-selection-background-semitransparent);
|
||||
color: var(--theme-selection-color);
|
||||
}
|
||||
.theme-light .devtools-toolbarbutton:not([disabled])[label][checked=true],
|
||||
.theme-light .devtools-toolbarbutton:not([disabled])[label][open],
|
||||
.theme-light .devtools-button:not(:empty)[checked=true] {
|
||||
background: rgba(76, 158, 217, .3); /* Select highlight blue */
|
||||
}
|
||||
|
||||
:root {
|
||||
--clear-icon-url: url("chrome://devtools/skin/images/clear.svg");
|
||||
}
|
||||
|
||||
.devtools-button.devtools-clear-icon::before {
|
||||
background-image: var(--clear-icon-url);
|
||||
}
|
||||
|
||||
.devtools-button.devtools-filter-icon::before {
|
||||
background-image: var(--filter-image);
|
||||
}
|
||||
|
||||
.devtools-toolbarbutton.devtools-clear-icon {
|
||||
list-style-image: var(--clear-icon-url);
|
||||
}
|
||||
|
||||
.devtools-option-toolbarbutton {
|
||||
list-style-image: var(--tool-options-image);
|
||||
}
|
||||
|
||||
.devtools-toolbarbutton-group > .devtools-toolbarbutton:last-child {
|
||||
margin-inline-end: 0;
|
||||
}
|
||||
|
||||
.devtools-toolbarbutton-group + .devtools-toolbarbutton {
|
||||
margin-inline-start: 3px;
|
||||
}
|
||||
|
||||
.devtools-separator + .devtools-toolbarbutton {
|
||||
margin-inline-start: 1px;
|
||||
}
|
||||
|
||||
/*
|
||||
* Filter buttons
|
||||
* @TODO : Fix when https://bugzilla.mozilla.org/show_bug.cgi?id=1255116 lands
|
||||
*/
|
||||
.menu-filter-button {
|
||||
-moz-appearance: none;
|
||||
background: rgba(128,128,128,0.1);
|
||||
border: none;
|
||||
border-radius: 2px;
|
||||
min-width: 0;
|
||||
padding: 0 5px;
|
||||
margin: 2px;
|
||||
color: var(--theme-body-color);
|
||||
}
|
||||
|
||||
.menu-filter-button:hover {
|
||||
background: rgba(128,128,128,0.2);
|
||||
}
|
||||
|
||||
.menu-filter-button:hover:active {
|
||||
background-color: var(--theme-selection-background-semitransparent);
|
||||
}
|
||||
|
||||
.menu-filter-button:not(:active).checked {
|
||||
background-color: var(--theme-selection-background);
|
||||
color: var(--theme-selection-color);
|
||||
}
|
||||
|
||||
/* Text input */
|
||||
|
||||
.devtools-textinput,
|
||||
.devtools-searchinput,
|
||||
.devtools-filterinput {
|
||||
-moz-appearance: none;
|
||||
margin: 1px 3px;
|
||||
border: 1px solid;
|
||||
border-radius: 2px;
|
||||
padding: 4px 6px;
|
||||
border-color: var(--theme-splitter-color);
|
||||
font: message-box;
|
||||
}
|
||||
|
||||
:root[platform="mac"] .devtools-textinput,
|
||||
:root[platform="mac"] .devtools-searchinput,
|
||||
:root[platform="mac"] .devtools-filterinput {
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.devtools-searchinput,
|
||||
.devtools-filterinput {
|
||||
padding: 0;
|
||||
padding-inline-start: 22px;
|
||||
padding-inline-end: 4px;
|
||||
background-position: 8px center;
|
||||
background-size: 11px 11px;
|
||||
background-repeat: no-repeat;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
.devtools-searchinput {
|
||||
background-image: var(--magnifying-glass-image);
|
||||
}
|
||||
|
||||
.devtools-filterinput {
|
||||
background-image: url(images/filter.svg#filterinput);
|
||||
}
|
||||
|
||||
.devtools-searchinput:-moz-locale-dir(rtl),
|
||||
.devtools-searchinput:dir(rtl),
|
||||
.devtools-filterinput:-moz-locale-dir(rtl),
|
||||
.devtools-filterinput:dir(rtl) {
|
||||
background-position: calc(100% - 8px) center;
|
||||
}
|
||||
|
||||
.devtools-searchinput > .textbox-input-box > .textbox-search-icons > .textbox-search-icon,
|
||||
.devtools-filterinput > .textbox-input-box > .textbox-search-icons > .textbox-search-icon {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.devtools-searchinput .textbox-input::-moz-placeholder,
|
||||
.devtools-filterinput .textbox-input::-moz-placeholder {
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.devtools-plaininput {
|
||||
border-color: transparent;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.theme-dark .devtools-plaininput {
|
||||
color: var(--theme-highlight-gray);
|
||||
}
|
||||
|
||||
/* Searchbox is a div container element for a search input element */
|
||||
.devtools-searchbox {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
height: 23px;
|
||||
position: relative;
|
||||
padding: 0 3px;
|
||||
}
|
||||
|
||||
/* The spacing is accomplished with a padding on the searchbox */
|
||||
.devtools-searchbox > .devtools-textinput,
|
||||
.devtools-searchbox > .devtools-searchinput,
|
||||
.devtools-searchbox > .devtools-filterinput {
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.devtools-searchbox > .devtools-textinput:-moz-focusring,
|
||||
.devtools-searchbox > .devtools-searchinput:-moz-focusring,
|
||||
.devtools-searchbox > .devtools-filterinput:-moz-focusring {
|
||||
border-color: var(--theme-focus-border-color-textbox);
|
||||
box-shadow: var(--theme-focus-box-shadow-textbox);
|
||||
transition: all 0.2s ease-in-out;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* Don't add 'double spacing' for inputs that are at beginning / end
|
||||
of a toolbar (since the toolbar has it's own spacing). */
|
||||
.devtools-toolbar > .devtools-textinput:first-child,
|
||||
.devtools-toolbar > .devtools-searchinput:first-child,
|
||||
.devtools-toolbar > .devtools-filterinput:first-child {
|
||||
margin-inline-start: 0;
|
||||
}
|
||||
.devtools-toolbar > .devtools-textinput:last-child,
|
||||
.devtools-toolbar > .devtools-searchinput:last-child,
|
||||
.devtools-toolbar > .devtools-filterinput:last-child {
|
||||
margin-inline-end: 0;
|
||||
}
|
||||
.devtools-toolbar > .devtools-searchbox:first-child {
|
||||
padding-inline-start: 0;
|
||||
}
|
||||
.devtools-toolbar > .devtools-searchbox:last-child {
|
||||
padding-inline-end: 0;
|
||||
}
|
||||
|
||||
.devtools-rule-searchbox {
|
||||
-moz-box-flex: 1;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.devtools-rule-searchbox[filled] {
|
||||
background-color: var(--searchbox-background-color);
|
||||
border-color: var(--searchbox-border-color);
|
||||
padding-inline-end: 23px;
|
||||
}
|
||||
|
||||
.devtools-style-searchbox-no-match {
|
||||
background-color: var(--searcbox-no-match-background-color) !important;
|
||||
border-color: var(--searcbox-no-match-border-color) !important;
|
||||
}
|
||||
|
||||
.devtools-searchinput-clear {
|
||||
position: absolute;
|
||||
top: 3.5px;
|
||||
offset-inline-end: 7px;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background-position: 0 0;
|
||||
background-repeat: no-repeat;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.devtools-searchinput-clear:dir(rtl) {
|
||||
right: unset;
|
||||
left: 7px;
|
||||
}
|
||||
|
||||
.theme-dark .devtools-searchinput-clear {
|
||||
background-image: url("chrome://devtools/skin/images/search-clear-dark.svg");
|
||||
}
|
||||
|
||||
.theme-light .devtools-searchinput-clear {
|
||||
background-image: url("chrome://devtools/skin/images/search-clear-light.svg");
|
||||
}
|
||||
|
||||
.devtools-style-searchbox-no-match + .devtools-searchinput-clear {
|
||||
background-image: url("chrome://devtools/skin/images/search-clear-failed.svg") !important;
|
||||
}
|
||||
|
||||
.devtools-searchinput-clear:hover {
|
||||
background-position: -16px 0;
|
||||
}
|
||||
|
||||
.theme-dark .devtools-searchinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear,
|
||||
.theme-dark .devtools-filterinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear {
|
||||
list-style-image: url("chrome://devtools/skin/images/search-clear-dark.svg");
|
||||
-moz-image-region: rect(0, 16px, 16px, 0);
|
||||
}
|
||||
|
||||
.theme-light .devtools-searchinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear,
|
||||
.theme-light .devtools-filterinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear {
|
||||
list-style-image: url("chrome://devtools/skin/images/search-clear-light.svg");
|
||||
-moz-image-region: rect(0, 16px, 16px, 0);
|
||||
}
|
||||
|
||||
.devtools-searchinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear,
|
||||
.devtools-filterinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.devtools-searchinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear:hover,
|
||||
.devtools-filterinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear:hover {
|
||||
-moz-image-region: rect(0, 32px, 16px, 16px);
|
||||
}
|
||||
|
||||
/* Throbbers */
|
||||
.devtools-throbber::before {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
vertical-align: bottom;
|
||||
margin-inline-end: 0.5em;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
border: 2px solid currentColor;
|
||||
border-right-color: transparent;
|
||||
border-radius: 50%;
|
||||
animation: 1.1s linear throbber-spin infinite;
|
||||
}
|
||||
|
||||
@keyframes throbber-spin {
|
||||
from {
|
||||
transform: none;
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
@ -65,14 +65,17 @@
|
||||
padding: 0;
|
||||
line-height: -moz-block-height;
|
||||
}
|
||||
|
||||
.devtools-toolbar checkbox .checkbox-check {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
.devtools-toolbar checkbox .checkbox-label-box {
|
||||
border: none !important; /* overrides .checkbox-label-box from checkbox.css */
|
||||
}
|
||||
|
||||
.devtools-toolbar checkbox .checkbox-label-box .checkbox-label {
|
||||
margin: 0 6px !important; /* overrides .checkbox-label from checkbox.css */
|
||||
padding: 0;
|
||||
@ -87,434 +90,6 @@
|
||||
background-position: 0, 1px, 2px;
|
||||
}
|
||||
|
||||
/* Toolbar buttons */
|
||||
|
||||
.devtools-menulist,
|
||||
.devtools-toolbarbutton,
|
||||
.devtools-button {
|
||||
-moz-appearance: none;
|
||||
background: transparent;
|
||||
min-height: 18px;
|
||||
text-shadow: none;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
color: var(--theme-body-color);
|
||||
transition: background 0.05s ease-in-out;
|
||||
}
|
||||
|
||||
.devtools-menulist,
|
||||
.devtools-toolbarbutton {
|
||||
-moz-box-align: center;
|
||||
min-width: 78px;
|
||||
padding: 1px;
|
||||
margin: 2px 1px;
|
||||
}
|
||||
|
||||
.devtools-toolbarbutton:not([label]) > .toolbarbutton-icon,
|
||||
.devtools-button::before {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
transition: opacity 0.05s ease-in-out;
|
||||
}
|
||||
|
||||
/* HTML buttons */
|
||||
.devtools-button {
|
||||
margin: 2px 1px;
|
||||
padding: 1px;
|
||||
min-width: 32px;
|
||||
/* The icon is absolutely positioned in the button using ::before */
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.devtools-button::before {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
margin: -8px 0 0 -8px;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
transition: opacity 0.05s ease-in-out;
|
||||
}
|
||||
|
||||
.devtools-button:-moz-focusring {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* Standalone buttons */
|
||||
.devtools-button[standalone],
|
||||
.devtools-button[data-standalone],
|
||||
.devtools-toolbarbutton[standalone],
|
||||
.devtools-toolbarbutton[data-standalone] {
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
min-height: 32px;
|
||||
background-color: var(--theme-toolbar-background);
|
||||
}
|
||||
|
||||
.devtools-toolbarbutton[standalone], .devtools-toolbarbutton[data-standalone] {
|
||||
margin-inline-end: 5px;
|
||||
}
|
||||
|
||||
.devtools-toolbarbutton[label][standalone] {
|
||||
min-height: 2em;
|
||||
}
|
||||
|
||||
.devtools-menulist,
|
||||
.devtools-toolbarbutton,
|
||||
.devtools-button {
|
||||
border-color: var(--toolbar-button-border-color);
|
||||
}
|
||||
|
||||
/* Icon button styles */
|
||||
.devtools-toolbarbutton:not([label]),
|
||||
.devtools-toolbarbutton[text-as-image] {
|
||||
min-width: 32px;
|
||||
}
|
||||
|
||||
/* Set flex attribute to Toolbox buttons and Picker container so,
|
||||
they don't overlapp with the tab bar */
|
||||
#toolbox-buttons {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#toolbox-picker-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.devtools-toolbarbutton:not([label]) > .toolbarbutton-text {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.devtools-toolbarbutton > .toolbarbutton-icon {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Menu button styles (eg. web console filters) */
|
||||
.devtools-toolbarbutton[type=menu-button] > .toolbarbutton-menubutton-button {
|
||||
-moz-appearance: none;
|
||||
color: inherit;
|
||||
border-width: 0;
|
||||
-moz-box-orient: horizontal;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.devtools-toolbarbutton[type=menu-button] {
|
||||
padding: 0 1px;
|
||||
-moz-box-align: stretch;
|
||||
}
|
||||
|
||||
.devtools-toolbarbutton > .toolbarbutton-menubutton-button > .toolbarbutton-icon {
|
||||
margin-inline-end: 4px;
|
||||
}
|
||||
|
||||
.devtools-menulist > .menulist-dropmarker {
|
||||
-moz-appearance: none;
|
||||
display: -moz-box;
|
||||
list-style-image: url("chrome://devtools/skin/images/dropmarker.svg");
|
||||
-moz-box-align: center;
|
||||
min-width: 16px;
|
||||
}
|
||||
|
||||
.devtools-toolbarbutton[type=menu] > .toolbarbutton-menu-dropmarker,
|
||||
.devtools-toolbarbutton[type=menu-button] > .toolbarbutton-menubutton-dropmarker {
|
||||
-moz-appearance: none !important;
|
||||
list-style-image: url("chrome://devtools/skin/images/dropmarker.svg");
|
||||
-moz-box-align: center;
|
||||
padding: 0 3px;
|
||||
}
|
||||
|
||||
/* Icon-only buttons */
|
||||
.devtools-button:empty::before,
|
||||
.devtools-toolbarbutton:not([label]):not([disabled]) > image {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.devtools-button:hover:empty::before,
|
||||
.devtools-button[checked]:empty::before,
|
||||
.devtools-button[open]:empty::before,
|
||||
.devtools-toolbarbutton:not([label]):hover > image,
|
||||
.devtools-toolbarbutton:not([label])[checked=true] > image,
|
||||
.devtools-toolbarbutton:not([label])[open=true] > image {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.devtools-button:disabled,
|
||||
.devtools-button[disabled],
|
||||
.devtools-toolbarbutton[disabled] {
|
||||
opacity: 0.5 !important;
|
||||
}
|
||||
|
||||
.devtools-button[checked]:empty::before,
|
||||
.devtools-button[open]:empty::before,
|
||||
.devtools-button.checked::before,
|
||||
.devtools-toolbarbutton:not([label])[checked=true] > image,
|
||||
.devtools-toolbarbutton:not([label])[open=true] > image {
|
||||
filter: var(--checked-icon-filter);
|
||||
}
|
||||
|
||||
/* Icon-and-text buttons */
|
||||
.devtools-toolbarbutton.icon-and-text .toolbarbutton-text {
|
||||
margin-inline-start: .5em !important;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Text-only buttons */
|
||||
.theme-light .devtools-toolbarbutton[label]:not([text-as-image]):not([type=menu-button]),
|
||||
.theme-light .devtools-toolbarbutton[data-text-only] {
|
||||
background-color: var(--toolbar-tab-hover);
|
||||
}
|
||||
.theme-dark .devtools-toolbarbutton[label]:not([text-as-image]):not([type=menu-button]),
|
||||
.theme-dark .devtools-toolbarbutton[data-text-only] {
|
||||
background-color: rgba(0, 0, 0, .2); /* Splitter */
|
||||
}
|
||||
|
||||
/* Text-only button states */
|
||||
.theme-dark .devtools-button:not(:empty):not([disabled]):hover,
|
||||
.theme-dark .devtools-toolbarbutton:not(:-moz-any([checked=true],[disabled],[text-as-image]))[label]:hover {
|
||||
background: rgba(0, 0, 0, .3); /* Splitters */
|
||||
}
|
||||
.theme-light .devtools-button:not(:empty):not([disabled]):hover,
|
||||
.theme-light .devtools-toolbarbutton:not(:-moz-any([checked=true],[disabled],[text-as-image]))[label]:hover {
|
||||
background: rgba(170, 170, 170, .3); /* Splitters */
|
||||
}
|
||||
|
||||
.theme-dark .devtools-button:not(:empty):not([disabled]):hover:active,
|
||||
.theme-dark .devtools-toolbarbutton:not(:-moz-any([checked=true],[disabled],[text-as-image]))[label]:hover:active {
|
||||
background: rgba(0, 0, 0, .4); /* Splitters */
|
||||
}
|
||||
.theme-light .devtools-button:not(:empty):not([disabled]):hover:active,
|
||||
.theme-light .devtools-toolbarbutton:not(:-moz-any([checked=true],[disabled],[text-as-image]))[label]:hover:active {
|
||||
background: var(--toolbar-tab-hover-active);
|
||||
}
|
||||
|
||||
.theme-dark .devtools-toolbarbutton:not([disabled])[label][checked=true],
|
||||
.theme-dark .devtools-toolbarbutton:not([disabled])[label][open],
|
||||
.theme-dark .devtools-button:not(:empty)[checked=true] {
|
||||
background: var(--theme-selection-background-semitransparent);
|
||||
color: var(--theme-selection-color);
|
||||
}
|
||||
.theme-light .devtools-toolbarbutton:not([disabled])[label][checked=true],
|
||||
.theme-light .devtools-toolbarbutton:not([disabled])[label][open],
|
||||
.theme-light .devtools-button:not(:empty)[checked=true] {
|
||||
background: rgba(76, 158, 217, .3); /* Select highlight blue */
|
||||
}
|
||||
|
||||
:root {
|
||||
--clear-icon-url: url("chrome://devtools/skin/images/clear.svg");
|
||||
}
|
||||
|
||||
.devtools-button.devtools-clear-icon::before {
|
||||
background-image: var(--clear-icon-url);
|
||||
}
|
||||
|
||||
.devtools-button.devtools-filter-icon::before {
|
||||
background-image: var(--filter-image);
|
||||
}
|
||||
|
||||
.devtools-toolbarbutton.devtools-clear-icon {
|
||||
list-style-image: var(--clear-icon-url);
|
||||
}
|
||||
|
||||
.devtools-option-toolbarbutton {
|
||||
list-style-image: var(--tool-options-image);
|
||||
}
|
||||
|
||||
.devtools-toolbarbutton-group > .devtools-toolbarbutton:last-child {
|
||||
margin-inline-end: 0;
|
||||
}
|
||||
|
||||
.devtools-toolbarbutton-group + .devtools-toolbarbutton {
|
||||
margin-inline-start: 3px;
|
||||
}
|
||||
|
||||
.devtools-separator + .devtools-toolbarbutton {
|
||||
margin-inline-start: 1px;
|
||||
}
|
||||
|
||||
/* Text input */
|
||||
|
||||
.devtools-textinput,
|
||||
.devtools-searchinput,
|
||||
.devtools-filterinput {
|
||||
-moz-appearance: none;
|
||||
margin: 1px 3px;
|
||||
border: 1px solid;
|
||||
border-radius: 2px;
|
||||
padding: 4px 6px;
|
||||
border-color: var(--theme-splitter-color);
|
||||
font: message-box;
|
||||
}
|
||||
|
||||
:root[platform="mac"] .devtools-textinput,
|
||||
:root[platform="mac"] .devtools-searchinput,
|
||||
:root[platform="mac"] .devtools-filterinput {
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.devtools-searchinput,
|
||||
.devtools-filterinput {
|
||||
padding: 0;
|
||||
padding-inline-start: 22px;
|
||||
padding-inline-end: 4px;
|
||||
background-position: 8px center;
|
||||
background-size: 11px 11px;
|
||||
background-repeat: no-repeat;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
.devtools-searchinput {
|
||||
background-image: var(--magnifying-glass-image);
|
||||
}
|
||||
|
||||
.devtools-filterinput {
|
||||
background-image: url(images/filter.svg#filterinput);
|
||||
}
|
||||
|
||||
.devtools-searchinput:-moz-locale-dir(rtl),
|
||||
.devtools-searchinput:dir(rtl),
|
||||
.devtools-filterinput:-moz-locale-dir(rtl),
|
||||
.devtools-filterinput:dir(rtl) {
|
||||
background-position: calc(100% - 8px) center;
|
||||
}
|
||||
|
||||
.devtools-searchinput > .textbox-input-box > .textbox-search-icons > .textbox-search-icon,
|
||||
.devtools-filterinput > .textbox-input-box > .textbox-search-icons > .textbox-search-icon {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.devtools-searchinput .textbox-input::-moz-placeholder,
|
||||
.devtools-filterinput .textbox-input::-moz-placeholder {
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.devtools-plaininput {
|
||||
border-color: transparent;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.theme-dark .devtools-plaininput {
|
||||
color: var(--theme-highlight-gray);
|
||||
}
|
||||
|
||||
/* Searchbox is a div container element for a search input element */
|
||||
.devtools-searchbox {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
height: 23px;
|
||||
position: relative;
|
||||
padding: 0 3px;
|
||||
}
|
||||
|
||||
/* The spacing is accomplished with a padding on the searchbox */
|
||||
.devtools-searchbox > .devtools-textinput,
|
||||
.devtools-searchbox > .devtools-searchinput,
|
||||
.devtools-searchbox > .devtools-filterinput {
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.devtools-searchbox > .devtools-textinput:-moz-focusring,
|
||||
.devtools-searchbox > .devtools-searchinput:-moz-focusring,
|
||||
.devtools-searchbox > .devtools-filterinput:-moz-focusring {
|
||||
border-color: var(--theme-focus-border-color-textbox);
|
||||
box-shadow: var(--theme-focus-box-shadow-textbox);
|
||||
transition: all 0.2s ease-in-out;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* Don't add 'double spacing' for inputs that are at beginning / end
|
||||
of a toolbar (since the toolbar has it's own spacing). */
|
||||
.devtools-toolbar > .devtools-textinput:first-child,
|
||||
.devtools-toolbar > .devtools-searchinput:first-child,
|
||||
.devtools-toolbar > .devtools-filterinput:first-child {
|
||||
margin-inline-start: 0;
|
||||
}
|
||||
.devtools-toolbar > .devtools-textinput:last-child,
|
||||
.devtools-toolbar > .devtools-searchinput:last-child,
|
||||
.devtools-toolbar > .devtools-filterinput:last-child {
|
||||
margin-inline-end: 0;
|
||||
}
|
||||
.devtools-toolbar > .devtools-searchbox:first-child {
|
||||
padding-inline-start: 0;
|
||||
}
|
||||
.devtools-toolbar > .devtools-searchbox:last-child {
|
||||
padding-inline-end: 0;
|
||||
}
|
||||
|
||||
.devtools-rule-searchbox {
|
||||
-moz-box-flex: 1;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.devtools-rule-searchbox[filled] {
|
||||
background-color: var(--searchbox-background-color);
|
||||
border-color: var(--searchbox-border-color);
|
||||
padding-inline-end: 23px;
|
||||
}
|
||||
|
||||
.devtools-style-searchbox-no-match {
|
||||
background-color: var(--searcbox-no-match-background-color) !important;
|
||||
border-color: var(--searcbox-no-match-border-color) !important;
|
||||
}
|
||||
|
||||
.devtools-searchinput-clear {
|
||||
position: absolute;
|
||||
top: 3.5px;
|
||||
offset-inline-end: 7px;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background-position: 0 0;
|
||||
background-repeat: no-repeat;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.devtools-searchinput-clear:dir(rtl) {
|
||||
right: unset;
|
||||
left: 7px;
|
||||
}
|
||||
|
||||
.theme-dark .devtools-searchinput-clear {
|
||||
background-image: url("chrome://devtools/skin/images/search-clear-dark.svg");
|
||||
}
|
||||
|
||||
.theme-light .devtools-searchinput-clear {
|
||||
background-image: url("chrome://devtools/skin/images/search-clear-light.svg");
|
||||
}
|
||||
|
||||
.devtools-style-searchbox-no-match + .devtools-searchinput-clear {
|
||||
background-image: url("chrome://devtools/skin/images/search-clear-failed.svg") !important;
|
||||
}
|
||||
|
||||
.devtools-searchinput-clear:hover {
|
||||
background-position: -16px 0;
|
||||
}
|
||||
|
||||
.theme-dark .devtools-searchinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear,
|
||||
.theme-dark .devtools-filterinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear {
|
||||
list-style-image: url("chrome://devtools/skin/images/search-clear-dark.svg");
|
||||
-moz-image-region: rect(0, 16px, 16px, 0);
|
||||
}
|
||||
|
||||
.theme-light .devtools-searchinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear,
|
||||
.theme-light .devtools-filterinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear {
|
||||
list-style-image: url("chrome://devtools/skin/images/search-clear-light.svg");
|
||||
-moz-image-region: rect(0, 16px, 16px, 0);
|
||||
}
|
||||
|
||||
.devtools-searchinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear,
|
||||
.devtools-filterinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.devtools-searchinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear:hover,
|
||||
.devtools-filterinput > .textbox-input-box > .textbox-search-icons > .textbox-search-clear:hover {
|
||||
-moz-image-region: rect(0, 32px, 16px, 16px);
|
||||
}
|
||||
|
||||
/* In-tools sidebar */
|
||||
.devtools-sidebar-tabs {
|
||||
-moz-appearance: none;
|
||||
@ -640,54 +215,3 @@
|
||||
.devtools-side-splitter {
|
||||
background-color: var(--theme-splitter-color);
|
||||
}
|
||||
|
||||
/* Throbbers */
|
||||
.devtools-throbber::before {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
vertical-align: bottom;
|
||||
margin-inline-end: 0.5em;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
border: 2px solid currentColor;
|
||||
border-right-color: transparent;
|
||||
border-radius: 50%;
|
||||
animation: 1.1s linear throbber-spin infinite;
|
||||
}
|
||||
|
||||
@keyframes throbber-spin {
|
||||
from {
|
||||
transform: none;
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Filter buttons
|
||||
* @TODO : Fix when https://bugzilla.mozilla.org/show_bug.cgi?id=1255116 lands
|
||||
*/
|
||||
.menu-filter-button {
|
||||
-moz-appearance: none;
|
||||
background: rgba(128,128,128,0.1);
|
||||
border: none;
|
||||
border-radius: 2px;
|
||||
min-width: 0;
|
||||
padding: 0 5px;
|
||||
margin: 2px;
|
||||
color: var(--theme-body-color);
|
||||
}
|
||||
|
||||
.menu-filter-button:hover {
|
||||
background: rgba(128,128,128,0.2);
|
||||
}
|
||||
|
||||
.menu-filter-button:hover:active {
|
||||
background-color: var(--theme-selection-background-semitransparent);
|
||||
}
|
||||
|
||||
.menu-filter-button:not(:active).checked {
|
||||
background-color: var(--theme-selection-background);
|
||||
color: var(--theme-selection-color);
|
||||
}
|
||||
|
@ -55,6 +55,16 @@
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Set flex attribute to Toolbox buttons and Picker container so,
|
||||
they don't overlapp with the tab bar */
|
||||
#toolbox-buttons {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#toolbox-picker-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
/* Toolbox tabs */
|
||||
|
||||
.devtools-tab {
|
||||
|
@ -42,7 +42,9 @@ const GRID_LINES_PROPERTIES = {
|
||||
* h.destroy();
|
||||
*
|
||||
* Available Options:
|
||||
* - infiniteLines {Boolean}
|
||||
* - showGridLineNumbers {Boolean}
|
||||
* Displays the grid line numbers
|
||||
* - showInfiniteLines {Boolean}
|
||||
* Displays an infinite line to represent the grid lines
|
||||
*/
|
||||
function CssGridHighlighter(highlighterEnv) {
|
||||
@ -243,7 +245,7 @@ CssGridHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
|
||||
let lineStartPos = (bounds[crossSide] / getCurrentZoom(this.win)) + startPos;
|
||||
let lineEndPos = (bounds[crossSide] / getCurrentZoom(this.win)) + endPos;
|
||||
|
||||
if (this.options.infiniteLines) {
|
||||
if (this.options.showInfiniteLines) {
|
||||
lineStartPos = 0;
|
||||
lineEndPos = parseInt(this.canvas.getAttribute(mainSize), 10);
|
||||
}
|
||||
@ -254,6 +256,10 @@ CssGridHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
|
||||
let line = gridDimension.lines[i];
|
||||
let linePos = (bounds[mainSide] / getCurrentZoom(this.win)) + line.start;
|
||||
|
||||
if (this.options.showGridLineNumbers) {
|
||||
this.renderGridLineNumber(line.number, linePos, lineStartPos, dimensionType);
|
||||
}
|
||||
|
||||
if (i == 0 || i == lastEdgeLineIndex) {
|
||||
this.renderLine(linePos, lineStartPos, lineEndPos, dimensionType, "edge");
|
||||
} else {
|
||||
@ -291,7 +297,7 @@ CssGridHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
|
||||
this.ctx.beginPath();
|
||||
this.ctx.translate(.5, .5);
|
||||
|
||||
if (dimensionType == COLUMNS) {
|
||||
if (dimensionType === COLUMNS) {
|
||||
this.ctx.moveTo(linePos, startPos);
|
||||
this.ctx.lineTo(linePos, endPos);
|
||||
} else {
|
||||
@ -304,6 +310,28 @@ CssGridHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
|
||||
this.ctx.restore();
|
||||
},
|
||||
|
||||
/**
|
||||
* Render the grid line number on the css grid highlighter canvas.
|
||||
*
|
||||
* @param {Number} lineNumber
|
||||
* The grid line number.
|
||||
* @param {Number} linePos
|
||||
* The line position along the x-axis for a column grid line and
|
||||
* y-axis for a row grid line.
|
||||
* @param {Number} startPos
|
||||
* The start position of the cross side of the grid line.
|
||||
* @param {String} dimensionType
|
||||
* The grid dimension type which is either the constant COLUMNS or ROWS.
|
||||
*/
|
||||
renderGridLineNumber(lineNumber, linePos, startPos, dimensionType) {
|
||||
if (dimensionType === COLUMNS) {
|
||||
this.ctx.fillText(lineNumber, linePos, startPos);
|
||||
} else {
|
||||
let textWidth = this.ctx.measureText(lineNumber).width;
|
||||
this.ctx.fillText(lineNumber, startPos - textWidth, linePos);
|
||||
}
|
||||
},
|
||||
|
||||
_hide() {
|
||||
setIgnoreLayoutChanges(true);
|
||||
this._hideGrid();
|
||||
|
@ -178,6 +178,8 @@ EyeDropper.prototype = {
|
||||
|
||||
this.getElement("root").setAttribute("hidden", "true");
|
||||
this.getElement("root").removeAttribute("drawn");
|
||||
|
||||
this.emit("hidden");
|
||||
},
|
||||
|
||||
prepareImageCapture() {
|
||||
|
@ -26,6 +26,8 @@ const { Class } = require("sdk/core/heritage");
|
||||
const events = require("sdk/event/core");
|
||||
const object = require("sdk/util/object");
|
||||
const nodeConstants = require("devtools/shared/dom-node-constants.js");
|
||||
loader.lazyRequireGetter(this, "CommandUtils",
|
||||
"devtools/client/shared/developer-toolbar", true);
|
||||
|
||||
const HIDDEN_CLASS = "__fx-devtools-hide-shortcut__";
|
||||
|
||||
@ -977,6 +979,22 @@ var InspectorFront = FrontClassWithSpec(inspectorSpec, {
|
||||
});
|
||||
}, {
|
||||
impl: "_getPageStyle"
|
||||
}),
|
||||
|
||||
pickColorFromPage: custom(Task.async(function* (toolbox, options) {
|
||||
if (toolbox) {
|
||||
// If the eyedropper was already started using the gcli command, hide it so we don't
|
||||
// end up with 2 instances of the eyedropper on the page.
|
||||
let {target} = toolbox;
|
||||
let requisition = yield CommandUtils.createRequisition(target, {
|
||||
environment: CommandUtils.createEnvironment({target})
|
||||
});
|
||||
yield requisition.updateExec("eyedropper --hide");
|
||||
}
|
||||
|
||||
yield this._pickColorFromPage(options);
|
||||
}), {
|
||||
impl: "_pickColorFromPage"
|
||||
})
|
||||
});
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include "Grid.h"
|
||||
|
||||
#include "GridArea.h"
|
||||
#include "GridDimension.h"
|
||||
#include "mozilla/dom/GridBinding.h"
|
||||
#include "nsGridContainerFrame.h"
|
||||
@ -13,7 +14,7 @@
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Grid, mParent, mRows, mCols)
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Grid, mParent, mRows, mCols, mAreas)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(Grid)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(Grid)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Grid)
|
||||
@ -43,6 +44,40 @@ Grid::Grid(nsISupports* aParent,
|
||||
aFrame->GetComputedTemplateColumnLines();
|
||||
mCols->SetTrackInfo(columnTrackInfo);
|
||||
mCols->SetLineInfo(columnTrackInfo, columnLineInfo);
|
||||
|
||||
// Add implicit areas first.
|
||||
nsGridContainerFrame::ImplicitNamedAreas* implicitAreas =
|
||||
aFrame->GetImplicitNamedAreas();
|
||||
if (implicitAreas) {
|
||||
for (auto iter = implicitAreas->Iter(); !iter.Done(); iter.Next()) {
|
||||
nsStringHashKey* entry = iter.Get();
|
||||
|
||||
GridArea* area = new GridArea(this,
|
||||
nsString(entry->GetKey()),
|
||||
GridDeclaration::Implicit,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0);
|
||||
mAreas.AppendElement(area);
|
||||
}
|
||||
}
|
||||
|
||||
// Add explicit areas next.
|
||||
nsGridContainerFrame::ExplicitNamedAreas* explicitAreas =
|
||||
aFrame->GetExplicitNamedAreas();
|
||||
if (explicitAreas) {
|
||||
for (auto areaInfo : *explicitAreas) {
|
||||
GridArea* area = new GridArea(this,
|
||||
areaInfo.mName,
|
||||
GridDeclaration::Explicit,
|
||||
areaInfo.mRowStart,
|
||||
areaInfo.mRowEnd,
|
||||
areaInfo.mColumnStart,
|
||||
areaInfo.mColumnEnd);
|
||||
mAreas.AppendElement(area);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Grid::~Grid()
|
||||
@ -67,5 +102,11 @@ Grid::Cols() const
|
||||
return mCols;
|
||||
}
|
||||
|
||||
void
|
||||
Grid::GetAreas(nsTArray<RefPtr<GridArea>>& aAreas) const
|
||||
{
|
||||
aAreas = mAreas;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
@ -7,6 +7,7 @@
|
||||
#ifndef mozilla_dom_Grid_h
|
||||
#define mozilla_dom_Grid_h
|
||||
|
||||
#include "GridArea.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "nsGridContainerFrame.h"
|
||||
#include "nsISupports.h"
|
||||
@ -38,11 +39,13 @@ public:
|
||||
|
||||
GridDimension* Rows() const;
|
||||
GridDimension* Cols() const;
|
||||
void GetAreas(nsTArray<RefPtr<GridArea>>& aAreas) const;
|
||||
|
||||
protected:
|
||||
nsCOMPtr<Element> mParent;
|
||||
RefPtr<GridDimension> mRows;
|
||||
RefPtr<GridDimension> mCols;
|
||||
nsTArray<RefPtr<GridArea>> mAreas;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
86
dom/grid/GridArea.cpp
Normal file
86
dom/grid/GridArea.cpp
Normal file
@ -0,0 +1,86 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "GridArea.h"
|
||||
#include "mozilla/dom/GridBinding.h"
|
||||
#include "Grid.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(GridArea, mParent)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(GridArea)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(GridArea)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(GridArea)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
GridArea::GridArea(Grid* aParent,
|
||||
const nsString& aName,
|
||||
GridDeclaration aType,
|
||||
uint32_t aRowStart,
|
||||
uint32_t aRowEnd,
|
||||
uint32_t aColumnStart,
|
||||
uint32_t aColumnEnd)
|
||||
: mParent(aParent)
|
||||
, mName(aName)
|
||||
, mType(aType)
|
||||
, mRowStart(aRowStart)
|
||||
, mRowEnd(aRowEnd)
|
||||
, mColumnStart(aColumnStart)
|
||||
, mColumnEnd(aColumnEnd)
|
||||
{
|
||||
}
|
||||
|
||||
GridArea::~GridArea()
|
||||
{
|
||||
}
|
||||
|
||||
JSObject*
|
||||
GridArea::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return GridAreaBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
void
|
||||
GridArea::GetName(nsString& aName) const
|
||||
{
|
||||
aName = mName;
|
||||
}
|
||||
|
||||
GridDeclaration
|
||||
GridArea::Type() const
|
||||
{
|
||||
return mType;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
GridArea::RowStart() const
|
||||
{
|
||||
return mRowStart;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
GridArea::RowEnd() const
|
||||
{
|
||||
return mRowEnd;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
GridArea::ColumnStart() const
|
||||
{
|
||||
return mColumnStart;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
GridArea::ColumnEnd() const
|
||||
{
|
||||
return mColumnEnd;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
63
dom/grid/GridArea.h
Normal file
63
dom/grid/GridArea.h
Normal file
@ -0,0 +1,63 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_GridArea_h
|
||||
#define mozilla_dom_GridArea_h
|
||||
|
||||
#include "mozilla/dom/GridBinding.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class Grid;
|
||||
|
||||
class GridArea : public nsISupports
|
||||
, public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
explicit GridArea(Grid *aParent,
|
||||
const nsString& aName,
|
||||
GridDeclaration aType,
|
||||
uint32_t aRowStart,
|
||||
uint32_t aRowEnd,
|
||||
uint32_t aColumnStart,
|
||||
uint32_t aColumnEnd);
|
||||
|
||||
protected:
|
||||
virtual ~GridArea();
|
||||
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(GridArea)
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
Grid* GetParentObject()
|
||||
{
|
||||
return mParent;
|
||||
}
|
||||
|
||||
void GetName(nsString& aName) const;
|
||||
GridDeclaration Type() const;
|
||||
uint32_t RowStart() const;
|
||||
uint32_t RowEnd() const;
|
||||
uint32_t ColumnStart() const;
|
||||
uint32_t ColumnEnd() const;
|
||||
|
||||
protected:
|
||||
RefPtr<Grid> mParent;
|
||||
const nsString mName;
|
||||
const GridDeclaration mType;
|
||||
const uint32_t mRowStart;
|
||||
const uint32_t mRowEnd;
|
||||
const uint32_t mColumnStart;
|
||||
const uint32_t mColumnEnd;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_dom_GridTrack_h */
|
@ -24,6 +24,7 @@ GridLine::GridLine(GridLines *aParent)
|
||||
: mParent(aParent)
|
||||
, mStart(0.0)
|
||||
, mBreadth(0.0)
|
||||
, mType(GridDeclaration::Implicit)
|
||||
, mNumber(0)
|
||||
{
|
||||
MOZ_ASSERT(aParent, "Should never be instantiated with a null GridLines");
|
||||
@ -57,6 +58,12 @@ GridLine::Breadth() const
|
||||
return mBreadth;
|
||||
}
|
||||
|
||||
GridDeclaration
|
||||
GridLine::Type() const
|
||||
{
|
||||
return mType;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
GridLine::Number() const
|
||||
{
|
||||
@ -64,15 +71,17 @@ GridLine::Number() const
|
||||
}
|
||||
|
||||
void
|
||||
GridLine::SetLineValues(double aStart,
|
||||
GridLine::SetLineValues(const nsTArray<nsString>& aNames,
|
||||
double aStart,
|
||||
double aBreadth,
|
||||
uint32_t aNumber,
|
||||
const nsTArray<nsString>& aNames)
|
||||
GridDeclaration aType)
|
||||
{
|
||||
mNames = aNames;
|
||||
mStart = aStart;
|
||||
mBreadth = aBreadth;
|
||||
mNumber = aNumber;
|
||||
mNames = aNames;
|
||||
mType = aType;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
|
@ -7,6 +7,7 @@
|
||||
#ifndef mozilla_dom_GridLine_h
|
||||
#define mozilla_dom_GridLine_h
|
||||
|
||||
#include "mozilla/dom/GridBinding.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsWrapperCache.h"
|
||||
@ -39,19 +40,22 @@ public:
|
||||
|
||||
double Start() const;
|
||||
double Breadth() const;
|
||||
GridDeclaration Type() const;
|
||||
uint32_t Number() const;
|
||||
|
||||
void SetLineValues(double aStart,
|
||||
void SetLineValues(const nsTArray<nsString>& aNames,
|
||||
double aStart,
|
||||
double aBreadth,
|
||||
uint32_t aNumber,
|
||||
const nsTArray<nsString>& aNames);
|
||||
GridDeclaration aType);
|
||||
|
||||
protected:
|
||||
RefPtr<GridLines> mParent;
|
||||
nsTArray<nsString> mNames;
|
||||
double mStart;
|
||||
double mBreadth;
|
||||
GridDeclaration mType;
|
||||
uint32_t mNumber;
|
||||
nsTArray<nsString> mNames;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
@ -97,11 +97,22 @@ GridLines::SetLineInfo(const ComputedGridTrackInfo* aTrackInfo,
|
||||
}
|
||||
|
||||
line->SetLineValues(
|
||||
lineNames,
|
||||
nsPresContext::AppUnitsToDoubleCSSPixels(endOfLastTrack),
|
||||
nsPresContext::AppUnitsToDoubleCSSPixels(startOfNextTrack -
|
||||
endOfLastTrack),
|
||||
i + 1,
|
||||
lineNames
|
||||
(
|
||||
// Implicit if there are no explicit tracks, or if the index
|
||||
// is before the first explicit track, or after
|
||||
// a track beyond the last explicit track.
|
||||
(aTrackInfo->mNumExplicitTracks == 0) ||
|
||||
(i < aTrackInfo->mNumLeadingImplicitTracks) ||
|
||||
(i > aTrackInfo->mNumLeadingImplicitTracks +
|
||||
aTrackInfo->mNumExplicitTracks) ?
|
||||
GridDeclaration::Implicit :
|
||||
GridDeclaration::Explicit
|
||||
)
|
||||
);
|
||||
|
||||
if (i < aTrackInfo->mEndFragmentTrack) {
|
||||
|
@ -24,7 +24,7 @@ GridTrack::GridTrack(GridTracks* aParent)
|
||||
: mParent(aParent)
|
||||
, mStart(0.0)
|
||||
, mBreadth(0.0)
|
||||
, mType(GridTrackType::Implicit)
|
||||
, mType(GridDeclaration::Implicit)
|
||||
, mState(GridTrackState::Static)
|
||||
{
|
||||
MOZ_ASSERT(aParent, "Should never be instantiated with a null GridTracks");
|
||||
@ -52,7 +52,7 @@ GridTrack::Breadth() const
|
||||
return mBreadth;
|
||||
}
|
||||
|
||||
GridTrackType
|
||||
GridDeclaration
|
||||
GridTrack::Type() const
|
||||
{
|
||||
return mType;
|
||||
@ -67,7 +67,7 @@ GridTrack::State() const
|
||||
void
|
||||
GridTrack::SetTrackValues(double aStart,
|
||||
double aBreadth,
|
||||
GridTrackType aType,
|
||||
GridDeclaration aType,
|
||||
GridTrackState aState)
|
||||
{
|
||||
mStart = aStart;
|
||||
|
@ -36,16 +36,19 @@ public:
|
||||
|
||||
double Start() const;
|
||||
double Breadth() const;
|
||||
GridTrackType Type() const;
|
||||
GridDeclaration Type() const;
|
||||
GridTrackState State() const;
|
||||
|
||||
void SetTrackValues(double aStart, double aBreadth, GridTrackType aType, GridTrackState aState);
|
||||
void SetTrackValues(double aStart,
|
||||
double aBreadth,
|
||||
GridDeclaration aType,
|
||||
GridTrackState aState);
|
||||
|
||||
protected:
|
||||
RefPtr<GridTracks> mParent;
|
||||
double mStart;
|
||||
double mBreadth;
|
||||
GridTrackType mType;
|
||||
GridDeclaration mType;
|
||||
GridTrackState mState;
|
||||
};
|
||||
|
||||
|
@ -86,8 +86,8 @@ GridTracks::SetTrackInfo(const ComputedGridTrackInfo* aTrackInfo)
|
||||
(i < aTrackInfo->mNumLeadingImplicitTracks) ||
|
||||
(i >= aTrackInfo->mNumLeadingImplicitTracks +
|
||||
aTrackInfo->mNumExplicitTracks) ?
|
||||
GridTrackType::Implicit :
|
||||
GridTrackType::Explicit
|
||||
GridDeclaration::Implicit :
|
||||
GridDeclaration::Explicit
|
||||
),
|
||||
GridTrackState(aTrackInfo->mStates[i])
|
||||
);
|
||||
|
@ -8,6 +8,7 @@ MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini']
|
||||
|
||||
EXPORTS.mozilla.dom += [
|
||||
'Grid.h',
|
||||
'GridArea.h',
|
||||
'GridDimension.h',
|
||||
'GridLine.h',
|
||||
'GridLines.h',
|
||||
@ -17,6 +18,7 @@ EXPORTS.mozilla.dom += [
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'Grid.cpp',
|
||||
'GridArea.cpp',
|
||||
'GridDimension.cpp',
|
||||
'GridLine.cpp',
|
||||
'GridLines.cpp',
|
||||
|
@ -1,4 +1,6 @@
|
||||
[chrome/test_grid_areas.html]
|
||||
[chrome/test_grid_fragmentation.html]
|
||||
[chrome/test_grid_implicit.html]
|
||||
[chrome/test_grid_lines.html]
|
||||
[chrome/test_grid_object.html]
|
||||
[chrome/test_grid_track_state.html]
|
||||
|
96
dom/grid/test/chrome/test_grid_areas.html
Normal file
96
dom/grid/test/chrome/test_grid_areas.html
Normal file
@ -0,0 +1,96 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
<style>
|
||||
body {
|
||||
margin: 40px;
|
||||
}
|
||||
.wrapper {
|
||||
display: grid;
|
||||
width: 600px;
|
||||
height: 600px;
|
||||
grid-gap: 20px;
|
||||
grid-template-columns: 50px 1fr 50px;
|
||||
grid-template-rows: 1fr 1fr 1fr;
|
||||
grid-template-areas: "areaA areaA ....."
|
||||
"areaB areaC areaC"
|
||||
"areaB areaC areaC";
|
||||
background-color: #f00;
|
||||
}
|
||||
.box {
|
||||
background-color: #444;
|
||||
color: #fff;
|
||||
}
|
||||
.a {
|
||||
grid-area: areaA;
|
||||
}
|
||||
.b {
|
||||
grid-area: areaB;
|
||||
}
|
||||
.c {
|
||||
grid-area: areaC;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function runTests() {
|
||||
var wrapper = document.getElementById("wrapper");
|
||||
var grid = wrapper.getGridFragments()[0];
|
||||
|
||||
// test existence of property
|
||||
isnot(typeof(grid.areas), "undefined", "Grid.areas property exists.");
|
||||
|
||||
if (typeof(grid.areas) != "undefined") {
|
||||
// test that the right number of areas are reported
|
||||
is(grid.areas.length, 3, "3 areas are reported.");
|
||||
|
||||
if (grid.areas.length == 3) {
|
||||
// test area names
|
||||
is(grid.areas[0].name, "areaA", "Area 0 has proper name.");
|
||||
is(grid.areas[1].name, "areaB", "Area 1 has proper name.");
|
||||
is(grid.areas[2].name, "areaC", "Area 2 has proper name.");
|
||||
|
||||
// test area types
|
||||
is(grid.areas[0].type, "explicit", "Area 0 is explicit.");
|
||||
is(grid.areas[1].type, "explicit", "Area 1 is explicit.");
|
||||
is(grid.areas[2].type, "explicit", "Area 2 is explicit.");
|
||||
|
||||
// test start and end lines
|
||||
is(grid.areas[0].rowStart, 1, "Area 0 has start row line of 1.");
|
||||
is(grid.areas[0].rowEnd, 2, "Area 0 has end row line of 2.");
|
||||
is(grid.areas[0].columnStart, 1, "Area 0 has start column line of 1.");
|
||||
is(grid.areas[0].columnEnd, 3, "Area 0 has end column line of 3.");
|
||||
|
||||
is(grid.areas[1].rowStart, 2, "Area 1 has start row line of 2.");
|
||||
is(grid.areas[1].rowEnd, 4, "Area 1 has end row line of 4.");
|
||||
is(grid.areas[1].columnStart, 1, "Area 1 has start column line of 1.");
|
||||
is(grid.areas[1].columnEnd, 2, "Area 1 has end column line of 2.");
|
||||
|
||||
is(grid.areas[2].rowStart, 2, "Area 2 has start row line of 2.");
|
||||
is(grid.areas[2].rowEnd, 4, "Area 2 has end row line of 4.");
|
||||
is(grid.areas[2].columnStart, 2, "Area 2 has start column line of 2.");
|
||||
is(grid.areas[2].columnEnd, 4, "Area 2 has end column line of 4.");
|
||||
}
|
||||
}
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onLoad="runTests();">
|
||||
|
||||
<div id="wrapper" class="wrapper">
|
||||
<div id="boxA" class="box a">A</div>
|
||||
<div id="boxB" class="box b">B</div>
|
||||
<div id="boxC" class="box c">C</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
129
dom/grid/test/chrome/test_grid_implicit.html
Normal file
129
dom/grid/test/chrome/test_grid_implicit.html
Normal file
@ -0,0 +1,129 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
<style>
|
||||
body {
|
||||
margin: 40px;
|
||||
}
|
||||
.wrapper {
|
||||
display: grid;
|
||||
grid-gap: 10px;
|
||||
grid-template-columns: 100px 50px 100px;
|
||||
grid-template-rows: 50px [areaD-start] 50px [areaD-end];
|
||||
grid-template-areas: "areaA areaA ....."
|
||||
"..... areaC areaC";
|
||||
grid-auto-columns: 20px;
|
||||
grid-auto-rows: 20px;
|
||||
background-color: #f00;
|
||||
}
|
||||
.box {
|
||||
background-color: #444;
|
||||
color: #fff;
|
||||
}
|
||||
.a {
|
||||
grid-area: areaA;
|
||||
}
|
||||
.b {
|
||||
grid-row: span 2 / 2;
|
||||
grid-column: areaA-end / span 2;
|
||||
}
|
||||
.c {
|
||||
grid-area: areaC;
|
||||
}
|
||||
.d {
|
||||
grid-area: areaD;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function runTests() {
|
||||
var wrapper = document.getElementById("wrapper");
|
||||
var grid = wrapper.getGridFragments()[0];
|
||||
var boxA = document.getElementById("boxA");
|
||||
var boxB = document.getElementById("boxB");
|
||||
var boxC = document.getElementById("boxC");
|
||||
|
||||
// test column and row line counts
|
||||
is(grid.cols.lines.length, 6,
|
||||
"Grid.cols.lines property has length that respects implicit expansion."
|
||||
);
|
||||
is(grid.rows.lines.length, 4,
|
||||
"Grid.rows.lines property has length that respects implicit expansion."
|
||||
);
|
||||
|
||||
if((grid.cols.lines.length == 6) &&
|
||||
(grid.rows.lines.length == 4)) {
|
||||
|
||||
// test explicit / implicit lines
|
||||
is(grid.cols.lines[0].type, "explicit", "Grid column line 1 is explicit.");
|
||||
is(grid.cols.lines[4].type, "implicit", "Grid column line 5 is implicit.");
|
||||
is(grid.cols.lines[5].type, "implicit", "Grid column line 6 is implicit.");
|
||||
|
||||
is(grid.rows.lines[0].type, "implicit", "Grid row line 1 is implicit.");
|
||||
is(grid.rows.lines[1].type, "explicit", "Grid row line 2 is explicit.");
|
||||
is(grid.rows.lines[3].type, "explicit", "Grid row line 4 is explicit.");
|
||||
}
|
||||
|
||||
// test column and row track counts
|
||||
is(grid.cols.tracks.length, 5,
|
||||
"Grid.cols.tracks property has length that respects implicit expansion."
|
||||
);
|
||||
is(grid.rows.tracks.length, 3,
|
||||
"Grid.rows.tracks property has length that respects implicit expansion."
|
||||
);
|
||||
|
||||
if((grid.cols.tracks.length == 5) &&
|
||||
(grid.rows.tracks.length == 3)) {
|
||||
|
||||
// test explicit / implicit tracks
|
||||
is(grid.cols.tracks[0].type, "explicit", "Grid column track 1 is explicit.");
|
||||
is(grid.cols.tracks[3].type, "implicit", "Grid column track 4 is implicit.");
|
||||
is(grid.cols.tracks[4].type, "implicit", "Grid column track 5 is implicit.");
|
||||
|
||||
is(grid.rows.tracks[0].type, "implicit", "Grid row track 1 is implicit.");
|
||||
is(grid.rows.tracks[1].type, "explicit", "Grid row track 2 is explicit.");
|
||||
is(grid.rows.tracks[2].type, "explicit", "Grid row track 3 is explicit.");
|
||||
}
|
||||
|
||||
// test area count
|
||||
is(grid.areas.length, 3,
|
||||
"Grid.areas property has length that respects implicit expansion."
|
||||
);
|
||||
|
||||
for(var i = 0; i < grid.areas.length; i++) {
|
||||
var area = grid.areas[i];
|
||||
if(area.name == "areaD") {
|
||||
is(area.type, "implicit", area.name + " is implicit.");
|
||||
|
||||
// test lines of implicit areas
|
||||
todo_is(area.rowStart, 2, area.name + " has start row line of 2.");
|
||||
todo_is(area.rowEnd, 3, area.name + " has end row line of 3.");
|
||||
todo_is(area.columnStart, 4, area.name + " has start column line of 4.");
|
||||
todo_is(area.columnEnd, 5, area.name + " has end column line of 5.");
|
||||
} else {
|
||||
is(area.type, "explicit", area.name + " is explicit.");
|
||||
}
|
||||
}
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onLoad="runTests();">
|
||||
|
||||
<div id="wrapper" class="wrapper">
|
||||
<div id="boxA" class="box a">A</div>
|
||||
<div id="boxB" class="box b">B</div>
|
||||
<div id="boxC" class="box c">C</div>
|
||||
<div id="boxD" class="box d">D</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -1,13 +1,31 @@
|
||||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
/* These objects support visualization of a css-grid by the dev tools. */
|
||||
|
||||
/**
|
||||
* Explicit and implicit types apply to tracks, lines, and areas.
|
||||
* https://drafts.csswg.org/css-grid/#explicit-grids
|
||||
* https://drafts.csswg.org/css-grid/#implicit-grids
|
||||
*/
|
||||
enum GridDeclaration { "explicit", "implicit" };
|
||||
|
||||
/**
|
||||
* Tracks expanded from auto-fill or auto-fit have repeat state, other tracks
|
||||
* are static.
|
||||
*/
|
||||
enum GridTrackState { "static", "repeat" };
|
||||
|
||||
[ChromeOnly]
|
||||
interface Grid
|
||||
{
|
||||
readonly attribute GridDimension rows;
|
||||
readonly attribute GridDimension cols;
|
||||
[Cached, Constant]
|
||||
readonly attribute sequence<GridArea> areas;
|
||||
};
|
||||
|
||||
[ChromeOnly]
|
||||
@ -21,34 +39,77 @@ interface GridDimension
|
||||
interface GridLines
|
||||
{
|
||||
readonly attribute unsigned long length;
|
||||
|
||||
/**
|
||||
* This accessor method allows array-like access to lines.
|
||||
* @param index A 0-indexed value.
|
||||
*/
|
||||
getter GridLine? item(unsigned long index);
|
||||
};
|
||||
|
||||
[ChromeOnly]
|
||||
interface GridLine
|
||||
{
|
||||
readonly attribute double start;
|
||||
readonly attribute double breadth;
|
||||
readonly attribute unsigned long number;
|
||||
[Cached, Pure]
|
||||
/**
|
||||
* Names include both explicit names and implicit names, which will be
|
||||
* assigned if the line contributes to a named area.
|
||||
* https://drafts.csswg.org/css-grid/#implicit-named-lines
|
||||
*/
|
||||
[Cached, Constant]
|
||||
readonly attribute sequence<DOMString> names;
|
||||
|
||||
readonly attribute double start;
|
||||
|
||||
/**
|
||||
* Breadth is the gap between the start of this line and the start of the
|
||||
* next track in flow direction. It primarily is set by use of the -gap
|
||||
* properties.
|
||||
* https://drafts.csswg.org/css-grid/#gutters
|
||||
*/
|
||||
readonly attribute double breadth;
|
||||
|
||||
readonly attribute GridDeclaration type;
|
||||
|
||||
/**
|
||||
* Number is the 1-indexed index of the line in flow order.
|
||||
*/
|
||||
readonly attribute unsigned long number;
|
||||
};
|
||||
|
||||
[ChromeOnly]
|
||||
interface GridTracks
|
||||
{
|
||||
readonly attribute unsigned long length;
|
||||
|
||||
/**
|
||||
* This accessor method allows array-like access to tracks.
|
||||
* @param index A 0-indexed value.
|
||||
*/
|
||||
getter GridTrack? item(unsigned long index);
|
||||
};
|
||||
|
||||
enum GridTrackType { "explicit", "implicit" };
|
||||
enum GridTrackState { "static", "repeat" };
|
||||
|
||||
[ChromeOnly]
|
||||
interface GridTrack
|
||||
{
|
||||
readonly attribute double start;
|
||||
readonly attribute double breadth;
|
||||
readonly attribute GridTrackType type;
|
||||
readonly attribute GridDeclaration type;
|
||||
readonly attribute GridTrackState state;
|
||||
};
|
||||
|
||||
[ChromeOnly]
|
||||
interface GridArea
|
||||
{
|
||||
readonly attribute DOMString name;
|
||||
readonly attribute GridDeclaration type;
|
||||
|
||||
/**
|
||||
* These values are 1-indexed line numbers bounding the area.
|
||||
* FIXME: Bug 1297189 - Implicit grid areas need boundary line numbers
|
||||
* exposed to dev tools
|
||||
*/
|
||||
readonly attribute unsigned long rowStart;
|
||||
readonly attribute unsigned long rowEnd;
|
||||
readonly attribute unsigned long columnStart;
|
||||
readonly attribute unsigned long columnEnd;
|
||||
};
|
||||
|
@ -5767,6 +5767,16 @@ nsGridContainerFrame::Reflow(nsPresContext* aPresContext,
|
||||
ComputedGridLineInfo* rowLineInfo = new ComputedGridLineInfo(
|
||||
Move(rowLineNames));
|
||||
Properties().Set(GridRowLineInfo(), rowLineInfo);
|
||||
|
||||
// Generate area info for explicit areas. Implicit areas are handled
|
||||
// elsewhere.
|
||||
if (gridReflowInput.mGridStyle->mGridTemplateAreas) {
|
||||
nsTArray<css::GridNamedArea>* areas = new nsTArray<css::GridNamedArea>(
|
||||
gridReflowInput.mGridStyle->mGridTemplateAreas->mNamedAreas);
|
||||
Properties().Set(ExplicitNamedAreasProperty(), areas);
|
||||
} else {
|
||||
Properties().Delete(ExplicitNamedAreasProperty());
|
||||
}
|
||||
}
|
||||
|
||||
if (!prevInFlow) {
|
||||
|
@ -151,6 +151,20 @@ public:
|
||||
return info;
|
||||
}
|
||||
|
||||
typedef nsTHashtable<nsStringHashKey> ImplicitNamedAreas;
|
||||
NS_DECLARE_FRAME_PROPERTY_DELETABLE(ImplicitNamedAreasProperty,
|
||||
ImplicitNamedAreas)
|
||||
ImplicitNamedAreas* GetImplicitNamedAreas() const {
|
||||
return Properties().Get(ImplicitNamedAreasProperty());
|
||||
}
|
||||
|
||||
typedef nsTArray<mozilla::css::GridNamedArea> ExplicitNamedAreas;
|
||||
NS_DECLARE_FRAME_PROPERTY_DELETABLE(ExplicitNamedAreasProperty,
|
||||
ExplicitNamedAreas)
|
||||
ExplicitNamedAreas* GetExplicitNamedAreas() const {
|
||||
return Properties().Get(ExplicitNamedAreasProperty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a containing grid frame, and ensure it has computed grid info
|
||||
* @return nullptr if aFrame has no grid container, or frame was destroyed
|
||||
@ -195,14 +209,8 @@ protected:
|
||||
* grid-template-columns / grid-template-rows are stored in this frame
|
||||
* property when needed, as a ImplicitNamedAreas* value.
|
||||
*/
|
||||
typedef nsTHashtable<nsStringHashKey> ImplicitNamedAreas;
|
||||
NS_DECLARE_FRAME_PROPERTY_DELETABLE(ImplicitNamedAreasProperty,
|
||||
ImplicitNamedAreas)
|
||||
void InitImplicitNamedAreas(const nsStylePosition* aStyle);
|
||||
void AddImplicitNamedAreas(const nsTArray<nsTArray<nsString>>& aLineNameLists);
|
||||
ImplicitNamedAreas* GetImplicitNamedAreas() const {
|
||||
return Properties().Get(ImplicitNamedAreasProperty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reflow and place our children.
|
||||
|
@ -989,9 +989,8 @@ var loadManifestFromWebManifest = Task.async(function*(aUri) {
|
||||
|
||||
addon.targetApplications = [{
|
||||
id: TOOLKIT_ID,
|
||||
minVersion: (bss.strict_min_version ||
|
||||
AddonManagerPrivate.webExtensionsMinPlatformVersion),
|
||||
maxVersion: bss.strict_max_version || "*",
|
||||
minVersion: bss.strict_min_version,
|
||||
maxVersion: bss.strict_max_version,
|
||||
}];
|
||||
|
||||
addon.targetPlatforms = [];
|
||||
@ -3994,6 +3993,13 @@ this.XPIProvider = {
|
||||
}
|
||||
let addon = yield loadManifestFromFile(aFile, TemporaryInstallLocation);
|
||||
|
||||
if (!addon.isCompatible) {
|
||||
let app = addon.matchingTargetApplication;
|
||||
throw new Error(`Add-on ${addon.id} is not compatible with application version. ` +
|
||||
`add-on minVersion: ${app.minVersion}, ` +
|
||||
`add-on maxVersion: ${app.maxVersion}`);
|
||||
}
|
||||
|
||||
if (!addon.bootstrap) {
|
||||
throw new Error("Only restartless (bootstrap) add-ons"
|
||||
+ " can be temporarily installed:", addon.id);
|
||||
@ -7014,6 +7020,10 @@ AddonInternal.prototype = {
|
||||
if (!app)
|
||||
return false;
|
||||
|
||||
// set reasonable defaults for minVersion and maxVersion
|
||||
let minVersion = app.minVersion || "0";
|
||||
let maxVersion = app.maxVersion || "*";
|
||||
|
||||
if (!aAppVersion)
|
||||
aAppVersion = Services.appinfo.version;
|
||||
if (!aPlatformVersion)
|
||||
@ -7050,14 +7060,14 @@ AddonInternal.prototype = {
|
||||
minCompatVersion = XPIProvider.minCompatiblePlatformVersion;
|
||||
|
||||
if (minCompatVersion &&
|
||||
Services.vc.compare(minCompatVersion, app.maxVersion) > 0)
|
||||
Services.vc.compare(minCompatVersion, maxVersion) > 0)
|
||||
return false;
|
||||
|
||||
return Services.vc.compare(version, app.minVersion) >= 0;
|
||||
return Services.vc.compare(version, minVersion) >= 0;
|
||||
}
|
||||
|
||||
return (Services.vc.compare(version, app.minVersion) >= 0) &&
|
||||
(Services.vc.compare(version, app.maxVersion) <= 0)
|
||||
return (Services.vc.compare(version, minVersion) >= 0) &&
|
||||
(Services.vc.compare(version, maxVersion) <= 0)
|
||||
},
|
||||
|
||||
get matchingTargetApplication() {
|
||||
|
@ -717,13 +717,11 @@ function writeInstallRDFForExtension(aData, aDir, aId, aExtraFile) {
|
||||
* An optional string to override the default installation aId
|
||||
* @return A file pointing to where the extension was installed
|
||||
*/
|
||||
function writeWebManifestForExtension(aData, aDir, aId = aData.applications.gecko.id) {
|
||||
function promiseWriteWebManifestForExtension(aData, aDir, aId = aData.applications.gecko.id) {
|
||||
let files = {
|
||||
"manifest.json": JSON.stringify(aData),
|
||||
}
|
||||
let promise = AddonTestUtils.promiseWriteFilesToExtension(aDir.path, aId, files);
|
||||
|
||||
return awaitPromise(promise);
|
||||
return AddonTestUtils.promiseWriteFilesToExtension(aDir.path, aId, files);
|
||||
}
|
||||
|
||||
var {writeFilesToZip} = AddonTestUtils;
|
||||
|
@ -20,6 +20,19 @@ const manifestSample = {
|
||||
}],
|
||||
};
|
||||
|
||||
const { Management } = Components.utils.import("resource://gre/modules/Extension.jsm", {});
|
||||
|
||||
function promiseAddonStartup() {
|
||||
return new Promise(resolve => {
|
||||
let listener = (extension) => {
|
||||
Management.off("startup", listener);
|
||||
resolve(extension);
|
||||
};
|
||||
|
||||
Management.on("startup", listener);
|
||||
});
|
||||
}
|
||||
|
||||
function* installAddon(fixtureName, addonID) {
|
||||
yield promiseInstallAllFiles([do_get_addon(fixtureName)]);
|
||||
return promiseAddonByID(addonID);
|
||||
@ -110,38 +123,58 @@ add_task(function* test_can_reload_permanent_addon() {
|
||||
yield tearDownAddon(addon);
|
||||
});
|
||||
|
||||
add_task(function* test_disabled_addon_can_be_enabled_after_reload() {
|
||||
add_task(function* test_reload_to_invalid_version_fails() {
|
||||
yield promiseRestartManager();
|
||||
let tempdir = gTmpD.clone();
|
||||
|
||||
// Create an add-on with strictCompatibility which should cause it
|
||||
// to be appDisabled.
|
||||
const unpackedAddon = writeInstallRDFToDir(
|
||||
Object.assign({}, manifestSample, {
|
||||
strictCompatibility: true,
|
||||
targetApplications: [{
|
||||
id: "xpcshell@tests.mozilla.org",
|
||||
minVersion: "0.1",
|
||||
maxVersion: "0.1"
|
||||
}],
|
||||
}), tempdir, manifestSample.id, "bootstrap.js");
|
||||
// The initial version of the add-on will be compatible, and will therefore load
|
||||
const addonId = "invalid_version_cannot_be_reloaded@tests.mozilla.org";
|
||||
let manifest = {
|
||||
name: "invalid_version_cannot_be_reloaded",
|
||||
description: "test invalid_version_cannot_be_reloaded",
|
||||
manifest_version: 2,
|
||||
version: "1.0",
|
||||
applications: {
|
||||
gecko: {
|
||||
id: addonId,
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
yield AddonManager.installTemporaryAddon(unpackedAddon);
|
||||
const addon = yield promiseAddonByID(manifestSample.id);
|
||||
let addonDir = yield promiseWriteWebManifestForExtension(manifest, tempdir, "invalid_version");
|
||||
yield AddonManager.installTemporaryAddon(addonDir);
|
||||
yield promiseAddonStartup();
|
||||
|
||||
let addon = yield promiseAddonByID(addonId);
|
||||
notEqual(addon, null);
|
||||
equal(addon.appDisabled, true);
|
||||
equal(addon.id, addonId);
|
||||
equal(addon.version, "1.0");
|
||||
equal(addon.appDisabled, false);
|
||||
equal(addon.userDisabled, false);
|
||||
addonDir.remove(true);
|
||||
|
||||
// Remove strictCompatibility from the manifest.
|
||||
writeInstallRDFToDir(manifestSample, tempdir, manifestSample.id);
|
||||
// update the manifest to make the add-on version incompatible, so the reload will reject
|
||||
manifest.applications.gecko.strict_min_version = "1";
|
||||
manifest.applications.gecko.strict_max_version = "1";
|
||||
manifest.version = "2.0";
|
||||
|
||||
yield addon.reload();
|
||||
addonDir = yield promiseWriteWebManifestForExtension(manifest, tempdir, "invalid_version", false);
|
||||
let expectedMsg = new RegExp("Add-on invalid_version_cannot_be_reloaded@tests.mozilla.org is not compatible with application version. " +
|
||||
"add-on minVersion: 1, add-on maxVersion: 1");
|
||||
|
||||
const reloadedAddon = yield promiseAddonByID(manifestSample.id);
|
||||
yield Assert.rejects(addon.reload(),
|
||||
expectedMsg,
|
||||
"Reload rejects when application version does not fall between minVersion and maxVersion");
|
||||
|
||||
let reloadedAddon = yield promiseAddonByID(addonId);
|
||||
notEqual(reloadedAddon, null);
|
||||
equal(reloadedAddon.id, addonId);
|
||||
equal(reloadedAddon.version, "1.0");
|
||||
equal(reloadedAddon.appDisabled, false);
|
||||
equal(reloadedAddon.userDisabled, false);
|
||||
|
||||
yield tearDownAddon(reloadedAddon);
|
||||
unpackedAddon.remove(true);
|
||||
addonDir.remove(true);
|
||||
});
|
||||
|
||||
add_task(function* test_manifest_changes_are_refreshed() {
|
||||
|
@ -124,7 +124,7 @@ add_task(function*() {
|
||||
|
||||
// Writing the manifest direct to the profile should work
|
||||
add_task(function*() {
|
||||
writeWebManifestForExtension({
|
||||
yield promiseWriteWebManifestForExtension({
|
||||
name: "Web Extension Name",
|
||||
version: "1.0",
|
||||
manifest_version: 2,
|
||||
@ -190,7 +190,7 @@ add_task(function* test_manifest_localization() {
|
||||
|
||||
// Missing version should cause a failure
|
||||
add_task(function*() {
|
||||
writeWebManifestForExtension({
|
||||
yield promiseWriteWebManifestForExtension({
|
||||
name: "Web Extension Name",
|
||||
manifest_version: 2,
|
||||
applications: {
|
||||
@ -213,7 +213,7 @@ add_task(function*() {
|
||||
|
||||
// Incorrect manifest version should cause a failure
|
||||
add_task(function*() {
|
||||
writeWebManifestForExtension({
|
||||
yield promiseWriteWebManifestForExtension({
|
||||
name: "Web Extension Name",
|
||||
version: "1.0",
|
||||
manifest_version: 1,
|
||||
|
@ -26,7 +26,7 @@ function promiseAddonStartup() {
|
||||
|
||||
// Test simple icon set parsing
|
||||
add_task(function*() {
|
||||
writeWebManifestForExtension({
|
||||
yield promiseWriteWebManifestForExtension({
|
||||
name: "Web Extension Name",
|
||||
version: "1.0",
|
||||
manifest_version: 2,
|
||||
@ -90,7 +90,7 @@ add_task(function*() {
|
||||
|
||||
// Test AddonManager.getPreferredIconURL for retina screen sizes
|
||||
add_task(function*() {
|
||||
writeWebManifestForExtension({
|
||||
yield promiseWriteWebManifestForExtension({
|
||||
name: "Web Extension Name",
|
||||
version: "1.0",
|
||||
manifest_version: 2,
|
||||
@ -136,7 +136,7 @@ add_task(function*() {
|
||||
|
||||
// Handles no icons gracefully
|
||||
add_task(function*() {
|
||||
writeWebManifestForExtension({
|
||||
yield promiseWriteWebManifestForExtension({
|
||||
name: "Web Extension Name",
|
||||
version: "1.0",
|
||||
manifest_version: 2,
|
||||
|
@ -79,7 +79,7 @@ add_task(function* test_unsigned_no_id_temp_install() {
|
||||
version: "1.0"
|
||||
};
|
||||
|
||||
const addonDir = writeWebManifestForExtension(manifest, gTmpD,
|
||||
const addonDir = yield promiseWriteWebManifestForExtension(manifest, gTmpD,
|
||||
"the-addon-sub-dir");
|
||||
const addon = yield AddonManager.installTemporaryAddon(addonDir);
|
||||
ok(addon.id, "ID should have been auto-generated");
|
||||
@ -112,9 +112,9 @@ add_task(function* test_multiple_no_id_extensions() {
|
||||
version: "1.0"
|
||||
};
|
||||
|
||||
const firstAddonDir = writeWebManifestForExtension(manifest, gTmpD,
|
||||
const firstAddonDir = yield promiseWriteWebManifestForExtension(manifest, gTmpD,
|
||||
"addon-sub-dir-one");
|
||||
const secondAddonDir = writeWebManifestForExtension(manifest, gTmpD,
|
||||
const secondAddonDir = yield promiseWriteWebManifestForExtension(manifest, gTmpD,
|
||||
"addon-sub-dir-two");
|
||||
const [firstAddon, secondAddon] = yield Promise.all([
|
||||
AddonManager.installTemporaryAddon(firstAddonDir),
|
||||
@ -155,7 +155,7 @@ add_task(function* test_bss_id() {
|
||||
let addon = yield promiseAddonByID(ID);
|
||||
do_check_eq(addon, null);
|
||||
|
||||
writeWebManifestForExtension(manifest, profileDir, ID);
|
||||
yield promiseWriteWebManifestForExtension(manifest, profileDir, ID);
|
||||
yield promiseRestartManager();
|
||||
|
||||
addon = yield promiseAddonByID(ID);
|
||||
@ -189,7 +189,7 @@ add_task(function* test_two_ids() {
|
||||
}
|
||||
}
|
||||
|
||||
writeWebManifestForExtension(manifest, profileDir, GOOD_ID);
|
||||
yield promiseWriteWebManifestForExtension(manifest, profileDir, GOOD_ID);
|
||||
yield promiseRestartManager();
|
||||
|
||||
let addon = yield promiseAddonByID(BAD_ID);
|
||||
@ -199,3 +199,168 @@ add_task(function* test_two_ids() {
|
||||
|
||||
addon.uninstall();
|
||||
});
|
||||
|
||||
// Test that strict_min_version and strict_max_version are enforced for
|
||||
// loading temporary extension.
|
||||
add_task(function* test_strict_min_max() {
|
||||
// the app version being compared to is 1.9.2
|
||||
const addonId = "strict_min_max@tests.mozilla.org";
|
||||
const MANIFEST = {
|
||||
name: "strict min max test",
|
||||
description: "test strict min and max with temporary loading",
|
||||
manifest_version: 2,
|
||||
version: "1.0",
|
||||
};
|
||||
|
||||
function flushAndRemove(file) {
|
||||
// flush JAR cache and remove the file
|
||||
Services.obs.notifyObservers(file, "flush-cache-entry", null);
|
||||
file.remove(true);
|
||||
}
|
||||
|
||||
// bad max good min
|
||||
let apps = {
|
||||
applications: {
|
||||
gecko: {
|
||||
id: addonId,
|
||||
strict_min_version: "1",
|
||||
strict_max_version: "1"
|
||||
},
|
||||
},
|
||||
}
|
||||
let testManifest = Object.assign(apps, MANIFEST);
|
||||
|
||||
let addonDir = yield promiseWriteWebManifestForExtension(testManifest, gTmpD,
|
||||
"the-addon-sub-dir");
|
||||
|
||||
let expectedMsg = new RegExp("Add-on strict_min_max@tests.mozilla.org is not compatible with application version. " +
|
||||
"add-on minVersion: 1, add-on maxVersion: 1");
|
||||
yield Assert.rejects(AddonManager.installTemporaryAddon(addonDir),
|
||||
expectedMsg,
|
||||
"Install rejects when specified maxVersion is not valid");
|
||||
|
||||
let addon = yield promiseAddonByID(addonId);
|
||||
do_check_eq(addon, null);
|
||||
flushAndRemove(addonDir);
|
||||
|
||||
// bad min good max
|
||||
apps = {
|
||||
applications: {
|
||||
gecko: {
|
||||
id: addonId,
|
||||
strict_min_version: "2",
|
||||
strict_max_version: "2"
|
||||
},
|
||||
},
|
||||
}
|
||||
testManifest = Object.assign(apps, MANIFEST);
|
||||
|
||||
addonDir = yield promiseWriteWebManifestForExtension(testManifest, gTmpD,
|
||||
"the-addon-sub-dir");
|
||||
|
||||
expectedMsg = new RegExp("Add-on strict_min_max@tests.mozilla.org is not compatible with application version. " +
|
||||
"add-on minVersion: 2, add-on maxVersion: 2");
|
||||
yield Assert.rejects(AddonManager.installTemporaryAddon(addonDir),
|
||||
expectedMsg,
|
||||
"Install rejects when specified minVersion is not valid");
|
||||
|
||||
addon = yield promiseAddonByID(addonId);
|
||||
do_check_eq(addon, null);
|
||||
flushAndRemove(addonDir);
|
||||
|
||||
// bad both
|
||||
apps = {
|
||||
applications: {
|
||||
gecko: {
|
||||
id: addonId,
|
||||
strict_min_version: "2",
|
||||
strict_max_version: "1"
|
||||
},
|
||||
},
|
||||
}
|
||||
testManifest = Object.assign(apps, MANIFEST);
|
||||
|
||||
addonDir = yield promiseWriteWebManifestForExtension(testManifest, gTmpD,
|
||||
"the-addon-sub-dir");
|
||||
|
||||
expectedMsg = new RegExp("Add-on strict_min_max@tests.mozilla.org is not compatible with application version. " +
|
||||
"add-on minVersion: 2, add-on maxVersion: 1");
|
||||
yield Assert.rejects(AddonManager.installTemporaryAddon(addonDir),
|
||||
expectedMsg,
|
||||
"Install rejects when specified minVersion and maxVersion are not valid");
|
||||
|
||||
addon = yield promiseAddonByID(addonId);
|
||||
do_check_eq(addon, null);
|
||||
flushAndRemove(addonDir);
|
||||
|
||||
// good both
|
||||
apps = {
|
||||
applications: {
|
||||
gecko: {
|
||||
id: addonId,
|
||||
strict_min_version: "1",
|
||||
strict_max_version: "2"
|
||||
},
|
||||
},
|
||||
}
|
||||
testManifest = Object.assign(apps, MANIFEST);
|
||||
|
||||
addonDir = yield promiseWriteWebManifestForExtension(testManifest, gTmpD,
|
||||
"strict_min_max");
|
||||
|
||||
yield AddonManager.installTemporaryAddon(addonDir);
|
||||
addon = yield promiseAddonByID(addonId);
|
||||
|
||||
do_check_neq(addon, null);
|
||||
do_check_eq(addon.id, addonId);
|
||||
addon.uninstall();
|
||||
flushAndRemove(addonDir);
|
||||
|
||||
// good only min
|
||||
let newId = "strict_min_only@tests.mozilla.org";
|
||||
apps = {
|
||||
applications: {
|
||||
gecko: {
|
||||
id: newId,
|
||||
strict_min_version: "1",
|
||||
},
|
||||
},
|
||||
}
|
||||
testManifest = Object.assign(apps, MANIFEST);
|
||||
|
||||
addonDir = yield promiseWriteWebManifestForExtension(testManifest, gTmpD,
|
||||
"strict_min_only");
|
||||
|
||||
yield AddonManager.installTemporaryAddon(addonDir);
|
||||
addon = yield promiseAddonByID(newId);
|
||||
|
||||
do_check_neq(addon, null);
|
||||
do_check_eq(addon.id, newId);
|
||||
|
||||
addon.uninstall();
|
||||
flushAndRemove(addonDir);
|
||||
|
||||
// good only max
|
||||
newId = "strict_max_only@tests.mozilla.org";
|
||||
apps = {
|
||||
applications: {
|
||||
gecko: {
|
||||
id: newId,
|
||||
strict_max_version: "2",
|
||||
},
|
||||
},
|
||||
}
|
||||
testManifest = Object.assign(apps, MANIFEST);
|
||||
|
||||
addonDir = yield promiseWriteWebManifestForExtension(testManifest, gTmpD,
|
||||
"strict_max_only");
|
||||
|
||||
yield AddonManager.installTemporaryAddon(addonDir);
|
||||
addon = yield promiseAddonByID(newId);
|
||||
|
||||
do_check_neq(addon, null);
|
||||
do_check_eq(addon.id, newId);
|
||||
|
||||
addon.uninstall();
|
||||
flushAndRemove(addonDir);
|
||||
});
|
||||
|
@ -37,7 +37,7 @@ add_task(function* test_bad_unpacked_path() {
|
||||
|
||||
for (let dir of directories) {
|
||||
try {
|
||||
writeWebManifestForExtension(manifest, profileDir, dir);
|
||||
yield promiseWriteWebManifestForExtension(manifest, profileDir, dir);
|
||||
} catch (ex) {
|
||||
// This can fail if the underlying filesystem (looking at you windows)
|
||||
// doesn't handle some of the characters in the ID. In that case,
|
||||
|
@ -6,7 +6,8 @@ tags = addons
|
||||
[test_AddonRepository.js]
|
||||
[test_reload.js]
|
||||
# Bug 676992: test consistently hangs on Android
|
||||
skip-if = os == "android"
|
||||
# There's a problem removing a temp file without manually clearing the cache on Windows
|
||||
skip-if = os == "android" || os == "win"
|
||||
tags = webextensions
|
||||
[test_AddonRepository_cache.js]
|
||||
# Bug 676992: test consistently hangs on Android
|
||||
|
Loading…
Reference in New Issue
Block a user