Bug 1668414 - Handle srcset in WebBrowserPersist. r=edgar

Rewrite the srcset URIs appropriately...

This code was completely untested (modulo one test for forms...) I added a
generic reftest framework to compare original vs. persisted document, so that
testing changes to this code is easier next time.

Differential Revision: https://phabricator.services.mozilla.com/D92133
This commit is contained in:
Emilio Cobos Álvarez 2020-10-02 16:32:07 +00:00
Родитель 2cdceb9b78
Коммит d52ce2e888
7 изменённых файлов: 188 добавлений и 14 удалений

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

@ -472,6 +472,9 @@ struct ResponsiveImageDescriptors {
// We don't support "h" descriptors yet and they are not spec'd, but the
// current spec does specify that they can be silently ignored (whereas
// entirely unknown descriptors cause us to invalidate the candidate)
//
// If we ever start honoring them we should serialize them in
// AppendDescriptors.
Maybe<int32_t> mFutureCompatHeight;
// If this descriptor set is bogus, e.g. a value was added twice (and thus
// dropped) or an unknown descriptor was added.
@ -676,6 +679,26 @@ double ResponsiveImageCandidate::Density(
return Density(-1);
}
void ResponsiveImageCandidate::AppendDescriptors(
nsAString& aDescriptors) const {
MOZ_ASSERT(IsValid());
switch (mType) {
case CandidateType::Default:
case CandidateType::Invalid:
return;
case CandidateType::ComputedFromWidth:
aDescriptors.Append(' ');
aDescriptors.AppendInt(mValue.mWidth);
aDescriptors.Append('w');
return;
case CandidateType::Density:
aDescriptors.Append(' ');
aDescriptors.AppendFloat(mValue.mDensity);
aDescriptors.Append('x');
return;
}
}
double ResponsiveImageCandidate::Density(double aMatchingWidth) const {
if (mType == CandidateType::Invalid) {
MOZ_ASSERT(false, "Getting density for uninitialized candidate");

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

@ -82,6 +82,10 @@ class ResponsiveImageSelector {
// return - true if the selected image result changed.
bool SelectImage(bool aReselect = false);
Span<const ResponsiveImageCandidate> AllCandidates() const {
return mCandidates;
}
protected:
virtual ~ResponsiveImageSelector();
@ -162,6 +166,9 @@ class ResponsiveImageCandidate {
// avoid having each call re-compute the width.
double Density(double aMatchingWidth) const;
// Append the descriptors for this candidate serialized as a string.
void AppendDescriptors(nsAString&) const;
bool IsValid() const { return mType != CandidateType::Invalid; }
// If this selector is computed from the selector's matching width.

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

@ -12,6 +12,7 @@
#include "mozilla/dom/Element.h"
#include "mozilla/dom/HTMLAnchorElement.h"
#include "mozilla/dom/HTMLAreaElement.h"
#include "mozilla/dom/HTMLImageElement.h"
#include "mozilla/dom/HTMLInputElement.h"
#include "mozilla/dom/HTMLLinkElement.h"
#include "mozilla/dom/HTMLObjectElement.h"
@ -20,8 +21,10 @@
#include "mozilla/dom/HTMLTextAreaElement.h"
#include "mozilla/dom/NodeFilterBinding.h"
#include "mozilla/dom/ProcessingInstruction.h"
#include "mozilla/dom/ResponsiveImageSelector.h"
#include "mozilla/dom/BrowserParent.h"
#include "mozilla/dom/TreeWalker.h"
#include "mozilla/Encoding.h"
#include "mozilla/Unused.h"
#include "nsComponentManagerUtils.h"
#include "nsContentUtils.h"
@ -415,9 +418,18 @@ nsresult ResourceReader::OnWalkDOMNode(nsINode* aNode) {
}
// Test the node to see if it's an image, frame, iframe, css, js
if (aNode->IsHTMLElement(nsGkAtoms::img)) {
return OnWalkAttribute(aNode->AsElement(), nsIContentPolicy::TYPE_IMAGE,
"src");
if (auto* img = dom::HTMLImageElement::FromNode(*aNode)) {
MOZ_TRY(OnWalkAttribute(img, nsIContentPolicy::TYPE_IMAGE, "src"));
if (auto* selector = img->GetResponsiveImageSelector()) {
for (const auto& candidate : selector->AllCandidates()) {
if (!candidate.IsValid()) {
continue;
}
MOZ_TRY(OnWalkURI(NS_ConvertUTF16toUTF8(candidate.URLString()),
nsIContentPolicy::TYPE_IMAGE));
}
}
return NS_OK;
}
if (aNode->IsSVGElement(nsGkAtoms::img)) {
@ -891,18 +903,41 @@ PersistNodeFixup::FixupNode(nsINode* aNodeIn, bool* aSerializeCloneKids,
return rv;
}
if (content->IsHTMLElement(nsGkAtoms::img)) {
nsresult rv = GetNodeToFixup(aNodeIn, aNodeOut);
if (NS_SUCCEEDED(rv) && *aNodeOut) {
// Disable image loads
nsCOMPtr<nsIImageLoadingContent> imgCon = do_QueryInterface(*aNodeOut);
if (imgCon) {
imgCon->SetLoadingEnabled(false);
}
FixupAnchor(*aNodeOut);
FixupAttribute(*aNodeOut, "src");
if (auto* img = dom::HTMLImageElement::FromNode(*content)) {
MOZ_TRY(GetNodeToFixup(aNodeIn, aNodeOut));
if (!*aNodeOut) {
return NS_OK;
}
return rv;
// Disable image loads
nsCOMPtr<nsIImageLoadingContent> imgCon = do_QueryInterface(*aNodeOut);
if (imgCon) {
imgCon->SetLoadingEnabled(false);
}
// FIXME(emilio): Why fixing up <img href>? Looks bogus
FixupAnchor(*aNodeOut);
FixupAttribute(*aNodeOut, "src");
if (auto* selector = img->GetResponsiveImageSelector()) {
nsAutoString srcset;
bool first = true;
for (const auto& candidate : selector->AllCandidates()) {
if (!candidate.IsValid()) {
continue;
}
if (!first) {
srcset.AppendLiteral(", ");
}
first = false;
nsAutoString uri(candidate.URLString());
FixupURI(uri);
srcset.Append(uri);
candidate.AppendDescriptors(srcset);
}
(*aNodeOut)->AsElement()->SetAttr(nsGkAtoms::srcset, srcset,
IgnoreErrors());
}
return NS_OK;
}
if (content->IsAnyOfHTMLElements(nsGkAtoms::audio, nsGkAtoms::video)) {

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

@ -7,3 +7,7 @@ support-files =
[browser_save_form_input_state.js]
support-files =
file_form_state.html
[browser_persist.js]
support-files =
file_persist_srcset.html
file_persist_image.png

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

@ -0,0 +1,102 @@
"use strict"; // -*- js-indent-level: 2; indent-tabs-mode: nil -*-
Services.scriptloader.loadSubScript(
"chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js",
this
);
const contentBase =
"https://example.com/browser/toolkit/components/windowcreator/test/";
const chromeBase =
"chrome://mochitests/content/browser/toolkit/components/windowcreator/test/";
// Checks that the source and target documents are the same.
const REFTESTS = [
"file_persist_srcset.html",
// ...
];
async function persist(name, uri) {
return BrowserTestUtils.withNewTab(uri, async function(browser) {
// Snapshot the doc as loaded, this is our reference.
info("snapshotting reference");
let reference = await snapshotWindow(browser);
info("starting persistence");
let doc = await new Promise(function(resolve) {
browser.frameLoader.startPersistence(null, {
onDocumentReady(d) {
resolve(d);
},
onError(e) {
ok(false, "startPersistence failed: " + e);
},
});
});
let wbp = Cc[
"@mozilla.org/embedding/browser/nsWebBrowserPersist;1"
].createInstance(Ci.nsIWebBrowserPersist);
let tmp = Services.dirsvc.get("TmpD", Ci.nsIFile);
let tmpFile = tmp.clone();
tmpFile.append(name + "_saved.html");
let tmpDir = tmp.clone();
tmpDir.append(name + "_files");
registerCleanupFunction(function cleanUp() {
if (tmpFile.exists()) {
tmpFile.remove(/* recursive = */ false);
}
if (tmpDir.exists()) {
tmpDir.remove(/* recursive = */ true);
}
});
info("persisting document");
// Wait for the persisted document.
await new Promise(function(resolve) {
wbp.progressListener = {
onProgressChange() {},
onLocationChange() {},
onStatusChange() {},
onSecurityChange() {},
onContentBlockingEvent() {},
onStateChange(_wbp, _req, state, _status) {
if (state & Ci.nsIWebProgressListener.STATE_STOP) {
resolve();
}
},
};
wbp.saveDocument(doc, tmpFile, tmpDir, null, 0, 0);
});
info("load done, loading persisted document");
let fileUri = Services.io.newFileURI(tmpFile).spec;
let test = await BrowserTestUtils.withNewTab(fileUri, async function(
persistedBrowser
) {
info("snapshotting persisted document");
return snapshotWindow(persistedBrowser);
});
return { test, reference };
});
}
add_task(async function() {
for (let filename of REFTESTS) {
let uri = contentBase + filename;
let { test, reference } = await persist(filename, uri);
let expectEqual = true;
let [same, testUri, refUri] = compareSnapshots(
test,
reference,
expectEqual
);
ok(same, "test and references should match");
if (!same) {
info(testUri);
info(refUri);
}
}
});

Двоичные данные
toolkit/components/windowcreator/test/file_persist_image.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1010 B

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

@ -0,0 +1,3 @@
<!doctype html>
<img width=100 height=100 srcset="file_persist_image.png">
<img width=100 height=100 srcset="file_persist_image.png 1x, file_persist_image.png 2x, file_persist_image.png 100w, file_persist_image_bad.png">