Bug 1621415 - Ensure adopted styles are applied when printing r=emilio

- Add functionality to clone adopted style sheets for printing.
- Add reftest to ensure that the document's adopted styles show in print.
- Add reftest to ensure that a shadow root's adopted styles show in print.

Differential Revision: https://phabricator.services.mozilla.com/D66517

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Erik Nordin 2020-03-24 22:54:18 +00:00
parent e13b4495b3
commit ca4fa23626
12 changed files with 116 additions and 6 deletions

View File

@ -12076,6 +12076,7 @@ already_AddRefed<Document> Document::CreateStaticClone(
}
}
}
clonedDoc->CloneAdoptedSheetsFrom(*this);
for (int t = 0; t < AdditionalSheetTypeCount; ++t) {
auto& sheets = mAdditionalSheets[additionalSheetType(t)];

View File

@ -198,6 +198,27 @@ void DocumentOrShadowRoot::ClearAdoptedStyleSheets() {
mAdoptedStyleSheets.Clear();
}
void DocumentOrShadowRoot::CloneAdoptedSheetsFrom(
DocumentOrShadowRoot& aSource) {
Document& ownerDoc = *AsNode().OwnerDoc();
Sequence<OwningNonNull<StyleSheet>> list;
if (!list.SetCapacity(mAdoptedStyleSheets.Length(), fallible)) {
return;
}
// TODO(nordzilla, bug 1621415) We can improve the performance here.
// https://bugzilla.mozilla.org/show_bug.cgi?id=1622322
for (auto& sheet : aSource.mAdoptedStyleSheets) {
DebugOnly<bool> succeeded =
list.AppendElement(sheet->CloneAdoptedSheet(ownerDoc), fallible);
MOZ_ASSERT(succeeded);
}
ErrorResult rv;
SetAdoptedStyleSheets(list, rv);
MOZ_ASSERT(!rv.Failed());
}
Element* DocumentOrShadowRoot::GetElementById(const nsAString& aElementId) {
if (MOZ_UNLIKELY(aElementId.IsEmpty())) {
nsContentUtils::ReportEmptyGetElementByIdArg(AsNode().OwnerDoc());

View File

@ -245,6 +245,12 @@ class DocumentOrShadowRoot {
void RemoveSheetFromStylesIfApplicable(StyleSheet&);
void ClearAdoptedStyleSheets();
/**
* Clone's the argument's adopted style sheets into this.
* This should only be used when cloning a static document for printing.
*/
void CloneAdoptedSheetsFrom(DocumentOrShadowRoot&);
// Returns the reference to the sheet, if found in mStyleSheets.
already_AddRefed<StyleSheet> RemoveSheet(StyleSheet& aSheet);
void InsertSheetAt(size_t aIndex, StyleSheet& aSheet);

View File

@ -107,6 +107,7 @@ void ShadowRoot::CloneInternalDataFrom(ShadowRoot* aOther) {
}
}
}
CloneAdoptedSheetsFrom(*aOther);
}
nsresult ShadowRoot::Bind() {

View File

@ -1,5 +1,6 @@
[DEFAULT]
prefs =
layout.css.constructable-stylesheets.enabled=true
layout.css.individual-transform.enabled=true
layout.css.motion-path.enabled=true
plugin.load_flash_only=false
@ -21,6 +22,10 @@ support-files =
printpreview_font_mozprintcallback_ref.html
printpreview_quirks.html
printpreview_quirks_ref.html
test_document_adopted_styles.html
test_document_adopted_styles_ref.html
test_shadow_root_adopted_styles.html
test_shadow_root_adopted_styles_ref.html
file_bug1018265.xhtml
[test_bug396367-1.html]

View File

@ -31,7 +31,7 @@ function printpreview(hasMozPrintCallback) {
gWbp = frameElts[1].docShell.initOrReusePrintPreviewViewer();
var listener = {
onLocationChange: function(webProgress, request, location, flags) { },
onProgressChange: function(webProgress, request, curSelfProgress,
onProgressChange: function(webProgress, request, curSelfProgress,
maxSelfProgress, curTotalProgress,
maxTotalProgress) { },
onSecurityChange: function(webProgress, request, state) { },
@ -100,10 +100,14 @@ function runTests()
}
function compareCanvases() {
return window.windowUtils
.compareCanvases(document.getElementsByTagName("canvas")[0],
document.getElementsByTagName("canvas")[1],
{}) == 0;
const canvas1 = document.getElementsByTagName("canvas")[0];
const canvas2 = document.getElementsByTagName("canvas")[1];
const result = window.windowUtils.compareCanvases(canvas1, canvas2, {}) == 0;
if (!result) {
todo(false, "TEST CASE: " + canvas1.toDataURL());
todo(false, "REFERENCE: " + canvas2.toDataURL());
}
return result;
}
function addHTMLContent(parent) {
@ -422,8 +426,20 @@ async function runTest11() {
requestAnimationFrame(function() { setTimeout(runTest12); } );
}
// Crash test for bug 1615261
// bug 1621415
async function runTest12() {
await compareFiles("test_document_adopted_styles.html", "test_document_adopted_styles_ref.html");
requestAnimationFrame(function() { setTimeout(runTest13); } );
}
// bug 1621415
async function runTest13() {
await compareFiles("test_shadow_root_adopted_styles.html", "test_shadow_root_adopted_styles_ref.html");
requestAnimationFrame(function() { setTimeout(runTest14); } );
}
// Crash test for bug 1615261
async function runTest14() {
frameElts[0].contentDocument.body.innerHTML =
'<style> div { width: 100px; height: 100px; background-image: url("animated.gif"); } </style>' +
'<div>Firefox will crash if you try and print this page</div>';

View File

@ -0,0 +1,8 @@
<!doctype html>
<meta charset="utf-8">
<div class="target"></div>
<script>
const sheet = new CSSStyleSheet();
document.adoptedStyleSheets = [sheet];
sheet.replaceSync(".target { width: 100px; height: 100px; border-style: solid; border-color: blue; }");
</script>

View File

@ -0,0 +1,6 @@
<!doctype html>
<meta charset="utf-8">
<body>
<div class="target"></div>
<style> .target { width: 100px; height: 100px; border-style: solid; border-color: blue; } </style>
</body>

View File

@ -0,0 +1,11 @@
<!doctype html>
<meta charset="utf-8">
<body>
<script>
document.body.attachShadow({mode: "open"}).innerHTML = `
<div class="target"></div>
`;
const sheet = new CSSStyleSheet();
document.body.shadowRoot.adoptedStyleSheets = [sheet];
sheet.replaceSync(".target { width: 100px; height: 100px; border-style: solid; border-color: blue; }");
</script>

View File

@ -0,0 +1,11 @@
<!doctype html>
<meta charset="utf-8">
<body>
<script>
document.body.attachShadow({mode: "open"}).innerHTML = `
<div class="target"></div>
<style>
.target { width: 100px; height: 100px; border-style: solid; border-color: blue; }
</style>
`;
</script>

View File

@ -1321,12 +1321,29 @@ already_AddRefed<StyleSheet> StyleSheet::Clone(
StyleSheet* aCloneParent, dom::CSSImportRule* aCloneOwnerRule,
dom::DocumentOrShadowRoot* aCloneDocumentOrShadowRoot,
nsINode* aCloneOwningNode) const {
MOZ_ASSERT(!IsConstructed(),
"Cannot create a non-constructed sheet from a constructed sheet");
RefPtr<StyleSheet> clone =
new StyleSheet(*this, aCloneParent, aCloneOwnerRule,
aCloneDocumentOrShadowRoot, aCloneOwningNode);
return clone.forget();
}
already_AddRefed<StyleSheet> StyleSheet::CloneAdoptedSheet(
Document& aConstructorDocument) const {
MOZ_ASSERT(IsConstructed(),
"Cannot create a constructed sheet from a non-constructed sheet");
MOZ_ASSERT(aConstructorDocument.IsStaticDocument(),
"Should never clone adopted sheets for a non-static document");
RefPtr<StyleSheet> clone = new StyleSheet(*this,
/* aParentToUse */ nullptr,
/* aOwnerRuleToUse */ nullptr,
/* aDocumentOrShadowRoot */ nullptr,
/* aOwningNodeToUse */ nullptr);
clone->mConstructorDocument = &aConstructorDocument;
return clone.forget();
}
ServoCSSRuleList* StyleSheet::GetCssRulesInternal() {
if (!mRuleList) {
EnsureUniqueInner();

View File

@ -221,6 +221,13 @@ class StyleSheet final : public nsICSSLoaderObserver, public nsWrapperCache {
dom::DocumentOrShadowRoot* aCloneDocumentOrShadowRoot,
nsINode* aCloneOwningNode) const;
/**
* Creates a clone of the adopted style sheet as though it were constructed
* by aConstructorDocument. This should only be used for printing.
*/
already_AddRefed<StyleSheet> CloneAdoptedSheet(
dom::Document& aConstructorDocument) const;
bool HasForcedUniqueInner() const {
return bool(mState & State::ForcedUniqueInner);
}