зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
2cdceb9b78
Коммит
d52ce2e888
|
@ -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);
|
||||
}
|
||||
}
|
||||
});
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 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">
|
Загрузка…
Ссылка в новой задаче