From cbc0b2ac4066aaac781820a58f7d85bf5448796f Mon Sep 17 00:00:00 2001 From: Romain Deltour Date: Tue, 15 Nov 2022 09:16:50 +0100 Subject: [PATCH] feat: improve checking of data URLs This commit introduces the following checks: - `RSC-029`(new): check that `data` URLs are not used when they would result in a top-level browsing context - check `data` URLs for foreign resource restrictions (fallbacks) An OPFItem instance can now represent a manifest item defined as a data URL. A `hasDataURL()` method will tell if this is the case. Fix #1238, fix #1239. --- .../epubcheck/messages/DefaultSeverities.java | 1 + .../adobe/epubcheck/messages/MessageId.java | 1 + .../com/adobe/epubcheck/ocf/OCFContainer.java | 3 +- .../com/adobe/epubcheck/opf/OPFChecker.java | 6 +- .../com/adobe/epubcheck/opf/OPFChecker30.java | 6 + .../java/com/adobe/epubcheck/opf/OPFItem.java | 17 ++ .../epubcheck/opf/ValidationContext.java | 4 +- .../com/adobe/epubcheck/opf/XRefChecker.java | 186 +++++++++++++----- .../com/adobe/epubcheck/ops/OPSHandler.java | 2 +- .../com/adobe/epubcheck/ops/OPSHandler30.java | 4 + .../xml/handlers/BaseURLHandler.java | 25 +-- .../java/org/w3c/epubcheck/url/URLUtils.java | 47 ++++- .../messages/MessageBundle.properties | 1 + .../epub3/00-minimal/minimal.feature | 2 +- .../files/data-url-in-html-a-href-error.xhtml | 11 ++ .../data-url-in-html-area-href-error.xhtml | 14 ++ .../EPUB/content_001.xhtml | 40 ++++ .../EPUB/nav.xhtml | 14 ++ .../EPUB/package.opf | 16 ++ .../META-INF/container.xml | 6 + .../data-url-in-html-img-cmt-valid/mimetype | 1 + .../EPUB/content_001.xhtml | 40 ++++ .../EPUB/image.jpeg | Bin 0 -> 1962 bytes .../EPUB/nav.xhtml | 14 ++ .../EPUB/package.opf | 17 ++ .../META-INF/container.xml | 6 + .../mimetype | 1 + .../EPUB/content_001.xhtml | 37 ++++ .../EPUB/image.jpeg | Bin 0 -> 1962 bytes .../EPUB/nav.xhtml | 14 ++ .../EPUB/package.opf | 45 +++++ .../META-INF/container.xml | 6 + .../mimetype | 1 + .../EPUB/content_001.xhtml | 37 ++++ .../EPUB/nav.xhtml | 14 ++ .../EPUB/package.opf | 16 ++ .../META-INF/container.xml | 6 + .../mimetype | 1 + .../EPUB/content_001.xhtml | 40 ++++ .../EPUB/nav.xhtml | 14 ++ .../EPUB/package.opf | 16 ++ .../META-INF/container.xml | 6 + .../mimetype | 1 + ...ta-url-in-manifest-item-in-spine-error.opf | 19 ++ .../files/data-url-in-manifest-item-valid.opf | 47 +++++ .../files/data-url-in-svg-a-href-error.xhtml | 15 ++ .../epub3/03-resources/resources.feature | 56 ++++++ 47 files changed, 806 insertions(+), 70 deletions(-) create mode 100644 src/test/resources/epub3/03-resources/files/data-url-in-html-a-href-error.xhtml create mode 100644 src/test/resources/epub3/03-resources/files/data-url-in-html-area-href-error.xhtml create mode 100644 src/test/resources/epub3/03-resources/files/data-url-in-html-img-cmt-valid/EPUB/content_001.xhtml create mode 100644 src/test/resources/epub3/03-resources/files/data-url-in-html-img-cmt-valid/EPUB/nav.xhtml create mode 100644 src/test/resources/epub3/03-resources/files/data-url-in-html-img-cmt-valid/EPUB/package.opf create mode 100644 src/test/resources/epub3/03-resources/files/data-url-in-html-img-cmt-valid/META-INF/container.xml create mode 100644 src/test/resources/epub3/03-resources/files/data-url-in-html-img-cmt-valid/mimetype create mode 100644 src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-intrinsic-fallback-valid/EPUB/content_001.xhtml create mode 100644 src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-intrinsic-fallback-valid/EPUB/image.jpeg create mode 100644 src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-intrinsic-fallback-valid/EPUB/nav.xhtml create mode 100644 src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-intrinsic-fallback-valid/EPUB/package.opf create mode 100644 src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-intrinsic-fallback-valid/META-INF/container.xml create mode 100644 src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-intrinsic-fallback-valid/mimetype create mode 100644 src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-manifest-fallback-valid/EPUB/content_001.xhtml create mode 100644 src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-manifest-fallback-valid/EPUB/image.jpeg create mode 100644 src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-manifest-fallback-valid/EPUB/nav.xhtml create mode 100644 src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-manifest-fallback-valid/EPUB/package.opf create mode 100644 src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-manifest-fallback-valid/META-INF/container.xml create mode 100644 src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-manifest-fallback-valid/mimetype create mode 100644 src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-no-fallback-error/EPUB/content_001.xhtml create mode 100644 src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-no-fallback-error/EPUB/nav.xhtml create mode 100644 src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-no-fallback-error/EPUB/package.opf create mode 100644 src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-no-fallback-error/META-INF/container.xml create mode 100644 src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-no-fallback-error/mimetype create mode 100644 src/test/resources/epub3/03-resources/files/data-url-in-html-link-exempt-valid/EPUB/content_001.xhtml create mode 100644 src/test/resources/epub3/03-resources/files/data-url-in-html-link-exempt-valid/EPUB/nav.xhtml create mode 100644 src/test/resources/epub3/03-resources/files/data-url-in-html-link-exempt-valid/EPUB/package.opf create mode 100644 src/test/resources/epub3/03-resources/files/data-url-in-html-link-exempt-valid/META-INF/container.xml create mode 100644 src/test/resources/epub3/03-resources/files/data-url-in-html-link-exempt-valid/mimetype create mode 100644 src/test/resources/epub3/03-resources/files/data-url-in-manifest-item-in-spine-error.opf create mode 100644 src/test/resources/epub3/03-resources/files/data-url-in-manifest-item-valid.opf create mode 100644 src/test/resources/epub3/03-resources/files/data-url-in-svg-a-href-error.xhtml diff --git a/src/main/java/com/adobe/epubcheck/messages/DefaultSeverities.java b/src/main/java/com/adobe/epubcheck/messages/DefaultSeverities.java index 10b2529ec..a24f07318 100644 --- a/src/main/java/com/adobe/epubcheck/messages/DefaultSeverities.java +++ b/src/main/java/com/adobe/epubcheck/messages/DefaultSeverities.java @@ -340,6 +340,7 @@ private void initialize() severities.put(MessageId.RSC_026, Severity.ERROR); severities.put(MessageId.RSC_027, Severity.WARNING); severities.put(MessageId.RSC_028, Severity.ERROR); + severities.put(MessageId.RSC_029, Severity.ERROR); // Scripting severities.put(MessageId.SCP_001, Severity.SUPPRESSED); // checking scripts is out of scope diff --git a/src/main/java/com/adobe/epubcheck/messages/MessageId.java b/src/main/java/com/adobe/epubcheck/messages/MessageId.java index 031bcc044..475d76c15 100644 --- a/src/main/java/com/adobe/epubcheck/messages/MessageId.java +++ b/src/main/java/com/adobe/epubcheck/messages/MessageId.java @@ -334,6 +334,7 @@ public enum MessageId implements Comparable RSC_026("RSC-026"), RSC_027("RSC-027"), RSC_028("RSC-028"), + RSC_029("RSC-029"), // Messages relating to scripting SCP_001("SCP-001"), diff --git a/src/main/java/com/adobe/epubcheck/ocf/OCFContainer.java b/src/main/java/com/adobe/epubcheck/ocf/OCFContainer.java index 372ba8406..13baade18 100644 --- a/src/main/java/com/adobe/epubcheck/ocf/OCFContainer.java +++ b/src/main/java/com/adobe/epubcheck/ocf/OCFContainer.java @@ -130,8 +130,9 @@ public boolean isRemote(URL url) } else { - return !(URLUtils.isSameOrigin(url, rootURL)); + return URLUtils.isRemote(url, rootURL); } } + } diff --git a/src/main/java/com/adobe/epubcheck/opf/OPFChecker.java b/src/main/java/com/adobe/epubcheck/opf/OPFChecker.java index f69190ffb..72a34f234 100755 --- a/src/main/java/com/adobe/epubcheck/opf/OPFChecker.java +++ b/src/main/java/com/adobe/epubcheck/opf/OPFChecker.java @@ -210,7 +210,7 @@ protected boolean checkContent() // only check the filename in single-file mode // (it is checked by the container checker in full-publication mode) // and for local resources (i.e. computed to a file URL) - if (!context.container.isPresent() && !item.isRemote()) + if (!context.container.isPresent() && !item.isRemote() && !item.hasDataURL()) { new OCFFilenameChecker(item.getPath(), context, item.getLocation()).check(); } @@ -378,6 +378,10 @@ else if (isBlessedStyleType(mimeType)) protected void checkItemContent(OPFItem item) { + // We do not currently support checking resources defined as data URLs + if (item.hasDataURL()) { + return; + } // Create a new validation context for the OPF item // FIXME 2022 set context OPFItem here // (instead of from XRefChecker in the builder code) diff --git a/src/main/java/com/adobe/epubcheck/opf/OPFChecker30.java b/src/main/java/com/adobe/epubcheck/opf/OPFChecker30.java index 65541d98b..08681930f 100644 --- a/src/main/java/com/adobe/epubcheck/opf/OPFChecker30.java +++ b/src/main/java/com/adobe/epubcheck/opf/OPFChecker30.java @@ -186,6 +186,12 @@ else if (!overlayTextChecker.isCorrectOverlay(docURL, mo)) @Override protected void checkSpineItem(OPFItem item, OPFHandler opfHandler) { + // Items with `data:` URLs are not allowed in the spine + if (item.hasDataURL()) { + report.message(MessageId.RSC_029, item.getLocation()); + return; + } + String mimeType = item.getMimeType(); if (item.getProperties() diff --git a/src/main/java/com/adobe/epubcheck/opf/OPFItem.java b/src/main/java/com/adobe/epubcheck/opf/OPFItem.java index feddac838..6bc72a001 100755 --- a/src/main/java/com/adobe/epubcheck/opf/OPFItem.java +++ b/src/main/java/com/adobe/epubcheck/opf/OPFItem.java @@ -109,6 +109,13 @@ private OPFItem(Builder builder) { this.path = url.toHumanString(); } + // If the item is defined with a data URL, return + // the URL string truncated arbitrarily to 30 chars + else if ("data".equals(url.scheme())) + { + String urlString = url.toString(); + this.path = url.toString().substring(0, Math.min(urlString.length(), 30)) + "…"; + } // If a container is present (full-publication check) // the item path is relative to the root of the container else if (builder.container.isPresent()) @@ -289,6 +296,16 @@ public boolean isFixedLayout() return fixedLayout; } + /** + * Returns true iff this item is a remote resource. + * + * @return true iff this item is a remote resource. + */ + public boolean hasDataURL() + { + return "data".equals(url.scheme()); + } + /** * Returns true iff this item is a remote resource. * diff --git a/src/main/java/com/adobe/epubcheck/opf/ValidationContext.java b/src/main/java/com/adobe/epubcheck/opf/ValidationContext.java index ad15e1cdd..d62d5f181 100644 --- a/src/main/java/com/adobe/epubcheck/opf/ValidationContext.java +++ b/src/main/java/com/adobe/epubcheck/opf/ValidationContext.java @@ -141,7 +141,7 @@ private String computePath() { if (container.isPresent() && !container.get().isRemote(url)) { - if (!url.path().isEmpty()) + if (url.path() != null && !url.path().isEmpty()) { return url.path().substring(1); } @@ -181,7 +181,7 @@ public boolean isRemote(URL url) } else { - return !(URLUtils.isSameOrigin(url, this.url)); + return URLUtils.isRemote(url, this.url); } } diff --git a/src/main/java/com/adobe/epubcheck/opf/XRefChecker.java b/src/main/java/com/adobe/epubcheck/opf/XRefChecker.java index 742b6ae5f..1820f402b 100755 --- a/src/main/java/com/adobe/epubcheck/opf/XRefChecker.java +++ b/src/main/java/com/adobe/epubcheck/opf/XRefChecker.java @@ -119,18 +119,65 @@ public ID(String id, int position, Type type) private static class Resource { + public static final class Builder + { + + private URL url; + private OPFItem item = null; + private boolean hasItemFallback = false; + private boolean hasImageFallback = false; + + public Builder url(URL url) + { + this.url = url; + return this; + } + + public Builder item(OPFItem item) + { + this.url = item.getURL(); + this.item = item; + return this; + } + + public Builder hasItemFallback(boolean hasItemFallback) + { + this.hasItemFallback = hasItemFallback; + return this; + } + + public Builder hasImageFallback(boolean hasImageFallback) + { + this.hasImageFallback = hasImageFallback; + return this; + } - public final OPFItem item; + public Resource build() + { + return new Resource(this); + } + } + + private final URL url; + private final String mimetype; + private final Optional item; private final Map ids; - public final boolean hasValidItemFallback; - public final boolean hasValidImageFallback; + private final boolean hasItemFallback; + private final boolean hasImageFallback; - Resource(OPFItem item, boolean hasValidItemFallback, boolean hasValidImageFallback) + private Resource(Builder builder) { - this.item = item; - this.hasValidItemFallback = hasValidItemFallback; - this.hasValidImageFallback = hasValidImageFallback; + Preconditions.checkState(builder.url != null, "A URL or OPF Item must be provided"); + Preconditions.checkState(builder.mimetype != null, "A MIME type must be provided"); + Preconditions + .checkState(builder.item == null || builder.item.getMimeType().equals(builder.mimetype)); + this.url = builder.url; + this.item = Optional.fromNullable(builder.item); + this.hasItemFallback = builder.hasItemFallback; + this.hasImageFallback = builder.hasImageFallback; this.ids = new HashMap(); + this.mimetype = builder.mimetype; + } /** @@ -138,8 +185,8 @@ private static class Resource * resource. * * @return {@code -1} if the ID wasn't found in the document, or {@code 0} - * if the given ID is {@code null} or an empty string, or the - * 1-based position of the ID otherwise. + * if the given ID is {@code null} or an empty string, or the + * 1-based position of the ID otherwise. */ public int getIDPosition(String id) { @@ -148,17 +195,40 @@ public int getIDPosition(String id) return (anchor != null) ? anchor.position : -1; } - // FIXME 2022 refactor ID registration - // public boolean hasID(String id) - // { - // return ids.containsKey(id); - // } - // - // public Type getIDType(String id) - // { - // return ids.get(id).type; - // } + public String getMimeType() + { + return mimetype; + } + + public boolean hasItem() + { + return item.isPresent(); + } + public OPFItem getItem() + { + return item.orNull(); + } + + public URL getURL() + { + return url; + } + + public boolean hasItemFallback() + { + return hasItemFallback; + } + + public boolean hasImageFallback() + { + return hasImageFallback; + } + + public boolean isInSpine() + { + return item.isPresent() && item.get().isInSpine(); + } } private static final Pattern REGEX_SVG_VIEW = Pattern.compile("svgView\\(.*\\)"); @@ -191,7 +261,7 @@ public XRefChecker(ValidationContext context) public String getMimeType(URL resource) { - return resources.containsKey(resource) ? resources.get(resource).item.getMimeType() : null; + return resources.containsKey(resource) ? resources.get(resource).getMimeType() : null; } /** @@ -202,7 +272,7 @@ public String getMimeType(URL resource) public Optional getResource(URL url) { return (url == null || !resources.containsKey(url)) ? Optional. absent() - : Optional.of(resources.get(url).item); + : Optional.fromNullable(resources.get(url).getItem()); } /** @@ -243,14 +313,26 @@ public void registerBinding(String mimeType, String handlerId) bindings.put(mimeType, handlerId); } + public void registerResource(URL url, String mimetype) + { + Preconditions.checkArgument(url != null); + if (!resources.containsKey(url)) + { + resources.put(url, new Resource.Builder().url(url).mimetype(mimetype).build()); + } + } + // FIXME 2022 simplify signature: fallback info can be moved to OPFItem public void registerResource(OPFItem item, boolean hasValidItemFallback, boolean hasValidImageFallback) { + Preconditions.checkArgument(item != null); // Note: Duplicate manifest items are already checked in OPFChecker. if (!resources.containsKey(item.getURL())) { - resources.put(item.getURL(), new Resource(item, hasValidItemFallback, hasValidImageFallback)); + + resources.put(item.getURL(), new Resource.Builder().item(item) + .hasItemFallback(hasValidItemFallback).hasImageFallback(hasValidImageFallback).build()); } } @@ -270,12 +352,6 @@ public void registerReference(URL url, Type type, EPUBLocation location) { if (url == null) return; - // Do not register data URLs - if ("data".equals(url.scheme())) - { - return; - } - // Remove query component of local URLs // FIXME 2022 check how to deal with local query strings // see http://code.google.com/p/epubcheck/issues/detail?id=190 @@ -295,6 +371,12 @@ public void registerReference(URL url, Type type, EPUBLocation location) URLReference xref = new URLReference(url, type, location); references.add(xref); report.info(location.getPath(), FeatureEnum.RESOURCE, container.relativize(xref.targetDoc)); + + // If it is a data URL, also register a new resource + if ("data".equals(url.scheme())) + { + registerResource(url, URLUtils.getDataURLType(url)); + } } public void checkReferences() @@ -330,8 +412,10 @@ public void checkReferences() private void checkReference(URLReference reference) { - Resource targetResource = resources.get(reference.targetDoc); Resource hostResource = resources.get(reference.location.url); + Resource targetResource = resources.get(reference.targetDoc); + // If the resource was not declared in the manifest, + // we build a new Resource object for the data URL. // Check remote resources if (container.isRemote(reference.url) @@ -339,13 +423,13 @@ private void checkReference(URLReference reference) && !EnumSet.of(Type.LINK, Type.HYPERLINK).contains(reference.type) // spine items are checked in OPFChecker30 && !(version == EPUBVersion.VERSION_3 && targetResource != null - && targetResource.item.isInSpine()) + && targetResource.isInSpine()) // audio, video, and fonts can be remote resources in EPUB 3 && !(version == EPUBVersion.VERSION_3 && (targetResource != null // if the item is declared, check its mime type - && (OPFChecker30.isAudioType(targetResource.item.getMimeType()) - || OPFChecker30.isVideoType(targetResource.item.getMimeType()) - || OPFChecker30.isFontType(targetResource.item.getMimeType())) + && (OPFChecker30.isAudioType(targetResource.getMimeType()) + || OPFChecker30.isVideoType(targetResource.getMimeType()) + || OPFChecker30.isFontType(targetResource.getMimeType())) // else, check if the reference is a type allowing remote resources || reference.type == Type.FONT || reference.type == Type.AUDIO || reference.type == Type.VIDEO))) @@ -386,20 +470,21 @@ else if (!undeclared.contains(reference.targetDoc) return; } + String mimetype = targetResource.getMimeType(); + // Type-specific checks switch (reference.type) { case HYPERLINK: // if mimeType is null, we should have reported an error already - if (!OPFChecker.isBlessedItemType(targetResource.item.getMimeType(), version) - && !OPFChecker.isDeprecatedBlessedItemType(targetResource.item.getMimeType()) - && !targetResource.hasValidItemFallback) + if (!OPFChecker.isBlessedItemType(mimetype, version) + && !OPFChecker.isDeprecatedBlessedItemType(mimetype) && !targetResource.hasItemFallback()) { report.message(MessageId.RSC_010, reference.location.context(container.relativize(reference.url))); return; } - if (/* !res.mimeType.equals("font/opentype") && */!targetResource.item.isInSpine()) + if (/* !res.mimeType.equals("font/opentype") && */!targetResource.isInSpine()) { report.message(MessageId.RSC_011, reference.location.context(container.relativize(reference.url))); @@ -409,33 +494,32 @@ else if (!undeclared.contains(reference.targetDoc) case IMAGE: case PICTURE_SOURCE: case PICTURE_SOURCE_FOREIGN: - if (reference.url.fragment() != null - && !targetResource.item.getMimeType().equals("image/svg+xml")) + if (reference.url.fragment() != null && !mimetype.equals("image/svg+xml")) { report.message(MessageId.RSC_009, reference.location.context(container.relativize(reference.url))); return; } // if mimeType is null, we should have reported an error already - if (!OPFChecker.isBlessedImageType(targetResource.item.getMimeType(), version)) + if (!OPFChecker.isBlessedImageType(mimetype, version)) { if (version == EPUBVersion.VERSION_3 && reference.type == Type.PICTURE_SOURCE) { report.message(MessageId.MED_007, reference.location, - container.relativize(reference.targetDoc), targetResource.item.getMimeType()); + container.relativize(reference.targetDoc), mimetype); return; } - else if (reference.type == Type.IMAGE && !targetResource.hasValidImageFallback) + else if (reference.type == Type.IMAGE && !targetResource.hasImageFallback()) { report.message(MessageId.MED_003, reference.location, - container.relativize(reference.targetDoc), targetResource.item.getMimeType()); + container.relativize(reference.targetDoc), mimetype); } } break; case SEARCH_KEY: // TODO update when we support EPUB CFI if ((reference.url.fragment() == null || !reference.url.fragment().startsWith("epubcfi(")) - && !targetResource.item.isInSpine()) + && !targetResource.isInSpine()) { report.message(MessageId.RSC_021, reference.location, container.relativize(reference.targetDoc)); @@ -488,15 +572,15 @@ else if (reference.type == Type.IMAGE && !targetResource.hasValidImageFallback) return; } // Media fragments in Data Navigation Documents - else if (fragment.contains("=") && hostResource != null && hostResource.item.getProperties() - .contains(PackageVocabs.ITEM_VOCAB.get(PackageVocabs.ITEM_PROPERTIES.DATA_NAV))) + else if (fragment.contains("=") && hostResource != null && hostResource.hasItem() + && hostResource.getItem().getProperties() + .contains(PackageVocabs.ITEM_VOCAB.get(PackageVocabs.ITEM_PROPERTIES.DATA_NAV))) { // Ignore, return; } // SVG view fragments are ignored - else if (targetResource.item.getMimeType().equals("image/svg+xml") - && REGEX_SVG_VIEW.matcher(fragment).matches()) + else if (mimetype.equals("image/svg+xml") && REGEX_SVG_VIEW.matcher(fragment).matches()) { return; } @@ -538,7 +622,7 @@ private void checkRegionBasedNav(URLReference ref) { Preconditions.checkArgument(ref.type == Type.REGION_BASED_NAV); Resource res = resources.get(ref.targetDoc); - if (!res.item.isFixedLayout()) + if (res != null && res.hasItem() && !res.getItem().isFixedLayout()) { report.message(MessageId.NAV_009, ref.location); } @@ -556,10 +640,10 @@ private void checkReadingOrder(Queue references, int lastSpinePosi Resource res = resources.get(ref.targetDoc); // abort early if the link target is not a spine item (checked elsewhere) - if (res == null || !res.item.isInSpine()) return; + if (res == null || !res.hasItem() || !res.getItem().isInSpine()) return; // check that the link is in spine order - int targetSpinePosition = res.item.getSpinePosition(); + int targetSpinePosition = res.getItem().getSpinePosition(); if (targetSpinePosition < lastSpinePosition) { String orderContext = LocalizedMessages.getInstance(locale).getSuggestion(MessageId.NAV_011, diff --git a/src/main/java/com/adobe/epubcheck/ops/OPSHandler.java b/src/main/java/com/adobe/epubcheck/ops/OPSHandler.java index 3a9098593..7ce928429 100755 --- a/src/main/java/com/adobe/epubcheck/ops/OPSHandler.java +++ b/src/main/java/com/adobe/epubcheck/ops/OPSHandler.java @@ -246,7 +246,7 @@ else if (name.equals("script")) } else if (ns.equals(EpubConstants.HtmlNamespaceUri)) { - if (name.equals("a")) + if (name.equals("a") || name.equals("area")) { checkHRef(null, "href"); } diff --git a/src/main/java/com/adobe/epubcheck/ops/OPSHandler30.java b/src/main/java/com/adobe/epubcheck/ops/OPSHandler30.java index 544fe226b..3e6fc0f7a 100644 --- a/src/main/java/com/adobe/epubcheck/ops/OPSHandler30.java +++ b/src/main/java/com/adobe/epubcheck/ops/OPSHandler30.java @@ -538,6 +538,10 @@ protected void processVideo() protected void processHyperlink(URL href) { super.processHyperlink(href); + if ("data".equals(href.scheme())) { + report.message(MessageId.RSC_029, location()); + return; + } if (inRegionBasedNav && xrefChecker.isPresent()) { xrefChecker.get().registerReference(href, XRefChecker.Type.REGION_BASED_NAV, location()); diff --git a/src/main/java/com/adobe/epubcheck/xml/handlers/BaseURLHandler.java b/src/main/java/com/adobe/epubcheck/xml/handlers/BaseURLHandler.java index ea4bdfd0a..383b7d312 100644 --- a/src/main/java/com/adobe/epubcheck/xml/handlers/BaseURLHandler.java +++ b/src/main/java/com/adobe/epubcheck/xml/handlers/BaseURLHandler.java @@ -114,20 +114,21 @@ private URL resolveURL(String string, boolean isBase) if (string == null) return null; try { + // Collapse formatting whitespace in data URLs + if (string.startsWith("data:")) + { + string = string.replaceAll("\\s", ""); + } URL url = URL.parse(baseURL, string); // non-strict parsing - // Also try to parse the URL in strict mode, to report any invalid URL - // Except for 'data:' URLs (formatting whitespace is a common practice) - if (!"data".equals(url.scheme())) + // Also try to parse the URL in strict mode, to report any invalid URL, + // but continue the processing if an exception is thrown + try + { + URL.parse(STRICT_PARSING_SETTINGS, baseURL, string); + } catch (GalimatiasParseException e) { - try - { - URL.parse(STRICT_PARSING_SETTINGS, baseURL, string); - } catch (GalimatiasParseException e) - { - // TODO should this be a mere warning? - report.message(MessageId.RSC_020, location(), string, e.getLocalizedMessage()); - } + report.message(MessageId.RSC_020, location(), string, e.getLocalizedMessage()); } // if we are resolving a new base URL, also resolve the test URLs @@ -162,7 +163,7 @@ else if (!isBase && !testA.toString().startsWith(TEST_BASE_A_FULL) return url; } catch (GalimatiasParseException e) { - // the non-strict parsing + // URL parsing error thrown during non-strict parsing report.message(MessageId.RSC_020, location(), string, e.getLocalizedMessage()); return null; } diff --git a/src/main/java/org/w3c/epubcheck/url/URLUtils.java b/src/main/java/org/w3c/epubcheck/url/URLUtils.java index a680601d9..89a6cf033 100644 --- a/src/main/java/org/w3c/epubcheck/url/URLUtils.java +++ b/src/main/java/org/w3c/epubcheck/url/URLUtils.java @@ -6,6 +6,8 @@ import static io.mola.galimatias.URLUtils.percentEncode; import java.io.File; +import java.text.CharacterIterator; +import java.text.StringCharacterIterator; import com.google.common.base.Preconditions; @@ -68,6 +70,25 @@ else if (urlA.equals(urlB)) } } + /** + * Test if a URL is "remote" compared to a another URL. A URL is considered + * remote if it is not same origin as the test URL **and** it is not a `data` + * URL. + * + * Note that this relation is not defined in the URL standard, but is useful + * in EPUB (to test for remote resources compared to container URLs). + * + * @param test + * the URL to test + * @param local + * the URL it is tested against + * @return `true` if and only if `test` is remote compared to `local`. + */ + public static boolean isRemote(URL test, URL local) + { + return (test == null || !test.scheme().equals("data")) && !isSameOrigin(test, local); + } + public static boolean isAbsoluteURLString(String string) { try @@ -126,9 +147,33 @@ public static String encodePath(String string) return buffer.toString(); } - public static String decode(String string) { return percentDecode(string); } + + /** + * Returns the MIME type of a `data:` URL. + * + * @param url + * a URL, can be `null`. + * @return the MIME type declared in the data URL (can be an empty string), or + * `null` if `url` is not a data URL. + */ + public static String getDataURLType(URL url) + { + if (!"data".equals(url.scheme())) + { + return null; + } + StringBuilder type = new StringBuilder(); + CharacterIterator characters = new StringCharacterIterator(url.schemeData()); + char c = characters.current(); + while (c != CharacterIterator.DONE && c != ',' && c != ';') + { + type.append(c); + c = characters.next(); + } + return type.toString(); + } } diff --git a/src/main/resources/com/adobe/epubcheck/messages/MessageBundle.properties b/src/main/resources/com/adobe/epubcheck/messages/MessageBundle.properties index 3cef47ca3..9c361cf09 100644 --- a/src/main/resources/com/adobe/epubcheck/messages/MessageBundle.properties +++ b/src/main/resources/com/adobe/epubcheck/messages/MessageBundle.properties @@ -350,6 +350,7 @@ RSC_025=Informative parsing error: %1$s RSC_026=URL "%1$s" leaks outside the container (it is not a valid-relative-ocf-URL-with-fragment string) RSC_027=XML document is encoded in UTF-16. It should be encoded in UTF-8 instead. RSC_028=XML documents must be encoded in UTF-8, but %1%s was detected. +RSC_029=Data URL is not allowed in this context. #Scripting SCP_001=Use of Javascript eval() function in EPUB scripts is a security risk. diff --git a/src/test/resources/epub3/00-minimal/minimal.feature b/src/test/resources/epub3/00-minimal/minimal.feature index 0f853039f..4a37141ba 100644 --- a/src/test/resources/epub3/00-minimal/minimal.feature +++ b/src/test/resources/epub3/00-minimal/minimal.feature @@ -20,7 +20,7 @@ Scenario: Verify a minimal package document When checking file 'minimal.opf' Then no errors or warnings are reported - + # FIXME the current API doesn’t allow the version to be explicitly set # PKG-001 should either be removed, or made a fatal error diff --git a/src/test/resources/epub3/03-resources/files/data-url-in-html-a-href-error.xhtml b/src/test/resources/epub3/03-resources/files/data-url-in-html-a-href-error.xhtml new file mode 100644 index 000000000..2236506de --- /dev/null +++ b/src/test/resources/epub3/03-resources/files/data-url-in-html-a-href-error.xhtml @@ -0,0 +1,11 @@ + + + + + Test + + +

