Backed out changeset 2f74f8f2ed19 (bug 1689816) for causing reftest failures on skip-ink-multiline-position.html.

This commit is contained in:
Marian-Vasile Laza
2022-09-26 23:24:03 +03:00
parent b7f6194b8d
commit 338fc0a451
72 changed files with 1250 additions and 339 deletions

View File

@@ -75,6 +75,7 @@
#include "mozilla/Services.h" #include "mozilla/Services.h"
#include "mozilla/StaticPrefs_accessibility.h" #include "mozilla/StaticPrefs_accessibility.h"
#include "mozilla/SVGGeometryFrame.h" #include "mozilla/SVGGeometryFrame.h"
#include "nsDeckFrame.h"
#include "XULAlertAccessible.h" #include "XULAlertAccessible.h"
#include "XULComboboxAccessible.h" #include "XULComboboxAccessible.h"
@@ -442,20 +443,63 @@ LocalAccessible* nsAccessibilityService::GetRootDocumentAccessible(
return nullptr; return nullptr;
} }
void nsAccessibilityService::NotifyOfTabPanelVisibilityChange( void nsAccessibilityService::DeckPanelSwitched(PresShell* aPresShell,
PresShell* aPresShell, Element* aPanel, bool aNowVisible) { nsIContent* aDeckNode,
MOZ_ASSERT(aPanel->GetParent()->IsXULElement(nsGkAtoms::tabpanels)); nsIFrame* aPrevBoxFrame,
nsIFrame* aCurrentBoxFrame) {
DocAccessible* document = GetDocAccessible(aPresShell); DocAccessible* document = GetDocAccessible(aPresShell);
if (!document) { if (!document) {
return; return;
} }
// A deck with a LocalAccessible is a tabpanels element.
const bool isTabPanels = document->HasAccessible(aDeckNode);
MOZ_ASSERT(!isTabPanels || aDeckNode->IsXULElement(nsGkAtoms::tabpanels),
"A deck with a LocalAccessible should be a tabpanels element");
if (LocalAccessible* acc = document->GetAccessible(aPanel)) { if (aPrevBoxFrame) {
nsIContent* panelNode = aPrevBoxFrame->GetContent();
#ifdef A11Y_LOG
if (logging::IsEnabled(logging::eTree)) {
logging::MsgBegin("TREE", "deck panel unselected");
logging::Node("container", panelNode);
logging::Node("content", aDeckNode);
logging::MsgEnd();
}
#endif
if (isTabPanels) {
// Tabpanels are accessible even when not selected.
if (LocalAccessible* acc = document->GetAccessible(panelNode)) {
RefPtr<AccEvent> event = RefPtr<AccEvent> event =
new AccStateChangeEvent(acc, states::OFFSCREEN, aNowVisible); new AccStateChangeEvent(acc, states::OFFSCREEN, true);
document->FireDelayedEvent(event); document->FireDelayedEvent(event);
} }
} else {
document->ContentRemoved(panelNode);
}
}
if (aCurrentBoxFrame) {
nsIContent* panelNode = aCurrentBoxFrame->GetContent();
#ifdef A11Y_LOG
if (logging::IsEnabled(logging::eTree)) {
logging::MsgBegin("TREE", "deck panel selected");
logging::Node("container", panelNode);
logging::Node("content", aDeckNode);
logging::MsgEnd();
}
#endif
if (isTabPanels) {
// Tabpanels are accessible even when not selected, so we don't have to
// insert a LocalAccessible.
if (LocalAccessible* acc = document->GetAccessible(panelNode)) {
RefPtr<AccEvent> event =
new AccStateChangeEvent(acc, states::OFFSCREEN, false);
document->FireDelayedEvent(event);
}
} else {
document->ContentInserted(panelNode, panelNode->GetNextSibling());
}
}
} }
void nsAccessibilityService::ContentRangeInserted(PresShell* aPresShell, void nsAccessibilityService::ContentRangeInserted(PresShell* aPresShell,
@@ -1084,6 +1128,16 @@ LocalAccessible* nsAccessibilityService::CreateAccessible(
// XUL accessibles. // XUL accessibles.
if (!newAcc && content->IsXULElement()) { if (!newAcc && content->IsXULElement()) {
// No accessible for not selected deck panel and its children.
if (!aContext->IsXULTabpanels()) {
nsDeckFrame* deckFrame = do_QueryFrame(frame->GetParent());
if (deckFrame && deckFrame->GetSelectedBox() != frame) {
if (aIsSubtreeHidden) *aIsSubtreeHidden = true;
return nullptr;
}
}
if (content->IsXULElement(nsGkAtoms::panel)) { if (content->IsXULElement(nsGkAtoms::panel)) {
// We filter here instead of in the XUL map because // We filter here instead of in the XUL map because
// if we filter there and return null, we still end up // if we filter there and return null, we still end up

View File

@@ -153,6 +153,13 @@ class nsAccessibilityService final : public mozilla::a11y::DocManager,
void GetStringRelationType(uint32_t aRelationType, nsAString& aString); void GetStringRelationType(uint32_t aRelationType, nsAString& aString);
// nsAccesibilityService // nsAccesibilityService
/**
* Notification used to update the accessible tree when deck panel is
* switched.
*/
void DeckPanelSwitched(mozilla::PresShell* aPresShell, nsIContent* aDeckNode,
nsIFrame* aPrevBoxFrame, nsIFrame* aCurrentBoxFrame);
/** /**
* Notification used to update the accessible tree when new content is * Notification used to update the accessible tree when new content is
* inserted. * inserted.
@@ -233,10 +240,6 @@ class nsAccessibilityService final : public mozilla::a11y::DocManager,
void NotifyOfComputedStyleChange(mozilla::PresShell* aPresShell, void NotifyOfComputedStyleChange(mozilla::PresShell* aPresShell,
nsIContent* aContent); nsIContent* aContent);
void NotifyOfTabPanelVisibilityChange(mozilla::PresShell* aPresShell,
mozilla::dom::Element* aPanel,
bool aVisible);
void NotifyOfResolutionChange(mozilla::PresShell* aPresShell, void NotifyOfResolutionChange(mozilla::PresShell* aPresShell,
float aResolution); float aResolution);

View File

@@ -14,6 +14,7 @@
#include "nsAccCache.h" #include "nsAccCache.h"
#include "nsAccessiblePivot.h" #include "nsAccessiblePivot.h"
#include "nsAccUtils.h" #include "nsAccUtils.h"
#include "nsDeckFrame.h"
#include "nsEventShell.h" #include "nsEventShell.h"
#include "nsLayoutUtils.h" #include "nsLayoutUtils.h"
#include "nsTextEquivUtils.h" #include "nsTextEquivUtils.h"
@@ -1051,6 +1052,21 @@ LocalAccessible* DocAccessible::GetAccessibleOrContainer(
return nullptr; return nullptr;
} }
// Check if node is in an unselected deck panel
if (aNoContainerIfPruned && currNode->IsXULElement()) {
if (nsIFrame* frame = currNode->AsContent()->GetPrimaryFrame()) {
nsDeckFrame* deckFrame = do_QueryFrame(frame->GetParent());
if (deckFrame && deckFrame->GetSelectedBox() != frame) {
// If deck is not a <tabpanels>, return null
nsIContent* parentFrameContent = deckFrame->GetContent();
if (!parentFrameContent ||
!parentFrameContent->IsXULElement(nsGkAtoms::tabpanels)) {
return nullptr;
}
}
}
}
// Check if node is in zero-sized map // Check if node is in zero-sized map
if (aNoContainerIfPruned && currNode->IsHTMLElement(nsGkAtoms::map)) { if (aNoContainerIfPruned && currNode->IsHTMLElement(nsGkAtoms::map)) {
if (nsIFrame* frame = currNode->AsContent()->GetPrimaryFrame()) { if (nsIFrame* frame = currNode->AsContent()->GetPrimaryFrame()) {

View File

@@ -50,6 +50,7 @@
#include "nsIContent.h" #include "nsIContent.h"
#include "nsIFormControl.h" #include "nsIFormControl.h"
#include "nsDeckFrame.h"
#include "nsLayoutUtils.h" #include "nsLayoutUtils.h"
#include "nsPresContext.h" #include "nsPresContext.h"
#include "nsIFrame.h" #include "nsIFrame.h"
@@ -69,7 +70,6 @@
#include "nsArrayUtils.h" #include "nsArrayUtils.h"
#include "nsWhitespaceTokenizer.h" #include "nsWhitespaceTokenizer.h"
#include "nsAttrName.h" #include "nsAttrName.h"
#include "nsContainerFrame.h"
#include "mozilla/Assertions.h" #include "mozilla/Assertions.h"
#include "mozilla/BasicEvents.h" #include "mozilla/BasicEvents.h"
@@ -324,16 +324,22 @@ uint64_t LocalAccessible::VisibilityState() const {
return states::INVISIBLE; return states::INVISIBLE;
} }
if (nsLayoutUtils::IsPopup(curFrame)) { if (nsLayoutUtils::IsPopup(curFrame)) return 0;
return 0;
}
if (curFrame->StyleUIReset()->mMozSubtreeHiddenOnlyVisually) { // Offscreen state for background tab content and invisible for not selected
// Offscreen state for background tab content. // deck panel.
nsIFrame* parentFrame = curFrame->GetParent();
nsDeckFrame* deckFrame = do_QueryFrame(parentFrame);
if (deckFrame && deckFrame->GetSelectedBox() != curFrame) {
if (deckFrame->GetContent()->IsXULElement(nsGkAtoms::tabpanels)) {
return states::OFFSCREEN; return states::OFFSCREEN;
} }
nsIFrame* parentFrame = curFrame->GetParent(); MOZ_ASSERT_UNREACHABLE(
"Children of not selected deck panel are not accessible.");
return states::INVISIBLE;
}
// If contained by scrollable frame then check that at least 12 pixels // If contained by scrollable frame then check that at least 12 pixels
// around the object is visible, otherwise the object is offscreen. // around the object is visible, otherwise the object is offscreen.
nsIScrollableFrame* scrollableFrame = do_QueryFrame(parentFrame); nsIScrollableFrame* scrollableFrame = do_QueryFrame(parentFrame);

View File

@@ -13,6 +13,7 @@
#include "XULTabAccessible.h" #include "XULTabAccessible.h"
#include "HTMLFormControlAccessible.h" #include "HTMLFormControlAccessible.h"
#include "nsDeckFrame.h"
#include "nsObjCExceptions.h" #include "nsObjCExceptions.h"
using namespace mozilla::a11y; using namespace mozilla::a11y;
@@ -125,17 +126,26 @@ enum CheckboxValue {
@implementation mozPaneAccessible @implementation mozPaneAccessible
- (NSArray*)moxChildren { - (NSArray*)moxChildren {
// By default, all tab panels are exposed in the a11y tree if (!mGeckoAccessible->AsLocal()) return nil;
// even if the tab they represent isn't the active tab. To
// prevent VoiceOver from navigating background tab content, nsDeckFrame* deckFrame =
// only expose the tab panel that is currently on screen. do_QueryFrame(mGeckoAccessible->AsLocal()->GetFrame());
for (mozAccessible* child in [super moxChildren]) { nsIFrame* selectedFrame = deckFrame ? deckFrame->GetSelectedBox() : nullptr;
if (!([child state] & states::OFFSCREEN)) {
return [NSArray arrayWithObject:GetObjectOrRepresentedView(child)]; LocalAccessible* selectedAcc = nullptr;
if (selectedFrame) {
nsINode* node = selectedFrame->GetContent();
selectedAcc = mGeckoAccessible->AsLocal()->Document()->GetAccessible(node);
} }
if (selectedAcc) {
mozAccessible* curNative = GetNativeFromGeckoAccessible(selectedAcc);
if (curNative)
return
[NSArray arrayWithObjects:GetObjectOrRepresentedView(curNative), nil];
} }
MOZ_ASSERT_UNREACHABLE("We have no on screen tab content?");
return @[]; return nil;
} }
@end @end

View File

@@ -22,6 +22,7 @@
// CSS display // CSS display
testCSSAttrs("display_mozbox"); testCSSAttrs("display_mozbox");
testCSSAttrs("display_mozinlinebox"); testCSSAttrs("display_mozinlinebox");
testCSSAttrs("display_mozdeck");
testCSSAttrs("display_mozpopup"); testCSSAttrs("display_mozpopup");
SimpleTest.finish(); SimpleTest.finish();
@@ -49,6 +50,7 @@
<vbox id="display_mozbox" style="display: -moz-box;" role="img"/> <vbox id="display_mozbox" style="display: -moz-box;" role="img"/>
<vbox id="display_mozinlinebox" style="display: -moz-inline-box;" role="img"/> <vbox id="display_mozinlinebox" style="display: -moz-inline-box;" role="img"/>
<vbox id="display_mozdeck" style="display: -moz-deck;" role="img"/>
<vbox id="display_mozpopup" style="display: -moz-popup;" role="img"/> <vbox id="display_mozpopup" style="display: -moz-popup;" role="img"/>
</hbox> </hbox>

View File

@@ -2171,23 +2171,23 @@ var gBrowserInit = {
return; return;
} }
if (gBrowser.selectedBrowser.isRemoteBrowser) {
// If the initial browser is remote, in order to optimize for first paint, // If the initial browser is remote, in order to optimize for first paint,
// we'll defer switching focus to that browser until it has painted. // we'll defer switching focus to that browser until it has painted.
// Otherwise use a regular promise to guarantee that mutationobserver this._firstContentWindowPaintDeferred.promise.then(() => {
// microtasks that could affect focusability have run. // If focus didn't move while we were waiting for first paint, we're okay
let promise = gBrowser.selectedBrowser.isRemoteBrowser // to move to the browser.
? this._firstContentWindowPaintDeferred.promise
: Promise.resolve();
promise.then(() => {
// If focus didn't move while we were waiting, we're okay to move to
// the browser.
if ( if (
document.commandDispatcher.focusedElement == initiallyFocusedElement document.commandDispatcher.focusedElement == initiallyFocusedElement
) { ) {
gBrowser.selectedBrowser.focus(); gBrowser.selectedBrowser.focus();
} }
}); });
} else {
// If the initial browser is not remote, we can focus the browser
// immediately with no paint performance impact.
gBrowser.selectedBrowser.focus();
}
}); });
// Delay removing the attribute using requestAnimationFrame to avoid // Delay removing the attribute using requestAnimationFrame to avoid

View File

@@ -47,7 +47,7 @@ add_task(async function() {
Assert.equal( Assert.equal(
win.document.activeElement, win.document.activeElement,
expectedActiveElement, expectedActiveElement,
`${uri}: the active element is expected: ${win.document.activeElement?.nodeName}` uri + ": the active element is expected"
); );
Assert.equal(win.gURLBar.value, "", uri + ": urlbar is empty"); Assert.equal(win.gURLBar.value, "", uri + ": urlbar is empty");
Assert.ok(win.gURLBar.placeholder, uri + ": placeholder text is present"); Assert.ok(win.gURLBar.placeholder, uri + ": placeholder text is present");

View File

@@ -254,7 +254,11 @@ add_task(async function() {
// The widget is still fetching tabs, as we've neutered everything that // The widget is still fetching tabs, as we've neutered everything that
// provides them // provides them
is(deck.selectedIndex, DECKINDEX_FETCHING, "first deck entry is visible"); is(
deck.selectedIndex,
"" + DECKINDEX_FETCHING,
"first deck entry is visible"
);
// Tell the widget there are tabs available, but with zero clients. // Tell the widget there are tabs available, but with zero clients.
mockedInternal.getTabClients = () => { mockedInternal.getTabClients = () => {
@@ -265,7 +269,7 @@ add_task(async function() {
// The UI should be showing the "no clients" pane. // The UI should be showing the "no clients" pane.
is( is(
deck.selectedIndex, deck.selectedIndex,
DECKINDEX_NOCLIENTS, "" + DECKINDEX_NOCLIENTS,
"no-clients deck entry is visible" "no-clients deck entry is visible"
); );
@@ -316,7 +320,11 @@ add_task(async function() {
await updateTabsPanel(); await updateTabsPanel();
// The UI should be showing tabs! // The UI should be showing tabs!
is(deck.selectedIndex, DECKINDEX_TABS, "no-clients deck entry is visible"); is(
deck.selectedIndex,
"" + DECKINDEX_TABS,
"no-clients deck entry is visible"
);
let tabList = document.getElementById("PanelUI-remotetabs-tabslist"); let tabList = document.getElementById("PanelUI-remotetabs-tabslist");
let node = tabList.firstElementChild; let node = tabList.firstElementChild;
// First entry should be the client with the most-recent tab. // First entry should be the client with the most-recent tab.
@@ -459,7 +467,7 @@ add_task(async function() {
let subpanel = document.getElementById("PanelUI-remotetabs-main"); let subpanel = document.getElementById("PanelUI-remotetabs-main");
ok(!subpanel.hidden, "main pane is visible"); ok(!subpanel.hidden, "main pane is visible");
let deck = document.getElementById("PanelUI-remotetabs-deck"); let deck = document.getElementById("PanelUI-remotetabs-deck");
is(deck.selectedIndex, DECKINDEX_TABS, "we should be showing tabs"); is(deck.selectedIndex, "" + DECKINDEX_TABS, "we should be showing tabs");
function checkTabsPage(tabsShownCount, showMoreLabel) { function checkTabsPage(tabsShownCount, showMoreLabel) {
let tabList = document.getElementById("PanelUI-remotetabs-tabslist"); let tabList = document.getElementById("PanelUI-remotetabs-tabslist");

View File

@@ -25,7 +25,7 @@ add_task(async function() {
); );
is( is(
weavePrefsDeck.selectedIndex, weavePrefsDeck.selectedIndex,
0, "0",
"Should select the #noFxaAccount child node" "Should select the #noFxaAccount child node"
); );

View File

@@ -103,7 +103,7 @@ add_task(async function test_infobar() {
let notif = showTranslationUI("fr"); let notif = showTranslationUI("fr");
is( is(
notif.state, notif.state,
Translation.STATE_OFFER, "" + Translation.STATE_OFFER,
"the infobar is offering translation" "the infobar is offering translation"
); );
is( is(
@@ -117,7 +117,7 @@ add_task(async function test_infobar() {
notif._getAnonElt("translate").click(); notif._getAnonElt("translate").click();
is( is(
notif.state, notif.state,
Translation.STATE_TRANSLATING, "" + Translation.STATE_TRANSLATING,
"the infobar is in the translating state" "the infobar is in the translating state"
); );
ok( ok(
@@ -134,14 +134,14 @@ add_task(async function test_infobar() {
info("Make the translation fail and check we are in the error state."); info("Make the translation fail and check we are in the error state.");
notif.translation.failTranslation(); notif.translation.failTranslation();
is(notif.state, Translation.STATE_ERROR, "infobar in the error state"); is(notif.state, "" + Translation.STATE_ERROR, "infobar in the error state");
checkURLBarIcon(); checkURLBarIcon();
info("Click the try again button"); info("Click the try again button");
notif._getAnonElt("tryAgain").click(); notif._getAnonElt("tryAgain").click();
is( is(
notif.state, notif.state,
Translation.STATE_TRANSLATING, "" + Translation.STATE_TRANSLATING,
"infobar in the translating state" "infobar in the translating state"
); );
ok( ok(
@@ -162,7 +162,7 @@ add_task(async function test_infobar() {
notif.translation.finishTranslation(); notif.translation.finishTranslation();
is( is(
notif.state, notif.state,
Translation.STATE_TRANSLATED, "" + Translation.STATE_TRANSLATED,
"infobar in the translated state" "infobar in the translated state"
); );
checkURLBarIcon(true); checkURLBarIcon(true);
@@ -207,7 +207,7 @@ add_task(async function test_infobar() {
from.doCommand(); from.doCommand();
is( is(
notif.state, notif.state,
Translation.STATE_TRANSLATING, "" + Translation.STATE_TRANSLATING,
"infobar in the translating state" "infobar in the translating state"
); );
ok( ok(
@@ -232,7 +232,7 @@ add_task(async function test_infobar() {
to.doCommand(); to.doCommand();
is( is(
notif.state, notif.state,
Translation.STATE_TRANSLATING, "" + Translation.STATE_TRANSLATING,
"infobar in the translating state" "infobar in the translating state"
); );
ok( ok(
@@ -254,7 +254,7 @@ add_task(async function test_infobar() {
notif = showTranslationUI("fr"); notif = showTranslationUI("fr");
is( is(
notif.state, notif.state,
Translation.STATE_OFFER, "" + Translation.STATE_OFFER,
"the infobar is offering translation" "the infobar is offering translation"
); );
is( is(
@@ -267,7 +267,7 @@ add_task(async function test_infobar() {
notif._getAnonElt("translate").click(); notif._getAnonElt("translate").click();
is( is(
notif.state, notif.state,
Translation.STATE_TRANSLATING, "" + Translation.STATE_TRANSLATING,
"the infobar is in the translating state" "the infobar is in the translating state"
); );
ok( ok(
@@ -332,7 +332,7 @@ add_task(async function test_infobar_using_page() {
let notif = notificationBox.getNotificationWithValue("translation"); let notif = notificationBox.getNotificationWithValue("translation");
is( is(
notif.state, notif.state,
Translation.STATE_OFFER, "" + Translation.STATE_OFFER,
"the infobar is offering translation" "the infobar is offering translation"
); );
is( is(

View File

@@ -468,7 +468,7 @@ class AsyncTabSwitcher {
let index = Array.prototype.indexOf.call(tabpanels.children, showPanel); let index = Array.prototype.indexOf.call(tabpanels.children, showPanel);
if (index != -1) { if (index != -1) {
this.log(`Switch to tab ${index} - ${this.tinfo(showTab)}`); this.log(`Switch to tab ${index} - ${this.tinfo(showTab)}`);
tabpanels.updateSelectedIndex(index); tabpanels.setAttribute("selectedIndex", index);
if (showTab === this.requestedTab) { if (showTab === this.requestedTab) {
if (requestedTabState == this.STATE_LOADED) { if (requestedTabState == this.STATE_LOADED) {
// The new tab will be made visible in the next paint, record the expected // The new tab will be made visible in the next paint, record the expected

View File

@@ -397,11 +397,9 @@ moz-input-box > menupopup .context-menu-add-engine > .menu-iconic-left {
} }
#tabbrowser-tabs { #tabbrowser-tabs {
/* overriding tabbox.css */
-moz-box-align: stretch; -moz-box-align: stretch;
margin-bottom: 0; margin-bottom: 0;
position: static; position: static;
z-index: auto;
} }
/* Bookmark drag and drop styles */ /* Bookmark drag and drop styles */

View File

@@ -113,7 +113,6 @@ exports.ANIMATION_TYPE_FOR_LONGHANDS = [
"object-fit", "object-fit",
"-moz-orient", "-moz-orient",
"-moz-osx-font-smoothing", "-moz-osx-font-smoothing",
"-moz-subtree-hidden-only-visually",
"outline-style", "outline-style",
"overflow-anchor", "overflow-anchor",
"overflow-block", "overflow-block",

View File

@@ -10,14 +10,18 @@
#include "Layers.h" #include "Layers.h"
#include "mozilla/dom/BrowserChild.h" #include "mozilla/dom/BrowserChild.h"
#include "mozilla/dom/Document.h" #include "mozilla/dom/Document.h"
#include "mozilla/gfx/gfxVars.h"
#include "mozilla/gfx/Point.h" #include "mozilla/gfx/Point.h"
#include "mozilla/layers/APZCCallbackHelper.h"
#include "mozilla/layers/APZPublicUtils.h" #include "mozilla/layers/APZPublicUtils.h"
#include "mozilla/layers/CompositorBridgeChild.h" #include "mozilla/layers/CompositorBridgeChild.h"
#include "mozilla/layers/LayersMessageUtils.h" #include "mozilla/layers/LayersMessageUtils.h"
#include "mozilla/layers/PAPZ.h" #include "mozilla/layers/PAPZ.h"
#include "mozilla/layers/RepaintRequest.h"
#include "mozilla/PresShell.h" #include "mozilla/PresShell.h"
#include "mozilla/StaticPrefs_layers.h" #include "mozilla/StaticPrefs_layers.h"
#include "mozilla/StaticPrefs_layout.h" #include "mozilla/StaticPrefs_layout.h"
#include "nsDeckFrame.h"
#include "nsIScrollableFrame.h" #include "nsIScrollableFrame.h"
#include "nsLayoutUtils.h" #include "nsLayoutUtils.h"
#include "nsPlaceholderFrame.h" #include "nsPlaceholderFrame.h"
@@ -29,9 +33,13 @@
namespace mozilla { namespace mozilla {
using gfx::gfxVars;
using gfx::IntSize; using gfx::IntSize;
using layers::APZCCallbackHelper;
using layers::FrameMetrics; using layers::FrameMetrics;
using layers::LayerManager;
using layers::RepaintRequest;
using layers::ScrollableLayerGuid; using layers::ScrollableLayerGuid;
typedef ScrollableLayerGuid::ViewID ViewID; typedef ScrollableLayerGuid::ViewID ViewID;
@@ -797,11 +805,12 @@ bool DisplayPortUtils::CalculateAndSetDisplayPortMargins(
aRepaintMode); aRepaintMode);
} }
bool DisplayPortUtils::MaybeCreateDisplayPort( bool DisplayPortUtils::MaybeCreateDisplayPort(nsDisplayListBuilder* aBuilder,
nsDisplayListBuilder* aBuilder, nsIFrame* aScrollFrame, nsIFrame* aScrollFrame,
nsIScrollableFrame* aScrollFrameAsScrollable, RepaintMode aRepaintMode) { RepaintMode aRepaintMode) {
nsIContent* content = aScrollFrame->GetContent(); nsIContent* content = aScrollFrame->GetContent();
if (!content) { nsIScrollableFrame* scrollableFrame = do_QueryFrame(aScrollFrame);
if (!content || !scrollableFrame) {
return false; return false;
} }
@@ -814,7 +823,7 @@ bool DisplayPortUtils::MaybeCreateDisplayPort(
if (aBuilder->IsPaintingToWindow() && if (aBuilder->IsPaintingToWindow() &&
nsLayoutUtils::AsyncPanZoomEnabled(aScrollFrame) && nsLayoutUtils::AsyncPanZoomEnabled(aScrollFrame) &&
!aBuilder->HaveScrollableDisplayPort() && !aBuilder->HaveScrollableDisplayPort() &&
aScrollFrameAsScrollable->WantAsyncScroll()) { scrollableFrame->WantAsyncScroll()) {
// If we don't already have a displayport, calculate and set one. // If we don't already have a displayport, calculate and set one.
if (!haveDisplayPort) { if (!haveDisplayPort) {
// We only use the viewId for logging purposes, but create it // We only use the viewId for logging purposes, but create it
@@ -826,7 +835,7 @@ bool DisplayPortUtils::MaybeCreateDisplayPort(
sDisplayportLog, LogLevel::Debug, sDisplayportLog, LogLevel::Debug,
("Setting DP on first-encountered scrollId=%" PRIu64 "\n", viewId)); ("Setting DP on first-encountered scrollId=%" PRIu64 "\n", viewId));
CalculateAndSetDisplayPortMargins(aScrollFrameAsScrollable, aRepaintMode); CalculateAndSetDisplayPortMargins(scrollableFrame, aRepaintMode);
#ifdef DEBUG #ifdef DEBUG
haveDisplayPort = HasNonMinimalDisplayPort(content); haveDisplayPort = HasNonMinimalDisplayPort(content);
MOZ_ASSERT(haveDisplayPort, MOZ_ASSERT(haveDisplayPort,
@@ -875,8 +884,10 @@ bool DisplayPortUtils::MaybeCreateDisplayPortInFirstScrollFrameEncountered(
aFrame->GetContent()->GetID() == nsGkAtoms::tabbrowser_arrowscrollbox) { aFrame->GetContent()->GetID() == nsGkAtoms::tabbrowser_arrowscrollbox) {
return false; return false;
} }
if (nsIScrollableFrame* sf = do_QueryFrame(aFrame)) {
if (MaybeCreateDisplayPort(aBuilder, aFrame, sf, RepaintMode::Repaint)) { nsIScrollableFrame* sf = do_QueryFrame(aFrame);
if (sf) {
if (MaybeCreateDisplayPort(aBuilder, aFrame, RepaintMode::Repaint)) {
return true; return true;
} }
} }
@@ -890,21 +901,28 @@ bool DisplayPortUtils::MaybeCreateDisplayPortInFirstScrollFrameEncountered(
if (aFrame->IsSubDocumentFrame()) { if (aFrame->IsSubDocumentFrame()) {
PresShell* presShell = static_cast<nsSubDocumentFrame*>(aFrame) PresShell* presShell = static_cast<nsSubDocumentFrame*>(aFrame)
->GetSubdocumentPresShellForPainting(0); ->GetSubdocumentPresShellForPainting(0);
if (nsIFrame* root = presShell ? presShell->GetRootFrame() : nullptr) { nsIFrame* root = presShell ? presShell->GetRootFrame() : nullptr;
if (root) {
if (MaybeCreateDisplayPortInFirstScrollFrameEncountered(root, aBuilder)) { if (MaybeCreateDisplayPortInFirstScrollFrameEncountered(root, aBuilder)) {
return true; return true;
} }
} }
} }
if (aFrame->StyleUIReset()->mMozSubtreeHiddenOnlyVisually) { if (aFrame->IsDeckFrame()) {
// Only descend the visible card of deck / tabpanels // only descend the visible card of a decks
return false; nsIFrame* child = static_cast<nsDeckFrame*>(aFrame)->GetSelectedBox();
if (child) {
return MaybeCreateDisplayPortInFirstScrollFrameEncountered(child,
aBuilder);
} }
}
for (nsIFrame* child : aFrame->PrincipalChildList()) { for (nsIFrame* child : aFrame->PrincipalChildList()) {
if (MaybeCreateDisplayPortInFirstScrollFrameEncountered(child, aBuilder)) { if (MaybeCreateDisplayPortInFirstScrollFrameEncountered(child, aBuilder)) {
return true; return true;
} }
} }
return false; return false;
} }

View File

@@ -278,9 +278,9 @@ class DisplayPortUtils {
* Returns true if there is a displayport on an async scrollable scrollframe * Returns true if there is a displayport on an async scrollable scrollframe
* after this call, either because one was just added or it already existed. * after this call, either because one was just added or it already existed.
*/ */
static bool MaybeCreateDisplayPort( static bool MaybeCreateDisplayPort(nsDisplayListBuilder* aBuilder,
nsDisplayListBuilder* aBuilder, nsIFrame* aScrollFrame, nsIFrame* aScrollFrame,
nsIScrollableFrame* aScrollFrameAsScrollable, RepaintMode aRepaintMode); RepaintMode aRepaintMode);
/** /**
* Sets a zero margin display port on all proper ancestors of aFrame that * Sets a zero margin display port on all proper ancestors of aFrame that

View File

@@ -3961,24 +3961,38 @@ void PresShell::ClearMouseCaptureOnView(nsView* aView) {
} }
void PresShell::ClearMouseCapture() { void PresShell::ClearMouseCapture() {
nsIContent* capturingContent = GetCapturingContent();
if (!capturingContent) {
AllowMouseCapture(false);
return;
}
ReleaseCapturingContent(); ReleaseCapturingContent();
AllowMouseCapture(false); AllowMouseCapture(false);
} }
void PresShell::ClearMouseCapture(nsIFrame* aFrame) { void PresShell::ClearMouseCapture(nsIFrame* aFrame) {
MOZ_ASSERT(aFrame); MOZ_ASSERT(
aFrame && aFrame->GetParent() &&
aFrame->GetParent()->Type() == LayoutFrameType::Deck,
"This function should only be called with a child frame of <deck>");
nsIContent* capturingContent = GetCapturingContent(); nsIContent* capturingContent = GetCapturingContent();
if (!capturingContent) { if (!capturingContent) {
AllowMouseCapture(false);
return; return;
} }
nsIFrame* capturingFrame = capturingContent->GetPrimaryFrame(); nsIFrame* capturingFrame = capturingContent->GetPrimaryFrame();
const bool shouldClear = if (!capturingFrame) {
!capturingFrame || ReleaseCapturingContent();
nsLayoutUtils::IsAncestorFrameCrossDocInProcess(aFrame, capturingFrame); AllowMouseCapture(false);
if (shouldClear) { return;
ClearMouseCapture(); }
if (nsLayoutUtils::IsAncestorFrameCrossDocInProcess(aFrame, capturingFrame)) {
ReleaseCapturingContent();
AllowMouseCapture(false);
} }
} }

View File

@@ -205,22 +205,13 @@ class PresShell final : public nsStubDocumentObserver,
static void ClearMouseCaptureOnView(nsView* aView); static void ClearMouseCaptureOnView(nsView* aView);
// Clear the capture content if it exists in this process.
static void ClearMouseCapture();
// If a frame in the subtree rooted at aFrame is capturing the mouse then // If a frame in the subtree rooted at aFrame is capturing the mouse then
// clears that capture. // clears that capture.
//
// NOTE(emilio): This is needed only so that mouse events captured by a remote
// frame don't remain being captured by the frame while hidden, see
// dom/events/test/browser_mouse_enterleave_switch_tab.js, which is the only
// test that meaningfully exercises this code path.
//
// We could consider maybe removing this, since the capturing content gets
// reset on mouse/pointerdown? Or maybe exposing an API so that the front-end
// does this.
static void ClearMouseCapture(nsIFrame* aFrame); static void ClearMouseCapture(nsIFrame* aFrame);
// Clear the capture content if it exists in this process.
static void ClearMouseCapture();
#ifdef ACCESSIBILITY #ifdef ACCESSIBILITY
/** /**
* Return the document accessible for this PresShell if there is one. * Return the document accessible for this PresShell if there is one.

View File

@@ -2624,7 +2624,7 @@ MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(ServoPostTraversalFlags)
// flags for kids. // flags for kids.
static ServoPostTraversalFlags SendA11yNotifications( static ServoPostTraversalFlags SendA11yNotifications(
nsPresContext* aPresContext, Element* aElement, nsPresContext* aPresContext, Element* aElement,
const ComputedStyle& aOldStyle, const ComputedStyle& aNewStyle, ComputedStyle* aOldComputedStyle, ComputedStyle* aNewComputedStyle,
ServoPostTraversalFlags aFlags) { ServoPostTraversalFlags aFlags) {
using Flags = ServoPostTraversalFlags; using Flags = ServoPostTraversalFlags;
MOZ_ASSERT(!(aFlags & Flags::SkipA11yNotifications) || MOZ_ASSERT(!(aFlags & Flags::SkipA11yNotifications) ||
@@ -2638,24 +2638,13 @@ static ServoPostTraversalFlags SendA11yNotifications(
// enabled. Just skip everything. // enabled. Just skip everything.
return Flags::Empty; return Flags::Empty;
} }
if (aNewStyle.StyleUIReset()->mMozSubtreeHiddenOnlyVisually !=
aOldStyle.StyleUIReset()->mMozSubtreeHiddenOnlyVisually) {
if (aElement->GetParent() &&
aElement->GetParent()->IsXULElement(nsGkAtoms::tabpanels)) {
accService->NotifyOfTabPanelVisibilityChange(
aPresContext->PresShell(), aElement,
aNewStyle.StyleUIReset()->mMozSubtreeHiddenOnlyVisually);
}
}
if (aFlags & Flags::SkipA11yNotifications) { if (aFlags & Flags::SkipA11yNotifications) {
// Propagate the skipping flag to descendants. // Propogate the skipping flag to descendants.
return Flags::SkipA11yNotifications; return Flags::SkipA11yNotifications;
} }
bool needsNotify = false; bool needsNotify = false;
bool isVisible = aNewStyle.StyleVisibility()->IsVisible(); bool isVisible = aNewComputedStyle->StyleVisibility()->IsVisible();
if (aFlags & Flags::SendA11yNotificationsIfShown) { if (aFlags & Flags::SendA11yNotificationsIfShown) {
if (!isVisible) { if (!isVisible) {
// Propagate the sending-if-shown flag to descendants. // Propagate the sending-if-shown flag to descendants.
@@ -2668,7 +2657,7 @@ static ServoPostTraversalFlags SendA11yNotifications(
} else { } else {
// If we shouldn't skip in any case, we need to check whether our // If we shouldn't skip in any case, we need to check whether our
// own visibility has changed. // own visibility has changed.
bool wasVisible = aOldStyle.StyleVisibility()->IsVisible(); bool wasVisible = aOldComputedStyle->StyleVisibility()->IsVisible();
needsNotify = wasVisible != isVisible; needsNotify = wasVisible != isVisible;
} }
@@ -2898,9 +2887,9 @@ bool RestyleManager::ProcessPostTraversal(Element* aElement,
AddLayerChangesForAnimation(styleFrame, primaryFrame, aElement, changeHint, AddLayerChangesForAnimation(styleFrame, primaryFrame, aElement, changeHint,
aRestyleState.ChangeList()); aRestyleState.ChangeList());
childrenFlags |= SendA11yNotifications(mPresContext, aElement, childrenFlags |=
*oldOrDisplayContentsStyle, SendA11yNotifications(mPresContext, aElement, oldOrDisplayContentsStyle,
*upToDateStyle, aFlags); upToDateStyle, aFlags);
} }
const bool traverseElementChildren = const bool traverseElementChildren =

View File

@@ -0,0 +1,10 @@
<html><body>
<object style="display:-moz-deck;">
<noscript>
</noscript>
</object>
</body></html>

View File

@@ -58,13 +58,16 @@ window.location.reload();
<s style=" display: -moz-box; position: absolute; direction: ltr;"> <s style=" display: -moz-box; position: absolute; direction: ltr;">
<q style=" display: table; direction: ltr;"> <q style=" display: table; direction: ltr;">
<bdo style=" display: block;">mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m <bdo style=" display: block;">mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m
<q style=" display: -moz-deck; position: absolute; direction: rtl;">mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m
</q>
</bdo> </bdo>
</q> </q>
</s> </s>
<map style=" display: inline-table; position: fixed; direction: ltr;"> <map style=" display: inline-table; position: fixed; direction: ltr;">
<q style=" display: table; direction: ltr;"> <q style=" display: table; direction: ltr;">
<bdo style=" display: block;"> <bdo style=" display: block;">
mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m <q style=" display: -moz-deck; position: absolute; direction: rtl;">mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m
</q>mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m
<q style=" display: inline; direction: ltr;">mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m <q style=" display: inline; direction: ltr;">mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m
</q> </q>
</bdo> </bdo>
@@ -363,6 +366,7 @@ mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m
</q> </q>
</q> </q>
</q> </q>
<listing style=" display: -moz-deck; direction: ltr;">
<p style=" display: block; position: fixed; direction: ltr;"> <p style=" display: block; position: fixed; direction: ltr;">
<q style=" display: table-header-group; "> <q style=" display: table-header-group; ">
<q style=" display: table-cell; "> <q style=" display: table-cell; ">
@@ -401,6 +405,7 @@ mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m
</q> </q>
</q> </q>
</p> </p>
</listing>
<q style=" display: table-header-group; "> <q style=" display: table-header-group; ">
<q style=" display: table-cell; "> <q style=" display: table-cell; ">
<q style=" display: table; direction: ltr;"> <q style=" display: table; direction: ltr;">
@@ -651,7 +656,7 @@ mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m
</map> </map>
<ol style="display: inline-block;direction: ltr;"> <ol style="display: inline-block;direction: ltr;">
<p style="display: inherit;position: fixed;direction: ltr;"> <p style="display: inherit;position: fixed;direction: ltr;">
<body style="display: position: absolute;direction: ltr;">mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m <body style="display: -moz-deck;position: absolute;direction: ltr;">mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m
</ol>mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m </ol>mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m
</html>mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m </html>mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m
</map>mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m </map>mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m mm m

View File

@@ -0,0 +1,4 @@
<!DOCTYPE html>
<html>
<body><div style="display: -moz-deck;"><div style="overflow: auto; border: 4294967296px solid blue;">M</div></div></body>
</html>

View File

@@ -105,6 +105,7 @@ load 343540-1.html
load 344057-1.xhtml load 344057-1.xhtml
load 344064-1.html load 344064-1.html
load 344300-1.html load 344300-1.html
load 344300-2.html
load chrome://reftest/content/crashtests/layout/base/crashtests/344340-1.xhtml load chrome://reftest/content/crashtests/layout/base/crashtests/344340-1.xhtml
load 347898-1.html load 347898-1.html
load 348126-1.html load 348126-1.html
@@ -315,6 +316,7 @@ load 543648-1.html
load 560447-1.html load 560447-1.html
load 564063-1.html load 564063-1.html
load 569018-1.html load 569018-1.html
load chrome://reftest/content/crashtests/layout/base/crashtests/570038-1.html
load chrome://reftest/content/crashtests/layout/base/crashtests/572003.xhtml load chrome://reftest/content/crashtests/layout/base/crashtests/572003.xhtml
load 572582-1.xhtml load 572582-1.xhtml
load 576649-1.html load 576649-1.html

View File

@@ -215,6 +215,8 @@ nsContainerFrame* NS_NewRootBoxFrame(PresShell* aPresShell,
nsContainerFrame* NS_NewDocElementBoxFrame(PresShell* aPresShell, nsContainerFrame* NS_NewDocElementBoxFrame(PresShell* aPresShell,
ComputedStyle* aStyle); ComputedStyle* aStyle);
nsIFrame* NS_NewDeckFrame(PresShell* aPresShell, ComputedStyle* aStyle);
nsIFrame* NS_NewLeafBoxFrame(PresShell* aPresShell, ComputedStyle* aStyle); nsIFrame* NS_NewLeafBoxFrame(PresShell* aPresShell, ComputedStyle* aStyle);
nsIFrame* NS_NewRangeFrame(PresShell* aPresShell, ComputedStyle* aStyle); nsIFrame* NS_NewRangeFrame(PresShell* aPresShell, ComputedStyle* aStyle);
@@ -4547,6 +4549,11 @@ nsCSSFrameConstructor::FindDisplayData(const nsStyleDisplay& aDisplay,
FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRuby)); FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRuby));
return &data; return &data;
} }
case StyleDisplayInside::MozDeck: {
static constexpr FrameConstructionData data =
SIMPLE_XUL_FCDATA(NS_NewDeckFrame);
return &data;
}
case StyleDisplayInside::MozPopup: { case StyleDisplayInside::MozPopup: {
static constexpr FrameConstructionData data( static constexpr FrameConstructionData data(
NS_NewMenuPopupFrame, FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_IS_POPUP | NS_NewMenuPopupFrame, FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_IS_POPUP |

View File

@@ -117,6 +117,7 @@
#include "nsCSSPseudoElements.h" #include "nsCSSPseudoElements.h"
#include "nsCSSRendering.h" #include "nsCSSRendering.h"
#include "nsTHashMap.h" #include "nsTHashMap.h"
#include "nsDeckFrame.h"
#include "nsDisplayList.h" #include "nsDisplayList.h"
#include "nsFlexContainerFrame.h" #include "nsFlexContainerFrame.h"
#include "nsFontInflationData.h" #include "nsFontInflationData.h"

View File

@@ -35,6 +35,7 @@
#include "nsRepeatService.h" #include "nsRepeatService.h"
#include "nsFloatManager.h" #include "nsFloatManager.h"
#include "nsSprocketLayout.h" #include "nsSprocketLayout.h"
#include "nsStackLayout.h"
#include "nsTextControlFrame.h" #include "nsTextControlFrame.h"
#include "txMozillaXSLTProcessor.h" #include "txMozillaXSLTProcessor.h"
#include "nsTreeSanitizer.h" #include "nsTreeSanitizer.h"
@@ -328,6 +329,7 @@ void nsLayoutStatics::Shutdown() {
nsColorNames::ReleaseTable(); nsColorNames::ReleaseTable();
nsCSSProps::ReleaseTable(); nsCSSProps::ReleaseTable();
nsRepeatService::Shutdown(); nsRepeatService::Shutdown();
nsStackLayout::Shutdown();
nsXULContentUtils::Finish(); nsXULContentUtils::Finish();
nsXULPrototypeCache::ReleaseGlobals(); nsXULPrototypeCache::ReleaseGlobals();

View File

@@ -0,0 +1,16 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<body style="direction: rtl;">
<form style="display: -moz-deck; width: 1em;">
<fieldset>
<legend>Your name</legend>
<input type="text" name="name" size="20"/>
</fieldset>
<fieldset>
<legend>Your E-mail address</legend>
<input type="text" name="email" size="25"/>
</fieldset>
</form>
</body>
</html>

View File

@@ -20,6 +20,7 @@ load 370703-1.html
load 370940-1.html load 370940-1.html
load 370967.html load 370967.html
load 378369.html load 378369.html
load 378413-1.xhtml
load 380116-1.xhtml load 380116-1.xhtml
load 382610-1.html load 382610-1.html
load 383887-1.html load 383887-1.html

View File

@@ -22,6 +22,7 @@ FRAME_CLASSES = [
Frame("nsComboboxDisplayFrame", "ComboboxDisplay", NOT_LEAF), Frame("nsComboboxDisplayFrame", "ComboboxDisplay", NOT_LEAF),
Frame("nsContinuingTextFrame", "Text", LEAF), Frame("nsContinuingTextFrame", "Text", LEAF),
Frame("nsDateTimeControlFrame", "DateTimeControl", NOT_LEAF), Frame("nsDateTimeControlFrame", "DateTimeControl", NOT_LEAF),
Frame("nsDeckFrame", "Deck", NOT_LEAF),
Frame("nsDocElementBoxFrame", "DocElementBox", NOT_LEAF), Frame("nsDocElementBoxFrame", "DocElementBox", NOT_LEAF),
Frame("nsFieldSetFrame", "FieldSet", NOT_LEAF), Frame("nsFieldSetFrame", "FieldSet", NOT_LEAF),
Frame("nsFileControlFrame", "Block", LEAF), Frame("nsFileControlFrame", "Block", LEAF),

View File

@@ -0,0 +1,14 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
</head>
<body>
<table style="display: -moz-deck;"><tbody><tr><td>
<span style="position: relative;"><marquee><marquee><span style="position: absolute;">X</span></marquee></marquee></span>
</td></tr></tbody></table>
</body>
</html>

View File

@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<body>
<div style="display:-moz-deck">
<div>
<span style="float: right; width: 0;">x</span>
</div>
<div style="position: relative;">
<span style="float: right;">
<span style="position: absolute;">y</span>
</span>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<style>
div { display: flex; }
div:after { display: -moz-deck; content: counter(b); }
</style>
</head>
<body>
<div></div>
</body>
</html>

View File

@@ -87,6 +87,7 @@ load 370174-2.html
load 370174-3.html load 370174-3.html
load 370699-1.html load 370699-1.html
load 370794-1.html load 370794-1.html
load 370866-1.xhtml
load 370884-1.xhtml load 370884-1.xhtml
load 371348-1.xhtml load 371348-1.xhtml
load 371561-1.html load 371561-1.html
@@ -103,6 +104,7 @@ load 379217-2.xhtml
load 379917-1.xhtml load 379917-1.xhtml
load 380012-1.html load 380012-1.html
load 381152-1.html load 381152-1.html
load 381786-1.html
load 382129-1.xhtml load 382129-1.xhtml
load 382131-1.html load 382131-1.html
load 382199-1.html load 382199-1.html
@@ -534,6 +536,7 @@ asserts(8-46) load 850931.html # nested multicols, inner multicol has column-wid
load 851396-1.html load 851396-1.html
load 854263-1.html load 854263-1.html
load 862185.html load 862185.html
load 862947-1.html
load 863935.html load 863935.html
load 866547-1.html load 866547-1.html
needs-focus pref(accessibility.browsewithcaret,true) load 868906.html needs-focus pref(accessibility.browsewithcaret,true) load 868906.html

View File

@@ -100,6 +100,7 @@
#include "nsBlockFrame.h" #include "nsBlockFrame.h"
#include "nsDisplayList.h" #include "nsDisplayList.h"
#include "nsChangeHint.h" #include "nsChangeHint.h"
#include "nsDeckFrame.h"
#include "nsSubDocumentFrame.h" #include "nsSubDocumentFrame.h"
#include "RetainedDisplayListBuilder.h" #include "RetainedDisplayListBuilder.h"
@@ -374,19 +375,17 @@ bool nsIFrame::IsVisibleConsideringAncestors(uint32_t aFlags) const {
const nsIFrame* frame = this; const nsIFrame* frame = this;
while (frame) { while (frame) {
nsView* view = frame->GetView(); nsView* view = frame->GetView();
if (view && view->GetVisibility() == nsViewVisibility_kHide) { if (view && view->GetVisibility() == nsViewVisibility_kHide) return false;
return false;
if (this != frame && frame->HidesContent()) return false;
nsIFrame* parent = frame->GetParent();
nsDeckFrame* deck = do_QueryFrame(parent);
if (deck) {
if (deck->GetSelectedBox() != frame) return false;
} }
if (frame->StyleUIReset()->mMozSubtreeHiddenOnlyVisually) { if (parent) {
return false;
}
if (this != frame && frame->HidesContent()) {
return false;
}
if (nsIFrame* parent = frame->GetParent()) {
frame = parent; frame = parent;
} else { } else {
parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(frame); parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(frame);
@@ -1287,10 +1286,6 @@ void nsIFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) {
scrollableFrame->PostPendingResnap(); scrollableFrame->PostPendingResnap();
} }
} }
if (StyleUIReset()->mMozSubtreeHiddenOnlyVisually &&
!aOldComputedStyle->StyleUIReset()->mMozSubtreeHiddenOnlyVisually) {
PresShell::ClearMouseCapture(this);
}
} else { // !aOldComputedStyle } else { // !aOldComputedStyle
handleStickyChange = disp->mPosition == StylePositionProperty::Sticky; handleStickyChange = disp->mPosition == StylePositionProperty::Sticky;
} }
@@ -3988,21 +3983,22 @@ static bool ShouldSkipFrame(nsDisplayListBuilder* aBuilder,
if (aBuilder->IsBackgroundOnly()) { if (aBuilder->IsBackgroundOnly()) {
return true; return true;
} }
if (aBuilder->IsForGenerateGlyphMask() && if (aBuilder->IsForGenerateGlyphMask() &&
(!aFrame->IsTextFrame() && aFrame->IsLeaf())) { (!aFrame->IsTextFrame() && aFrame->IsLeaf())) {
return true; return true;
} }
// The placeholder frame should have the same content as the OOF frame. // The placeholder frame should have the same content as the OOF frame.
if (aBuilder->GetSelectedFramesOnly() && if (aBuilder->GetSelectedFramesOnly() &&
(aFrame->IsLeaf() && !aFrame->IsSelected())) { (aFrame->IsLeaf() && !aFrame->IsSelected())) {
return true; return true;
} }
static const nsFrameState skipFlags = static const nsFrameState skipFlags =
(NS_FRAME_TOO_DEEP_IN_FRAME_TREE | NS_FRAME_IS_NONDISPLAY); (NS_FRAME_TOO_DEEP_IN_FRAME_TREE | NS_FRAME_IS_NONDISPLAY);
if (aFrame->HasAnyStateBits(skipFlags)) {
return true; return aFrame->HasAnyStateBits(skipFlags);
}
return aFrame->StyleUIReset()->mMozSubtreeHiddenOnlyVisually;
} }
void nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, void nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,

View File

@@ -0,0 +1,8 @@
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div style="display: -moz-deck;" id="s">x</div>
</body>
</html>

View File

@@ -0,0 +1,8 @@
<!DOCTYPE html>
<html>
<head>
</head>
<body onload="document.getElementById('s').firstChild.data = 'x';">
<div style="display: -moz-deck;" id="s"> </div>
</body>
</html>

View File

@@ -926,6 +926,7 @@ fuzzy-if(winWidget,0-123,0-1900) fuzzy-if(swgl,0-1,0-39) == 409659-1d.html 40965
== 410621-1.html 410621-1-ref.html == 410621-1.html 410621-1-ref.html
== 411059-1.html 411059-1-ref.html == 411059-1.html 411059-1-ref.html
fuzzy-if(winWidget,46-129,652-770) == 411334-1.xml 411334-1-ref.xml fuzzy-if(winWidget,46-129,652-770) == 411334-1.xml 411334-1-ref.xml
== 411367-3.html 411367-3-ref.html
== 411585-1.html 411585-1-ref.html == 411585-1.html 411585-1-ref.html
== 411585-2.html 411585-2-ref.html == 411585-2.html 411585-2-ref.html
fails == 411585-3.html 411585-3-ref.html # bug 426909 fails == 411585-3.html 411585-3-ref.html # bug 426909

View File

@@ -607,7 +607,6 @@ cbindgen-types = [
{ gecko = "StyleFontStyle", servo = "crate::values::computed::font::FontStyle" }, { gecko = "StyleFontStyle", servo = "crate::values::computed::font::FontStyle" },
{ gecko = "StyleFontWeight", servo = "crate::values::computed::font::FontWeight" }, { gecko = "StyleFontWeight", servo = "crate::values::computed::font::FontWeight" },
{ gecko = "StyleFontStretch", servo = "crate::values::computed::font::FontStretch" }, { gecko = "StyleFontStretch", servo = "crate::values::computed::font::FontStretch" },
{ gecko = "StyleBoolInteger", servo = "crate::values::computed::BoolInteger" },
] ]
mapped-generic-types = [ mapped-generic-types = [

View File

@@ -91,6 +91,8 @@ enum class StyleDisplay : uint16_t {
StyleDisplayFrom(StyleDisplayOutside::Block, StyleDisplayInside::MozBox), StyleDisplayFrom(StyleDisplayOutside::Block, StyleDisplayInside::MozBox),
MozInlineBox = MozInlineBox =
StyleDisplayFrom(StyleDisplayOutside::Inline, StyleDisplayInside::MozBox), StyleDisplayFrom(StyleDisplayOutside::Inline, StyleDisplayInside::MozBox),
MozDeck =
StyleDisplayFrom(StyleDisplayOutside::XUL, StyleDisplayInside::MozDeck),
MozPopup = MozPopup =
StyleDisplayFrom(StyleDisplayOutside::XUL, StyleDisplayInside::MozPopup), StyleDisplayFrom(StyleDisplayOutside::XUL, StyleDisplayInside::MozPopup),
}; };

View File

@@ -3213,8 +3213,7 @@ nsChangeHint nsStyleUI::CalcDifference(const nsStyleUI& aNewData) const {
nsStyleUIReset::nsStyleUIReset(const Document& aDocument) nsStyleUIReset::nsStyleUIReset(const Document& aDocument)
: mUserSelect(StyleUserSelect::Auto), : mUserSelect(StyleUserSelect::Auto),
mScrollbarWidth(StyleScrollbarWidth::Auto), mScrollbarWidth(StyleScrollbarWidth::Auto),
mMozForceBrokenImageIcon(false), mMozForceBrokenImageIcon(0),
mMozSubtreeHiddenOnlyVisually(false),
mIMEMode(StyleImeMode::Auto), mIMEMode(StyleImeMode::Auto),
mWindowDragging(StyleWindowDragging::Default), mWindowDragging(StyleWindowDragging::Default),
mWindowShadow(StyleWindowShadow::Default), mWindowShadow(StyleWindowShadow::Default),
@@ -3251,7 +3250,6 @@ nsStyleUIReset::nsStyleUIReset(const nsStyleUIReset& aSource)
: mUserSelect(aSource.mUserSelect), : mUserSelect(aSource.mUserSelect),
mScrollbarWidth(aSource.mScrollbarWidth), mScrollbarWidth(aSource.mScrollbarWidth),
mMozForceBrokenImageIcon(aSource.mMozForceBrokenImageIcon), mMozForceBrokenImageIcon(aSource.mMozForceBrokenImageIcon),
mMozSubtreeHiddenOnlyVisually(aSource.mMozSubtreeHiddenOnlyVisually),
mIMEMode(aSource.mIMEMode), mIMEMode(aSource.mIMEMode),
mWindowDragging(aSource.mWindowDragging), mWindowDragging(aSource.mWindowDragging),
mWindowShadow(aSource.mWindowShadow), mWindowShadow(aSource.mWindowShadow),
@@ -3289,9 +3287,6 @@ nsChangeHint nsStyleUIReset::CalcDifference(
if (mMozForceBrokenImageIcon != aNewData.mMozForceBrokenImageIcon) { if (mMozForceBrokenImageIcon != aNewData.mMozForceBrokenImageIcon) {
hint |= nsChangeHint_ReconstructFrame; hint |= nsChangeHint_ReconstructFrame;
} }
if (mMozSubtreeHiddenOnlyVisually != aNewData.mMozSubtreeHiddenOnlyVisually) {
hint |= nsChangeHint_RepaintFrame;
}
if (mScrollbarWidth != aNewData.mScrollbarWidth) { if (mScrollbarWidth != aNewData.mScrollbarWidth) {
// For scrollbar-width change, we need some special handling similar // For scrollbar-width change, we need some special handling similar
// to overflow properties. Specifically, we may need to reconstruct // to overflow properties. Specifically, we may need to reconstruct

View File

@@ -1863,8 +1863,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleUIReset {
return mAnimations[aIndex % mAnimationTimelineCount].GetTimeline(); return mAnimations[aIndex % mAnimationTimelineCount].GetTimeline();
} }
mozilla::StyleBoolInteger mMozForceBrokenImageIcon; uint8_t mMozForceBrokenImageIcon; // (0 if not forcing, otherwise forcing)
mozilla::StyleBoolInteger mMozSubtreeHiddenOnlyVisually;
mozilla::StyleImeMode mIMEMode; mozilla::StyleImeMode mIMEMode;
mozilla::StyleWindowDragging mWindowDragging; mozilla::StyleWindowDragging mWindowDragging;
mozilla::StyleWindowShadow mWindowShadow; mozilla::StyleWindowShadow mWindowShadow;

View File

@@ -104,7 +104,6 @@ const char* gInaccessibleProperties[] = {
"-moz-min-font-size-ratio", // parsed by UA sheets only "-moz-min-font-size-ratio", // parsed by UA sheets only
"-moz-box-layout", // chrome-only internal properties "-moz-box-layout", // chrome-only internal properties
"-moz-font-smoothing-background-color", // chrome-only internal properties "-moz-font-smoothing-background-color", // chrome-only internal properties
"-moz-subtree-hidden-only-visually", // chrome-only internal properties
"-moz-window-input-region-margin", // chrome-only internal properties "-moz-window-input-region-margin", // chrome-only internal properties
"-moz-window-opacity", // chrome-only internal properties "-moz-window-opacity", // chrome-only internal properties
"-moz-window-transform", // chrome-only internal properties "-moz-window-transform", // chrome-only internal properties

View File

@@ -134,6 +134,8 @@ skip-if = true # Bug 701060
[test_bug418986-2.html] [test_bug418986-2.html]
[test_bug437915.html] [test_bug437915.html]
[test_bug450191.html] [test_bug450191.html]
[test_bug453896_deck.html]
support-files = bug453896_iframe.html
[test_bug470769.html] [test_bug470769.html]
[test_bug499655.html] [test_bug499655.html]
[test_bug499655.xhtml] [test_bug499655.xhtml]

View File

@@ -0,0 +1,42 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=453896
-->
<head>
<title>Test for Bug 453896</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body onload="run()">
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=453896">Mozilla Bug 453896</a>
<div id="display">
<div style="display:-moz-deck; height: 300px; width: 300px;">
<iframe src="about:blank"></iframe>
<iframe id="subdoc" src="bug453896_iframe.html"></iframe>
<iframe src="about:blank"></iframe>
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
/** Test for Bug 453896 **/
function run()
{
var iframe = document.getElementById("subdoc");
var subdoc = iframe.contentDocument;
var subwin = iframe.contentWindow;
subwin.run(window);
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
</script>
</pre>
</body>
</html>

View File

@@ -19,6 +19,7 @@ const NON_CONTENT_ACCESSIBLE_VALUES = {
"-moz-autofill-background", "-moz-autofill-background",
], ],
"display": [ "display": [
"-moz-deck",
"-moz-popup", "-moz-popup",
"-moz-box", "-moz-box",
"-moz-inline-box", "-moz-inline-box",

View File

@@ -0,0 +1,6 @@
<html>
<head><title>Testcase bug 290743 - This display:-moz-deck testcase freezes Mozilla</title></head>
<body style="display:-moz-deck;">
<input type="radio"><input type="radio">
</body>
</html>

View File

@@ -0,0 +1,12 @@
<html><head>
</head>
<body>
<div style="display: -moz-deck"><div style="display: -moz-popup"></div></div>
<div style="position: relative">Y</div>
</body>
</html>

View File

@@ -0,0 +1,5 @@
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<tabpanels>
<treechildren style="display: -moz-deck;"/>
</tabpanels>
</window>

View File

@@ -0,0 +1,31 @@
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<menulist id="b" style="display: -moz-box;">
<panel id="c" style=" position: absolute;">
<popup onunderflow="document.getElementById('c').removeAttribute('style')"/>
</panel>
<menupopup id="a" style="display: -moz-box;">
<menulist/>
</menupopup>
<panel style="display: -moz-deck;" onoverflow="document.getElementById('b').removeAttribute('style')">
<popup style="display: -moz-deck;"/>
</panel>
</menulist>
<script id="script" xmlns="http://www.w3.org/1999/xhtml"><![CDATA[
function doe() {
document.getElementById('c').removeAttribute('style');
document.documentElement.clientHeight;
document.getElementById('b').removeAttribute('style');
document.getElementById('a').setAttribute('selected', 'true');
document.getElementById('a').setAttribute('style', 'position: fixed;');
document.documentElement.clientHeight;
document.getElementById('a').removeAttribute('style');
}
function doe2() {
window.location.reload();
}
setTimeout(doe2, 200);
setTimeout(doe,100);
]]></script>
</window>

View File

@@ -5,10 +5,12 @@ load chrome://reftest/content/crashtests/layout/xul/crashtests/151826-1.xhtml
load chrome://reftest/content/crashtests/layout/xul/crashtests/168724-1.xhtml load chrome://reftest/content/crashtests/layout/xul/crashtests/168724-1.xhtml
load chrome://reftest/content/crashtests/layout/xul/crashtests/189814-1.xhtml load chrome://reftest/content/crashtests/layout/xul/crashtests/189814-1.xhtml
skip-if(Android) load chrome://reftest/content/crashtests/layout/xul/crashtests/289410-1.xhtml skip-if(Android) load chrome://reftest/content/crashtests/layout/xul/crashtests/289410-1.xhtml
load 290743.html
load chrome://reftest/content/crashtests/layout/xul/crashtests/291702-1.xhtml load chrome://reftest/content/crashtests/layout/xul/crashtests/291702-1.xhtml
load chrome://reftest/content/crashtests/layout/xul/crashtests/291702-2.xhtml load chrome://reftest/content/crashtests/layout/xul/crashtests/291702-2.xhtml
load chrome://reftest/content/crashtests/layout/xul/crashtests/291702-3.xhtml load chrome://reftest/content/crashtests/layout/xul/crashtests/291702-3.xhtml
load chrome://reftest/content/crashtests/layout/xul/crashtests/294371-1.xhtml load chrome://reftest/content/crashtests/layout/xul/crashtests/294371-1.xhtml
load 311457-1.html
load chrome://reftest/content/crashtests/layout/xul/crashtests/322786-1.xhtml load chrome://reftest/content/crashtests/layout/xul/crashtests/322786-1.xhtml
skip-if(Android) load chrome://reftest/content/crashtests/layout/xul/crashtests/325377.xhtml skip-if(Android) load chrome://reftest/content/crashtests/layout/xul/crashtests/325377.xhtml
skip-if(Android) load chrome://reftest/content/crashtests/layout/xul/crashtests/326879-1.xhtml skip-if(Android) load chrome://reftest/content/crashtests/layout/xul/crashtests/326879-1.xhtml
@@ -22,6 +24,7 @@ load chrome://reftest/content/crashtests/layout/xul/crashtests/366112-1.xhtml
skip-if(Android) load chrome://reftest/content/crashtests/layout/xul/crashtests/366203-1.xhtml skip-if(Android) load chrome://reftest/content/crashtests/layout/xul/crashtests/366203-1.xhtml
load 367185-1.xhtml load 367185-1.xhtml
load 369942-1.xhtml load 369942-1.xhtml
skip-if(Android) load chrome://reftest/content/crashtests/layout/xul/crashtests/374102-1.xhtml
load 376137-1.html load 376137-1.html
load 376137-2.html load 376137-2.html
load 377592-1.svg load 377592-1.svg
@@ -36,6 +39,7 @@ load 384871-1.html
load chrome://reftest/content/crashtests/layout/xul/crashtests/386642.xhtml load chrome://reftest/content/crashtests/layout/xul/crashtests/386642.xhtml
load chrome://reftest/content/crashtests/layout/xul/crashtests/387080-1.xhtml load chrome://reftest/content/crashtests/layout/xul/crashtests/387080-1.xhtml
load 391974-1.html load 391974-1.html
skip-if(Android) load chrome://reftest/content/crashtests/layout/xul/crashtests/399013.xhtml
load 402912-1.xhtml load 402912-1.xhtml
load 404192.xhtml load 404192.xhtml
load chrome://reftest/content/crashtests/layout/xul/crashtests/408904-1.xhtml load chrome://reftest/content/crashtests/layout/xul/crashtests/408904-1.xhtml

View File

@@ -23,6 +23,7 @@ UNIFIED_SOURCES += [
"nsBoxFrame.cpp", "nsBoxFrame.cpp",
"nsBoxLayout.cpp", "nsBoxLayout.cpp",
"nsBoxLayoutState.cpp", "nsBoxLayoutState.cpp",
"nsDeckFrame.cpp",
"nsDocElementBoxFrame.cpp", "nsDocElementBoxFrame.cpp",
"nsImageBoxFrame.cpp", "nsImageBoxFrame.cpp",
"nsLeafBoxFrame.cpp", "nsLeafBoxFrame.cpp",
@@ -38,6 +39,7 @@ UNIFIED_SOURCES += [
"nsSliderFrame.cpp", "nsSliderFrame.cpp",
"nsSplitterFrame.cpp", "nsSplitterFrame.cpp",
"nsSprocketLayout.cpp", "nsSprocketLayout.cpp",
"nsStackLayout.cpp",
"nsTextBoxFrame.cpp", "nsTextBoxFrame.cpp",
"nsXULPopupManager.cpp", "nsXULPopupManager.cpp",
"nsXULTooltipListener.cpp", "nsXULTooltipListener.cpp",

View File

@@ -308,7 +308,8 @@ nsresult nsIFrame::SyncXULLayout(nsBoxLayoutState& aBoxLayoutState) {
nsresult nsIFrame::XULRedraw(nsBoxLayoutState& aState) { nsresult nsIFrame::XULRedraw(nsBoxLayoutState& aState) {
if (aState.PaintingDisabled()) return NS_OK; if (aState.PaintingDisabled()) return NS_OK;
// Unclear whether we could get away with just InvalidateFrame(). // nsStackLayout, at least, expects us to repaint descendants even
// if a damage rect is provided
InvalidateFrameSubtree(); InvalidateFrameSubtree();
return NS_OK; return NS_OK;

283
layout/xul/nsDeckFrame.cpp Normal file
View File

@@ -0,0 +1,283 @@
/* -*- 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/. */
//
// Eric Vaughan
// Netscape Communications
//
// See documentation in associated header file
//
#include "nsDeckFrame.h"
#include "mozilla/ComputedStyle.h"
#include "mozilla/PresShell.h"
#include "nsLayoutUtils.h"
#include "nsPresContext.h"
#include "nsIContent.h"
#include "nsCOMPtr.h"
#include "nsNameSpaceManager.h"
#include "nsGkAtoms.h"
#include "nsHTMLParts.h"
#include "nsCSSRendering.h"
#include "nsViewManager.h"
#include "nsBoxLayoutState.h"
#include "nsStackLayout.h"
#include "nsDisplayList.h"
#include "nsContainerFrame.h"
#include "nsContentUtils.h"
#include "nsXULPopupManager.h"
#include "nsImageBoxFrame.h"
#include "nsImageFrame.h"
#ifdef ACCESSIBILITY
# include "nsAccessibilityService.h"
#endif
using namespace mozilla;
nsIFrame* NS_NewDeckFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
return new (aPresShell) nsDeckFrame(aStyle, aPresShell->GetPresContext());
}
NS_IMPL_FRAMEARENA_HELPERS(nsDeckFrame)
NS_QUERYFRAME_HEAD(nsDeckFrame)
NS_QUERYFRAME_ENTRY(nsDeckFrame)
NS_QUERYFRAME_TAIL_INHERITING(nsBoxFrame)
nsDeckFrame::nsDeckFrame(ComputedStyle* aStyle, nsPresContext* aPresContext)
: nsBoxFrame(aStyle, aPresContext, kClassID) {
nsCOMPtr<nsBoxLayout> layout;
NS_NewStackLayout(layout);
SetXULLayoutManager(layout);
}
nsresult nsDeckFrame::AttributeChanged(int32_t aNameSpaceID, nsAtom* aAttribute,
int32_t aModType) {
nsresult rv =
nsBoxFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
// if the index changed hide the old element and make the new element visible
if (aAttribute == nsGkAtoms::selectedIndex) {
IndexChanged();
}
return rv;
}
void nsDeckFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
nsIFrame* aPrevInFlow) {
nsBoxFrame::Init(aContent, aParent, aPrevInFlow);
mIndex = GetSelectedIndex();
}
void nsDeckFrame::ShowBox(nsIFrame* aBox) { Animate(aBox, true); }
void nsDeckFrame::HideBox(nsIFrame* aBox) {
PresShell::ClearMouseCapture(aBox);
Animate(aBox, false);
}
void nsDeckFrame::IndexChanged() {
// did the index change?
int32_t index = GetSelectedIndex();
if (index == mIndex) return;
// redraw
InvalidateFrame();
// hide the currently showing box
nsIFrame* currentBox = GetSelectedBox();
if (currentBox) // only hide if it exists
HideBox(currentBox);
mSelectedBoxCache = nullptr;
mIndex = index;
ShowBox(GetSelectedBox());
#ifdef ACCESSIBILITY
nsAccessibilityService* accService = GetAccService();
if (accService) {
accService->DeckPanelSwitched(PresContext()->GetPresShell(), mContent,
currentBox, GetSelectedBox());
}
#endif
// Force any popups that might be anchored on elements within hidden
// box to update.
nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
if (pm && currentBox) {
pm->UpdatePopupPositions(currentBox->PresContext()->RefreshDriver());
}
}
int32_t nsDeckFrame::GetSelectedIndex() {
// default index is 0
int32_t index = 0;
// get the index attribute
nsAutoString value;
if (mContent->AsElement()->GetAttr(kNameSpaceID_None,
nsGkAtoms::selectedIndex, value)) {
nsresult error;
// convert it to an integer
index = value.ToInteger(&error);
}
return index;
}
nsIFrame* nsDeckFrame::GetSelectedBox() {
if (!mSelectedBoxCache && mIndex >= 0) {
mSelectedBoxCache = (mIndex >= 0) ? mFrames.FrameAt(mIndex) : nullptr;
}
return mSelectedBoxCache;
}
void nsDeckFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
const nsDisplayListSet& aLists) {
// if a tab is hidden all its children are too.
if (StyleVisibility()->mVisible == StyleVisibility::Hidden) {
return;
}
nsBoxFrame::BuildDisplayList(aBuilder, aLists);
}
void nsDeckFrame::RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame) {
nsIFrame* currentFrame = GetSelectedBox();
if (aOldFrame == currentFrame) {
mSelectedBoxCache = nullptr;
}
if (currentFrame && aOldFrame && currentFrame != aOldFrame) {
// If the frame we're removing is at an index that's less
// than mIndex, that means we're going to be shifting indexes
// by 1.
//
// We attempt to keep the same child displayed by automatically
// updating our internal notion of the current index.
int32_t removedIndex = mFrames.IndexOf(aOldFrame);
MOZ_ASSERT(removedIndex >= 0,
"A deck child was removed that was not in mFrames.");
if (removedIndex < mIndex) {
// This shouldn't invalidate our cache, but be really paranoid, it's not
// that important to keep our cache here.
mSelectedBoxCache = nullptr;
mIndex--;
// This is going to cause us to handle the index change in IndexedChanged,
// but since the new index will match mIndex, it's essentially a noop.
nsContentUtils::AddScriptRunner(new nsSetAttrRunnable(
mContent->AsElement(), nsGkAtoms::selectedIndex, mIndex));
}
}
nsBoxFrame::RemoveFrame(aListID, aOldFrame);
}
void nsDeckFrame::BuildDisplayListForChildren(nsDisplayListBuilder* aBuilder,
const nsDisplayListSet& aLists) {
// only paint the selected box
nsIFrame* box = GetSelectedBox();
if (!box) return;
// Putting the child in the background list. This is a little weird but
// it matches what we were doing before.
nsDisplayListSet set(aLists, aLists.BlockBorderBackgrounds());
BuildDisplayListForChild(aBuilder, box, set);
}
void nsDeckFrame::Animate(nsIFrame* aParentBox, bool start) {
if (!aParentBox) return;
nsImageBoxFrame* imgBoxFrame = do_QueryFrame(aParentBox);
nsImageFrame* imgFrame = do_QueryFrame(aParentBox);
if (imgBoxFrame) {
if (start)
imgBoxFrame->RestartAnimation();
else
imgBoxFrame->StopAnimation();
}
if (imgFrame) {
if (start)
imgFrame->RestartAnimation();
else
imgFrame->StopAnimation();
}
for (const auto& childList : aParentBox->ChildLists()) {
for (nsIFrame* child : childList.mList) {
Animate(child, start);
}
}
}
NS_IMETHODIMP
nsDeckFrame::DoXULLayout(nsBoxLayoutState& aState) {
// Make sure we tweak the state so it does not resize our children.
// We will do that.
ReflowChildFlags oldFlags = aState.LayoutFlags();
aState.SetLayoutFlags(ReflowChildFlags::NoSizeView);
// do a normal layout
nsresult rv = nsBoxFrame::DoXULLayout(aState);
// <deck> and <tabpanels> other than our browser's tab shouldn't have any
// <browser> or <iframe> to avoid running into troubles with Fission.
MOZ_ASSERT(
(mContent->IsXULElement(nsGkAtoms::tabpanels) &&
mContent->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::id,
#ifdef MOZ_THUNDERBIRD
u"tabpanelcontainer"_ns,
#else
u"tabbrowser-tabpanels"_ns,
#endif
eCaseMatters)) ||
!HasPossiblyRemoteContents());
// run though each child. Hide all but the selected one
nsIFrame* box = nsIFrame::GetChildXULBox(this);
nscoord count = 0;
while (box) {
// make collapsed children not show up
if (count != mIndex) {
HideBox(box);
} else {
ShowBox(box);
}
box = GetNextXULBox(box);
count++;
}
aState.SetLayoutFlags(oldFlags);
return rv;
}
bool nsDeckFrame::HasPossiblyRemoteContents() const {
auto hasRemoteOrMayChangeRemoteNessAttribute =
[](dom::Element& aElement) -> bool {
return (aElement.AttrValueIs(kNameSpaceID_None, nsGkAtoms::remote,
nsGkAtoms::_true, eCaseMatters) ||
aElement.HasAttribute(u"maychangeremoteness"_ns));
};
for (nsIContent* node = mContent; node; node = node->GetNextNode(mContent)) {
if ((node->IsXULElement(nsGkAtoms::browser) ||
node->IsHTMLElement(nsGkAtoms::iframe)) &&
hasRemoteOrMayChangeRemoteNessAttribute(*(node->AsElement()))) {
return true;
}
}
return false;
}

78
layout/xul/nsDeckFrame.h Normal file
View File

@@ -0,0 +1,78 @@
/* -*- 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/. */
/**
Eric D Vaughan
A frame that can have multiple children. Only one child may be displayed at
one time. So the can be flipped though like a deck of cards.
**/
#ifndef nsDeckFrame_h___
#define nsDeckFrame_h___
#include "mozilla/Attributes.h"
#include "nsBoxFrame.h"
namespace mozilla {
class PresShell;
} // namespace mozilla
class nsDeckFrame final : public nsBoxFrame {
public:
NS_DECL_QUERYFRAME
NS_DECL_FRAMEARENA_HELPERS(nsDeckFrame)
friend nsIFrame* NS_NewDeckFrame(mozilla::PresShell* aPresShell,
ComputedStyle* aStyle);
virtual nsresult AttributeChanged(int32_t aNameSpaceID, nsAtom* aAttribute,
int32_t aModType) override;
NS_IMETHOD DoXULLayout(nsBoxLayoutState& aState) override;
virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
const nsDisplayListSet& aLists) override;
virtual void RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame) override;
virtual void BuildDisplayListForChildren(
nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) override;
virtual void Init(nsIContent* aContent, nsContainerFrame* aParent,
nsIFrame* aPrevInFlow) override;
#ifdef DEBUG_FRAME_DUMP
virtual nsresult GetFrameName(nsAString& aResult) const override {
return MakeFrameName(u"Deck"_ns, aResult);
}
#endif
explicit nsDeckFrame(ComputedStyle* aStyle, nsPresContext* aPresContext);
nsIFrame* GetSelectedBox();
// Returns whether this frame has any <browser> or <iframe> elements.
// Note that this function traverses down all descendants so this function
// should be used only in debug builds.
bool HasPossiblyRemoteContents() const;
protected:
void IndexChanged();
int32_t GetSelectedIndex();
void HideBox(nsIFrame* aBox);
void ShowBox(nsIFrame* aBox);
private:
int32_t mIndex = 0;
nsIFrame* mSelectedBoxCache = nullptr;
void Animate(nsIFrame*, bool);
}; // class nsDeckFrame
#endif

View File

@@ -15,6 +15,7 @@
#include "nsStyleConsts.h" #include "nsStyleConsts.h"
#include "nsGkAtoms.h" #include "nsGkAtoms.h"
#include "nsBoxFrame.h" #include "nsBoxFrame.h"
#include "nsStackLayout.h"
#include "nsIAnonymousContentCreator.h" #include "nsIAnonymousContentCreator.h"
#include "nsNodeInfoManager.h" #include "nsNodeInfoManager.h"
#include "nsContentCreatorFunctions.h" #include "nsContentCreatorFunctions.h"

View File

@@ -9,6 +9,7 @@
#include "nsGkAtoms.h" #include "nsGkAtoms.h"
#include "nsBoxFrame.h" #include "nsBoxFrame.h"
#include "nsDisplayList.h" #include "nsDisplayList.h"
#include "nsStackLayout.h"
#include "nsIPopupContainer.h" #include "nsIPopupContainer.h"
#include "nsIContent.h" #include "nsIContent.h"
#include "nsFrameManager.h" #include "nsFrameManager.h"

View File

@@ -0,0 +1,209 @@
/* -*- 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/. */
//
// Eric Vaughan
// Netscape Communications
//
// See documentation in associated header file
//
#include "nsStackLayout.h"
#include "nsCOMPtr.h"
#include "nsBoxLayoutState.h"
#include "nsBoxFrame.h"
#include "nsGkAtoms.h"
#include "nsIContent.h"
#include "nsNameSpaceManager.h"
using namespace mozilla;
nsBoxLayout* nsStackLayout::gInstance = nullptr;
nsresult NS_NewStackLayout(nsCOMPtr<nsBoxLayout>& aNewLayout) {
if (!nsStackLayout::gInstance) {
nsStackLayout::gInstance = new nsStackLayout();
NS_IF_ADDREF(nsStackLayout::gInstance);
}
// we have not instance variables so just return our static one.
aNewLayout = nsStackLayout::gInstance;
return NS_OK;
}
/*static*/
void nsStackLayout::Shutdown() { NS_IF_RELEASE(gInstance); }
nsStackLayout::nsStackLayout() = default;
/*
* Sizing: we are as wide as the widest child
* we are tall as the tallest child.
*/
nsSize nsStackLayout::GetXULPrefSize(nsIFrame* aBox, nsBoxLayoutState& aState) {
nsSize prefSize(0, 0);
nsIFrame* child = nsIFrame::GetChildXULBox(aBox);
while (child) {
nsSize pref = child->GetXULPrefSize(aState);
AddXULMargin(child, pref);
if (pref.width > prefSize.width) {
prefSize.width = pref.width;
}
if (pref.height > prefSize.height) {
prefSize.height = pref.height;
}
child = nsIFrame::GetNextXULBox(child);
}
AddXULBorderAndPadding(aBox, prefSize);
return prefSize;
}
nsSize nsStackLayout::GetXULMinSize(nsIFrame* aBox, nsBoxLayoutState& aState) {
nsSize minSize(0, 0);
nsIFrame* child = nsIFrame::GetChildXULBox(aBox);
while (child) {
nsSize min = child->GetXULMinSize(aState);
AddXULMargin(child, min);
if (min.width > minSize.width) {
minSize.width = min.width;
}
if (min.height > minSize.height) {
minSize.height = min.height;
}
child = nsIFrame::GetNextXULBox(child);
}
AddXULBorderAndPadding(aBox, minSize);
return minSize;
}
nsSize nsStackLayout::GetXULMaxSize(nsIFrame* aBox, nsBoxLayoutState& aState) {
nsSize maxSize(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
nsIFrame* child = nsIFrame::GetChildXULBox(aBox);
while (child) {
nsSize min = child->GetXULMinSize(aState);
nsSize max = child->GetXULMaxSize(aState);
max = nsIFrame::XULBoundsCheckMinMax(min, max);
AddXULMargin(child, max);
if (max.width < maxSize.width) {
maxSize.width = max.width;
}
if (max.height < maxSize.height) {
maxSize.height = max.height;
}
child = nsIFrame::GetNextXULBox(child);
}
AddXULBorderAndPadding(aBox, maxSize);
return maxSize;
}
nscoord nsStackLayout::GetAscent(nsIFrame* aBox, nsBoxLayoutState& aState) {
nscoord vAscent = 0;
nsIFrame* child = nsIFrame::GetChildXULBox(aBox);
while (child) {
nscoord ascent = child->GetXULBoxAscent(aState);
nsMargin margin;
child->GetXULMargin(margin);
ascent += margin.top;
if (ascent > vAscent) vAscent = ascent;
child = nsIFrame::GetNextXULBox(child);
}
return vAscent;
}
NS_IMETHODIMP
nsStackLayout::XULLayout(nsIFrame* aBox, nsBoxLayoutState& aState) {
nsRect clientRect;
aBox->GetXULClientRect(clientRect);
bool grow;
do {
nsIFrame* child = nsIFrame::GetChildXULBox(aBox);
grow = false;
while (child) {
nsMargin margin;
child->GetXULMargin(margin);
nsRect childRect(clientRect);
childRect.Deflate(margin);
if (childRect.width < 0) childRect.width = 0;
if (childRect.height < 0) childRect.height = 0;
nsRect oldRect(child->GetRect());
bool sizeChanged = !oldRect.IsEqualEdges(childRect);
// only lay out dirty children or children whose sizes have changed
if (sizeChanged || child->IsSubtreeDirty()) {
// add in the child's margin
nsMargin margin;
child->GetXULMargin(margin);
// Now place the child.
child->SetXULBounds(aState, childRect);
// Flow the child.
child->XULLayout(aState);
// Get the child's new rect.
childRect = child->GetRect();
childRect.Inflate(margin);
// Did the child push back on us and get bigger?
if (childRect.width > clientRect.width) {
clientRect.width = childRect.width;
grow = true;
}
if (childRect.height > clientRect.height) {
clientRect.height = childRect.height;
grow = true;
}
}
child = nsIFrame::GetNextXULBox(child);
}
} while (grow);
// if some HTML inside us got bigger we need to force ourselves to
// get bigger
nsRect bounds(aBox->GetRect());
nsMargin bp;
aBox->GetXULBorderAndPadding(bp);
clientRect.Inflate(bp);
if (clientRect.width > bounds.width || clientRect.height > bounds.height) {
if (clientRect.width > bounds.width) bounds.width = clientRect.width;
if (clientRect.height > bounds.height) bounds.height = clientRect.height;
aBox->SetXULBounds(aState, bounds);
}
return NS_OK;
}

View File

@@ -0,0 +1,48 @@
/* -*- 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/. */
/**
Eric D Vaughan
A frame that can have multiple children. Only one child may be displayed at
one time. So the can be flipped though like a deck of cards.
**/
#ifndef nsStackLayout_h___
#define nsStackLayout_h___
#include "mozilla/Attributes.h"
#include "nsBoxLayout.h"
#include "nsCOMPtr.h"
#include "nsCoord.h"
nsresult NS_NewStackLayout(nsCOMPtr<nsBoxLayout>& aNewLayout);
class nsStackLayout : public nsBoxLayout {
public:
friend nsresult NS_NewStackLayout(nsCOMPtr<nsBoxLayout>& aNewLayout);
static void Shutdown();
nsStackLayout();
NS_IMETHOD XULLayout(nsIFrame* aBox, nsBoxLayoutState& aState) override;
virtual nsSize GetXULPrefSize(nsIFrame* aBox,
nsBoxLayoutState& aBoxLayoutState) override;
virtual nsSize GetXULMinSize(nsIFrame* aBox,
nsBoxLayoutState& aBoxLayoutState) override;
virtual nsSize GetXULMaxSize(nsIFrame* aBox,
nsBoxLayoutState& aBoxLayoutState) override;
virtual nscoord GetAscent(nsIFrame* aBox,
nsBoxLayoutState& aBoxLayoutState) override;
private:
static nsBoxLayout* gInstance;
}; // class nsStackLayout
#endif

View File

@@ -487,7 +487,7 @@ class Longhand(Property):
"LineBreak", "LineBreak",
"LineClamp", "LineClamp",
"MasonryAutoFlow", "MasonryAutoFlow",
"BoolInteger", "MozForceBrokenImageIcon",
"text::MozControlCharacterVisibility", "text::MozControlCharacterVisibility",
"MathDepth", "MathDepth",
"MozScriptMinSize", "MozScriptMinSize",

View File

@@ -108,23 +108,11 @@ ${helpers.predefined_type(
enabled_in="chrome", enabled_in="chrome",
)} )}
// Hack to allow chrome to hide stuff only visually (without hiding it from
// a11y).
${helpers.predefined_type(
"-moz-subtree-hidden-only-visually",
"BoolInteger",
"computed::BoolInteger::zero()",
engines="gecko",
animation_value_type="discrete",
spec="None (Nonstandard internal property)",
enabled_in="chrome",
)}
// TODO(emilio): Probably also should be hidden from content. // TODO(emilio): Probably also should be hidden from content.
${helpers.predefined_type( ${helpers.predefined_type(
"-moz-force-broken-image-icon", "-moz-force-broken-image-icon",
"BoolInteger", "MozForceBrokenImageIcon",
"computed::BoolInteger::zero()", "computed::MozForceBrokenImageIcon::false_value()",
engines="gecko", engines="gecko",
animation_value_type="discrete", animation_value_type="discrete",
spec="None (Nonstandard Firefox-only property)", spec="None (Nonstandard Firefox-only property)",

View File

@@ -97,7 +97,7 @@ pub use self::transform::{Rotate, Scale, Transform, TransformOperation};
pub use self::transform::{TransformOrigin, TransformStyle, Translate}; pub use self::transform::{TransformOrigin, TransformStyle, Translate};
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
pub use self::ui::CursorImage; pub use self::ui::CursorImage;
pub use self::ui::{Cursor, BoolInteger, UserSelect}; pub use self::ui::{Cursor, MozForceBrokenImageIcon, UserSelect};
pub use super::specified::TextTransform; pub use super::specified::TextTransform;
pub use super::specified::ViewportVariant; pub use super::specified::ViewportVariant;
pub use super::specified::{BorderStyle, TextDecorationLine}; pub use super::specified::{BorderStyle, TextDecorationLine};

View File

@@ -10,7 +10,7 @@ use crate::values::computed::Number;
use crate::values::generics::ui as generics; use crate::values::generics::ui as generics;
pub use crate::values::specified::ui::CursorKind; pub use crate::values::specified::ui::CursorKind;
pub use crate::values::specified::ui::{BoolInteger, UserSelect}; pub use crate::values::specified::ui::{MozForceBrokenImageIcon, UserSelect};
/// A computed value for the `cursor` property. /// A computed value for the `cursor` property.
pub type Cursor = generics::GenericCursor<CursorImage>; pub type Cursor = generics::GenericCursor<CursorImage>;

View File

@@ -101,6 +101,8 @@ pub enum DisplayInside {
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
MozBox, MozBox,
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
MozDeck,
#[cfg(feature = "gecko")]
MozPopup, MozPopup,
} }
@@ -214,6 +216,8 @@ impl Display {
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
pub const MozInlineBox: Self = Self::new(DisplayOutside::Inline, DisplayInside::MozBox); pub const MozInlineBox: Self = Self::new(DisplayOutside::Inline, DisplayInside::MozBox);
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
pub const MozDeck: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozDeck);
#[cfg(feature = "gecko")]
pub const MozPopup: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozPopup); pub const MozPopup: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozPopup);
/// Make a raw display value from <display-outside> and <display-inside> values. /// Make a raw display value from <display-outside> and <display-inside> values.
@@ -594,6 +598,8 @@ impl Parse for Display {
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
"-moz-inline-box" if moz_display_values_enabled(context) => Display::MozInlineBox, "-moz-inline-box" if moz_display_values_enabled(context) => Display::MozInlineBox,
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
"-moz-deck" if moz_display_values_enabled(context) => Display::MozDeck,
#[cfg(feature = "gecko")]
"-moz-popup" if moz_display_values_enabled(context) => Display::MozPopup, "-moz-popup" if moz_display_values_enabled(context) => Display::MozPopup,
}) })
} }

View File

@@ -97,7 +97,7 @@ pub use self::transform::{Rotate, Scale, Transform};
pub use self::transform::{TransformOrigin, TransformStyle, Translate}; pub use self::transform::{TransformOrigin, TransformStyle, Translate};
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
pub use self::ui::CursorImage; pub use self::ui::CursorImage;
pub use self::ui::{Cursor, BoolInteger, UserSelect}; pub use self::ui::{Cursor, MozForceBrokenImageIcon, UserSelect};
pub use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent; pub use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent;
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]

View File

@@ -88,32 +88,31 @@ impl SpecifiedValueInfo for CursorImage {
ToResolvedValue, ToResolvedValue,
ToShmem, ToShmem,
)] )]
#[repr(transparent)] pub struct MozForceBrokenImageIcon(pub bool);
pub struct BoolInteger(pub bool);
impl BoolInteger { impl MozForceBrokenImageIcon {
/// Returns 0 /// Return initial value of -moz-force-broken-image-icon which is false.
#[inline] #[inline]
pub fn zero() -> Self { pub fn false_value() -> MozForceBrokenImageIcon {
Self(false) MozForceBrokenImageIcon(false)
} }
} }
impl Parse for BoolInteger { impl Parse for MozForceBrokenImageIcon {
fn parse<'i, 't>( fn parse<'i, 't>(
_context: &ParserContext, _context: &ParserContext,
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> { ) -> Result<MozForceBrokenImageIcon, ParseError<'i>> {
// We intentionally don't support calc values here. // We intentionally don't support calc values here.
match input.expect_integer()? { match input.expect_integer()? {
0 => Ok(Self(false)), 0 => Ok(MozForceBrokenImageIcon(false)),
1 => Ok(Self(true)), 1 => Ok(MozForceBrokenImageIcon(true)),
_ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)), _ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
} }
} }
} }
impl ToCss for BoolInteger { impl ToCss for MozForceBrokenImageIcon {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where where
W: Write, W: Write,
@@ -122,6 +121,22 @@ impl ToCss for BoolInteger {
} }
} }
impl From<u8> for MozForceBrokenImageIcon {
fn from(bits: u8) -> MozForceBrokenImageIcon {
MozForceBrokenImageIcon(bits == 1)
}
}
impl From<MozForceBrokenImageIcon> for u8 {
fn from(v: MozForceBrokenImageIcon) -> u8 {
if v.0 {
1
} else {
0
}
}
}
/// A specified value for `scrollbar-color` property /// A specified value for `scrollbar-color` property
pub type ScrollbarColor = generics::ScrollbarColor<Color>; pub type ScrollbarColor = generics::ScrollbarColor<Color>;

