Bug 1009116 - Redo resizing architecture of panelmultiview. r=Gijs

The height of the "panelmultiview" binding is now determined by the stack layout code, and doesn't have to be calculated manually via JavaScript anymore. This allows the removal of mutation and overflow observers, and reduces the number of synchronous layouts being made.

There is still a workaround included for wrapping blocks not being taken into account in height calculations.

MozReview-Commit-ID: 9rrPU5O5hUx

--HG--
extra : rebase_source : b872c14a553c4293ac476d5d22c634a5a0f6cb24
extra : intermediate-source : bf96469b6ea7daee29eb75a60d11f017a1c86a64
extra : source : 719bb4e7286fbd3baf32061929e4b7d9f953c671
This commit is contained in:
Paolo Amadini 2017-05-23 17:08:01 +01:00
Родитель c6a0819b83
Коммит 33ebc4466d
9 изменённых файлов: 200 добавлений и 288 удалений

Просмотреть файл

@ -7334,7 +7334,8 @@ var gIdentityHandler = {
this._sharingState = tab._sharingState;
if (this._identityPopup.state == "open") {
this._handleHeightChange(() => this.updateSitePermissions());
this.updateSitePermissions();
this._identityPopupMultiView.descriptionHeightWorkaround();
}
},
@ -7845,20 +7846,6 @@ var gIdentityHandler = {
}
},
_handleHeightChange(aFunction, aWillShowReloadHint) {
let heightBefore = getComputedStyle(this._permissionList).height;
aFunction();
let heightAfter = getComputedStyle(this._permissionList).height;
// Showing the reload hint increases the height, we need to account for it.
if (aWillShowReloadHint) {
heightAfter = parseInt(heightAfter) +
parseInt(getComputedStyle(this._permissionList.nextSibling).height);
}
let heightChange = parseInt(heightAfter) - parseInt(heightBefore);
if (heightChange)
this._identityPopupMultiView.setHeightToFit(heightChange);
},
_createPermissionItem(aPermission) {
let container = document.createElement("hbox");
container.setAttribute("class", "identity-popup-permission-item");
@ -7895,9 +7882,7 @@ var gIdentityHandler = {
button.setAttribute("tooltiptext", tooltiptext);
button.addEventListener("command", () => {
let browser = gBrowser.selectedBrowser;
// Only resize the window if the reload hint was previously hidden.
this._handleHeightChange(() => this._permissionList.removeChild(container),
this._permissionReloadHint.hasAttribute("hidden"));
this._permissionList.removeChild(container);
if (aPermission.inUse &&
["camera", "microphone", "screen"].includes(aPermission.id)) {
let windowId = this._sharingState.windowId;
@ -7928,6 +7913,7 @@ var gIdentityHandler = {
SitePermissions.remove(gBrowser.currentURI, aPermission.id, browser);
this._permissionReloadHint.removeAttribute("hidden");
this._identityPopupMultiView.descriptionHeightWorkaround();
// Set telemetry values for clearing a permission
let histogram = Services.telemetry.getKeyedHistogramById("WEB_PERMISSION_CLEARED");

Просмотреть файл

@ -16,7 +16,8 @@
</broadcasterset>
<panelmultiview id="identity-popup-multiView"
mainViewId="identity-popup-mainView">
mainViewId="identity-popup-mainView"
descriptionheightworkaround="true">
<panelview id="identity-popup-mainView" flex="1">
<!-- Security Section -->
@ -96,7 +97,7 @@
</panelview>
<!-- Security SubView -->
<panelview id="identity-popup-securityView" flex="1">
<panelview id="identity-popup-securityView">
<vbox id="identity-popup-securityView-header">
<label class="plain">
<label class="identity-popup-headline identity-popup-host"></label>
@ -108,7 +109,7 @@
when-connection="secure secure-ev">&identity.connectionSecure;</description>
</vbox>
<vbox id="identity-popup-securityView-body" flex="1">
<vbox id="identity-popup-securityView-body" class="panel-view-body-unscrollable">
<!-- (EV) Certificate Information -->
<description id="identity-popup-content-verified-by"
when-connection="secure-ev">&identity.connectionVerified2;</description>

Просмотреть файл

@ -171,20 +171,6 @@ this.PanelMultiView = class {
return this.node.getAttribute("mainViewIsSubView") == "true";
}
get ignoreMutations() {
return this._ignoreMutations;
}
set ignoreMutations(val) {
this._ignoreMutations = val;
if (!val && this._panel.state == "open") {
if (this.showingSubView) {
this._syncContainerWithSubView();
} else {
this._syncContainerWithMainView();
}
}
}
get _transitioning() {
return this.__transitioning;
}
@ -264,13 +250,6 @@ this.PanelMultiView = class {
} else {
this._panel.addEventListener("popupshown", this);
this._clickCapturer.addEventListener("click", this);
this._subViews.addEventListener("overflow", this);
this._mainViewContainer.addEventListener("overflow", this);
// Get a MutationObserver ready to react to subview size changes. We
// only attach this MutationObserver when a subview is being displayed.
this._subViewObserver = new window.MutationObserver(this._syncContainerWithSubView.bind(this));
this._mainViewObserver = new window.MutationObserver(this._syncContainerWithMainView.bind(this));
this._mainViewContainer.setAttribute("panelid", this._panel.id);
@ -290,7 +269,8 @@ this.PanelMultiView = class {
set: (val) => this[property] = val
});
});
["goBack", "setHeightToFit", "setMainView", "showMainView", "showSubView"].forEach(method => {
["goBack", "descriptionHeightWorkaround", "setMainView", "showMainView",
"showSubView"].forEach(method => {
Object.defineProperty(this.node, method, {
enumerable: true,
value: (...args) => this[method](...args)
@ -305,10 +285,6 @@ this.PanelMultiView = class {
if (this.panelViews) {
this.panelViews.clear();
} else {
this._mainViewObserver.disconnect();
this._subViewObserver.disconnect();
this._subViews.removeEventListener("overflow", this);
this._mainViewContainer.removeEventListener("overflow", this);
this._clickCapturer.removeEventListener("click", this);
}
this._panel.removeEventListener("popupshowing", this);
@ -345,7 +321,6 @@ this.PanelMultiView = class {
}
} else {
if (this._mainView) {
this._mainViewObserver.disconnect();
this._subViews.appendChild(this._mainView);
this._mainView.removeAttribute("mainview");
}
@ -364,14 +339,11 @@ this.PanelMultiView = class {
let evt = new this.window.CustomEvent("ViewHiding", { bubbles: true, cancelable: true });
viewNode.dispatchEvent(evt);
viewNode.removeAttribute("current");
this._currentSubView = null;
this._subViewObserver.disconnect();
this._setViewContainerHeight(this._mainViewHeight);
this.node.setAttribute("viewtype", "main");
this._transitionHeight(() => {
viewNode.removeAttribute("current");
this._currentSubView = null;
this.node.setAttribute("viewtype", "main");
});
}
this._shiftMainView();
@ -479,9 +451,9 @@ this.PanelMultiView = class {
//
// All three of these actions make use of CSS transformations, so they
// should all occur simultaneously.
this.node.setAttribute("viewtype", "subview");
if (this.panelViews && playTransition) {
this.node.setAttribute("viewtype", "subview");
// Sliding the next subview in means that the previous panelview stays
// where it is and the active panelview slides in from the left in LTR
// mode, right in RTL mode.
@ -604,34 +576,86 @@ this.PanelMultiView = class {
});
}, { once: true });
} else if (!this.panelViews) {
this._shiftMainView(aAnchor);
this._mainViewHeight = this._viewStack.clientHeight;
let newHeight = this._heightOfSubview(viewNode, this._subViews);
this._setViewContainerHeight(newHeight);
this._subViewObserver.observe(viewNode, {
attributes: true,
characterData: true,
childList: true,
subtree: true
this._transitionHeight(() => {
viewNode.setAttribute("current", true);
this.node.setAttribute("viewtype", "subview");
// Now that the subview is visible, we can check the height of the
// description elements it contains.
this.descriptionHeightWorkaround(viewNode);
});
this._shiftMainView(aAnchor);
}
})();
}
_setViewContainerHeight(aHeight) {
let container = this._viewContainer;
this._transitioning = true;
/**
* Applies the height transition for which <panelmultiview> is designed.
*
* The height transition involves two elements, the viewContainer and its only
* immediate child the viewStack. In order for this to work correctly, the
* viewContainer must have "overflow: hidden;" and the two elements must have
* no margins or padding. This means that the height of the viewStack is never
* limited by the viewContainer, but when the height of the container is not
* constrained it matches the height of the viewStack.
*
* @param changeFn
* This synchronous function is called to make the DOM changes
* that will result in a new height of the viewStack.
*/
_transitionHeight(changeFn) {
if (this._panel.state != "open") {
changeFn();
return;
}
let onTransitionEnd = () => {
container.removeEventListener("transitionend", onTransitionEnd);
this._transitioning = false;
};
// Lock the dimensions of the window that hosts the popup panel. This
// in turn constrains the height of the viewContainer.
let rect = this._panel.popupBoxObject.getOuterScreenRect();
this._panel.setAttribute("width", rect.width);
this._panel.setAttribute("height", rect.height);
container.addEventListener("transitionend", onTransitionEnd);
container.style.height = `${aHeight}px`;
// Read the current height of the viewStack. If we are in the middle
// of a transition, this is the actual height of the element at this
// point in time.
let oldHeight = this._dwu.getBoundsWithoutFlushing(this._viewStack).height;
// Make the necessary DOM changes, and remove the "height" property of the
// viewStack to ensure that we read its final value even if we are in the
// middle of a transition. To avoid flickering, we have to prevent the panel
// from being painted in this temporary state, which requires a synchronous
// layout when reading the new height.
this._viewStack.style.removeProperty("height");
changeFn();
let newHeight = this._viewStack.getBoundingClientRect().height;
// Now we can allow the popup panel to resize again. This must occur
// in the same tick as the code below, but we can do this before
// setting the starting height in case the transition is not needed.
this._panel.removeAttribute("width");
this._panel.removeAttribute("height");
if (oldHeight != newHeight) {
// Height transitions can only occur between two numeric values, and
// cannot start if the height is not set. In case a transition is
// needed, we have to set the height to the old value, then force a
// synchronous layout so the panel won't resize unexpectedly.
this._viewStack.style.height = oldHeight + "px";
this._viewStack.getBoundingClientRect().height;
// We can now set the new height to start the transition, but
// before doing that we set up a listener to reset the height to
// "auto" at the end, so that DOM changes made after the
// transition ends are still reflected by the height of the panel.
let onTransitionEnd = event => {
if (event.target != this._viewStack) {
return;
}
this._viewStack.removeEventListener("transitionend", onTransitionEnd);
this._viewStack.style.removeProperty("height");
};
this._viewStack.addEventListener("transitionend", onTransitionEnd);
this._viewStack.style.height = newHeight + "px";
}
}
_shiftMainView(aAnchor) {
@ -687,16 +711,6 @@ this.PanelMultiView = class {
case "mousemove":
this._resetKeyNavigation();
break;
case "overflow":
if (!this.panelViews && aEvent.target.localName == "vbox") {
// Resize the right view on the next tick.
if (this.showingSubView) {
this.window.setTimeout(this._syncContainerWithSubView.bind(this), 0);
} else if (!this.transitioning) {
this.window.setTimeout(this._syncContainerWithMainView.bind(this), 0);
}
}
break;
case "popupshowing":
this.node.setAttribute("panelopen", "true");
// Bug 941196 - The panel can get taller when opening a subview. Disabling
@ -705,30 +719,46 @@ this.PanelMultiView = class {
// direction that the panel originally opened in. This property resets
// every time the popup closes, which is why we have to set it each time.
this._panel.autoPosition = false;
if (!this.panelViews) {
this._syncContainerWithMainView();
this._mainViewObserver.observe(this._mainView, {
attributes: true,
characterData: true,
childList: true,
subtree: true
});
} else {
if (this.panelViews) {
this.window.addEventListener("keydown", this);
this._panel.addEventListener("mousemove", this);
}
break;
case "popupshown":
this._setMaxHeight();
// Now that the main view is visible, we can check the height of the
// description elements it contains.
this.descriptionHeightWorkaround();
// Now that the panel has opened, we can compute the distance from its
// anchor to the available margin of the screen, based on whether the
// panel actually opened towards the top or the bottom. We use this to
// limit its maximum height, which is relevant when opening a subview.
let maxHeight;
if (this._panel.alignmentPosition.startsWith("before_")) {
maxHeight = this._panel.getOuterScreenRect().bottom -
this.window.screen.availTop;
} else {
maxHeight = this.window.screen.availTop +
this.window.screen.availHeight -
this._panel.getOuterScreenRect().top;
}
// To go from the maximum height of the panel to the maximum height of
// the view stack, we start by subtracting the height of the arrow box.
// We don't need to trigger a new layout because this does not change.
let arrowBox = this.document.getAnonymousElementByAttribute(
this._panel, "anonid", "arrowbox");
maxHeight -= this._dwu.getBoundsWithoutFlushing(arrowBox).height;
// We subtract a fixed margin to account for variable borders. We don't
// try to measure this accurately so it's faster, we don't depend on
// the arrowpanel structure, and we don't hit rounding errors. Instead,
// we use a value that is much greater than the typical borders and
// makes sense visually.
const EXTRA_MARGIN_PX = 8;
this._viewStack.style.maxHeight = (maxHeight - EXTRA_MARGIN_PX) + "px";
break;
case "popuphidden":
this.node.removeAttribute("panelopen");
this._mainView.style.removeProperty("height");
this.showMainView();
if (!this.panelViews) {
this._mainViewObserver.disconnect();
} else {
if (this.panelViews) {
this.window.removeEventListener("keydown", this);
this._panel.removeEventListener("mousemove", this);
this._resetKeyNavigation();
@ -872,133 +902,47 @@ this.PanelMultiView = class {
return buttons;
}
_shouldSetPosition() {
return this.node.getAttribute("nosubviews") == "true";
}
_shouldSetHeight() {
return this.node.getAttribute("nosubviews") != "true";
}
_setMaxHeight() {
if (!this._shouldSetHeight())
return;
// Ignore the mutation that'll fire when we set the height of
// the main view.
this.ignoreMutations = true;
this._mainView.style.height = this.node.getBoundingClientRect().height + "px";
this.ignoreMutations = false;
}
_adjustContainerHeight() {
if (!this.ignoreMutations && !this.showingSubView && !this._transitioning) {
let height;
if (this.showingSubViewAsMainView) {
height = this._heightOfSubview(this._mainView);
} else {
height = this._mainView.scrollHeight;
}
this._viewContainer.style.height = height + "px";
}
}
_syncContainerWithSubView() {
// Check that this panel is still alive:
if (!this._panel || !this._panel.parentNode) {
return;
}
if (!this.ignoreMutations && this.showingSubView) {
let newHeight = this._heightOfSubview(this._currentSubView, this._subViews);
this._viewContainer.style.height = newHeight + "px";
}
}
_syncContainerWithMainView() {
// Check that this panel is still alive:
if (!this._panel || !this._panel.parentNode) {
return;
}
if (this._shouldSetPosition()) {
this._panel.adjustArrowPosition();
}
if (this._shouldSetHeight()) {
this._adjustContainerHeight();
}
}
/**
* Call this when the height of one of your views (the main view or a
* subview) changes and you want the heights of the multiview and panel
* to be the same as the view's height.
* If the caller can give a hint of the expected height change with the
* optional aExpectedChange parameter, it prevents flicker.
* If the main view or a subview contains wrapping elements, the attribute
* "descriptionheightworkaround" should be set on the view to force all the
* "description" elements to a fixed height. If the attribute is set and the
* visibility, contents, or width of any of these elements changes, this
* function should be called to refresh the calculated heights.
*
* @note While both "label" and "description" elements may contain wrapping
* text, only "description" elements are used that way in panels.
*
* @param viewNode
* Indicates the node to scan for descendant elements. This is the main
* view if omitted.
*/
setHeightToFit(aExpectedChange) {
// Set the max-height to zero, wait until the height is actually
// updated, and then remove it. If it's not removed, weird things can
// happen, like widgets in the panel won't respond to clicks even
// though they're visible.
const {window} = this;
let count = 5;
let height = window.getComputedStyle(this.node).height;
if (aExpectedChange)
this.node.style.maxHeight = (parseInt(height, 10) + aExpectedChange) + "px";
else
this.node.style.maxHeight = "0";
let interval = window.setInterval(() => {
if (height != window.getComputedStyle(this.node).height || --count == 0) {
window.clearInterval(interval);
this.node.style.removeProperty("max-height");
}
}, 0);
}
descriptionHeightWorkaround(viewNode = this._mainView) {
if (!this.node.hasAttribute("descriptionheightworkaround")) {
// This view does not require the workaround.
return;
}
_heightOfSubview(aSubview, aContainerToCheck) {
function getFullHeight(element) {
// XXXgijs: unfortunately, scrollHeight rounds values, and there's no alternative
// that works with overflow: auto elements. Fortunately for us,
// we have exactly 1 (potentially) scrolling element in here (the subview body),
// and rounding 1 value is OK - rounding more than 1 and adding them means we get
// off-by-1 errors. Now we might be off by a subpixel, but we care less about that.
// So, use scrollHeight *only* if the element is vertically scrollable.
let height;
let elementCS;
if (element.scrollTopMax) {
height = element.scrollHeight;
// Bounding client rects include borders, scrollHeight doesn't:
elementCS = win.getComputedStyle(element);
height += parseFloat(elementCS.borderTopWidth) +
parseFloat(elementCS.borderBottomWidth);
} else {
height = element.getBoundingClientRect().height;
if (height > 0) {
elementCS = win.getComputedStyle(element);
}
// We batch DOM changes together in order to reduce synchronous layouts.
// First we reset any change we may have made previously. The first time
// this is called, and in the best case scenario, this has no effect.
let items = [];
for (let element of viewNode.getElementsByTagName("description")) {
element.style.removeProperty("height");
items.push({ element });
}
// We now read the computed style to store the height of any element that
// may contain wrapping text, which will be zero if the element is hidden.
// This might trigger a synchronous layout, but if this function was called
// from a _transitionHeight callback and there are no description elements
// visible, then _transitionHeight will not trigger a layout again.
for (let item of items) {
item.height = item.element.getBoundingClientRect().height;
}
// Now we can make all the necessary DOM changes at once.
for (let item of items) {
if (item.height) {
item.element.style.height = item.height + "px";
}
if (elementCS) {
// Include margins - but not borders or paddings because they
// were dealt with above.
height += parseFloat(elementCS.marginTop) + parseFloat(elementCS.marginBottom);
}
return height;
}
let win = aSubview.ownerGlobal;
let body = aSubview.querySelector(".panel-subview-body");
let height = getFullHeight(body || aSubview);
if (body) {
let header = aSubview.querySelector(".panel-subview-header");
let footer = aSubview.querySelector(".panel-subview-footer");
height += (header ? getFullHeight(header) : 0) +
(footer ? getFullHeight(footer) : 0);
}
if (aContainerToCheck) {
let containerCS = win.getComputedStyle(aContainerToCheck);
height += parseFloat(containerCS.paddingTop) + parseFloat(containerCS.paddingBottom);
}
return Math.ceil(height);
}
}

Просмотреть файл

@ -6,20 +6,21 @@
pointer-events: none;
}
.panel-mainview,
.panel-viewcontainer,
.panel-viewstack {
.panel-viewcontainer {
overflow: hidden;
}
.panel-viewstack {
position: relative;
transition: height var(--panelui-subview-transition-duration);
}
.panel-subviews {
-moz-stack-sizing: ignore;
-moz-stack-sizing: ignore-horizontal;
transform: translateX(0);
overflow-y: auto;
}
.panel-viewstack[viewtype="main"] > .panel-subviews {
-moz-stack-sizing: ignore;
}
.panel-subviews[panelopen] {

Просмотреть файл

@ -18,7 +18,7 @@
<stylesheet src="chrome://browser/content/customizableui/panelUI.css"/>
</resources>
<content>
<xul:box anonid="viewContainer" class="panel-viewcontainer" xbl:inherits="panelopen,viewtype,transitioning">
<xul:vbox anonid="viewContainer" class="panel-viewcontainer" xbl:inherits="panelopen,viewtype,transitioning">
<xul:stack anonid="viewStack" xbl:inherits="viewtype,transitioning" class="panel-viewstack">
<xul:vbox anonid="mainViewContainer" class="panel-mainview" xbl:inherits="viewtype"/>
@ -37,18 +37,18 @@
<children includes="panelview"/>
</xul:vbox>
</xul:stack>
</xul:box>
</xul:vbox>
</content>
<implementation>
<constructor><![CDATA[
const {PanelMultiView} = Components.utils.import("resource:///modules/PanelMultiView.jsm", {});
this.instance = new PanelMultiView(this);
]]></constructor>
<destructor><![CDATA[
]]></constructor>
<destructor><![CDATA[
this.instance.destructor();
]]></destructor>
</implementation>
]]></destructor>
</implementation>
</binding>
<binding id="photonpanelmultiview" extends="chrome://browser/content/customizableui/panelUI.xml#panelmultiview">

Просмотреть файл

@ -303,20 +303,6 @@ const DownloadsPanel = {
case "keypress":
this._onKeyPress(aEvent);
break;
case "popupshown":
if (this.setHeightToFitOnShow) {
this.setHeightToFitOnShow = false;
this.setHeightToFit();
}
break;
}
},
setHeightToFit() {
if (this._state == this.kStateShown) {
DownloadsBlockedSubview.view.setHeightToFit();
} else {
this.setHeightToFitOnShow = true;
}
},
@ -406,8 +392,6 @@ const DownloadsPanel = {
// Handle keypress to be able to preventDefault() events before they reach
// the richlistbox, for keyboard navigation.
this.panel.addEventListener("keypress", this);
// Handle height adjustment on show.
this.panel.addEventListener("popupshown", this);
},
/**
@ -417,7 +401,6 @@ const DownloadsPanel = {
_unattachEventListeners() {
this.panel.removeEventListener("keydown", this);
this.panel.removeEventListener("keypress", this);
this.panel.removeEventListener("popupshown", this);
},
_onKeyPress(aEvent) {
@ -858,9 +841,6 @@ const DownloadsView = {
}
this._itemCountChanged();
// Adjust the panel height if we removed items.
DownloadsPanel.setHeightToFit();
},
/**
@ -1545,10 +1525,6 @@ const DownloadsFooter = {
} else {
this._footerNode.removeAttribute("showingsummary");
}
if (!aValue && this._showingSummary) {
// Make sure the panel's height shrinks when the summary is hidden.
DownloadsPanel.setHeightToFit();
}
this._showingSummary = aValue;
}
return aValue;

Просмотреть файл

@ -108,22 +108,20 @@
<panelmultiview id="downloadsPanel-multiView"
mainViewId="downloadsPanel-mainView"
align="stretch">
descriptionheightworkaround="true">
<panelview id="downloadsPanel-mainView"
flex="1"
align="stretch">
<richlistbox id="downloadsListBox"
context="downloadsContextMenu"
onmouseover="DownloadsView.onDownloadMouseOver(event);"
onmouseout="DownloadsView.onDownloadMouseOut(event);"
oncontextmenu="DownloadsView.onDownloadContextMenu(event);"
ondragstart="DownloadsView.onDownloadDragStart(event);"/>
<description id="emptyDownloads"
mousethrough="always">
&downloadsPanelEmpty.label;
</description>
<spacer flex="1"/>
<panelview id="downloadsPanel-mainView">
<vbox class="panel-view-body-unscrollable">
<richlistbox id="downloadsListBox"
context="downloadsContextMenu"
onmouseover="DownloadsView.onDownloadMouseOver(event);"
onmouseout="DownloadsView.onDownloadMouseOut(event);"
oncontextmenu="DownloadsView.onDownloadContextMenu(event);"
ondragstart="DownloadsView.onDownloadDragStart(event);"/>
<description id="emptyDownloads"
mousethrough="always"
value="&downloadsPanelEmpty.label;"/>
</vbox>
<vbox id="downloadsFooter"
class="downloadsPanelFooter">
<stack>
@ -159,13 +157,12 @@
</vbox>
</panelview>
<panelview id="downloadsPanel-blockedSubview"
orient="vertical"
flex="1">
<description id="downloadsPanel-blockedSubview-title"/>
<description id="downloadsPanel-blockedSubview-details1"/>
<description id="downloadsPanel-blockedSubview-details2"/>
<spacer flex="1"/>
<panelview id="downloadsPanel-blockedSubview">
<vbox class="panel-view-body-unscrollable">
<description id="downloadsPanel-blockedSubview-title"/>
<description id="downloadsPanel-blockedSubview-details1"/>
<description id="downloadsPanel-blockedSubview-details2"/>
</vbox>
<hbox id="downloadsPanel-blockedSubview-buttons"
class="downloadsPanelFooter"
align="stretch">

Просмотреть файл

@ -198,7 +198,8 @@ panelmultiview[nosubviews=true] > .panel-viewcontainer > .panel-viewstack > .pan
display: none;
}
.panel-viewstack:not([viewtype="main"]) > .panel-mainview > #PanelUI-mainView {
panelview {
-moz-box-orient: vertical;
-moz-box-flex: 1;
}
@ -208,6 +209,11 @@ panelmultiview[nosubviews=true] > .panel-viewcontainer > .panel-viewstack > .pan
-moz-box-flex: 1;
}
.panel-view-body-unscrollable {
overflow: hidden;
-moz-box-flex: 1;
}
#PanelUI-popup .panel-subview-body {
margin: -4px;
padding: 4px 4px;
@ -225,6 +231,8 @@ panelmultiview[nosubviews=true] > .panel-viewcontainer > .panel-viewstack > .pan
border-bottom: 1px solid var(--panel-separator-color);
color: GrayText;
font-variant: small-caps;
/* Workaround for min-height not being accounted for in vertical layout. */
height: 41px;
}
.cui-widget-panelview .panel-subview-header,

Просмотреть файл

@ -15,7 +15,6 @@
#downloadsPanel > .panel-arrowcontainer > .panel-arrowcontent {
overflow: hidden;
display: block;
}
#downloadsPanel > .panel-arrowcontainer > .panel-arrowcontent,