Test

+ data URL link + + diff --git a/src/test/resources/epub3/03-resources/files/data-url-in-html-area-href-error.xhtml b/src/test/resources/epub3/03-resources/files/data-url-in-html-area-href-error.xhtml new file mode 100644 index 000000000..e49c1202f --- /dev/null +++ b/src/test/resources/epub3/03-resources/files/data-url-in-html-area-href-error.xhtml @@ -0,0 +1,14 @@ + + + + + Test + + +

Test

+ example shape + + example area + + + diff --git a/src/test/resources/epub3/03-resources/files/data-url-in-html-img-cmt-valid/EPUB/content_001.xhtml b/src/test/resources/epub3/03-resources/files/data-url-in-html-img-cmt-valid/EPUB/content_001.xhtml new file mode 100644 index 000000000..da975b1bd --- /dev/null +++ b/src/test/resources/epub3/03-resources/files/data-url-in-html-img-cmt-valid/EPUB/content_001.xhtml @@ -0,0 +1,40 @@ + + + + + Minimal EPUB + + +

Loomings

+

Call me Ishmael.

+ example image + + diff --git a/src/test/resources/epub3/03-resources/files/data-url-in-html-img-cmt-valid/EPUB/nav.xhtml b/src/test/resources/epub3/03-resources/files/data-url-in-html-img-cmt-valid/EPUB/nav.xhtml new file mode 100644 index 000000000..240745e63 --- /dev/null +++ b/src/test/resources/epub3/03-resources/files/data-url-in-html-img-cmt-valid/EPUB/nav.xhtml @@ -0,0 +1,14 @@ + + + + + Minimal Nav + + + + + diff --git a/src/test/resources/epub3/03-resources/files/data-url-in-html-img-cmt-valid/EPUB/package.opf b/src/test/resources/epub3/03-resources/files/data-url-in-html-img-cmt-valid/EPUB/package.opf new file mode 100644 index 000000000..0d1eec6e9 --- /dev/null +++ b/src/test/resources/epub3/03-resources/files/data-url-in-html-img-cmt-valid/EPUB/package.opf @@ -0,0 +1,16 @@ + + + + Minimal EPUB 3.0 + en + NOID + 2017-06-14T00:00:01Z + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/epub3/03-resources/files/data-url-in-html-img-cmt-valid/META-INF/container.xml b/src/test/resources/epub3/03-resources/files/data-url-in-html-img-cmt-valid/META-INF/container.xml new file mode 100644 index 000000000..318782179 --- /dev/null +++ b/src/test/resources/epub3/03-resources/files/data-url-in-html-img-cmt-valid/META-INF/container.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/test/resources/epub3/03-resources/files/data-url-in-html-img-cmt-valid/mimetype b/src/test/resources/epub3/03-resources/files/data-url-in-html-img-cmt-valid/mimetype new file mode 100644 index 000000000..57ef03f24 --- /dev/null +++ b/src/test/resources/epub3/03-resources/files/data-url-in-html-img-cmt-valid/mimetype @@ -0,0 +1 @@ +application/epub+zip \ No newline at end of file diff --git a/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-intrinsic-fallback-valid/EPUB/content_001.xhtml b/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-intrinsic-fallback-valid/EPUB/content_001.xhtml new file mode 100644 index 000000000..e1b7ebdb7 --- /dev/null +++ b/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-intrinsic-fallback-valid/EPUB/content_001.xhtml @@ -0,0 +1,40 @@ + + + + + Minimal EPUB + + + + + the whale + + + diff --git a/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-intrinsic-fallback-valid/EPUB/image.jpeg b/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-intrinsic-fallback-valid/EPUB/image.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..ff336a51f6dead6a18ee333037587f44c8f1c62f GIT binary patch literal 1962 zcmZ9NX;jh)7smgrBBim55Q!@iBBDT6Clzx+6Bk^{$QCsbt$AGrM$%~^v@Em2HOF#I zkrdNRGt1Ocb1coYM$M_5#$MOAGOgyc_|lnk=EL0k&d zAOHaA8(4V;r~pGa9E(BYa3~xBkHce$M4|}}PcbninVFgpC?;m>$d(k^dJ4tTY%Rge z(azF|%d)dGH)C@h);rrU?CBswB+_V=5owJviD_YG#bq&h4i0u)t{s=fVRG%*4m=i* z!?Rs>mF%j9sr&#aXW0F3}P0$C7{1pr2aAZXCaJ%9`VUa4?_+NA|1PTMe^_~qH0E0mgFdXvFWj!4Y#jvsFFbh4r!O`j1$~dqZ z0@BBYpaEAPr4xz*;R}>+xi)>W*-5f`eMO;ZN;l2Z5Zor&P-xFz^Fw4q^yjuQ6Y0R0_{miCI`)s)3+3(>g6w7X*V>NZm(`?Jf7(h zuZm6a_)rJWk3E5^=&;2uq%XHju@0Mm@-h+*+-&Js>|4kVyq9;u*qkAZEk)>Bdw0|~ z5TMt%)U+bm6n0xFQcdq^P3bwgjV;Wl<)$hvErz3u3{U|;!{=Vv4WNc?73aSd?|q4}s~hoh-|=Yi0U>W~exg0~jnDAaGyHk>(NTXTf{KhEO5H#I z;bwRqI@sf~g*IrAx;a~78!CK34jT`T8<6_3%X5v_wtsHBjgt&)NcFstG31`0O^29| zGla5{KcxeNu%BDvcAV-cd)#07I{mmRX;br&mSRxrO)(+Apdv@|sX$hSi(Pl`uZbs< zSl-Qqu}{J3I2hbrnTwmO^t&kz%qb}s=`vp}`=2vh3N3HDHh(g8-=f*;ujS)Mq7Mf} z+)=fgW{rL=&Bya5@f89g?+^kz^oxtV({sZr0bd8^}rxS?@Fx4^9A+Le2Bbuz;k6q5CK6Y6wdndn@EnZwW zTog6CY$6OYxul?1X=Kq2onnPOuIUfKP9W$xJoJ^Yw@HL2=mx9jdk;ws2=ovB6~{K& z%578bK9(z*19vH;2MtYY;tV0Tx|-iFXqXYO{Gp$_W$q(N{u22~#1bWABTev8#v8?z zKCBv({;FEa^y%`y7#(+MHaqaCs(XA4`k=o&=1D?@gYJw~6o(6(f8sVDpk4bGgqFDGpasKR^GXlQ`8u5gbOPe_|IF?$H>K5vF4z4wLg02WpBS_gA+vUEyvE5w~)Y$ zXTpHJnV1a`0geUa9Z)qgw94xO8mAe(&XUksm7a9Q<|Jv|>4Qz2T83|~$lZ-vRHBNi zHO0J|7Cb^#^tn!>_jW%>yVT(Bjq<%6g4Q<9=Q1^e?rL(s*X%Y37`Cr=hR26nV`)utL%uobOB7^$qHB}aA$!C^JY)I1PPAlGcpy9}* z05V3ItIL{IZMR+2PLZu?ck%M}K}lWslUB}5T&UGv11C3F!iMtX=e2*`1(s`Ke8?$F$qkT$p3 tR2BM$*=lqAV;Ul(=0!E9u8+;$x3y;KABX3?E(G-`f~o|lN(5wO?Em1hBGLc= literal 0 HcmV?d00001 diff --git a/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-intrinsic-fallback-valid/EPUB/nav.xhtml b/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-intrinsic-fallback-valid/EPUB/nav.xhtml new file mode 100644 index 000000000..240745e63 --- /dev/null +++ b/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-intrinsic-fallback-valid/EPUB/nav.xhtml @@ -0,0 +1,14 @@ + + + + + Minimal Nav + + + + + diff --git a/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-intrinsic-fallback-valid/EPUB/package.opf b/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-intrinsic-fallback-valid/EPUB/package.opf new file mode 100644 index 000000000..373e44db3 --- /dev/null +++ b/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-intrinsic-fallback-valid/EPUB/package.opf @@ -0,0 +1,17 @@ + + + + Minimal EPUB 3.0 + en + NOID + 2017-06-14T00:00:01Z + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-intrinsic-fallback-valid/META-INF/container.xml b/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-intrinsic-fallback-valid/META-INF/container.xml new file mode 100644 index 000000000..318782179 --- /dev/null +++ b/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-intrinsic-fallback-valid/META-INF/container.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-intrinsic-fallback-valid/mimetype b/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-intrinsic-fallback-valid/mimetype new file mode 100644 index 000000000..57ef03f24 --- /dev/null +++ b/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-intrinsic-fallback-valid/mimetype @@ -0,0 +1 @@ +application/epub+zip \ No newline at end of file diff --git a/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-manifest-fallback-valid/EPUB/content_001.xhtml b/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-manifest-fallback-valid/EPUB/content_001.xhtml new file mode 100644 index 000000000..6d1175045 --- /dev/null +++ b/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-manifest-fallback-valid/EPUB/content_001.xhtml @@ -0,0 +1,37 @@ + + + + + Minimal EPUB + + + the whale + + diff --git a/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-manifest-fallback-valid/EPUB/image.jpeg b/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-manifest-fallback-valid/EPUB/image.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..ff336a51f6dead6a18ee333037587f44c8f1c62f GIT binary patch literal 1962 zcmZ9NX;jh)7smgrBBim55Q!@iBBDT6Clzx+6Bk^{$QCsbt$AGrM$%~^v@Em2HOF#I zkrdNRGt1Ocb1coYM$M_5#$MOAGOgyc_|lnk=EL0k&d zAOHaA8(4V;r~pGa9E(BYa3~xBkHce$M4|}}PcbninVFgpC?;m>$d(k^dJ4tTY%Rge z(azF|%d)dGH)C@h);rrU?CBswB+_V=5owJviD_YG#bq&h4i0u)t{s=fVRG%*4m=i* z!?Rs>mF%j9sr&#aXW0F3}P0$C7{1pr2aAZXCaJ%9`VUa4?_+NA|1PTMe^_~qH0E0mgFdXvFWj!4Y#jvsFFbh4r!O`j1$~dqZ z0@BBYpaEAPr4xz*;R}>+xi)>W*-5f`eMO;ZN;l2Z5Zor&P-xFz^Fw4q^yjuQ6Y0R0_{miCI`)s)3+3(>g6w7X*V>NZm(`?Jf7(h zuZm6a_)rJWk3E5^=&;2uq%XHju@0Mm@-h+*+-&Js>|4kVyq9;u*qkAZEk)>Bdw0|~ z5TMt%)U+bm6n0xFQcdq^P3bwgjV;Wl<)$hvErz3u3{U|;!{=Vv4WNc?73aSd?|q4}s~hoh-|=Yi0U>W~exg0~jnDAaGyHk>(NTXTf{KhEO5H#I z;bwRqI@sf~g*IrAx;a~78!CK34jT`T8<6_3%X5v_wtsHBjgt&)NcFstG31`0O^29| zGla5{KcxeNu%BDvcAV-cd)#07I{mmRX;br&mSRxrO)(+Apdv@|sX$hSi(Pl`uZbs< zSl-Qqu}{J3I2hbrnTwmO^t&kz%qb}s=`vp}`=2vh3N3HDHh(g8-=f*;ujS)Mq7Mf} z+)=fgW{rL=&Bya5@f89g?+^kz^oxtV({sZr0bd8^}rxS?@Fx4^9A+Le2Bbuz;k6q5CK6Y6wdndn@EnZwW zTog6CY$6OYxul?1X=Kq2onnPOuIUfKP9W$xJoJ^Yw@HL2=mx9jdk;ws2=ovB6~{K& z%578bK9(z*19vH;2MtYY;tV0Tx|-iFXqXYO{Gp$_W$q(N{u22~#1bWABTev8#v8?z zKCBv({;FEa^y%`y7#(+MHaqaCs(XA4`k=o&=1D?@gYJw~6o(6(f8sVDpk4bGgqFDGpasKR^GXlQ`8u5gbOPe_|IF?$H>K5vF4z4wLg02WpBS_gA+vUEyvE5w~)Y$ zXTpHJnV1a`0geUa9Z)qgw94xO8mAe(&XUksm7a9Q<|Jv|>4Qz2T83|~$lZ-vRHBNi zHO0J|7Cb^#^tn!>_jW%>yVT(Bjq<%6g4Q<9=Q1^e?rL(s*X%Y37`Cr=hR26nV`)utL%uobOB7^$qHB}aA$!C^JY)I1PPAlGcpy9}* z05V3ItIL{IZMR+2PLZu?ck%M}K}lWslUB}5T&UGv11C3F!iMtX=e2*`1(s`Ke8?$F$qkT$p3 tR2BM$*=lqAV;Ul(=0!E9u8+;$x3y;KABX3?E(G-`f~o|lN(5wO?Em1hBGLc= literal 0 HcmV?d00001 diff --git a/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-manifest-fallback-valid/EPUB/nav.xhtml b/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-manifest-fallback-valid/EPUB/nav.xhtml new file mode 100644 index 000000000..240745e63 --- /dev/null +++ b/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-manifest-fallback-valid/EPUB/nav.xhtml @@ -0,0 +1,14 @@ + + + + + Minimal Nav + + + + + diff --git a/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-manifest-fallback-valid/EPUB/package.opf b/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-manifest-fallback-valid/EPUB/package.opf new file mode 100644 index 000000000..2a1ff0420 --- /dev/null +++ b/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-manifest-fallback-valid/EPUB/package.opf @@ -0,0 +1,45 @@ + + + + Minimal EPUB 3.0 + en + NOID + 2017-06-14T00:00:01Z + + + + + + + + + + + diff --git a/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-manifest-fallback-valid/META-INF/container.xml b/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-manifest-fallback-valid/META-INF/container.xml new file mode 100644 index 000000000..318782179 --- /dev/null +++ b/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-manifest-fallback-valid/META-INF/container.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-manifest-fallback-valid/mimetype b/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-manifest-fallback-valid/mimetype new file mode 100644 index 000000000..57ef03f24 --- /dev/null +++ b/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-manifest-fallback-valid/mimetype @@ -0,0 +1 @@ +application/epub+zip \ No newline at end of file diff --git a/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-no-fallback-error/EPUB/content_001.xhtml b/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-no-fallback-error/EPUB/content_001.xhtml new file mode 100644 index 000000000..6d1175045 --- /dev/null +++ b/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-no-fallback-error/EPUB/content_001.xhtml @@ -0,0 +1,37 @@ + + + + + Minimal EPUB + + + the whale + + diff --git a/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-no-fallback-error/EPUB/nav.xhtml b/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-no-fallback-error/EPUB/nav.xhtml new file mode 100644 index 000000000..240745e63 --- /dev/null +++ b/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-no-fallback-error/EPUB/nav.xhtml @@ -0,0 +1,14 @@ + + + + + Minimal Nav + + + + + diff --git a/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-no-fallback-error/EPUB/package.opf b/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-no-fallback-error/EPUB/package.opf new file mode 100644 index 000000000..da51b66a6 --- /dev/null +++ b/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-no-fallback-error/EPUB/package.opf @@ -0,0 +1,16 @@ + + + + Minimal EPUB 3.0 + en + NOID + 2017-06-14T00:00:01Z + + + + + + + + + diff --git a/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-no-fallback-error/META-INF/container.xml b/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-no-fallback-error/META-INF/container.xml new file mode 100644 index 000000000..318782179 --- /dev/null +++ b/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-no-fallback-error/META-INF/container.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-no-fallback-error/mimetype b/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-no-fallback-error/mimetype new file mode 100644 index 000000000..57ef03f24 --- /dev/null +++ b/src/test/resources/epub3/03-resources/files/data-url-in-html-img-foreign-no-fallback-error/mimetype @@ -0,0 +1 @@ +application/epub+zip \ No newline at end of file diff --git a/src/test/resources/epub3/03-resources/files/data-url-in-html-link-exempt-valid/EPUB/content_001.xhtml b/src/test/resources/epub3/03-resources/files/data-url-in-html-link-exempt-valid/EPUB/content_001.xhtml new file mode 100644 index 000000000..448fec2ef --- /dev/null +++ b/src/test/resources/epub3/03-resources/files/data-url-in-html-link-exempt-valid/EPUB/content_001.xhtml @@ -0,0 +1,40 @@ + + + + + Minimal EPUB + + + +

Loomings

+

Call me Ishmael.

+ + diff --git a/src/test/resources/epub3/03-resources/files/data-url-in-html-link-exempt-valid/EPUB/nav.xhtml b/src/test/resources/epub3/03-resources/files/data-url-in-html-link-exempt-valid/EPUB/nav.xhtml new file mode 100644 index 000000000..240745e63 --- /dev/null +++ b/src/test/resources/epub3/03-resources/files/data-url-in-html-link-exempt-valid/EPUB/nav.xhtml @@ -0,0 +1,14 @@ + + + + + Minimal Nav + + + + + diff --git a/src/test/resources/epub3/03-resources/files/data-url-in-html-link-exempt-valid/EPUB/package.opf b/src/test/resources/epub3/03-resources/files/data-url-in-html-link-exempt-valid/EPUB/package.opf new file mode 100644 index 000000000..0d1eec6e9 --- /dev/null +++ b/src/test/resources/epub3/03-resources/files/data-url-in-html-link-exempt-valid/EPUB/package.opf @@ -0,0 +1,16 @@ + + + + Minimal EPUB 3.0 + en + NOID + 2017-06-14T00:00:01Z + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/epub3/03-resources/files/data-url-in-html-link-exempt-valid/META-INF/container.xml b/src/test/resources/epub3/03-resources/files/data-url-in-html-link-exempt-valid/META-INF/container.xml new file mode 100644 index 000000000..318782179 --- /dev/null +++ b/src/test/resources/epub3/03-resources/files/data-url-in-html-link-exempt-valid/META-INF/container.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/test/resources/epub3/03-resources/files/data-url-in-html-link-exempt-valid/mimetype b/src/test/resources/epub3/03-resources/files/data-url-in-html-link-exempt-valid/mimetype new file mode 100644 index 000000000..57ef03f24 --- /dev/null +++ b/src/test/resources/epub3/03-resources/files/data-url-in-html-link-exempt-valid/mimetype @@ -0,0 +1 @@ +application/epub+zip \ No newline at end of file diff --git a/src/test/resources/epub3/03-resources/files/data-url-in-manifest-item-in-spine-error.opf b/src/test/resources/epub3/03-resources/files/data-url-in-manifest-item-in-spine-error.opf new file mode 100644 index 000000000..648b177f8 --- /dev/null +++ b/src/test/resources/epub3/03-resources/files/data-url-in-manifest-item-in-spine-error.opf @@ -0,0 +1,19 @@ + + + + Minimal EPUB 3.0 + en + NOID + 2017-06-14T00:00:01Z + + + + + + + + + + + + diff --git a/src/test/resources/epub3/03-resources/files/data-url-in-manifest-item-valid.opf b/src/test/resources/epub3/03-resources/files/data-url-in-manifest-item-valid.opf new file mode 100644 index 000000000..1ff70673a --- /dev/null +++ b/src/test/resources/epub3/03-resources/files/data-url-in-manifest-item-valid.opf @@ -0,0 +1,47 @@ + + + + Minimal EPUB 3.0 + en + NOID + 2017-06-14T00:00:01Z + + + + + + + + + + + diff --git a/src/test/resources/epub3/03-resources/files/data-url-in-svg-a-href-error.xhtml b/src/test/resources/epub3/03-resources/files/data-url-in-svg-a-href-error.xhtml new file mode 100644 index 000000000..15debbebc --- /dev/null +++ b/src/test/resources/epub3/03-resources/files/data-url-in-svg-a-href-error.xhtml @@ -0,0 +1,15 @@ + + + + + Test + + +

Test

+ + Example + + + + diff --git a/src/test/resources/epub3/03-resources/resources.feature b/src/test/resources/epub3/03-resources/resources.feature index 501f065a2..105f4efb2 100644 --- a/src/test/resources/epub3/03-resources/resources.feature +++ b/src/test/resources/epub3/03-resources/resources.feature @@ -385,6 +385,62 @@ Then error RSC-006 is reported And no other errors or warnings are reported + ## 3.7 Data URLs + + @spec @xref:sec-data-urls + Scenario: Allow a data URL in a manifest item not in the spine + When checking file 'data-url-in-manifest-item-valid.opf' + Then no errors or warnings are reported + + @spec @xref:sec-data-urls + Scenario: Report a data URL in a manifest item referenced in the spine + When checking file 'data-url-in-manifest-item-in-spine-error.opf' + Then error RSC-029 is reported + And no other errors or warnings are reported + + @spec @xref:sec-data-urls + Scenario: Report a data URL in the `href` attribute of an HTML `a` element + When checking file 'data-url-in-html-a-href-error.xhtml' + Then error RSC-029 is reported + And no other errors or warnings are reported + + @spec @xref:sec-data-urls + Scenario: Report a data URL in the `href` attribute of an SVG `a` element + When checking file 'data-url-in-svg-a-href-error.xhtml' + Then error RSC-029 is reported + And no other errors or warnings are reported + + @spec @xref:sec-data-urls + Scenario: Report a data URL in the `href` attribute of an HTML `area` element + When checking file 'data-url-in-html-area-href-error.xhtml' + Then error RSC-029 is reported + And no other errors or warnings are reported + + @spec @xref:sec-data-urls + Scenario: Allow a data URL defining an exmpt resource (in an HTML `link` element) + When checking EPUB 'data-url-in-html-link-exempt-valid' + And no errors or warnings are reported + + @spec @xref:sec-data-urls + Scenario: Allow a data URL defining a CMT resource (in an HTML `img` element) + When checking EPUB 'data-url-in-html-img-cmt-valid' + And no errors or warnings are reported + + @spec @xref:sec-data-urls + Scenario: Allow a data URL defining a foreign resource with intrinsic fallback (in an HTML `img` element) + When checking EPUB 'data-url-in-html-img-foreign-intrinsic-fallback-valid' + And no errors or warnings are reported + + @spec @xref:sec-data-urls + Scenario: Allow a data URL defining a foreign resource with a manifest fallback (in an HTML `img` element) + When checking EPUB 'data-url-in-html-img-foreign-manifest-fallback-valid' + And no errors or warnings are reported + + @spec @xref:sec-data-urls + Scenario: Report a data URL defining a foreign resource with no fallback (in an HTML `img` element) + When checking EPUB 'data-url-in-html-img-foreign-no-fallback-error' + Then error MED-003 is reported + And no other errors or warnings are reported ## 3.9 XML conformance