View File

@@ -79,7 +79,6 @@ include = [
"BreakBetween", "BreakBetween",
"BreakWithin", "BreakWithin",
"BorderStyle", "BorderStyle",
"BoolInteger",
"OutlineStyle", "OutlineStyle",
"CaptionSide", "CaptionSide",
"FontSizeAdjust", "FontSizeAdjust",

View File

@@ -113,7 +113,7 @@
ok(deck instanceof MozXULElement, "instance of MozXULElement"); ok(deck instanceof MozXULElement, "instance of MozXULElement");
ok(XULElement.isInstance(deck), "instance of XULElement"); ok(XULElement.isInstance(deck), "instance of XULElement");
is(deck.id, "foo", "attribute set"); is(deck.id, "foo", "attribute set");
is(deck.selectedIndex, 0, "Custom Element is property attached"); is(deck.selectedIndex, "0", "Custom Element is property attached");
deck.remove(); deck.remove();
info("Checking that whitespace text is removed but non-whitespace text isn't"); info("Checking that whitespace text is removed but non-whitespace text isn't");

View File

@@ -5,13 +5,14 @@
XUL Widget Test for deck XUL Widget Test for deck
--> -->
<window title="Deck Test" <window title="Deck Test"
onload="setTimeout(run_tests, 0);"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
<deck id="deck1" style="padding-top: 5px; padding-bottom: 12px;"> <deck id="deck1" style="padding-top: 5px; padding-bottom: 12px;">
<button id="d1b1" label="Button One"/> <button id="d1b1" label="Button One"/>
<button id="d1b2" label="Button Two is larger" style="height: 80px; margin: 1px;"/> <button id="d1b2" label="Button Two is larger" height="80" style="margin: 1px;"/>
</deck> </deck>
<deck id="deck2" selectedIndex="1"> <deck id="deck2" selectedIndex="1">
<button id="d2b1" label="Button One"/> <button id="d2b1" label="Button One"/>
@@ -43,15 +44,19 @@
<!-- test code goes here --> <!-- test code goes here -->
<script type="application/javascript"><![CDATA[ <script type="application/javascript"><![CDATA[
add_task(async function run_tests() {
SimpleTest.waitForExplicitFinish();
function run_tests() {
test_deck(); test_deck();
await test_deck_child_removal(); test_deck_child_removal();
}); SimpleTest.finish();
}
function test_deck() function test_deck()
{ {
var deck = $("deck1"); var deck = $("deck1");
is(deck.selectedIndex, 0, "deck one selectedIndex"); ok(deck.selectedIndex === '0', "deck one selectedIndex");
// this size is the button height, 80, plus the button padding of 1px on each side, // this size is the button height, 80, plus the button padding of 1px on each side,
// plus the deck's 5px top padding and the 12px bottom padding. // plus the deck's 5px top padding and the 12px bottom padding.
var rect = deck.getBoundingClientRect(); var rect = deck.getBoundingClientRect();
@@ -61,52 +66,47 @@ function test_deck()
// change the selected page of the deck and ensure that the mouse click goes // change the selected page of the deck and ensure that the mouse click goes
// to the button on that page // to the button on that page
deck.selectedIndex = 1; deck.selectedIndex = 1;
is(deck.selectedIndex, 1, "deck one selectedIndex after change"); ok(deck.selectedIndex === '1', "deck one selectedIndex after change");
synthesizeMouseExpectEvent(deck, 9, 9, { }, $("d1b2"), "click", "mouse on deck one after change"); synthesizeMouseExpectEvent(deck, 9, 9, { }, $("d1b2"), "click", "mouse on deck one after change");
deck = $("deck2"); deck = $("deck2");
is(deck.selectedIndex, 1, "deck two selectedIndex"); ok(deck.selectedIndex === '1', "deck two selectedIndex");
synthesizeMouseExpectEvent(deck, 9, 9, { }, $("d2b2"), "click", "mouse on deck two"); synthesizeMouseExpectEvent(deck, 9, 9, { }, $("d2b2"), "click", "mouse on deck two");
} }
async function test_deck_child_removal() function test_deck_child_removal()
{ {
// Start with a simple case where we have two child nodes in a deck, with // Start with a simple case where we have two child nodes in a deck, with
// the second child (index 1) selected. Removing the first node should // the second child (index 1) selected. Removing the first node should
// automatically set the selectedIndex at 0. // automatically set the selectedIndex at 0.
let deck = $("deck3"); let deck = $("deck3");
let child = $("d3b1"); let child = $("d3b1");
is(deck.selectedIndex, 1, "Should have the deck element at index 1 selected"); is(deck.selectedIndex, "1", "Should have the deck element at index 1 selected");
// Remove the child at the 0th index. The deck should automatically // Remove the child at the 0th index. The deck should automatically
// set the selectedIndex to "0". // set the selectedIndex to "0".
child.remove(); child.remove();
is(deck.selectedIndex, "0", "Should have the deck element at index 0 selected");
await Promise.resolve();
is(deck.selectedIndex, 0, "Should have the deck element at index 0 selected");
// Now scale it up by using a deck with 7 child nodes, and remove the // Now scale it up by using a deck with 7 child nodes, and remove the
// first three, making sure that the selectedIndex is decremented // first three, making sure that the selectedIndex is decremented
// each time. // each time.
deck = $("deck4"); deck = $("deck4");
let expectedIndex = 5; let expectedIndex = 5;
is(deck.selectedIndex, expectedIndex, is(deck.selectedIndex, String(expectedIndex),
"Should have the deck element at index " + expectedIndex + " selected"); "Should have the deck element at index " + expectedIndex + " selected");
for (let i = 0; i < 3; ++i) { for (let i = 0; i < 3; ++i) {
deck.firstChild.remove(); deck.firstChild.remove();
expectedIndex--; expectedIndex--;
await Promise.resolve(); is(deck.selectedIndex, String(expectedIndex),
is(deck.selectedIndex, expectedIndex,
"Should have the deck element at index " + expectedIndex + " selected"); "Should have the deck element at index " + expectedIndex + " selected");
} }
// Check that removing the currently selected node doesn't change // Check that removing the currently selected node doesn't change
// behaviour. // behaviour.
deck.childNodes[expectedIndex].remove(); deck.childNodes[expectedIndex].remove();
await Promise.resolve(); is(deck.selectedIndex, String(expectedIndex),
is(deck.selectedIndex, expectedIndex,
"The selectedIndex should not change when removing the node " + "The selectedIndex should not change when removing the node " +
"at the selected index."); "at the selected index.");
@@ -114,16 +114,14 @@ async function test_deck_child_removal()
// nodes at indexes greater than the selected node. // nodes at indexes greater than the selected node.
deck = $("deck5"); deck = $("deck5");
expectedIndex = 2; expectedIndex = 2;
await Promise.resolve(); is(deck.selectedIndex, String(expectedIndex),
is(deck.selectedIndex, expectedIndex,
"Should have the deck element at index " + expectedIndex + " selected"); "Should have the deck element at index " + expectedIndex + " selected");
// And then remove all of the nodes, starting from last to first, making // And then remove all of the nodes, starting from last to first, making
// sure that the selectedIndex does not change. // sure that the selectedIndex does not change.
while (deck.lastChild) { while (deck.lastChild) {
deck.lastChild.remove(); deck.lastChild.remove();
await Promise.resolve(); is(deck.selectedIndex, String(expectedIndex),
is(deck.selectedIndex, expectedIndex,
"Should have the deck element at index " + expectedIndex + " selected"); "Should have the deck element at index " + expectedIndex + " selected");
} }
} }

View File

@@ -7,6 +7,40 @@
// This is loaded into chrome windows with the subscript loader. Wrap in // This is loaded into chrome windows with the subscript loader. Wrap in
// a block to prevent accidentally leaking globals onto `window`. // a block to prevent accidentally leaking globals onto `window`.
{ {
class MozDeck extends MozXULElement {
set selectedIndex(val) {
if (this.selectedIndex == val) {
return;
}
this.setAttribute("selectedIndex", val);
var event = document.createEvent("Events");
event.initEvent("select", true, true);
this.dispatchEvent(event);
}
get selectedIndex() {
return this.getAttribute("selectedIndex") || "0";
}
set selectedPanel(val) {
var selectedIndex = -1;
for (
var panel = val;
panel != null;
panel = panel.previousElementSibling
) {
++selectedIndex;
}
this.selectedIndex = selectedIndex;
}
get selectedPanel() {
return this.children[this.selectedIndex];
}
}
customElements.define("deck", MozDeck);
class MozDropmarker extends MozXULElement { class MozDropmarker extends MozXULElement {
constructor() { constructor() {
super(); super();

View File

@@ -158,107 +158,14 @@
customElements.define("tabbox", MozTabbox); customElements.define("tabbox", MozTabbox);
class MozDeck extends MozXULElement { class MozTabpanels extends MozXULElement {
get isAsync() {
return this.getAttribute("async") == "true";
}
connectedCallback() { connectedCallback() {
if (this.delayConnectedCallback()) { if (this.delayConnectedCallback()) {
return; return;
} }
this._selectedPanel = null;
this._inAsyncOperation = false;
let selectCurrentIndex = () => {
// Try to select the new node if any.
let index = this.selectedIndex;
let oldPanel = this._selectedPanel;
this._selectedPanel = this.children.item(index) || null;
this.updateSelectedIndex(index, oldPanel);
};
this._mutationObserver = new MutationObserver(records => {
let anyRemovals = records.some(record => !!record.removedNodes.length);
if (anyRemovals) {
// Try to keep the current selected panel in-place first.
let index = Array.from(this.children).indexOf(this._selectedPanel);
if (index != -1) {
// Try to keep the same node selected.
this.setAttribute("selectedIndex", index);
}
}
// Select the current index if needed in case mutations have made that
// available where it wasn't before.
if (!this._inAsyncOperation) {
selectCurrentIndex();
}
});
this._mutationObserver.observe(this, {
childList: true,
});
selectCurrentIndex();
}
disconnectedCallback() {
this._mutationObserver?.disconnect();
this._mutationObserver = null;
}
updateSelectedIndex(
val,
oldPanel = this.querySelector(":scope > .deck-selected")
) {
this._inAsyncOperation = false;
if (oldPanel != this._selectedPanel) {
oldPanel?.classList.remove("deck-selected");
this._selectedPanel?.classList.add("deck-selected");
}
this.setAttribute("selectedIndex", val);
}
set selectedIndex(val) {
if (val < 0 || val >= this.children.length) {
return;
}
let oldPanel = this._selectedPanel;
this._selectedPanel = this.children[val];
this._inAsyncOperation = this.isAsync;
if (!this._inAsyncOperation) {
this.updateSelectedIndex(val, oldPanel);
}
if (this._selectedPanel != oldPanel) {
let event = document.createEvent("Events");
event.initEvent("select", true, true);
this.dispatchEvent(event);
}
}
get selectedIndex() {
let indexStr = this.getAttribute("selectedIndex");
return indexStr ? parseInt(indexStr) : 0;
}
set selectedPanel(val) {
this.selectedIndex = Array.from(this.children).indexOf(val);
}
get selectedPanel() {
return this._selectedPanel;
}
}
customElements.define("deck", MozDeck);
class MozTabpanels extends MozDeck {
constructor() {
super();
this._tabbox = null; this._tabbox = null;
this._selectedPanel = this.children.item(this.selectedIndex);
} }
get tabbox() { get tabbox() {
@@ -279,6 +186,46 @@
return (this._tabbox = parent); return (this._tabbox = parent);
} }
set selectedIndex(val) {
if (val < 0 || val >= this.children.length) {
return;
}
let panel = this._selectedPanel;
this._selectedPanel = this.children[val];
if (this.getAttribute("async") != "true") {
this.setAttribute("selectedIndex", val);
}
if (this._selectedPanel != panel) {
let event = document.createEvent("Events");
event.initEvent("select", true, true);
this.dispatchEvent(event);
}
}
get selectedIndex() {
let indexStr = this.getAttribute("selectedIndex");
return indexStr ? parseInt(indexStr) : -1;
}
set selectedPanel(val) {
let selectedIndex = -1;
for (
let panel = val;
panel != null;
panel = panel.previousElementSibling
) {
++selectedIndex;
}
this.selectedIndex = selectedIndex;
}
get selectedPanel() {
return this._selectedPanel;
}
/** /**
* nsIDOMXULRelatedElement * nsIDOMXULRelatedElement
*/ */
@@ -580,9 +527,7 @@
set selectedIndex(val) { set selectedIndex(val) {
var tab = this.getItemAtIndex(val); var tab = this.getItemAtIndex(val);
if (!tab) { if (tab) {
return;
}
for (let otherTab of this.allTabs) { for (let otherTab of this.allTabs) {
if (otherTab != tab && otherTab.selected) { if (otherTab != tab && otherTab.selected) {
otherTab._selected = false; otherTab._selected = false;
@@ -601,6 +546,7 @@
this.tabbox.tabpanels.selectedPanel = linkedPanel; this.tabbox.tabpanels.selectedPanel = linkedPanel;
} }
} }
}
get selectedIndex() { get selectedIndex() {
const tabs = this.allTabs; const tabs = this.allTabs;

View File

@@ -448,34 +448,18 @@ treechildren::-moz-tree-cell(ltr) {
direction: ltr !important; direction: ltr !important;
} }
/********** deck, tabpanels & stack *********/ /********** deck & stack *********/
tabpanels > *|*:not(:-moz-native-anonymous) { deck {
/* tabpanels is special: we want to avoid displaying them, but we still want display: -moz-deck;
* the hidden children to be accessible */
-moz-subtree-hidden-only-visually: 1;
} }
deck > *|*:not(:-moz-native-anonymous) {
visibility: hidden;
}
tabpanels > .deck-selected,
deck > .deck-selected {
-moz-subtree-hidden-only-visually: 0;
visibility: inherit;
}
tabpanels,
deck,
stack { stack {
display: grid; display: grid;
position: relative; position: relative;
} }
/* We shouldn't style native anonymous children like scrollbars or what not. */ /* We shouldn't style native anonymous children like scrollbars or what not */
tabpanels > *|*:not(:-moz-native-anonymous),
deck > *|*:not(:-moz-native-anonymous),
stack > *|*:not(:-moz-native-anonymous) { stack > *|*:not(:-moz-native-anonymous) {
grid-area: 1 / 1; grid-area: 1 / 1;
z-index: 0; z-index: 0;
@@ -505,6 +489,10 @@ tab {
-moz-box-pack: center; -moz-box-pack: center;
} }
tabpanels {
display: -moz-deck;
}
/********** tooltip *********/ /********** tooltip *********/
tooltip[titletip="true"] { tooltip[titletip="true"] {

View File

@@ -26,9 +26,6 @@ tabs {
padding: 0 10px; padding: 0 10px;
margin-bottom: -12px; margin-bottom: -12px;
position: relative; position: relative;
/* Needs to sort on top of the tabbox, which is a grid container (and thus
* causes pseudo stacking contexts to be created) */
z-index: 1;
} }
tab { tab {